From 66f05868ad6dcba65a4494fd26d35d9207650974 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Mon, 10 Jan 2022 13:58:23 -0800 Subject: [PATCH 001/872] Doh....update the date in the Changelog Signed-off-by: Stephen Curran --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c55be3474..04faab87df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # 0.7.3 -## December 22, 2021 +## January 10, 2022 This release includes some new AIP 2.0 features out (Revocation Notification and Discover Features 2.0), a major new feature for those using Indy ledger (multi-ledger support), From 388ae2346ff92747828192e210bab48fd3b1554b Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Mon, 10 Jan 2022 15:25:59 -0800 Subject: [PATCH 002/872] Added missed new module -- upgrade Signed-off-by: Stephen Curran --- docs/generated/aries_cloudagent.commands.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/generated/aries_cloudagent.commands.rst b/docs/generated/aries_cloudagent.commands.rst index f7051c0780..f4353c5984 100644 --- a/docs/generated/aries_cloudagent.commands.rst +++ b/docs/generated/aries_cloudagent.commands.rst @@ -32,3 +32,11 @@ aries\_cloudagent.commands.start module :members: :undoc-members: :show-inheritance: + +aries\_cloudagent.commands.upgrade module +----------------------------------------- + +.. automodule:: aries_cloudagent.commands.upgrade + :members: + :undoc-members: + :show-inheritance: From 00954b357d9a4314eb6b4a71124c0f952c78c22b Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Mon, 10 Jan 2022 15:44:30 -0800 Subject: [PATCH 003/872] Update the publishing steps Signed-off-by: Stephen Curran --- PUBLISHING.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/PUBLISHING.md b/PUBLISHING.md index 9f97266bad..8846d840aa 100644 --- a/PUBLISHING.md +++ b/PUBLISHING.md @@ -4,11 +4,11 @@ The code to be published should be in the `main` branch. Make sure that all the merged, and decide on the release tag. Should it be a release candidate or the final tag, and should it be a major, minor or patch release, per [semver](https://semver.org/) rules. -Once ready to do a release, create a PR that includes the following updates: +Once ready to do a release, create a local branch that includes the following updates: -1. Update the CHANGELOG.md to add the new release. If transitioning for a Release Candidate to the final release for the tag, do not create a new section -- just drop the "RC" designation. +1. Update the CHANGELOG.md to add the new release. If transitioning for a Release Candidate to the final release for the tag, do not create a new section -- just drop the "RC" designation. Check the date of the new release. -2. include details of the closed PRs included in this release. General process to follow: +2. Include details of the merged PRs included in this release. General process to follow: - Gather the set of PRs since the last release and put them into a list. - An example query to use to get the list of PRs is: [https://github.com/hyperledger/aries-cloudagent-python/pulls?q=is%3Apr+is%3Amerged+sort%3Aupdated+merged%3A%3E2021-11-15](https://github.com/hyperledger/aries-cloudagent-python/pulls?q=is%3Apr+is%3Amerged+sort%3Aupdated+merged%3A%3E2021-11-15), where the date at the end is the date of the previous release. @@ -19,12 +19,14 @@ Once ready to do a release, create a PR that includes the following updates: 3. Update the ReadTheDocs in the `/docs` folder by following the instructions in the `docs/README.md` file. That will likely add a number of new and modified files to the PR. Eliminate all of the errors in the generation process, either by mocking external dependencies or by fixing ACA-Py code. If necessary, create an issue with the errors and assign it to the appropriate developer. Experience has demonstrated to use that documentation generation errors should be fixed in the code. -4. Update the version number listed in [aries_cloudagent/version.py](aries_cloudagent/version.py) and, prefixed with a "v" in [open-api/openapi.json](open-api/openapi.json) (e.g. "v0.7.2" in the openapi.json file, and "0.7.2" in the version.py file). The incremented version number should adhere to the [Semantic Versioning Specification](https://semver.org/#semantic-versioning-specification-semver) based on the changes since the last published release. For Release Candidates, the form of the tag is "0.7.2-rc0". +4. Update the version number listed in [aries_cloudagent/version.py](aries_cloudagent/version.py) and, prefixed with a "v" in [open-api/openapi.json](open-api/openapi.json) (e.g. "0.7.2" in the version.py file and "v0.7.2" in the openapi.json file). The incremented version number should adhere to the [Semantic Versioning Specification](https://semver.org/#semantic-versioning-specification-semver) based on the changes since the last published release. For Release Candidates, the form of the tag is "0.7.2-rc0". 5. An extra search of the repo for the existing tag is recommended to see if there are any other instances of the tag in the repo. If any are found to be required, finding a way to not need them is best, but if they are needed, please update this document to note where the tag can be found. -6. Create a new GitHub release. The tag name and title of the release should be the same as the version in [aries_cloudagent/version.py](aries_cloudagent/version.py). Include the additions to CHANGELOG.md in the release notes. +6. Double check all of these steps above, and then create a PR from the branch. If there are still further changes to be merged, mark the PR as "Draft", repeat **ALL** of the steps again, and then mark this PR as ready. -7. Publish a new docker container on Docker Hub ([bcgovimages/aries-cloudagent](https://hub.docker.com/r/bcgovimages/aries-cloudagent/)) by following the instructions to create a PR for the release in the repository [https://github.com/bcgov/aries-cloudagent-container](https://github.com/bcgov/aries-cloudagent-container). Appropriate permissions are required to publish the image. +7. Create a new GitHub tag representing the version. The tag name and title of the release should be the same as the version in [aries_cloudagent/version.py](aries_cloudagent/version.py). Use the "Generate Release Notes" capability to get a sequential listing of the PRs in the release, to complement the manually created Changelog. Verify on PyPi that the version is published. -8. Update the ACA-Py Read The Docs site by building the new "latest" (main branch) and activating and building the new release. Appropriate permissions are required to publish the new documentation version. +8. Publish a new docker container on Docker Hub ([bcgovimages/aries-cloudagent](https://hub.docker.com/r/bcgovimages/aries-cloudagent/)) by following the README.md instructions to create a PR for the release in the repository [https://github.com/bcgov/aries-cloudagent-container](https://github.com/bcgov/aries-cloudagent-container). Appropriate permissions are required to publish the image. + +9. Update the ACA-Py Read The Docs site by building the new "latest" (main branch) and activating and building the new release. Appropriate permissions are required to publish the new documentation version. From 61039671cb7b137ddc240810204ff0abdf01f528 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Thu, 13 Jan 2022 15:20:42 -0800 Subject: [PATCH 004/872] update pthid Signed-off-by: Shaanjot Gill --- .../protocols/didexchange/v1_0/manager.py | 27 +++++++++++---- .../didexchange/v1_0/tests/test_manager.py | 34 +++++++++++++++++++ 2 files changed, 54 insertions(+), 7 deletions(-) diff --git a/aries_cloudagent/protocols/didexchange/v1_0/manager.py b/aries_cloudagent/protocols/didexchange/v1_0/manager.py index 282bbb8335..0a7eadde7f 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/manager.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/manager.py @@ -2,6 +2,9 @@ import json import logging +import pydid + +from pydid import BaseDIDDocument as ResolvedDocument from ....connections.models.conn_record import ConnRecord from ....connections.models.diddoc import DIDDoc @@ -12,6 +15,8 @@ from ....messaging.decorators.attach_decorator import AttachDecorator from ....messaging.responder import BaseResponder from ....multitenant.base import BaseMultitenantManager +from ....resolver.base import ResolverError +from ....resolver.did_resolver import DIDResolver from ....storage.error import StorageNotFoundError from ....transport.inbound.receipt import MessageReceipt from ....wallet.base import BaseWallet @@ -296,14 +301,22 @@ async def create_request( filter(None, [base_mediation_record, mediation_record]) ), ) - if ( - conn_rec.their_public_did is not None - and conn_rec.their_public_did.startswith("did:") - ): + if conn_rec.their_public_did is not None: qualified_did = conn_rec.their_public_did - else: - qualified_did = f"did:sov:{conn_rec.their_public_did}" - pthid = conn_rec.invitation_msg_id or qualified_did + if not conn_rec.their_public_did.startswith("did:"): + qualified_did = f"did:sov:{conn_rec.their_public_did}" + resolver = self._profile.inject(DIDResolver) + try: + doc_dict: dict = await resolver.resolve(self._profile, qualified_did) + doc: ResolvedDocument = pydid.deserialize_document( + doc_dict, strict=True + ) + did_url = doc.service[0].id + except ResolverError as error: + raise DIDXManagerError( + "Failed to resolve public DID in invitation" + ) from error + pthid = conn_rec.invitation_msg_id or did_url attach = AttachDecorator.data_base64(did_doc.serialize()) async with self.profile.session() as session: wallet = session.inject(BaseWallet) diff --git a/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py index aee31fa30b..41f18473f8 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py @@ -1,6 +1,7 @@ import json from asynctest import mock as async_mock, TestCase as AsyncTestCase +from pydid import DIDDocument from .....cache.base import BaseCache from .....cache.in_memory import InMemoryCache @@ -18,6 +19,9 @@ from .....messaging.decorators.attach_decorator import AttachDecorator from .....multitenant.base import BaseMultitenantManager from .....multitenant.manager import MultitenantManager +from .....resolver.base import ResolverError +from .....resolver.did_resolver import DIDResolver +from .....resolver.tests import DOC from .....storage.error import StorageNotFoundError from .....transport.inbound.receipt import MessageReceipt from .....wallet.did_info import DIDInfo @@ -102,6 +106,10 @@ async def setUp(self): return_value=TestConfig.test_endpoint ) self.context.injector.bind_instance(BaseLedger, self.ledger) + self.resolver = async_mock.MagicMock() + did_doc = DIDDocument.deserialize(DOC) + self.resolver.resolve = async_mock.CoroutineMock(return_value=did_doc) + self.context.injector.bind_instance(DIDResolver, self.resolver) self.multitenant_mgr = async_mock.MagicMock(MultitenantManager, autospec=True) self.context.injector.bind_instance( @@ -266,6 +274,29 @@ async def test_create_request_implicit_use_public_did(self): assert info_public.did == conn_rec.my_did + async def test_create_request_implicit_resolver_error(self): + async with self.profile.session() as session: + await session.wallet.create_public_did( + DIDMethod.SOV, + KeyType.ED25519, + ) + with async_mock.patch.object( + self.resolver, + "resolve", + async_mock.CoroutineMock(side_effect=ResolverError()), + ): + with self.assertRaises(DIDXManagerError) as ctx: + conn_rec = await self.manager.create_request_implicit( + their_public_did=TestConfig.test_target_did, + my_label=None, + my_endpoint=None, + use_public_did=True, + alias="Tester", + ) + assert "Failed to resolve public DID in invitation" in str( + ctx.exception + ) + async def test_create_request_implicit_no_public_did(self): with self.assertRaises(WalletError) as context: await self.manager.create_request_implicit( @@ -292,6 +323,7 @@ async def test_create_request(self): ) ), save=async_mock.CoroutineMock(), + their_public_did=None, ) with async_mock.patch.object( @@ -345,6 +377,7 @@ async def test_create_request_multitenant(self): ) ), save=async_mock.CoroutineMock(), + their_public_did=None, ) ) @@ -419,6 +452,7 @@ async def test_create_request_my_endpoint(self): ) ), save=async_mock.CoroutineMock(), + their_public_did=None, ) with async_mock.patch.object( From dcb5cfdc4515d7b2133f9c5304f4124ed6e8e4de Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Thu, 13 Jan 2022 15:26:18 -0800 Subject: [PATCH 005/872] format fix Signed-off-by: Shaanjot Gill --- .../protocols/didexchange/v1_0/tests/test_manager.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py index 41f18473f8..3f74a37339 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py @@ -293,9 +293,7 @@ async def test_create_request_implicit_resolver_error(self): use_public_did=True, alias="Tester", ) - assert "Failed to resolve public DID in invitation" in str( - ctx.exception - ) + assert "Failed to resolve public DID in invitation" in str(ctx.exception) async def test_create_request_implicit_no_public_did(self): with self.assertRaises(WalletError) as context: From 61b9413cb5708de359c1bb08032696eec1a457aa Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Mon, 17 Jan 2022 16:07:59 -0800 Subject: [PATCH 006/872] DID updates for endorser Signed-off-by: Ian Costanzo --- aries_cloudagent/ledger/base.py | 10 +- aries_cloudagent/ledger/indy.py | 18 ++- aries_cloudagent/ledger/indy_vdr.py | 18 ++- aries_cloudagent/ledger/routes.py | 109 ++++++++++++++++++- aries_cloudagent/ledger/tests/test_routes.py | 9 +- 5 files changed, 143 insertions(+), 21 deletions(-) diff --git a/aries_cloudagent/ledger/base.py b/aries_cloudagent/ledger/base.py index 3ceaa7e1f3..68f91992ee 100644 --- a/aries_cloudagent/ledger/base.py +++ b/aries_cloudagent/ledger/base.py @@ -86,8 +86,14 @@ async def update_endpoint_for_did( @abstractmethod async def register_nym( - self, did: str, verkey: str, alias: str = None, role: str = None - ): + self, + did: str, + verkey: str, + alias: str = None, + role: str = None, + write_ledger: bool = True, + endorser_did: str = None, + ) -> Tuple[bool, dict]: """ Register a nym on the ledger. diff --git a/aries_cloudagent/ledger/indy.py b/aries_cloudagent/ledger/indy.py index 21d334a64f..2472354e52 100644 --- a/aries_cloudagent/ledger/indy.py +++ b/aries_cloudagent/ledger/indy.py @@ -928,8 +928,14 @@ async def update_endpoint_for_did( return False async def register_nym( - self, did: str, verkey: str, alias: str = None, role: str = None - ): + self, + did: str, + verkey: str, + alias: str = None, + role: str = None, + write_ledger: bool = True, + endorser_did: str = None, + ) -> Tuple[bool, dict]: """ Register a nym on the ledger. @@ -957,10 +963,11 @@ async def register_nym( request_json = await indy.ledger.build_nym_request( public_info.did, did, verkey, alias, role ) - await self._submit( - request_json + resp = await self._submit( + request_json, sign=True, sign_did=public_info, write_ledger=write_ledger ) # let ledger raise on insufficient privilege - + if not write_ledger: + return True, {"signed_txn": resp} try: did_info = await wallet.get_local_did(did) except WalletNotFoundError: @@ -968,6 +975,7 @@ async def register_nym( else: metadata = {**did_info.metadata, **DIDPosture.POSTED.metadata} await wallet.replace_local_did_metadata(did, metadata) + return True, None async def get_nym_role(self, did: str) -> Role: """ diff --git a/aries_cloudagent/ledger/indy_vdr.py b/aries_cloudagent/ledger/indy_vdr.py index d8e8910bc1..d8dd2ce15c 100644 --- a/aries_cloudagent/ledger/indy_vdr.py +++ b/aries_cloudagent/ledger/indy_vdr.py @@ -938,8 +938,14 @@ async def update_endpoint_for_did( return False async def register_nym( - self, did: str, verkey: str, alias: str = None, role: str = None - ): + self, + did: str, + verkey: str, + alias: str = None, + role: str = None, + write_ledger: bool = True, + endorser_did: str = None, + ) -> Tuple[bool, dict]: """ Register a nym on the ledger. @@ -965,8 +971,11 @@ async def register_nym( except VdrError as err: raise LedgerError("Exception when building nym request") from err - await self._submit(nym_req, sign=True, sign_did=public_info) - + resp = await self._submit( + nym_req, sign=True, sign_did=public_info, write_ledger=write_ledger + ) + if not write_ledger: + return True, {"signed_txn": resp} async with self.profile.session() as session: wallet = session.inject(BaseWallet) try: @@ -976,6 +985,7 @@ async def register_nym( else: metadata = {**did_info.metadata, **DIDPosture.POSTED.metadata} await wallet.replace_local_did_metadata(did, metadata) + return True, None async def get_nym_role(self, did: str) -> Role: """ diff --git a/aries_cloudagent/ledger/routes.py b/aries_cloudagent/ledger/routes.py index dda91013fd..1a12b43ef9 100644 --- a/aries_cloudagent/ledger/routes.py +++ b/aries_cloudagent/ledger/routes.py @@ -1,10 +1,14 @@ """Ledger admin routes.""" +import json + from aiohttp import web from aiohttp_apispec import docs, querystring_schema, request_schema, response_schema from marshmallow import fields, validate from ..admin.request_context import AdminRequestContext +from ..connections.models.conn_record import ConnRecord +from ..messaging.models.base import BaseModelError from ..messaging.models.openapi import OpenAPISchema from ..messaging.valid import ( ENDPOINT, @@ -12,8 +16,21 @@ INDY_DID, INDY_RAW_PUBLIC_KEY, INT_EPOCH, + UUIDFour, +) + +# from ..protocols.endorse_transaction.v1_0.manager import ( +# TransactionManager, +# TransactionManagerError, +# ) +from ..protocols.endorse_transaction.v1_0.models.transaction_record import ( + TransactionRecordSchema, +) +from ..protocols.endorse_transaction.v1_0.util import ( + is_author_role, + get_endorser_connection_id, ) -from ..storage.error import StorageError +from ..storage.error import StorageError, StorageNotFoundError from ..wallet.error import WalletError, WalletNotFoundError from .base import BaseLedger, Role as LedgerRole @@ -109,6 +126,23 @@ class RegisterLedgerNymQueryStringSchema(OpenAPISchema): ) +class CreateDidTxnForEndorserOptionSchema(OpenAPISchema): + """Class for user to input whether to create a transaction for endorser or not.""" + + create_transaction_for_endorser = fields.Boolean( + description="Create Transaction For Endorser's signature", + required=False, + ) + + +class SchemaConnIdMatchInfoSchema(OpenAPISchema): + """Path parameters and validators for request taking connection id.""" + + conn_id = fields.Str( + description="Connection identifier", required=False, example=UUIDFour.EXAMPLE + ) + + class QueryStringDIDSchema(OpenAPISchema): """Parameters and validators for query string with DID only.""" @@ -128,7 +162,7 @@ class QueryStringEndpointSchema(OpenAPISchema): ) -class RegisterLedgerNymResponseSchema(OpenAPISchema): +class TxnOrRegisterLedgerNymResponseSchema(OpenAPISchema): """Response schema for ledger nym registration.""" success = fields.Bool( @@ -136,6 +170,12 @@ class RegisterLedgerNymResponseSchema(OpenAPISchema): example=True, ) + txn = fields.Nested( + TransactionRecordSchema(), + required=False, + description="DID transaction to endorse", + ) + class GetNymRoleResponseSchema(OpenAPISchema): """Response schema to get nym role operation.""" @@ -172,7 +212,9 @@ class GetDIDEndpointResponseSchema(OpenAPISchema): summary="Send a NYM registration to the ledger.", ) @querystring_schema(RegisterLedgerNymQueryStringSchema()) -@response_schema(RegisterLedgerNymResponseSchema(), 200, description="") +@querystring_schema(CreateDidTxnForEndorserOptionSchema()) +@querystring_schema(SchemaConnIdMatchInfoSchema()) +@response_schema(TxnOrRegisterLedgerNymResponseSchema(), 200, description="") async def register_ledger_nym(request: web.BaseRequest): """ Request handler for registering a NYM with the ledger. @@ -201,11 +243,63 @@ async def register_ledger_nym(request: web.BaseRequest): if role == "reset": # indy: empty to reset, null for regular user role = "" # visually: confusing - correct 'reset' to empty string here + create_transaction_for_endorser = json.loads( + request.query.get("create_transaction_for_endorser", "false") + ) + write_ledger = not create_transaction_for_endorser + endorser_did = None + connection_id = request.query.get("conn_id") + + # check if we need to endorse + if is_author_role(context.profile): + # authors cannot write to the ledger + write_ledger = False + create_transaction_for_endorser = True + if not connection_id: + # author has not provided a connection id, so determine which to use + connection_id = await get_endorser_connection_id(context.profile) + if not connection_id: + raise web.HTTPBadRequest(reason="No endorser connection found") + + if not write_ledger: + try: + async with context.profile.session() as session: + connection_record = await ConnRecord.retrieve_by_id( + session, connection_id + ) + except StorageNotFoundError as err: + raise web.HTTPNotFound(reason=err.roll_up) from err + except BaseModelError as err: + raise web.HTTPBadRequest(reason=err.roll_up) from err + + async with context.profile.session() as session: + endorser_info = await connection_record.metadata_get( + session, "endorser_info" + ) + if not endorser_info: + raise web.HTTPForbidden( + reason="Endorser Info is not set up in " + "connection metadata for this connection record" + ) + if "endorser_did" not in endorser_info.keys(): + raise web.HTTPForbidden( + reason=' "endorser_did" is not set in "endorser_info"' + " in connection metadata for this connection record" + ) + endorser_did = endorser_info["endorser_did"] + success = False + txn = None async with ledger: try: - await ledger.register_nym(did, verkey, alias, role) - success = True + (success, txn) = await ledger.register_nym( + did, + verkey, + alias, + role, + write_ledger=write_ledger, + endorser_did=endorser_did, + ) except LedgerTransactionError as err: raise web.HTTPForbidden(reason=err.roll_up) except LedgerError as err: @@ -220,7 +314,10 @@ async def register_ledger_nym(request: web.BaseRequest): ) ) - return web.json_response({"success": success}) + if write_ledger: + return web.json_response({"success": success}) + else: + return web.json_response({"success": success, "txn": txn}) @docs( diff --git a/aries_cloudagent/ledger/tests/test_routes.py b/aries_cloudagent/ledger/tests/test_routes.py index 8b2ed92a1a..b452310b24 100644 --- a/aries_cloudagent/ledger/tests/test_routes.py +++ b/aries_cloudagent/ledger/tests/test_routes.py @@ -1,4 +1,5 @@ from asynctest import mock as async_mock, TestCase as AsyncTestCase +from typing import Tuple from ...admin.request_context import AdminRequestContext from ...core.in_memory import InMemoryProfile @@ -228,11 +229,11 @@ async def test_register_nym(self): with async_mock.patch.object( test_module.web, "json_response", async_mock.Mock() ) as json_response: - self.ledger.register_nym.return_value = True + success: bool = True + txn: dict = None + self.ledger.register_nym.return_value: Tuple[bool, dict] = (success, txn) result = await test_module.register_ledger_nym(self.request) - json_response.assert_called_once_with( - {"success": self.ledger.register_nym.return_value} - ) + json_response.assert_called_once_with({"success": success}) assert result is json_response.return_value async def test_register_nym_bad_request(self): From a142c9de3359bc45845fd4463fe91bf03d7694be Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Mon, 17 Jan 2022 16:08:15 -0800 Subject: [PATCH 007/872] updates Signed-off-by: Shaanjot Gill --- .../messaging/decorators/attach_decorator.py | 7 +- .../decorators/tests/test_attach_decorator.py | 1 + .../protocols/connections/v1_0/manager.py | 11 ++ .../connections/v1_0/tests/test_manager.py | 45 ++++++- .../protocols/didexchange/v1_0/manager.py | 76 ++++++++--- .../didexchange/v1_0/tests/test_manager.py | 125 ++++++++++++------ 6 files changed, 202 insertions(+), 63 deletions(-) diff --git a/aries_cloudagent/messaging/decorators/attach_decorator.py b/aries_cloudagent/messaging/decorators/attach_decorator.py index e86f17734d..b0fb504cf1 100644 --- a/aries_cloudagent/messaging/decorators/attach_decorator.py +++ b/aries_cloudagent/messaging/decorators/attach_decorator.py @@ -414,7 +414,7 @@ def build_protected(verkey: str): ) self.jws_ = AttachDecoratorDataJWS.deserialize(jws) - async def verify(self, wallet: BaseWallet) -> bool: + async def verify(self, wallet: BaseWallet, signer_verkey: str = None) -> bool: """ Verify the signature(s). @@ -428,7 +428,7 @@ async def verify(self, wallet: BaseWallet) -> bool: assert self.jws b64_payload = unpad(set_urlsafe_b64(self.base64, True)) - + verkey_list = [] for sig in [self.jws] if self.signatures == 1 else self.jws.signatures: b64_protected = sig.protected b64_sig = sig.signature @@ -438,10 +438,13 @@ async def verify(self, wallet: BaseWallet) -> bool: sign_input = (b64_protected + "." + b64_payload).encode("ascii") b_sig = b64_to_bytes(b64_sig, urlsafe=True) verkey = bytes_to_b58(b64_to_bytes(protected["jwk"]["x"], urlsafe=True)) + verkey_list.append(verkey) if not await wallet.verify_message( sign_input, b_sig, verkey, KeyType.ED25519 ): return False + if signer_verkey and signer_verkey not in verkey_list: + return False return True def __eq__(self, other): diff --git a/aries_cloudagent/messaging/decorators/tests/test_attach_decorator.py b/aries_cloudagent/messaging/decorators/tests/test_attach_decorator.py index 6cd9e1d67a..2687c7bf19 100644 --- a/aries_cloudagent/messaging/decorators/tests/test_attach_decorator.py +++ b/aries_cloudagent/messaging/decorators/tests/test_attach_decorator.py @@ -475,6 +475,7 @@ async def test_indy_sign(self, wallet, seed): assert deco_indy.data.header_map()["kid"] == did_key(did_info[0].verkey) assert deco_indy.data.header_map()["jwk"]["kid"] == did_key(did_info[0].verkey) assert await deco_indy.data.verify(wallet) + assert await deco_indy.data.verify(wallet, did_info[0].verkey) indy_cred = json.loads(deco_indy.data.signed.decode()) assert indy_cred == INDY_CRED diff --git a/aries_cloudagent/protocols/connections/v1_0/manager.py b/aries_cloudagent/protocols/connections/v1_0/manager.py index 96e72b8b37..c3e11cd68c 100644 --- a/aries_cloudagent/protocols/connections/v1_0/manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/manager.py @@ -833,6 +833,17 @@ async def accept_response( ) if their_did != conn_did_doc.did: raise ConnectionManagerError("Connection DID does not match DIDDoc id") + # Verify connection response using connection field + async with self.profile.session() as session: + wallet = session.inject(BaseWallet) + try: + await response.verify_signed_field( + "connection", wallet, connection.invitation_key + ) + except ValueError: + raise ConnectionManagerError( + "connection field verification using invitation_key failed" + ) await self.store_did_document(conn_did_doc) connection.their_did = their_did diff --git a/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py index c986cb88cf..4fb6efefd3 100644 --- a/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py @@ -1277,7 +1277,9 @@ async def test_accept_response_find_by_thread_id(self): mock_response.connection.did = self.test_target_did mock_response.connection.did_doc = async_mock.MagicMock() mock_response.connection.did_doc.did = self.test_target_did - + mock_response.verify_signed_field = async_mock.CoroutineMock( + return_value="sig_verkey" + ) receipt = MessageReceipt(recipient_did=self.test_did, recipient_did_public=True) with async_mock.patch.object( @@ -1294,6 +1296,7 @@ async def test_accept_response_find_by_thread_id(self): save=async_mock.CoroutineMock(), metadata_get=async_mock.CoroutineMock(), connection_id="test-conn-id", + invitation_key="test-invitation-key", ) conn_rec = await self.manager.accept_response(mock_response, receipt) assert conn_rec.their_did == self.test_target_did @@ -1306,6 +1309,9 @@ async def test_accept_response_not_found_by_thread_id_receipt_has_sender_did(sel mock_response.connection.did = self.test_target_did mock_response.connection.did_doc = async_mock.MagicMock() mock_response.connection.did_doc.did = self.test_target_did + mock_response.verify_signed_field = async_mock.CoroutineMock( + return_value="sig_verkey" + ) receipt = MessageReceipt(sender_did=self.test_target_did) @@ -1326,6 +1332,7 @@ async def test_accept_response_not_found_by_thread_id_receipt_has_sender_did(sel save=async_mock.CoroutineMock(), metadata_get=async_mock.CoroutineMock(return_value=False), connection_id="test-conn-id", + invitation_key="test-invitation-id", ) conn_rec = await self.manager.accept_response(mock_response, receipt) @@ -1426,14 +1433,47 @@ async def test_accept_response_find_by_thread_id_did_mismatch(self): with self.assertRaises(ConnectionManagerError): await self.manager.accept_response(mock_response, receipt) - async def test_accept_response_auto_send_mediation_request(self): + async def test_accept_response_verify_invitation_key_sign_failure(self): mock_response = async_mock.MagicMock() mock_response._thread = async_mock.MagicMock() mock_response.connection = async_mock.MagicMock() mock_response.connection.did = self.test_target_did mock_response.connection.did_doc = async_mock.MagicMock() mock_response.connection.did_doc.did = self.test_target_did + mock_response.verify_signed_field = async_mock.CoroutineMock( + side_effect=ValueError + ) + receipt = MessageReceipt(recipient_did=self.test_did, recipient_did_public=True) + + with async_mock.patch.object( + ConnRecord, "save", autospec=True + ) as mock_conn_rec_save, async_mock.patch.object( + ConnRecord, "retrieve_by_request_id", async_mock.CoroutineMock() + ) as mock_conn_retrieve_by_req_id, async_mock.patch.object( + MediationManager, "get_default_mediator", async_mock.CoroutineMock() + ): + mock_conn_retrieve_by_req_id.return_value = async_mock.MagicMock( + did=self.test_target_did, + did_doc=async_mock.MagicMock(did=self.test_target_did), + state=ConnRecord.State.RESPONSE.rfc23, + save=async_mock.CoroutineMock(), + metadata_get=async_mock.CoroutineMock(), + connection_id="test-conn-id", + invitation_key="test-invitation-key", + ) + with self.assertRaises(ConnectionManagerError): + await self.manager.accept_response(mock_response, receipt) + async def test_accept_response_auto_send_mediation_request(self): + mock_response = async_mock.MagicMock() + mock_response._thread = async_mock.MagicMock() + mock_response.connection = async_mock.MagicMock() + mock_response.connection.did = self.test_target_did + mock_response.connection.did_doc = async_mock.MagicMock() + mock_response.connection.did_doc.did = self.test_target_did + mock_response.verify_signed_field = async_mock.CoroutineMock( + return_value="sig_verkey" + ) receipt = MessageReceipt(recipient_did=self.test_did, recipient_did_public=True) with async_mock.patch.object( @@ -1450,6 +1490,7 @@ async def test_accept_response_auto_send_mediation_request(self): save=async_mock.CoroutineMock(), metadata_get=async_mock.CoroutineMock(return_value=True), connection_id="test-conn-id", + invitation_key="test-invitation-key", ) conn_rec = await self.manager.accept_response(mock_response, receipt) assert conn_rec.their_did == self.test_target_did diff --git a/aries_cloudagent/protocols/didexchange/v1_0/manager.py b/aries_cloudagent/protocols/didexchange/v1_0/manager.py index 0a7eadde7f..845f1ee3f7 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/manager.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/manager.py @@ -4,7 +4,7 @@ import logging import pydid -from pydid import BaseDIDDocument as ResolvedDocument +from pydid import BaseDIDDocument as ResolvedDocument, DIDCommService from ....connections.models.conn_record import ConnRecord from ....connections.models.diddoc import DIDDoc @@ -146,7 +146,13 @@ async def receive_invitation( # Save the invitation for later processing await conn_rec.attach_invitation(session, invitation) - + if not conn_rec.invitation_key and conn_rec.their_public_did: + did_document = await self.get_resolved_did_document( + conn_rec.their_public_did + ) + conn_rec.invitation_key = did_document.verification_method[ + 0 + ].public_key_base58 if conn_rec.accept == ConnRecord.ACCEPT_AUTO: request = await self.create_request(conn_rec, mediation_id=mediation_id) responder = self.profile.inject_or(BaseResponder) @@ -303,19 +309,8 @@ async def create_request( ) if conn_rec.their_public_did is not None: qualified_did = conn_rec.their_public_did - if not conn_rec.their_public_did.startswith("did:"): - qualified_did = f"did:sov:{conn_rec.their_public_did}" - resolver = self._profile.inject(DIDResolver) - try: - doc_dict: dict = await resolver.resolve(self._profile, qualified_did) - doc: ResolvedDocument = pydid.deserialize_document( - doc_dict, strict=True - ) - did_url = doc.service[0].id - except ResolverError as error: - raise DIDXManagerError( - "Failed to resolve public DID in invitation" - ) from error + did_document = await self.get_resolved_did_document(qualified_did) + did_url = await self.get_first_applicable_didcomm_service(did_document) pthid = conn_rec.invitation_msg_id or did_url attach = AttachDecorator.data_base64(did_doc.serialize()) async with self.profile.session() as session: @@ -481,9 +476,7 @@ async def receive_request( ) async with self.profile.session() as session: wallet = session.inject(BaseWallet) - if not await request.did_doc_attach.data.verify(wallet): - raise DIDXManagerError("DID Doc signature failed verification") - conn_did_doc = DIDDoc.from_json(request.did_doc_attach.data.signed.decode()) + conn_did_doc = await self.verify_diddoc(wallet, request.did_doc_attach) if request.did != conn_did_doc.did: raise DIDXManagerError( ( @@ -753,7 +746,9 @@ async def accept_response( raise DIDXManagerError("No DIDDoc attached; cannot connect to public DID") async with self.profile.session() as session: wallet = session.inject(BaseWallet) - conn_did_doc = await self.verify_diddoc(wallet, response.did_doc_attach) + conn_did_doc = await self.verify_diddoc( + wallet, response.did_doc_attach, conn_rec.invitation_key + ) if their_did != conn_did_doc.did: raise DIDXManagerError( f"Connection DID {their_did} " @@ -848,12 +843,53 @@ async def verify_diddoc( self, wallet: BaseWallet, attached: AttachDecorator, + invi_key: str = None, ) -> DIDDoc: """Verify DIDDoc attachment and return signed data.""" signed_diddoc_bytes = attached.data.signed if not signed_diddoc_bytes: raise DIDXManagerError("DID doc attachment is not signed.") - if not await attached.data.verify(wallet): + if not await attached.data.verify(wallet, invi_key): raise DIDXManagerError("DID doc attachment signature failed verification") return DIDDoc.deserialize(json.loads(signed_diddoc_bytes.decode())) + + async def get_resolved_did_document(self, qualified_did: str) -> ResolvedDocument: + """Return resolved DID document.""" + resolver = self._profile.inject(DIDResolver) + if not qualified_did.startswith("did:"): + qualified_did = f"did:sov:{qualified_did}" + try: + doc_dict: dict = await resolver.resolve(self._profile, qualified_did) + doc = pydid.deserialize_document(doc_dict, strict=True) + return doc + except ResolverError as error: + raise DIDXManagerError( + "Failed to resolve public DID in invitation" + ) from error + + async def get_first_applicable_didcomm_service( + self, did_doc: ResolvedDocument + ) -> str: + """Return first applicable DIDComm service url with highest priority.""" + if not did_doc.service: + raise DIDXManagerError( + "Cannot connect via public DID that has no associated services" + ) + + didcomm_services = sorted( + [ + service + for service in did_doc.service + if isinstance(service, DIDCommService) + ], + key=lambda service: service.priority, + ) + + if not didcomm_services: + raise DIDXManagerError( + "Cannot connect via public DID that has no associated DIDComm services" + ) + + first_didcomm_service, *_ = didcomm_services + return first_didcomm_service.id diff --git a/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py index 3f74a37339..2a99c15af4 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py @@ -176,6 +176,37 @@ async def test_receive_invitation(self): invitee_record = await self.manager.receive_invitation(invi_msg) assert invitee_record.state == ConnRecord.State.REQUEST.rfc23 + async def test_receive_invitation_oob_public_did(self): + async with self.profile.session() as session: + self.profile.context.update_settings({"public_invites": True}) + public_did_info = None + await session.wallet.create_public_did( + DIDMethod.SOV, + KeyType.ED25519, + ) + public_did_info = await session.wallet.get_public_did() + with async_mock.patch.object( + test_module, "AttachDecorator", autospec=True + ) as mock_attach_deco, async_mock.patch.object( + self.multitenant_mgr, "get_default_mediator" + ) as mock_get_default_mediator: + mock_get_default_mediator.return_value = None + invi_rec = await self.oob_manager.create_invitation( + my_endpoint="testendpoint", + hs_protos=[HSProto.RFC23], + ) + invi_msg = invi_rec.invitation + invi_msg.services = [public_did_info.did] + mock_attach_deco.data_base64 = async_mock.MagicMock( + return_value=async_mock.MagicMock( + data=async_mock.MagicMock(sign=async_mock.CoroutineMock()) + ) + ) + invitee_record = await self.manager.receive_invitation( + invi_msg, their_public_did=public_did_info.did + ) + assert invitee_record.state == ConnRecord.State.REQUEST.rfc23 + async def test_receive_invitation_no_auto_accept(self): async with self.profile.session() as session: mediation_record = MediationRecord( @@ -274,27 +305,6 @@ async def test_create_request_implicit_use_public_did(self): assert info_public.did == conn_rec.my_did - async def test_create_request_implicit_resolver_error(self): - async with self.profile.session() as session: - await session.wallet.create_public_did( - DIDMethod.SOV, - KeyType.ED25519, - ) - with async_mock.patch.object( - self.resolver, - "resolve", - async_mock.CoroutineMock(side_effect=ResolverError()), - ): - with self.assertRaises(DIDXManagerError) as ctx: - conn_rec = await self.manager.create_request_implicit( - their_public_did=TestConfig.test_target_did, - my_label=None, - my_endpoint=None, - use_public_did=True, - alias="Tester", - ) - assert "Failed to resolve public DID in invitation" in str(ctx.exception) - async def test_create_request_implicit_no_public_did(self): with self.assertRaises(WalletError) as context: await self.manager.create_request_implicit( @@ -321,7 +331,6 @@ async def test_create_request(self): ) ), save=async_mock.CoroutineMock(), - their_public_did=None, ) with async_mock.patch.object( @@ -375,7 +384,6 @@ async def test_create_request_multitenant(self): ) ), save=async_mock.CoroutineMock(), - their_public_did=None, ) ) @@ -450,7 +458,6 @@ async def test_create_request_my_endpoint(self): ) ), save=async_mock.CoroutineMock(), - their_public_did=None, ) with async_mock.patch.object( @@ -472,7 +479,6 @@ async def test_receive_request_explicit_public_did(self): did=TestConfig.test_did, did_doc_attach=async_mock.MagicMock( data=async_mock.MagicMock( - verify=async_mock.CoroutineMock(return_value=True), signed=async_mock.MagicMock( decode=async_mock.MagicMock(return_value="dummy-did-doc") ), @@ -511,6 +517,10 @@ async def test_receive_request_explicit_public_did(self): ) as mock_attach_deco, async_mock.patch.object( test_module, "DIDXResponse", autospec=True ) as mock_response, async_mock.patch.object( + self.manager, + "verify_diddoc", + async_mock.CoroutineMock(return_value=DIDDoc(TestConfig.test_did)), + ), async_mock.patch.object( self.manager, "create_did_document", async_mock.CoroutineMock() ) as mock_create_did_doc, async_mock.patch.object( MediationManager, "prepare_request", autospec=True @@ -885,7 +895,6 @@ async def test_receive_request_public_did_x_wrong_did(self): did=TestConfig.test_did, did_doc_attach=async_mock.MagicMock( data=async_mock.MagicMock( - verify=async_mock.CoroutineMock(return_value=True), signed=async_mock.MagicMock( decode=async_mock.MagicMock(return_value="dummy-did-doc") ), @@ -909,7 +918,11 @@ async def test_receive_request_public_did_x_wrong_did(self): test_module, "DIDPosture", autospec=True ) as mock_did_posture, async_mock.patch.object( test_module.DIDDoc, "from_json", async_mock.MagicMock() - ) as mock_did_doc_from_json: + ) as mock_did_doc_from_json, async_mock.patch.object( + self.manager, + "verify_diddoc", + async_mock.CoroutineMock(return_value=DIDDoc("LjgpST2rjsoxYegQDRm7EL")), + ): mock_conn_record = async_mock.MagicMock( accept=ConnRecord.ACCEPT_MANUAL, my_did=None, @@ -950,7 +963,9 @@ async def test_receive_request_public_did_x_did_doc_attach_bad_sig(self): did=TestConfig.test_did, did_doc_attach=async_mock.MagicMock( data=async_mock.MagicMock( - verify=async_mock.CoroutineMock(return_value=False) + signed=async_mock.MagicMock( + decode=async_mock.MagicMock(return_value="dummy-did-doc") + ), ) ), _thread=async_mock.MagicMock(pthid="did:sov:publicdid0000000000000"), @@ -969,7 +984,11 @@ async def test_receive_request_public_did_x_did_doc_attach_bad_sig(self): test_module, "ConnRecord", async_mock.MagicMock() ) as mock_conn_rec_cls, async_mock.patch.object( test_module, "DIDPosture", autospec=True - ) as mock_did_posture: + ) as mock_did_posture, async_mock.patch.object( + self.manager, + "verify_diddoc", + async_mock.CoroutineMock(side_effect=DIDXManagerError), + ): mock_conn_record = async_mock.MagicMock( accept=ConnRecord.ACCEPT_MANUAL, my_did=None, @@ -988,7 +1007,7 @@ async def test_receive_request_public_did_x_did_doc_attach_bad_sig(self): return_value=test_module.DIDPosture.PUBLIC ) - with self.assertRaises(DIDXManagerError) as context: + with self.assertRaises(DIDXManagerError): await self.manager.receive_request( request=mock_request, recipient_did=TestConfig.test_did, @@ -998,7 +1017,6 @@ async def test_receive_request_public_did_x_did_doc_attach_bad_sig(self): auto_accept_implicit=False, mediation_id=None, ) - assert "DID Doc signature failed" in str(context.exception) async def test_receive_request_public_did_no_public_invites(self): async with self.profile.session() as session: @@ -1055,7 +1073,6 @@ async def test_receive_request_public_did_no_auto_accept(self): did=TestConfig.test_did, did_doc_attach=async_mock.MagicMock( data=async_mock.MagicMock( - verify=async_mock.CoroutineMock(return_value=True), signed=async_mock.MagicMock( decode=async_mock.MagicMock(return_value="dummy-did-doc") ), @@ -1087,7 +1104,11 @@ async def test_receive_request_public_did_no_auto_accept(self): test_module, "DIDXResponse", autospec=True ) as mock_response, async_mock.patch.object( self.manager, "create_did_document", async_mock.CoroutineMock() - ) as mock_create_did_doc: + ) as mock_create_did_doc, async_mock.patch.object( + self.manager, + "verify_diddoc", + async_mock.CoroutineMock(return_value=DIDDoc(TestConfig.test_did)), + ): mock_conn_record = async_mock.MagicMock( accept=ConnRecord.ACCEPT_MANUAL, my_did=None, @@ -1129,7 +1150,6 @@ async def test_receive_request_peer_did(self): did=TestConfig.test_did, did_doc_attach=async_mock.MagicMock( data=async_mock.MagicMock( - verify=async_mock.CoroutineMock(return_value=True), signed=async_mock.MagicMock( decode=async_mock.MagicMock(return_value="dummy-did-doc") ), @@ -1171,7 +1191,11 @@ async def test_receive_request_peer_did(self): test_module, "AttachDecorator", autospec=True ) as mock_attach_deco, async_mock.patch.object( test_module, "DIDXResponse", autospec=True - ) as mock_response: + ) as mock_response, async_mock.patch.object( + self.manager, + "verify_diddoc", + async_mock.CoroutineMock(return_value=DIDDoc(TestConfig.test_did)), + ): mock_conn_rec_cls.retrieve_by_invitation_key = async_mock.CoroutineMock( return_value=mock_conn ) @@ -1226,7 +1250,6 @@ async def test_receive_request_multiuse_multitenant(self): did=TestConfig.test_did, did_doc_attach=async_mock.MagicMock( data=async_mock.MagicMock( - verify=async_mock.CoroutineMock(return_value=True), signed=async_mock.MagicMock( decode=async_mock.MagicMock(return_value="dummy-did-doc") ), @@ -1245,7 +1268,11 @@ async def test_receive_request_multiuse_multitenant(self): InMemoryWallet, "create_local_did", autospec=True ) as mock_wallet_create_local_did, async_mock.patch.object( test_module, "DIDDoc", autospec=True - ) as mock_did_doc: + ) as mock_did_doc, async_mock.patch.object( + self.manager, + "verify_diddoc", + async_mock.CoroutineMock(return_value=DIDDoc(TestConfig.test_did)), + ): mock_conn_rec = async_mock.CoroutineMock( connection_id="dummy", accept=ACCEPT_MANUAL, @@ -1295,7 +1322,6 @@ async def test_receive_request_implicit_multitenant(self): did=TestConfig.test_did, did_doc_attach=async_mock.MagicMock( data=async_mock.MagicMock( - verify=async_mock.CoroutineMock(return_value=True), signed=async_mock.MagicMock( decode=async_mock.MagicMock(return_value="dummy-did-doc") ), @@ -1324,7 +1350,11 @@ async def test_receive_request_implicit_multitenant(self): test_module, "DIDPosture", autospec=True ) as mock_did_posture, async_mock.patch.object( test_module, "DIDDoc", autospec=True - ) as mock_did_doc: + ) as mock_did_doc, async_mock.patch.object( + self.manager, + "verify_diddoc", + async_mock.CoroutineMock(return_value=DIDDoc(TestConfig.test_did)), + ): mock_conn_rec = async_mock.CoroutineMock( connection_id="dummy", accept=ACCEPT_MANUAL, @@ -2215,3 +2245,20 @@ async def test_diddoc_connection_targets_diddoc_underspecified(self): x_did_doc._service = {} with self.assertRaises(BaseConnectionManagerError): self.manager.diddoc_connection_targets(x_did_doc, TestConfig.test_verkey) + + async def test_resolve_did_document_error(self): + public_did_info = None + async with self.profile.session() as session: + await session.wallet.create_public_did( + DIDMethod.SOV, + KeyType.ED25519, + ) + public_did_info = await session.wallet.get_public_did() + with async_mock.patch.object( + self.resolver, + "resolve", + async_mock.CoroutineMock(side_effect=ResolverError()), + ): + with self.assertRaises(DIDXManagerError) as ctx: + await self.manager.get_resolved_did_document(public_did_info.did) + assert "Failed to resolve public DID in invitation" in str(ctx.exception) From 7d02e9a8378229a9d117de4418c8d36fd5acbaf2 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Tue, 18 Jan 2022 11:39:26 -0800 Subject: [PATCH 008/872] Add transaction support for writing DIDs Signed-off-by: Ian Costanzo --- aries_cloudagent/ledger/indy.py | 4 +++ aries_cloudagent/ledger/indy_vdr.py | 3 +++ aries_cloudagent/ledger/routes.py | 39 +++++++++++++++++++++++++---- aries_cloudagent/ledger/util.py | 16 ++++++++++++ 4 files changed, 57 insertions(+), 5 deletions(-) diff --git a/aries_cloudagent/ledger/indy.py b/aries_cloudagent/ledger/indy.py index 2472354e52..6a8945e4e9 100644 --- a/aries_cloudagent/ledger/indy.py +++ b/aries_cloudagent/ledger/indy.py @@ -963,6 +963,10 @@ async def register_nym( request_json = await indy.ledger.build_nym_request( public_info.did, did, verkey, alias, role ) + if endorser_did and not write_ledger: + request_json = await indy.ledger.append_request_endorser( + request_json, endorser_did + ) resp = await self._submit( request_json, sign=True, sign_did=public_info, write_ledger=write_ledger ) # let ledger raise on insufficient privilege diff --git a/aries_cloudagent/ledger/indy_vdr.py b/aries_cloudagent/ledger/indy_vdr.py index d8dd2ce15c..5f82082bf5 100644 --- a/aries_cloudagent/ledger/indy_vdr.py +++ b/aries_cloudagent/ledger/indy_vdr.py @@ -971,6 +971,9 @@ async def register_nym( except VdrError as err: raise LedgerError("Exception when building nym request") from err + if endorser_did and not write_ledger: + nym_req.set_endorser(endorser_did) + resp = await self._submit( nym_req, sign=True, sign_did=public_info, write_ledger=write_ledger ) diff --git a/aries_cloudagent/ledger/routes.py b/aries_cloudagent/ledger/routes.py index 1a12b43ef9..67aed5e1fc 100644 --- a/aries_cloudagent/ledger/routes.py +++ b/aries_cloudagent/ledger/routes.py @@ -19,10 +19,10 @@ UUIDFour, ) -# from ..protocols.endorse_transaction.v1_0.manager import ( -# TransactionManager, -# TransactionManagerError, -# ) +from ..protocols.endorse_transaction.v1_0.manager import ( + TransactionManager, + TransactionManagerError, +) from ..protocols.endorse_transaction.v1_0.models.transaction_record import ( TransactionRecordSchema, ) @@ -49,6 +49,7 @@ ) from .endpoint_type import EndpointType from .error import BadLedgerRequestError, LedgerError, LedgerTransactionError +from .util import notify_did_event class LedgerModulesResultSchema(OpenAPISchema): @@ -223,6 +224,7 @@ async def register_ledger_nym(request: web.BaseRequest): request: aiohttp request object """ context: AdminRequestContext = request["context"] + outbound_handler = request["outbound_message_router"] async with context.profile.session() as session: ledger = session.inject_or(BaseLedger) if not ledger: @@ -314,9 +316,36 @@ async def register_ledger_nym(request: web.BaseRequest): ) ) - if write_ledger: + meta_data = {"verkey": verkey, "alias": alias, "role": role} + if not create_transaction_for_endorser: + # Notify event + await notify_did_event(context.profile, did, meta_data) return web.json_response({"success": success}) else: + transaction_mgr = TransactionManager(context.profile) + try: + transaction = await transaction_mgr.create_record( + messages_attach=txn["signed_txn"], + connection_id=connection_id, + meta_data=meta_data, + ) + except StorageError as err: + raise web.HTTPBadRequest(reason=err.roll_up) from err + + # if auto-request, send the request to the endorser + if context.settings.get_value("endorser.auto_request"): + try: + transaction, transaction_request = await transaction_mgr.create_request( + transaction=transaction, + # TODO see if we need to parameterize these params + # expires_time=expires_time, + # endorser_write_txn=endorser_write_txn, + ) + except (StorageError, TransactionManagerError) as err: + raise web.HTTPBadRequest(reason=err.roll_up) from err + + await outbound_handler(transaction_request, connection_id=connection_id) + return web.json_response({"success": success, "txn": txn}) diff --git a/aries_cloudagent/ledger/util.py b/aries_cloudagent/ledger/util.py index 558ea411a9..2a302c1bd3 100644 --- a/aries_cloudagent/ledger/util.py +++ b/aries_cloudagent/ledger/util.py @@ -1,3 +1,19 @@ """Ledger utilities.""" +import re + +from ..core.profile import Profile + + TAA_ACCEPTED_RECORD_TYPE = "taa_accepted" + +DID_EVENT_PREFIX = "acapy::DID::" +EVENT_LISTENER_PATTERN = re.compile(f"^{DID_EVENT_PREFIX}(.*)?$") + + +async def notify_did_event(profile: Profile, did: str, meta_data: dict): + """Send notification for a DID post-process event.""" + await profile.notify( + DID_EVENT_PREFIX + did, + meta_data, + ) From b8c959877d3905fb7e65639bf35e9e37da40aa2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andra=C5=BE=20Cuderman?= Date: Tue, 18 Jan 2022 16:08:06 +0100 Subject: [PATCH 009/872] Fix version to askar 0.2.2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Andraž Cuderman --- requirements.askar.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.askar.txt b/requirements.askar.txt index 08d0c2161b..7af2ec91ac 100644 --- a/requirements.askar.txt +++ b/requirements.askar.txt @@ -1,3 +1,3 @@ -aries-askar~=0.2.2 +aries-askar==0.2.2 indy-credx~=0.3 indy-vdr~=0.3.3 From a5e4c7dc9d00673607882b141effdb2a7a9538cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andra=C5=BE=20Cuderman?= Date: Wed, 19 Jan 2022 08:49:46 +0100 Subject: [PATCH 010/872] Update aries askar to patch version 0.2.4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Andraž Cuderman --- requirements.askar.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.askar.txt b/requirements.askar.txt index 7af2ec91ac..3655606766 100644 --- a/requirements.askar.txt +++ b/requirements.askar.txt @@ -1,3 +1,3 @@ -aries-askar==0.2.2 +aries-askar~=0.2.4 indy-credx~=0.3 indy-vdr~=0.3.3 From 1b05b33424f910c7da03c57acb8c742e06039663 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Wed, 19 Jan 2022 06:27:14 -0800 Subject: [PATCH 011/872] Unit tests Signed-off-by: Ian Costanzo --- aries_cloudagent/ledger/tests/test_routes.py | 179 +++++++++++++++++++ 1 file changed, 179 insertions(+) diff --git a/aries_cloudagent/ledger/tests/test_routes.py b/aries_cloudagent/ledger/tests/test_routes.py index b452310b24..fb94d5c692 100644 --- a/aries_cloudagent/ledger/tests/test_routes.py +++ b/aries_cloudagent/ledger/tests/test_routes.py @@ -15,6 +15,7 @@ from .. import routes as test_module from ..indy import Role +from ...connections.models.conn_record import ConnRecord class TestLedgerRoutes(AsyncTestCase): @@ -267,6 +268,184 @@ async def test_register_nym_wallet_error(self): with self.assertRaises(test_module.web.HTTPBadRequest): await test_module.register_ledger_nym(self.request) + async def test_register_nym_create_transaction_for_endorser(self): + self.request.query = { + "did": "a_test_did", + "verkey": "a_test_verkey", + "alias": "did_alias", + "role": "ENDORSER", + "create_transaction_for_endorser": "true", + "conn_id": "dummy", + } + + with async_mock.patch.object( + ConnRecord, "retrieve_by_id", async_mock.CoroutineMock() + ) as mock_conn_rec_retrieve, async_mock.patch.object( + test_module, "TransactionManager", async_mock.MagicMock() + ) as mock_txn_mgr, async_mock.patch.object( + test_module.web, "json_response", async_mock.MagicMock() + ) as mock_response: + mock_txn_mgr.return_value = async_mock.MagicMock( + create_record=async_mock.CoroutineMock( + return_value=async_mock.MagicMock( + serialize=async_mock.MagicMock(return_value={"...": "..."}) + ) + ) + ) + mock_conn_rec_retrieve.return_value = async_mock.MagicMock( + metadata_get=async_mock.CoroutineMock( + return_value={ + "endorser_did": ("did"), + "endorser_name": ("name"), + } + ) + ) + self.ledger.register_nym.return_value: Tuple[bool, dict] = ( + True, + {"signed_txn": {"...": "..."}}, + ) + + result = await test_module.register_ledger_nym(self.request) + assert result == mock_response.return_value + mock_response.assert_called_once_with( + {"success": True, "txn": {"signed_txn": {"...": "..."}}} + ) + + async def test_register_nym_create_transaction_for_endorser_storage_x(self): + self.request.query = { + "did": "a_test_did", + "verkey": "a_test_verkey", + "alias": "did_alias", + "role": "ENDORSER", + "create_transaction_for_endorser": "true", + "conn_id": "dummy", + } + + with async_mock.patch.object( + ConnRecord, "retrieve_by_id", async_mock.CoroutineMock() + ) as mock_conn_rec_retrieve, async_mock.patch.object( + test_module, "TransactionManager", async_mock.MagicMock() + ) as mock_txn_mgr: + + mock_txn_mgr.return_value = async_mock.MagicMock( + create_record=async_mock.CoroutineMock( + side_effect=test_module.StorageError() + ) + ) + mock_conn_rec_retrieve.return_value = async_mock.MagicMock( + metadata_get=async_mock.CoroutineMock( + return_value={ + "endorser_did": ("did"), + "endorser_name": ("name"), + } + ) + ) + self.ledger.register_nym.return_value: Tuple[bool, dict] = ( + True, + {"signed_txn": {"...": "..."}}, + ) + + with self.assertRaises(test_module.web.HTTPBadRequest): + await test_module.register_ledger_nym(self.request) + + async def test_register_nym_create_transaction_for_endorser_not_found_x(self): + self.request.query = { + "did": "a_test_did", + "verkey": "a_test_verkey", + "alias": "did_alias", + "role": "ENDORSER", + "create_transaction_for_endorser": "true", + "conn_id": "dummy", + } + + with async_mock.patch.object( + ConnRecord, "retrieve_by_id", async_mock.CoroutineMock() + ) as mock_conn_rec_retrieve: + mock_conn_rec_retrieve.side_effect = test_module.StorageNotFoundError() + self.ledger.register_nym.return_value: Tuple[bool, dict] = ( + True, + {"signed_txn": {"...": "..."}}, + ) + + with self.assertRaises(test_module.web.HTTPNotFound): + await test_module.register_ledger_nym(self.request) + + async def test_register_nym_create_transaction_for_endorser_base_model_x(self): + self.request.query = { + "did": "a_test_did", + "verkey": "a_test_verkey", + "alias": "did_alias", + "role": "ENDORSER", + "create_transaction_for_endorser": "true", + "conn_id": "dummy", + } + + with async_mock.patch.object( + ConnRecord, "retrieve_by_id", async_mock.CoroutineMock() + ) as mock_conn_rec_retrieve: + mock_conn_rec_retrieve.side_effect = test_module.BaseModelError() + self.ledger.register_nym.return_value: Tuple[bool, dict] = ( + True, + {"signed_txn": {"...": "..."}}, + ) + + with self.assertRaises(test_module.web.HTTPBadRequest): + await test_module.register_ledger_nym(self.request) + + async def test_register_nym_create_transaction_for_endorser_no_endorser_info_x( + self, + ): + self.request.query = { + "did": "a_test_did", + "verkey": "a_test_verkey", + "alias": "did_alias", + "role": "ENDORSER", + "create_transaction_for_endorser": "true", + "conn_id": "dummy", + } + + with async_mock.patch.object( + ConnRecord, "retrieve_by_id", async_mock.CoroutineMock() + ) as mock_conn_rec_retrieve: + mock_conn_rec_retrieve.return_value = async_mock.MagicMock( + metadata_get=async_mock.CoroutineMock(return_value=None) + ) + self.ledger.register_nym.return_value: Tuple[bool, dict] = ( + True, + {"signed_txn": {"...": "..."}}, + ) + + with self.assertRaises(test_module.web.HTTPForbidden): + await test_module.register_ledger_nym(self.request) + + async def test_register_nym_create_transaction_for_endorser_no_endorser_did_x(self): + self.request.query = { + "did": "a_test_did", + "verkey": "a_test_verkey", + "alias": "did_alias", + "role": "ENDORSER", + "create_transaction_for_endorser": "true", + "conn_id": "dummy", + } + + with async_mock.patch.object( + ConnRecord, "retrieve_by_id", async_mock.CoroutineMock() + ) as mock_conn_rec_retrieve: + mock_conn_rec_retrieve.return_value = async_mock.MagicMock( + metadata_get=async_mock.CoroutineMock( + return_value={ + "endorser_name": ("name"), + } + ) + ) + self.ledger.register_nym.return_value: Tuple[bool, dict] = ( + True, + {"signed_txn": {"...": "..."}}, + ) + + with self.assertRaises(test_module.web.HTTPForbidden): + await test_module.register_ledger_nym(self.request) + async def test_get_nym_role_a(self): self.profile.context.injector.bind_instance( IndyLedgerRequestsExecutor, From d2f4f312866f01d0920436283d11965de27d00f3 Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Wed, 19 Jan 2022 11:14:10 -0800 Subject: [PATCH 012/872] replace blank credential/presentation exchange states with STATE_ABANDONED Signed-off-by: Andrew Whitehead --- .../issue_credential/v1_0/manager.py | 2 +- .../v1_0/models/credential_exchange.py | 6 ++-- .../v1_0/tests/test_manager.py | 2 +- .../issue_credential/v2_0/manager.py | 2 +- .../v2_0/models/cred_ex_record.py | 6 ++-- .../v2_0/tests/test_manager.py | 2 +- .../protocols/present_proof/v1_0/manager.py | 6 ++-- .../v1_0/models/presentation_exchange.py | 6 ++-- .../present_proof/v1_0/tests/test_manager.py | 6 +++- .../v2_0/models/pres_exchange.py | 5 ++-- demo/runners/agent_container.py | 30 +++++++++++++++---- 11 files changed, 52 insertions(+), 21 deletions(-) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/manager.py b/aries_cloudagent/protocols/issue_credential/v1_0/manager.py index 27c391222f..d94759ba05 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/manager.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/manager.py @@ -915,7 +915,7 @@ async def receive_problem_report( ) ) - cred_ex_record.state = None + cred_ex_record.state = V10CredentialExchange.STATE_ABANDONED code = message.description.get( "code", ProblemReportReason.ISSUANCE_ABANDONED.value, diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py b/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py index 129cfeb745..e8118bf591 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py @@ -51,6 +51,7 @@ class Meta: STATE_CREDENTIAL_RECEIVED = "credential_received" STATE_ACKED = "credential_acked" STATE_CREDENTIAL_REVOKED = "credential_revoked" + STATE_ABANDONED = "abandoned" def __init__( self, @@ -188,6 +189,7 @@ async def save_error_state( self, session: ProfileSession, *, + state: str = None, reason: str = None, log_params: Mapping[str, Any] = None, log_override: bool = False, @@ -202,10 +204,10 @@ async def save_error_state( override: Override configured logging regimen, print to stderr instead """ - if self._last_state is None: # already done + if self._last_state == state: # already done return - self.state = None + self.state = state or V10CredentialExchange.STATE_ABANDONED if reason: self.error_msg = reason diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py index 5084eebcca..91a34af9ac 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py @@ -1626,7 +1626,7 @@ async def test_receive_problem_report(self): ) save_ex.assert_called_once() - assert ret_exchange.state is None + assert ret_exchange.state == V10CredentialExchange.STATE_ABANDONED async def test_receive_problem_report_x(self): connection_id = "connection-id" diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/manager.py b/aries_cloudagent/protocols/issue_credential/v2_0/manager.py index 17d709e19e..a5931011d7 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/manager.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/manager.py @@ -732,7 +732,7 @@ async def receive_problem_report( ) ) - cred_ex_record.state = None + cred_ex_record.state = V20CredExRecord.STATE_ABANDONED code = message.description.get( "code", ProblemReportReason.ISSUANCE_ABANDONED.value, diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/models/cred_ex_record.py b/aries_cloudagent/protocols/issue_credential/v2_0/models/cred_ex_record.py index fc23fb5e5a..3ba98c7e98 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/models/cred_ex_record.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/models/cred_ex_record.py @@ -51,6 +51,7 @@ class Meta: STATE_CREDENTIAL_RECEIVED = "credential-received" STATE_DONE = "done" STATE_CREDENTIAL_REVOKED = "credential-revoked" + STATE_ABANDONED = "abandoned" def __init__( self, @@ -148,6 +149,7 @@ async def save_error_state( self, session: ProfileSession, *, + state: str = None, reason: str = None, log_params: Mapping[str, Any] = None, log_override: bool = False, @@ -162,10 +164,10 @@ async def save_error_state( override: Override configured logging regimen, print to stderr instead """ - if self._last_state is None: # already done + if self._last_state == state: # already done return - self.state = None + self.state = state or V20CredExRecord.STATE_ABANDONED if reason: self.error_msg = reason diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/tests/test_manager.py b/aries_cloudagent/protocols/issue_credential/v2_0/tests/test_manager.py index 534aa1360e..e941710107 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/tests/test_manager.py @@ -1400,7 +1400,7 @@ async def test_receive_problem_report(self): ) save_ex.assert_called_once() - assert ret_exchange.state is None + assert ret_exchange.state == V20CredExRecord.STATE_ABANDONED async def test_receive_problem_report_x(self): connection_id = "connection-id" diff --git a/aries_cloudagent/protocols/present_proof/v1_0/manager.py b/aries_cloudagent/protocols/present_proof/v1_0/manager.py index 50cf219c41..10354d3d29 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/manager.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/manager.py @@ -346,7 +346,9 @@ async def receive_presentation( name=name, value=value, ): - presentation_exchange_record.state = None + presentation_exchange_record.state = ( + V10PresentationExchange.STATE_ABANDONED + ) async with self._profile.session() as session: await presentation_exchange_record.save( session, @@ -497,7 +499,7 @@ async def receive_problem_report( ) ) - pres_ex_record.state = None + pres_ex_record.state = V10PresentationExchange.STATE_ABANDONED code = message.description.get("code", ProblemReportReason.ABANDONED.value) pres_ex_record.error_msg = f"{code}: {message.description.get('en', code)}" await pres_ex_record.save(session, reason="received problem report") diff --git a/aries_cloudagent/protocols/present_proof/v1_0/models/presentation_exchange.py b/aries_cloudagent/protocols/present_proof/v1_0/models/presentation_exchange.py index ef13ad5b89..bce40f896d 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/models/presentation_exchange.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/models/presentation_exchange.py @@ -54,6 +54,7 @@ class Meta: STATE_PRESENTATION_RECEIVED = "presentation_received" STATE_VERIFIED = "verified" STATE_PRESENTATION_ACKED = "presentation_acked" + STATE_ABANDONED = "abandoned" def __init__( self, @@ -158,6 +159,7 @@ async def save_error_state( self, session: ProfileSession, *, + state: str = None, reason: str = None, log_params: Mapping[str, Any] = None, log_override: bool = False, @@ -172,10 +174,10 @@ async def save_error_state( override: Override configured logging regimen, print to stderr instead """ - if self._last_state is None: # already done + if self._last_state == state: # already done return - self.state = None + self.state = state or V10PresentationExchange.STATE_ABANDONED if reason: self.error_msg = reason diff --git a/aries_cloudagent/protocols/present_proof/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/present_proof/v1_0/tests/test_manager.py index 4e0d9d3580..548a97c382 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/tests/test_manager.py @@ -4,6 +4,10 @@ from asynctest import mock as async_mock, TestCase as AsyncTestCase +from aries_cloudagent.protocols.issue_credential.v1_0.models.credential_exchange import ( + V10CredentialExchange, +) + from .....core.in_memory import InMemoryProfile from .....indy.holder import IndyHolder, IndyHolderError from .....indy.issuer import IndyIssuer @@ -1327,7 +1331,7 @@ async def test_receive_problem_report(self): ) save_ex.assert_called_once() - assert ret_exchange.state is None + assert ret_exchange.state == V10CredentialExchange.STATE_ABANDONED async def test_receive_problem_report_x(self): connection_id = "connection-id" diff --git a/aries_cloudagent/protocols/present_proof/v2_0/models/pres_exchange.py b/aries_cloudagent/protocols/present_proof/v2_0/models/pres_exchange.py index db98cf0539..298081b848 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/models/pres_exchange.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/models/pres_exchange.py @@ -145,6 +145,7 @@ async def save_error_state( self, session: ProfileSession, *, + state: str = None, reason: str = None, log_params: Mapping[str, Any] = None, log_override: bool = False, @@ -159,10 +160,10 @@ async def save_error_state( override: Override configured logging regimen, print to stderr instead """ - if self._last_state == V20PresExRecord.STATE_ABANDONED: # already done + if self._last_state == state: # already done return - self.state = V20PresExRecord.STATE_ABANDONED + self.state = state or V20PresExRecord.STATE_ABANDONED if reason: self.error_msg = reason diff --git a/demo/runners/agent_container.py b/demo/runners/agent_container.py index 922bcd51d2..33f245e91d 100644 --- a/demo/runners/agent_container.py +++ b/demo/runners/agent_container.py @@ -121,7 +121,7 @@ async def handle_connections(self, message): conn_id = message["connection_id"] # inviter: - if message["state"] == "invitation": + if message.get("state") == "invitation": self.connection_id = conn_id # invitee: @@ -158,7 +158,7 @@ async def handle_connections(self, message): ) async def handle_issue_credential(self, message): - state = message["state"] + state = message.get("state") credential_exchange_id = message["credential_exchange_id"] prev_state = self.cred_state.get(credential_exchange_id) if prev_state == state: @@ -221,8 +221,12 @@ async def handle_issue_credential(self, message): except ClientError: pass + elif state == "abandoned": + log_status("Credential exchange abandoned") + self.log("Problem report message:", message.get("error_msg")) + async def handle_issue_credential_v2_0(self, message): - state = message["state"] + state = message.get("state") cred_ex_id = message["cred_ex_id"] prev_state = self.cred_state.get(cred_ex_id) if prev_state == state: @@ -238,6 +242,7 @@ async def handle_issue_credential_v2_0(self, message): f"/issue-credential-2.0/records/{cred_ex_id}/issue", {"comment": f"Issuing credential, exchange {cred_ex_id}"}, ) + elif state == "offer-received": log_status("#15 After receiving credential offer, send credential request") if message["by_format"]["cred_offer"].get("indy"): @@ -253,10 +258,15 @@ async def handle_issue_credential_v2_0(self, message): await self.admin_POST( f"/issue-credential-2.0/records/{cred_ex_id}/send-request", data ) + elif state == "done": pass # Logic moved to detail record specific handler + elif state == "abandoned": + log_status("Credential exchange abandoned") + self.log("Problem report message:", message.get("error_msg")) + async def handle_issue_credential_v2_0_indy(self, message): rev_reg_id = message.get("rev_reg_id") cred_rev_id = message.get("cred_rev_id") @@ -284,7 +294,7 @@ async def handle_issuer_cred_rev(self, message): pass async def handle_present_proof(self, message): - state = message["state"] + state = message.get("state") presentation_exchange_id = message["presentation_exchange_id"] presentation_request = message["presentation_request"] @@ -366,8 +376,12 @@ async def handle_present_proof(self, message): ) self.log("Proof =", proof["verified"]) + elif state == "abandoned": + log_status("Presentation exchange abandoned") + self.log("Problem report message:", message.get("error_msg")) + async def handle_present_proof_v2_0(self, message): - state = message["state"] + state = message.get("state") pres_ex_id = message["pres_ex_id"] self.log(f"Presentation: state = {state}, pres_ex_id = {pres_ex_id}") @@ -507,11 +521,15 @@ async def handle_present_proof_v2_0(self, message): self.log("Proof =", proof["verified"]) self.last_proof_received = proof + elif state == "abandoned": + log_status("Presentation exchange abandoned") + self.log("Problem report message:", message.get("error_msg")) + async def handle_basicmessages(self, message): self.log("Received message:", message["content"]) async def handle_endorse_transaction(self, message): - self.log("Received transaction message:", message["state"]) + self.log("Received transaction message:", message.get("state")) async def handle_revocation_notification(self, message): self.log("Received revocation notification message:", message) From 9446d497b855a10809d3f3442adbc46c8bee4716 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Thu, 20 Jan 2022 17:50:09 -0800 Subject: [PATCH 013/872] Add auto-promote-did when endorsed and written WIP Signed-off-by: Ian Costanzo --- aries_cloudagent/config/argparse.py | 18 +++++++++++++ aries_cloudagent/ledger/routes.py | 6 ++--- aries_cloudagent/ledger/util.py | 4 +-- .../endorse_transaction/v1_0/manager.py | 6 +++++ aries_cloudagent/wallet/routes.py | 27 +++++++++++++++++++ aries_cloudagent/wallet/util.py | 16 +++++++++++ demo/runners/support/agent.py | 1 + 7 files changed, 73 insertions(+), 5 deletions(-) diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index 0f20901662..a71d82da83 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -1733,6 +1733,13 @@ def add_arguments(self, parser: ArgumentParser): " the controller must invoke the endpoints required to create the" " revocation registry and assign to the cred def.)", ) + parser.add_argument( + "--auto-promote-author-did", + action="store_true", + env_var="ACAPY_PROMOTE-AUTHOR-DID", + help="For Authors, specify whether to automatically promote" + " a DID to the wallet public DID after writing to the ledger.", + ) def get_settings(self, args: Namespace): """Extract endorser settings.""" @@ -1742,6 +1749,7 @@ def get_settings(self, args: Namespace): settings["endorser.auto_endorse"] = False settings["endorser.auto_write"] = False settings["endorser.auto_create_rev_reg"] = False + settings["endorser.auto_promote_author_did"] = False if args.endorser_protocol_role: if args.endorser_protocol_role == ENDORSER_AUTHOR: @@ -1826,6 +1834,16 @@ def get_settings(self, args: Namespace): "for transaction Authors" ) + if args.auto_promote_author_did: + if settings["endorser.author"]: + settings["endorser.auto_promote_author_did"] = True + else: + pass + raise ArgsParseError( + "Parameter --auto-promote-author-did should only be set " + "for transaction Authors" + ) + return settings diff --git a/aries_cloudagent/ledger/routes.py b/aries_cloudagent/ledger/routes.py index 67aed5e1fc..dfeffee73b 100644 --- a/aries_cloudagent/ledger/routes.py +++ b/aries_cloudagent/ledger/routes.py @@ -49,7 +49,7 @@ ) from .endpoint_type import EndpointType from .error import BadLedgerRequestError, LedgerError, LedgerTransactionError -from .util import notify_did_event +from .util import notify_register_did_event class LedgerModulesResultSchema(OpenAPISchema): @@ -316,10 +316,10 @@ async def register_ledger_nym(request: web.BaseRequest): ) ) - meta_data = {"verkey": verkey, "alias": alias, "role": role} + meta_data = {"did": did, "verkey": verkey, "alias": alias, "role": role} if not create_transaction_for_endorser: # Notify event - await notify_did_event(context.profile, did, meta_data) + await notify_register_did_event(context.profile, did, meta_data) return web.json_response({"success": success}) else: transaction_mgr = TransactionManager(context.profile) diff --git a/aries_cloudagent/ledger/util.py b/aries_cloudagent/ledger/util.py index 2a302c1bd3..165d64a8b4 100644 --- a/aries_cloudagent/ledger/util.py +++ b/aries_cloudagent/ledger/util.py @@ -7,11 +7,11 @@ TAA_ACCEPTED_RECORD_TYPE = "taa_accepted" -DID_EVENT_PREFIX = "acapy::DID::" +DID_EVENT_PREFIX = "acapy::REGISTER_DID::" EVENT_LISTENER_PATTERN = re.compile(f"^{DID_EVENT_PREFIX}(.*)?$") -async def notify_did_event(profile: Profile, did: str, meta_data: dict): +async def notify_register_did_event(profile: Profile, did: str, meta_data: dict): """Send notification for a DID post-process event.""" await profile.notify( DID_EVENT_PREFIX + did, diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py index dca138e444..a1eddbaf58 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py @@ -21,6 +21,7 @@ from ....storage.error import StorageError, StorageNotFoundError from ....transport.inbound.receipt import MessageReceipt from ....wallet.base import BaseWallet +from ....wallet.util import notify_endorse_did_event from .messages.cancel_transaction import CancelTransaction from .messages.endorsed_transaction_response import EndorsedTransactionResponse @@ -790,6 +791,11 @@ async def endorsed_txn_post_processing( self._profile, rev_reg_id, meta_data ) + elif ledger_response["result"]["txn"]["type"] == "1": + # write DID to ledger + did = ledger_response["result"]["txn"]["data"]["dest"] + await notify_endorse_did_event(self._profile, did, meta_data) + else: # TODO unknown ledger transaction type, just ignore for now ... pass diff --git a/aries_cloudagent/wallet/routes.py b/aries_cloudagent/wallet/routes.py index 62002f1bb6..922ea68396 100644 --- a/aries_cloudagent/wallet/routes.py +++ b/aries_cloudagent/wallet/routes.py @@ -11,6 +11,8 @@ from marshmallow import fields, validate from ..admin.request_context import AdminRequestContext +from ..core.event_bus import Event, EventBus +from ..core.profile import Profile from ..ledger.base import BaseLedger from ..ledger.endpoint_type import EndpointType from ..ledger.error import LedgerConfigError, LedgerError @@ -24,6 +26,9 @@ INDY_RAW_PUBLIC_KEY, ) from ..multitenant.base import BaseMultitenantManager +from ..protocols.endorse_transaction.v1_0.util import ( + is_author_role, +) from .base import BaseWallet from .did_info import DIDInfo @@ -31,6 +36,7 @@ from .did_method import DIDMethod from .error import WalletError, WalletNotFoundError from .key_type import KeyType +from .util import EVENT_LISTENER_PATTERN class WalletModuleResponseSchema(OpenAPISchema): @@ -544,6 +550,27 @@ async def wallet_rotate_did_keypair(request: web.BaseRequest): return web.json_response({}) +def register_events(event_bus: EventBus): + """Subscribe to any events we need to support.""" + event_bus.subscribe(EVENT_LISTENER_PATTERN, on_register_nym_event) + + +async def on_register_nym_event(profile: Profile, event: Event): + """Handle any events we need to support.""" + + # after the nym record is written, promote to wallet public DID + if is_author_role(profile) and profile.context.settings.get_value("endorser.auto_promote_author_did"): + did = event.payload["did"] + await promote_wallet_public_did(profile, did) + + +async def promote_wallet_public_did(profile: Profile, did: str): + """Promote supplied DID to the wallet public DID.""" + + # TODO + pass + + async def register(app: web.Application): """Register routes.""" diff --git a/aries_cloudagent/wallet/util.py b/aries_cloudagent/wallet/util.py index b8ecc67ccd..2ec43fde9a 100644 --- a/aries_cloudagent/wallet/util.py +++ b/aries_cloudagent/wallet/util.py @@ -1,11 +1,15 @@ """Wallet utility functions.""" +import re + import base58 import base64 import nacl.utils import nacl.bindings +from ..core.profile import Profile + def random_seed() -> bytes: """ @@ -86,3 +90,15 @@ def abbr_verkey(full_verkey: str, did: str = None) -> str: """Given a full verkey and DID, return the abbreviated verkey.""" did_len = len(b58_to_bytes(did.split(":")[-1])) if did else 16 return f"~{bytes_to_b58(b58_to_bytes(full_verkey)[did_len:])}" + + +DID_EVENT_PREFIX = "acapy::ENDORSE_DID::" +EVENT_LISTENER_PATTERN = re.compile(f"^{DID_EVENT_PREFIX}(.*)?$") + + +async def notify_endorse_did_event(profile: Profile, did: str, meta_data: dict): + """Send notification for a DID post-process event.""" + await profile.notify( + DID_EVENT_PREFIX + did, + meta_data, + ) diff --git a/demo/runners/support/agent.py b/demo/runners/support/agent.py index b8f7c33ffd..06c927989f 100644 --- a/demo/runners/support/agent.py +++ b/demo/runners/support/agent.py @@ -426,6 +426,7 @@ def get_agent_args(self): ("--auto-request-endorsement",), ("--auto-write-transactions",), ("--auto-create-revocation-transactions",), + ("--auto-promote-author-did"), ("--endorser-alias", "endorser"), ] ) From ec594c047765862c3f8855ec8baebb1700d7ca46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andra=C5=BE=20Cuderman?= Date: Fri, 21 Jan 2022 09:48:31 +0100 Subject: [PATCH 014/872] feat(IDENT-4048): Update aiohttp dependency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Andraž Cuderman --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index fde3ab2124..30119fb677 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,8 @@ -aiohttp~=3.7.4 +aiohttp~=3.8.1 aiohttp-apispec~=2.2.1 aiohttp-cors~=0.7.0 apispec~=3.3.0 -async-timeout~=3.0.1 +async-timeout~=4.0.2 aioredis~=2.0.0 base58~=2.1.0 deepmerge~=0.3.0 From 03cca99d1550535a82c7c8be262893cc97c3b404 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andra=C5=BE=20Cuderman?= Date: Fri, 21 Jan 2022 12:57:07 +0100 Subject: [PATCH 015/872] fix(IDENT-4038): Fixed deprecation warnings and setup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Andraž Cuderman --- .../transport/inbound/tests/test_http_transport.py | 5 ----- .../transport/inbound/tests/test_ws_transport.py | 2 -- .../transport/outbound/tests/test_http_transport.py | 7 +------ .../transport/outbound/tests/test_ws_transport.py | 2 +- aries_cloudagent/utils/tests/test_http.py | 10 +--------- aries_cloudagent/utils/tests/test_repeat.py | 8 ++++---- 6 files changed, 7 insertions(+), 27 deletions(-) diff --git a/aries_cloudagent/transport/inbound/tests/test_http_transport.py b/aries_cloudagent/transport/inbound/tests/test_http_transport.py index 4a594f526d..c440fe7a57 100644 --- a/aries_cloudagent/transport/inbound/tests/test_http_transport.py +++ b/aries_cloudagent/transport/inbound/tests/test_http_transport.py @@ -71,7 +71,6 @@ def receive_message( def get_application(self): return self.transport.make_application() - @unittest_run_loop async def test_start_x(self): with async_mock.patch.object( test_module.web, "TCPSite", async_mock.MagicMock() @@ -82,7 +81,6 @@ async def test_start_x(self): with pytest.raises(test_module.InboundTransportSetupError): await self.transport.start() - @unittest_run_loop async def test_send_message(self): await self.transport.start() @@ -97,7 +95,6 @@ async def test_send_message(self): await self.transport.stop() - @unittest_run_loop async def test_send_receive_message(self): await self.transport.start() @@ -112,7 +109,6 @@ async def test_send_receive_message(self): await self.transport.stop() - @unittest_run_loop async def test_send_message_outliers(self): await self.transport.start() @@ -149,7 +145,6 @@ async def test_send_message_outliers(self): await self.transport.stop() - @unittest_run_loop async def test_invite_message_handler(self): await self.transport.start() diff --git a/aries_cloudagent/transport/inbound/tests/test_ws_transport.py b/aries_cloudagent/transport/inbound/tests/test_ws_transport.py index 472d69f50c..0e47bbecde 100644 --- a/aries_cloudagent/transport/inbound/tests/test_ws_transport.py +++ b/aries_cloudagent/transport/inbound/tests/test_ws_transport.py @@ -67,7 +67,6 @@ def receive_message( if self.result_event: self.result_event.set() - @unittest_run_loop async def test_start_x(self): with async_mock.patch.object( test_module.web, "TCPSite", async_mock.MagicMock() @@ -78,7 +77,6 @@ async def test_start_x(self): with pytest.raises(test_module.InboundTransportSetupError): await self.transport.start() - @unittest_run_loop async def test_message_and_response(self): await self.transport.start() diff --git a/aries_cloudagent/transport/outbound/tests/test_http_transport.py b/aries_cloudagent/transport/outbound/tests/test_http_transport.py index a8cc6bf344..0954f2b9d7 100644 --- a/aries_cloudagent/transport/outbound/tests/test_http_transport.py +++ b/aries_cloudagent/transport/outbound/tests/test_http_transport.py @@ -19,6 +19,7 @@ async def setUpAsync(self): self.profile = InMemoryProfile.test_profile() self.message_results = [] self.headers = {} + await super().setUpAsync() async def receive_message(self, request): payload = await request.json() @@ -34,7 +35,6 @@ async def get_application(self): app.add_routes([web.post("/", self.receive_message)]) return app - @unittest_run_loop async def test_handle_message_no_api_key(self): server_addr = f"http://localhost:{self.server.port}" @@ -49,7 +49,6 @@ async def send_message(transport, payload, endpoint): assert self.headers.get("x-api-key") is None assert self.headers.get("content-type") == "application/json" - @unittest_run_loop async def test_handle_message_api_key(self): server_addr = f"http://localhost:{self.server.port}" api_key = "test1234" @@ -68,7 +67,6 @@ async def send_message(transport, payload, endpoint, api_key): assert self.message_results == [{}] assert self.headers.get("x-api-key") == api_key - @unittest_run_loop async def test_handle_message_packed_compat_mime_type(self): server_addr = f"http://localhost:{self.server.port}" @@ -84,7 +82,6 @@ async def send_message(transport, payload, endpoint): assert self.message_results == [{}] assert self.headers.get("content-type") == "application/ssi-agent-wire" - @unittest_run_loop async def test_handle_message_packed_standard_mime_type(self): server_addr = f"http://localhost:{self.server.port}" @@ -101,7 +98,6 @@ async def send_message(transport, payload, endpoint): assert self.message_results == [{}] assert self.headers.get("content-type") == "application/didcomm-envelope-enc" - @unittest_run_loop async def test_stats(self): server_addr = f"http://localhost:{self.server.port}" @@ -122,7 +118,6 @@ async def send_message(transport, payload, endpoint): "outbound-http:POST": 1, } - @unittest_run_loop async def test_transport_coverage(self): transport = HttpTransport() assert transport.wire_format is None diff --git a/aries_cloudagent/transport/outbound/tests/test_ws_transport.py b/aries_cloudagent/transport/outbound/tests/test_ws_transport.py index 061f8f37df..12b2a98a75 100644 --- a/aries_cloudagent/transport/outbound/tests/test_ws_transport.py +++ b/aries_cloudagent/transport/outbound/tests/test_ws_transport.py @@ -13,6 +13,7 @@ class TestWsTransport(AioHTTPTestCase): async def setUpAsync(self): self.profile = InMemoryProfile.test_profile() self.message_results = [] + await super().setUpAsync() async def receive_message(self, request): ws = web.WebSocketResponse() @@ -35,7 +36,6 @@ async def get_application(self): app.add_routes([web.get("/", self.receive_message)]) return app - @unittest_run_loop async def test_handle_message(self): server_addr = f"ws://localhost:{self.server.port}" diff --git a/aries_cloudagent/utils/tests/test_http.py b/aries_cloudagent/utils/tests/test_http.py index 5a2886723b..e760769958 100644 --- a/aries_cloudagent/utils/tests/test_http.py +++ b/aries_cloudagent/utils/tests/test_http.py @@ -9,6 +9,7 @@ class TestTransportUtils(AioHTTPTestCase): async def setUpAsync(self): self.fail_calls = 0 self.succeed_calls = 0 + await super().setUpAsync() async def get_application(self): app = web.Application() @@ -31,7 +32,6 @@ async def succeed_route(self, request): ret = web.json_response([True]) return ret - @unittest_run_loop async def test_fetch_stream(self): server_addr = f"http://localhost:{self.server.port}" stream = await fetch_stream( @@ -41,7 +41,6 @@ async def test_fetch_stream(self): assert result == b"[true]" assert self.succeed_calls == 1 - @unittest_run_loop async def test_fetch_stream_default_client(self): server_addr = f"http://localhost:{self.server.port}" stream = await fetch_stream(f"{server_addr}/succeed") @@ -49,7 +48,6 @@ async def test_fetch_stream_default_client(self): assert result == b"[true]" assert self.succeed_calls == 1 - @unittest_run_loop async def test_fetch_stream_fail(self): server_addr = f"http://localhost:{self.server.port}" with self.assertRaises(FetchError): @@ -61,7 +59,6 @@ async def test_fetch_stream_fail(self): ) assert self.fail_calls == 2 - @unittest_run_loop async def test_fetch(self): server_addr = f"http://localhost:{self.server.port}" result = await fetch( @@ -70,14 +67,12 @@ async def test_fetch(self): assert result == [1] assert self.succeed_calls == 1 - @unittest_run_loop async def test_fetch_default_client(self): server_addr = f"http://localhost:{self.server.port}" result = await fetch(f"{server_addr}/succeed", json=True) assert result == [1] assert self.succeed_calls == 1 - @unittest_run_loop async def test_fetch_fail(self): server_addr = f"http://localhost:{self.server.port}" with self.assertRaises(FetchError): @@ -89,7 +84,6 @@ async def test_fetch_fail(self): ) assert self.fail_calls == 2 - @unittest_run_loop async def test_put_file(self): server_addr = f"http://localhost:{self.server.port}" with async_mock.patch("builtins.open", mock_open(read_data="data")): @@ -103,7 +97,6 @@ async def test_put_file(self): assert result == [1] assert self.succeed_calls == 1 - @unittest_run_loop async def test_put_file_default_client(self): server_addr = f"http://localhost:{self.server.port}" with async_mock.patch("builtins.open", mock_open(read_data="data")): @@ -116,7 +109,6 @@ async def test_put_file_default_client(self): assert result == [1] assert self.succeed_calls == 1 - @unittest_run_loop async def test_put_file_fail(self): server_addr = f"http://localhost:{self.server.port}" with async_mock.patch("builtins.open", mock_open(read_data="data")): diff --git a/aries_cloudagent/utils/tests/test_repeat.py b/aries_cloudagent/utils/tests/test_repeat.py index 63ac160867..0056334a8a 100644 --- a/aries_cloudagent/utils/tests/test_repeat.py +++ b/aries_cloudagent/utils/tests/test_repeat.py @@ -4,7 +4,7 @@ class TestRepeat(TestCase): - def test_iter(self): + async def test_iter(self): expect = [5, 7, 11, 17, 25] seq = test_module.RepeatSequence(5, interval=5.0, backoff=0.25) assert [round(attempt.next_interval) for attempt in seq] == expect @@ -12,9 +12,9 @@ def test_iter(self): seq = test_module.RepeatSequence(2, interval=5.0, backoff=0.25) attempt = seq.start() attempt = attempt.next() - attempt.timeout(interval=0.01) - with self.assertRaises(StopIteration): - attempt.next() + async with attempt.timeout(interval=0.01): + with self.assertRaises(StopIteration): + attempt.next() async def test_aiter(self): seq = test_module.RepeatSequence(5, interval=5.0, backoff=0.25) From 1319e374a2c42f55f0d9226526082985d5454157 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Fri, 21 Jan 2022 09:45:38 -0800 Subject: [PATCH 016/872] Promote DID to public after writing endorsed transaction Signed-off-by: Ian Costanzo --- aries_cloudagent/wallet/routes.py | 116 ++++++++++++++++++------------ 1 file changed, 69 insertions(+), 47 deletions(-) diff --git a/aries_cloudagent/wallet/routes.py b/aries_cloudagent/wallet/routes.py index 922ea68396..ad16f0315e 100644 --- a/aries_cloudagent/wallet/routes.py +++ b/aries_cloudagent/wallet/routes.py @@ -7,7 +7,7 @@ request_schema, response_schema, ) - +import logging from marshmallow import fields, validate from ..admin.request_context import AdminRequestContext @@ -38,6 +38,8 @@ from .key_type import KeyType from .util import EVENT_LISTENER_PATTERN +LOGGER = logging.getLogger(__name__) + class WalletModuleResponseSchema(OpenAPISchema): """Response schema for Wallet Module.""" @@ -392,46 +394,16 @@ async def wallet_set_public_did(request: web.BaseRequest): did = request.query.get("did") if not did: raise web.HTTPBadRequest(reason="Request query must include DID") - wallet_id = context.settings.get("wallet.id") info: DIDInfo = None try: - ledger = context.profile.inject_or(BaseLedger) - if not ledger: - reason = "No ledger available" - if not context.settings.get_value("wallet.type"): - reason += ": missing wallet-type?" - raise web.HTTPForbidden(reason=reason) - - async with ledger: - if not await ledger.get_key_for_did(did): - raise web.HTTPNotFound(reason=f"DID {did} is not posted to the ledger") - did_info: DIDInfo = None - async with context.session() as session: - wallet = session.inject_or(BaseWallet) - did_info = await wallet.get_local_did(did) - info = await wallet.set_public_did(did_info) - if info: - # Publish endpoint if necessary - endpoint = did_info.metadata.get("endpoint") - - if not endpoint: - async with context.session() as session: - wallet = session.inject_or(BaseWallet) - endpoint = context.settings.get("default_endpoint") - await wallet.set_did_endpoint(info.did, endpoint, ledger) - - async with ledger: - await ledger.update_endpoint_for_did(info.did, endpoint) - - # Multitenancy setup - multitenant_mgr = context.profile.inject_or(BaseMultitenantManager) - # Add multitenant relay mapping so implicit invitations are still routed - if multitenant_mgr and wallet_id: - await multitenant_mgr.add_key( - wallet_id, info.verkey, skip_if_exists=True - ) - + info = await promote_wallet_public_did( + context.profile, context, context.session, did + ) + except LookupError as err: + raise web.HTTPNotFound(reason=str(err)) from err + except PermissionError as err: + raise web.HTTPForbidden(reason=str(err)) from err except WalletNotFoundError as err: raise web.HTTPNotFound(reason=err.roll_up) from err except (LedgerError, WalletError) as err: @@ -440,6 +412,52 @@ async def wallet_set_public_did(request: web.BaseRequest): return web.json_response({"result": format_did_info(info)}) +async def promote_wallet_public_did( + profile: Profile, context: AdminRequestContext, session_fn, did: str +) -> DIDInfo: + """Promote supplied DID to the wallet public DID.""" + + # if running in multitenant mode this will be the sub-wallet + wallet_id = context.settings.get("wallet.id") + + info: DIDInfo = None + ledger = profile.inject_or(BaseLedger) + if not ledger: + reason = "No ledger available" + if not context.settings.get_value("wallet.type"): + reason += ": missing wallet-type?" + raise PermissionError(reason) + + async with ledger: + if not await ledger.get_key_for_did(did): + raise LookupError(f"DID {did} is not posted to the ledger") + did_info: DIDInfo = None + async with session_fn() as session: + wallet = session.inject_or(BaseWallet) + did_info = await wallet.get_local_did(did) + info = await wallet.set_public_did(did_info) + if info: + # Publish endpoint if necessary + endpoint = did_info.metadata.get("endpoint") + + if not endpoint: + async with session_fn() as session: + wallet = session.inject_or(BaseWallet) + endpoint = context.settings.get("default_endpoint") + await wallet.set_did_endpoint(info.did, endpoint, ledger) + + async with ledger: + await ledger.update_endpoint_for_did(info.did, endpoint) + + # Multitenancy setup + multitenant_mgr = profile.inject_or(BaseMultitenantManager) + # Add multitenant relay mapping so implicit invitations are still routed + if multitenant_mgr and wallet_id: + await multitenant_mgr.add_key(wallet_id, info.verkey, skip_if_exists=True) + + return info + + @docs( tags=["wallet"], summary="Update endpoint in wallet and on ledger if posted to it" ) @@ -559,16 +577,20 @@ async def on_register_nym_event(profile: Profile, event: Event): """Handle any events we need to support.""" # after the nym record is written, promote to wallet public DID - if is_author_role(profile) and profile.context.settings.get_value("endorser.auto_promote_author_did"): + if is_author_role(profile) and profile.context.settings.get_value( + "endorser.auto_promote_author_did" + ): did = event.payload["did"] - await promote_wallet_public_did(profile, did) - - -async def promote_wallet_public_did(profile: Profile, did: str): - """Promote supplied DID to the wallet public DID.""" - - # TODO - pass + try: + await promote_wallet_public_did( + profile, profile.context, profile.session, did + ) + except Exception as e: + # log the error, but continue + LOGGER.exception( + "Error accepting endorser invitation/configuring endorser connection: %s", + str(e), + ) async def register(app: web.Application): From 739b89c2b72788df325c79e420ae765ba624910f Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Fri, 21 Jan 2022 11:35:57 -0800 Subject: [PATCH 017/872] Fix demo endorser/multitenant combo Signed-off-by: Ian Costanzo --- demo/runners/support/agent.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/demo/runners/support/agent.py b/demo/runners/support/agent.py index 06c927989f..2a8cf1ddb5 100644 --- a/demo/runners/support/agent.py +++ b/demo/runners/support/agent.py @@ -1341,7 +1341,8 @@ async def handle_connections(self, message): # author responds to a multi-use invitation if message["state"] == "request": self.endorser_connection_id = message["connection_id"] - self._connection_ready = asyncio.Future() + if not self._connection_ready: + self._connection_ready = asyncio.Future() # finish off the connection if message["connection_id"] == self.endorser_connection_id: @@ -1418,10 +1419,10 @@ async def connect_wallet_to_endorser(agent, endorser_agent): # Generate an invitation log_msg("Generate endorser invite ...") endorser_agent._connection_ready = asyncio.Future() - endorser_connection = await endorser_agent.admin_POST( - "/connections/create-invitation" + endorser_agent.endorser_connection_id = None + endorser_connection = await endorser_agent.get_invite( + use_did_exchange=endorser_agent.use_did_exchange, ) - endorser_agent.endorser_connection_id = endorser_connection["connection_id"] # accept the invitation log_msg("Accept endorser invite ...") From 0de6ea8e486ea544ea3dbb7dbbd9db089144ccb0 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Fri, 21 Jan 2022 17:21:03 -0800 Subject: [PATCH 018/872] Allow endorser to use a non-public DID to endorse Signed-off-by: Ian Costanzo --- aries_cloudagent/config/argparse.py | 22 +++++++++++++++++++ aries_cloudagent/ledger/base.py | 1 + aries_cloudagent/ledger/indy.py | 6 +++-- aries_cloudagent/ledger/indy_vdr.py | 3 ++- .../endorse_transaction/v1_0/manager.py | 21 +++++++++++++++--- .../endorse_transaction/v1_0/routes.py | 13 +++++++++++ 6 files changed, 60 insertions(+), 6 deletions(-) diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index a71d82da83..7a0cd5121a 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -1690,6 +1690,17 @@ def add_arguments(self, parser: ArgumentParser): "agent who will be endorsing transactions." ), ) + parser.add_argument( + "--endorser-endorse-with-did", + type=str, + metavar="", + env_var="ACAPY_ENDORSER_ENDORSE_WITH_DID", + help=( + "For transaction Endorsers, specify the DID to use to endorse " + "transactions. The default (if not specified) is to use the " + "Endorser's Public DID." + ), + ) parser.add_argument( "--endorser-alias", type=str, @@ -1766,6 +1777,17 @@ def get_settings(self, args: Namespace): "Authors" ) + if args.endorser_endorse_with_did: + if settings["endorser.endorser"]: + settings[ + "endorser.endorser_endorse_with_did" + ] = args.endorser_endorse_with_did + else: + raise ArgsParseError( + "Parameter --endorser-endorse-with-did should only be set for " + "transaction Endorsers" + ) + if args.endorser_alias: if settings["endorser.author"]: settings["endorser.endorser_alias"] = args.endorser_alias diff --git a/aries_cloudagent/ledger/base.py b/aries_cloudagent/ledger/base.py index 68f91992ee..bf52118c70 100644 --- a/aries_cloudagent/ledger/base.py +++ b/aries_cloudagent/ledger/base.py @@ -160,6 +160,7 @@ def taa_digest(self, version: str, text: str): async def txn_endorse( self, request_json: str, + endorse_did: DIDInfo = None, ) -> str: """Endorse (sign) the provided transaction.""" diff --git a/aries_cloudagent/ledger/indy.py b/aries_cloudagent/ledger/indy.py index 6a8945e4e9..abf5cfe561 100644 --- a/aries_cloudagent/ledger/indy.py +++ b/aries_cloudagent/ledger/indy.py @@ -291,13 +291,14 @@ async def get_wallet_public_did(self) -> DIDInfo: async def _endorse( self, request_json: str, + endorse_did: DIDInfo = None, ) -> str: if not self.pool.handle: raise ClosedPoolError( f"Cannot endorse request with closed pool '{self.pool.name}'" ) - public_info = await self.get_wallet_public_did() + public_info = endorse_did if endorse_did else await self.get_wallet_public_did() if not public_info: raise BadLedgerRequestError( "Cannot endorse transaction without a public DID" @@ -403,9 +404,10 @@ async def _submit( async def txn_endorse( self, request_json: str, + endorse_did: DIDInfo = None, ) -> str: """Endorse a (signed) ledger transaction.""" - return await self._endorse(request_json) + return await self._endorse(request_json, endorse_did=endorse_did) async def txn_submit( self, diff --git a/aries_cloudagent/ledger/indy_vdr.py b/aries_cloudagent/ledger/indy_vdr.py index 5f82082bf5..4e519d8c20 100644 --- a/aries_cloudagent/ledger/indy_vdr.py +++ b/aries_cloudagent/ledger/indy_vdr.py @@ -1351,6 +1351,7 @@ async def get_wallet_public_did(self) -> DIDInfo: async def txn_endorse( self, request_json: str, + endorse_did: DIDInfo = None, ) -> str: """Endorse (sign) the provided transaction.""" try: @@ -1360,7 +1361,7 @@ async def txn_endorse( async with self.profile.session() as session: wallet = session.inject(BaseWallet) - sign_did = await wallet.get_public_did() + sign_did = endorse_did if endorse_did else await wallet.get_public_did() if not sign_did: raise BadLedgerRequestError( "Cannot endorse transaction without a public DID" diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py index a1eddbaf58..4c8b7d0d21 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py @@ -205,6 +205,7 @@ async def create_endorse_response( self, transaction: TransactionRecord, state: str, + use_endorser_did: str = None, ): """ Create a response to endorse a transaction. @@ -234,10 +235,22 @@ async def create_endorse_response( wallet: BaseWallet = session.inject_or(BaseWallet) if not wallet: raise StorageError("No wallet available") - endorser_did_info = await wallet.get_public_did() + endorser_did_info = None + override_did = ( + use_endorser_did + if use_endorser_did + else session.context.settings.get_value( + "endorser.endorser_endorse_with_did" + ) + ) + if override_did: + endorser_did_info = await wallet.get_local_did(override_did) + else: + endorser_did_info = await wallet.get_public_did() if not endorser_did_info: raise StorageError( - "Transaction cannot be endorsed as there is no Public DID in wallet" + "Transaction cannot be endorsed as there is no Public DID in wallet " + "or Endorser DID specified" ) endorser_did = endorser_did_info.did endorser_verkey = endorser_did_info.verkey @@ -251,7 +264,9 @@ async def create_endorse_response( raise LedgerError(reason=reason) async with ledger: - endorsed_msg = await shield(ledger.txn_endorse(transaction_json)) + endorsed_msg = await shield( + ledger.txn_endorse(transaction_json, endorse_did=endorser_did_info) + ) # need to return the endorsed msg or else the ledger will reject the # eventual transaction write diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py index 4a31f089e2..dfb2749049 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py @@ -60,6 +60,15 @@ class TranIdMatchInfoSchema(OpenAPISchema): ) +class EndorserDIDInfoSchema(OpenAPISchema): + """Path parameters and validators for request Endorser DID.""" + + endorser_did = fields.Str( + description="Endorser DID", + required=False, + ) + + class AssignTransactionJobsSchema(OpenAPISchema): """Assign transaction related jobs to connection record.""" @@ -289,6 +298,7 @@ async def transaction_create_request(request: web.BaseRequest): tags=["endorse-transaction"], summary="For Endorser to endorse a particular transaction record", ) +@querystring_schema(EndorserDIDInfoSchema()) @match_info_schema(TranIdMatchInfoSchema()) @response_schema(TransactionRecordSchema(), 200) async def endorse_transaction_response(request: web.BaseRequest): @@ -305,6 +315,7 @@ async def endorse_transaction_response(request: web.BaseRequest): outbound_handler = request["outbound_message_router"] transaction_id = request.match_info["tran_id"] + endorser_did = request.query.get("endorser_did") try: async with context.profile.session() as session: transaction = await TransactionRecord.retrieve_by_id( @@ -313,6 +324,7 @@ async def endorse_transaction_response(request: web.BaseRequest): connection_record = await ConnRecord.retrieve_by_id( session, transaction.connection_id ) + # provided DID is validated in the TransactionManager except StorageNotFoundError as err: raise web.HTTPNotFound(reason=err.roll_up) from err @@ -341,6 +353,7 @@ async def endorse_transaction_response(request: web.BaseRequest): ) = await transaction_mgr.create_endorse_response( transaction=transaction, state=TransactionRecord.STATE_TRANSACTION_ENDORSED, + use_endorser_did=endorser_did, ) except (IndyIssuerError, LedgerError) as err: raise web.HTTPBadRequest(reason=err.roll_up) from err From 3c9ae0be1186a371db73cebdfdd456a1722b1eaa Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Sat, 22 Jan 2022 09:20:00 -0800 Subject: [PATCH 019/872] FFix some code issues Signed-off-by: Ian Costanzo --- aries_cloudagent/config/argparse.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index 7a0cd5121a..5d2e55af4e 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -1820,7 +1820,6 @@ def get_settings(self, args: Namespace): if settings["endorser.author"]: settings["endorser.auto_request"] = True else: - pass raise ArgsParseError( "Parameter --auto-request-endorsement should only be set for " "transaction Authors" @@ -1830,7 +1829,6 @@ def get_settings(self, args: Namespace): if settings["endorser.endorser"]: settings["endorser.auto_endorse"] = True else: - pass raise ArgsParseError( "Parameter --auto-endorser-transactions should only be set for " "transaction Endorsers" @@ -1840,7 +1838,6 @@ def get_settings(self, args: Namespace): if settings["endorser.author"]: settings["endorser.auto_write"] = True else: - pass raise ArgsParseError( "Parameter --auto-write-transactions should only be set for " "transaction Authors" @@ -1850,7 +1847,6 @@ def get_settings(self, args: Namespace): if settings["endorser.author"]: settings["endorser.auto_create_rev_reg"] = True else: - pass raise ArgsParseError( "Parameter --auto-create-revocation-transactions should only be set " "for transaction Authors" @@ -1860,7 +1856,6 @@ def get_settings(self, args: Namespace): if settings["endorser.author"]: settings["endorser.auto_promote_author_did"] = True else: - pass raise ArgsParseError( "Parameter --auto-promote-author-did should only be set " "for transaction Authors" From 482fd7ded82db2690c471a38fcc10c8fa7f636dd Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Mon, 24 Jan 2022 05:31:29 -0800 Subject: [PATCH 020/872] Fix exception logging Signed-off-by: Ian Costanzo --- aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py | 3 +-- aries_cloudagent/wallet/routes.py | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py index dfb2749049..7e3bc6820e 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py @@ -800,11 +800,10 @@ async def on_startup_event(profile: Profile, event: Event): value = {"endorser_did": endorser_did, "endorser_name": endorser_alias} await conn_record.metadata_set(session, key="endorser_info", value=value) - except Exception as e: + except Exception: # log the error, but continue LOGGER.exception( "Error accepting endorser invitation/configuring endorser connection: %s", - str(e), ) diff --git a/aries_cloudagent/wallet/routes.py b/aries_cloudagent/wallet/routes.py index ad16f0315e..7cdd27be0e 100644 --- a/aries_cloudagent/wallet/routes.py +++ b/aries_cloudagent/wallet/routes.py @@ -585,11 +585,10 @@ async def on_register_nym_event(profile: Profile, event: Event): await promote_wallet_public_did( profile, profile.context, profile.session, did ) - except Exception as e: + except Exception: # log the error, but continue LOGGER.exception( "Error accepting endorser invitation/configuring endorser connection: %s", - str(e), ) From cb000e3048caa9d9cdc071025f8e1c616e4f77a6 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 31 Jan 2022 14:20:54 +0100 Subject: [PATCH 021/872] feat: enable webhook events for mediation records Signed-off-by: Timo Glastra --- .../coordinate_mediation/v1_0/models/mediation_record.py | 1 + 1 file changed, 1 insertion(+) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/models/mediation_record.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/models/mediation_record.py index 9841a70265..0bfda79c7f 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/models/mediation_record.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/models/mediation_record.py @@ -19,6 +19,7 @@ class Meta: schema_class = "MediationRecordSchema" RECORD_TYPE = "mediation_requests" + RECORD_TOPIC = "mediation" RECORD_ID_NAME = "mediation_id" TAG_NAMES = {"state", "role", "connection_id"} From 8dcd715d02d46644b7ceea8d455f260b7a845ec4 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 31 Jan 2022 14:40:56 +0100 Subject: [PATCH 022/872] style: format with stable black release Signed-off-by: Timo Glastra --- aries_cloudagent/admin/server.py | 32 +++++++------------ .../messaging/jsonld/tests/test_routes.py | 2 +- aries_cloudagent/messaging/util.py | 2 +- .../utils/tests/test_outofband.py | 2 +- demo/features/steps/0160-connection.py | 4 +-- demo/features/steps/0453-issue-credential.py | 4 +-- 6 files changed, 19 insertions(+), 27 deletions(-) diff --git a/aries_cloudagent/admin/server.py b/aries_cloudagent/admin/server.py index 01b3b2196f..3043f10ca4 100644 --- a/aries_cloudagent/admin/server.py +++ b/aries_cloudagent/admin/server.py @@ -151,14 +151,10 @@ def send_fn(self) -> Coroutine: async def ready_middleware(request: web.BaseRequest, handler: Coroutine): """Only continue if application is ready to take work.""" - if ( - str(request.rel_url).rstrip("/") - in ( - "/status/live", - "/status/ready", - ) - or request.app._state.get("ready") - ): + if str(request.rel_url).rstrip("/") in ( + "/status/live", + "/status/ready", + ) or request.app._state.get("ready"): try: return await handler(request) except (LedgerConfigError, LedgerTransactionError) as e: @@ -267,18 +263,14 @@ async def make_application(self) -> web.Application: assert self.admin_insecure_mode ^ bool(self.admin_api_key) def is_unprotected_path(path: str): - return ( - path - in [ - "/api/doc", - "/api/docs/swagger.json", - "/favicon.ico", - "/ws", # ws handler checks authentication - "/status/live", - "/status/ready", - ] - or path.startswith("/static/swagger/") - ) + return path in [ + "/api/doc", + "/api/docs/swagger.json", + "/favicon.ico", + "/ws", # ws handler checks authentication + "/status/live", + "/status/ready", + ] or path.startswith("/static/swagger/") # If admin_api_key is None, then admin_insecure_mode must be set so # we can safely enable the admin server with no security diff --git a/aries_cloudagent/messaging/jsonld/tests/test_routes.py b/aries_cloudagent/messaging/jsonld/tests/test_routes.py index cf2fbc35e9..129ac26515 100644 --- a/aries_cloudagent/messaging/jsonld/tests/test_routes.py +++ b/aries_cloudagent/messaging/jsonld/tests/test_routes.py @@ -446,7 +446,7 @@ async def test_sign_credential(self): ), "degree": { "type": "BachelorDegree", - "name": u"Bachelor of Encyclopædic Arts", + "name": "Bachelor of Encyclopædic Arts", }, }, }, diff --git a/aries_cloudagent/messaging/util.py b/aries_cloudagent/messaging/util.py index a5e28b15a1..2c23795a36 100644 --- a/aries_cloudagent/messaging/util.py +++ b/aries_cloudagent/messaging/util.py @@ -11,7 +11,7 @@ LOGGER = logging.getLogger(__name__) -I32_BOUND = 2 ** 31 +I32_BOUND = 2**31 def datetime_to_str(dt: Union[str, datetime]) -> str: diff --git a/aries_cloudagent/utils/tests/test_outofband.py b/aries_cloudagent/utils/tests/test_outofband.py index 22c4c059ee..2029553471 100644 --- a/aries_cloudagent/utils/tests/test_outofband.py +++ b/aries_cloudagent/utils/tests/test_outofband.py @@ -17,7 +17,7 @@ class TestOutOfBand(TestCase): def test_serialize_oob(self): invi = InvitationMessage( - comment="my sister", label=u"ma sœur", services=[TestOutOfBand.test_did] + comment="my sister", label="ma sœur", services=[TestOutOfBand.test_did] ) result = test_module.serialize_outofband( diff --git a/demo/features/steps/0160-connection.py b/demo/features/steps/0160-connection.py index 479611964d..3814f847c6 100644 --- a/demo/features/steps/0160-connection.py +++ b/demo/features/steps/0160-connection.py @@ -28,7 +28,7 @@ @given("{n} agents") -@given(u"we have {n} agents") +@given("we have {n} agents") def step_impl(context, n): """Startup 'n' agents based on the options provided in the context table parameters.""" @@ -112,7 +112,7 @@ def step_impl(context, agent_name): @given('"{sender}" and "{receiver}" have an existing connection') def step_impl(context, sender, receiver): context.execute_steps( - u''' + ''' When "''' + sender + '''" generates a connection invitation diff --git a/demo/features/steps/0453-issue-credential.py b/demo/features/steps/0453-issue-credential.py index 7cac93d76c..2914489406 100644 --- a/demo/features/steps/0453-issue-credential.py +++ b/demo/features/steps/0453-issue-credential.py @@ -226,7 +226,7 @@ def step_impl(context, holder): ) def step_impl(context, holder, schema_name, credential_data, issuer): context.execute_steps( - u''' + ''' Given "''' + issuer + """" is ready to issue a json-ld credential for """ @@ -255,7 +255,7 @@ def step_impl(context, holder, schema_name, credential_data, issuer): ) def step_impl(context, holder, schema_name, credential_data, issuer): context.execute_steps( - u''' + ''' Given "''' + issuer + """" is ready to issue a credential for """ From e16c6f6d5ed25b52f7fc2f63e8c1cc4103a80423 Mon Sep 17 00:00:00 2001 From: Wade Barnes Date: Thu, 10 Feb 2022 13:03:28 -0800 Subject: [PATCH 023/872] Upgrade ConfigArgParse to version 1.5.3 - This allows multi-instance multi-argument parameters such as `--inbound-transport` to be specified using the associated environment variable. Version 1.2.3 of ConfigArgParse was unable to parse the associated environment variables properly. - Example using the environment variable for `--inbound-transport`: `ACAPY_INBOUND_TRANSPORT=[[\"http\",\"0.0.0.0\",\"8021\"],[\"ws\",\"0.0.0.0\",\"8023\"]]` Results In: ``` :::::::::::::::::::::::::::::::::::::::::::::: :: Aries Cloud Agent :: :: :: :: :: :: Inbound Transports: :: :: :: :: - http://0.0.0.0:8021 :: :: - ws://0.0.0.0:8023 :: ... ``` Signed-off-by: Wade Barnes --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 30119fb677..6d98ce200c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -16,7 +16,7 @@ requests~=2.25.0 packaging~=20.4 pyld~=2.0.3 pyyaml~=5.4.0 -ConfigArgParse~=1.2.3 +ConfigArgParse~=1.5.3 pyjwt~=1.7.1 pydid~=0.3.3 jsonpath_ng==1.5.2 From abe3744800fc3b2daa78ca6cd8dea9b0a17199fd Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Fri, 11 Feb 2022 12:38:46 -0800 Subject: [PATCH 024/872] Fix for non-revoc proof with no timestamp Signed-off-by: Ian Costanzo --- aries_cloudagent/indy/verifier.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/indy/verifier.py b/aries_cloudagent/indy/verifier.py index 7c219ec136..3c2a143ccb 100644 --- a/aries_cloudagent/indy/verifier.py +++ b/aries_cloudagent/indy/verifier.py @@ -70,7 +70,14 @@ def non_revoc_intervals(self, pres_req: dict, pres: dict, cred_defs: dict): uuid, ) - if all(spec.get("timestamp") is None for spec in pres["identifiers"]): + if all( + ( + spec.get("timestamp") is None + and "revocation" + not in cred_defs[spec["cred_def_id"]]["value"] + ) + for spec in pres["identifiers"] + ): pres_req.pop("non_revoked", None) LOGGER.warning( ( From 289b6a4fbc26865597cd99009562b1dc9fce7828 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Fri, 11 Feb 2022 12:45:09 -0800 Subject: [PATCH 025/872] Black formatting Signed-off-by: Ian Costanzo --- aries_cloudagent/admin/server.py | 32 +++++++++++++++++++----------- aries_cloudagent/indy/verifier.py | 3 +-- aries_cloudagent/messaging/util.py | 2 +- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/aries_cloudagent/admin/server.py b/aries_cloudagent/admin/server.py index 3043f10ca4..01b3b2196f 100644 --- a/aries_cloudagent/admin/server.py +++ b/aries_cloudagent/admin/server.py @@ -151,10 +151,14 @@ def send_fn(self) -> Coroutine: async def ready_middleware(request: web.BaseRequest, handler: Coroutine): """Only continue if application is ready to take work.""" - if str(request.rel_url).rstrip("/") in ( - "/status/live", - "/status/ready", - ) or request.app._state.get("ready"): + if ( + str(request.rel_url).rstrip("/") + in ( + "/status/live", + "/status/ready", + ) + or request.app._state.get("ready") + ): try: return await handler(request) except (LedgerConfigError, LedgerTransactionError) as e: @@ -263,14 +267,18 @@ async def make_application(self) -> web.Application: assert self.admin_insecure_mode ^ bool(self.admin_api_key) def is_unprotected_path(path: str): - return path in [ - "/api/doc", - "/api/docs/swagger.json", - "/favicon.ico", - "/ws", # ws handler checks authentication - "/status/live", - "/status/ready", - ] or path.startswith("/static/swagger/") + return ( + path + in [ + "/api/doc", + "/api/docs/swagger.json", + "/favicon.ico", + "/ws", # ws handler checks authentication + "/status/live", + "/status/ready", + ] + or path.startswith("/static/swagger/") + ) # If admin_api_key is None, then admin_insecure_mode must be set so # we can safely enable the admin server with no security diff --git a/aries_cloudagent/indy/verifier.py b/aries_cloudagent/indy/verifier.py index 3c2a143ccb..d8a2d61a10 100644 --- a/aries_cloudagent/indy/verifier.py +++ b/aries_cloudagent/indy/verifier.py @@ -73,8 +73,7 @@ def non_revoc_intervals(self, pres_req: dict, pres: dict, cred_defs: dict): if all( ( spec.get("timestamp") is None - and "revocation" - not in cred_defs[spec["cred_def_id"]]["value"] + and "revocation" not in cred_defs[spec["cred_def_id"]]["value"] ) for spec in pres["identifiers"] ): diff --git a/aries_cloudagent/messaging/util.py b/aries_cloudagent/messaging/util.py index 2c23795a36..a5e28b15a1 100644 --- a/aries_cloudagent/messaging/util.py +++ b/aries_cloudagent/messaging/util.py @@ -11,7 +11,7 @@ LOGGER = logging.getLogger(__name__) -I32_BOUND = 2**31 +I32_BOUND = 2 ** 31 def datetime_to_str(dt: Union[str, datetime]) -> str: From 27012bf0e1c700912dd70edeac816bd7bbc05588 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Fri, 11 Feb 2022 12:50:40 -0800 Subject: [PATCH 026/872] black formatting Signed-off-by: Ian Costanzo --- aries_cloudagent/admin/server.py | 32 +++++++++++------------------- aries_cloudagent/messaging/util.py | 2 +- 2 files changed, 13 insertions(+), 21 deletions(-) diff --git a/aries_cloudagent/admin/server.py b/aries_cloudagent/admin/server.py index 01b3b2196f..3043f10ca4 100644 --- a/aries_cloudagent/admin/server.py +++ b/aries_cloudagent/admin/server.py @@ -151,14 +151,10 @@ def send_fn(self) -> Coroutine: async def ready_middleware(request: web.BaseRequest, handler: Coroutine): """Only continue if application is ready to take work.""" - if ( - str(request.rel_url).rstrip("/") - in ( - "/status/live", - "/status/ready", - ) - or request.app._state.get("ready") - ): + if str(request.rel_url).rstrip("/") in ( + "/status/live", + "/status/ready", + ) or request.app._state.get("ready"): try: return await handler(request) except (LedgerConfigError, LedgerTransactionError) as e: @@ -267,18 +263,14 @@ async def make_application(self) -> web.Application: assert self.admin_insecure_mode ^ bool(self.admin_api_key) def is_unprotected_path(path: str): - return ( - path - in [ - "/api/doc", - "/api/docs/swagger.json", - "/favicon.ico", - "/ws", # ws handler checks authentication - "/status/live", - "/status/ready", - ] - or path.startswith("/static/swagger/") - ) + return path in [ + "/api/doc", + "/api/docs/swagger.json", + "/favicon.ico", + "/ws", # ws handler checks authentication + "/status/live", + "/status/ready", + ] or path.startswith("/static/swagger/") # If admin_api_key is None, then admin_insecure_mode must be set so # we can safely enable the admin server with no security diff --git a/aries_cloudagent/messaging/util.py b/aries_cloudagent/messaging/util.py index a5e28b15a1..2c23795a36 100644 --- a/aries_cloudagent/messaging/util.py +++ b/aries_cloudagent/messaging/util.py @@ -11,7 +11,7 @@ LOGGER = logging.getLogger(__name__) -I32_BOUND = 2 ** 31 +I32_BOUND = 2**31 def datetime_to_str(dt: Union[str, datetime]) -> str: From 0da83a190de51492e7406800e71c24dd2fd6107f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andra=C5=BE=20Cuderman?= Date: Fri, 18 Feb 2022 10:20:25 +0100 Subject: [PATCH 027/872] fix(IDENT-4193): Move save error state to session context MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Andraž Cuderman --- .../protocols/present_proof/v2_0/routes.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/aries_cloudagent/protocols/present_proof/v2_0/routes.py b/aries_cloudagent/protocols/present_proof/v2_0/routes.py index ede46b7d4f..8d24d1afe4 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/routes.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/routes.py @@ -1197,13 +1197,13 @@ async def present_proof_problem_report(request: web.BaseRequest): description = body["description"] try: - async with await context.profile.session() as session: + async with context.profile.session() as session: pres_ex_record = await V20PresExRecord.retrieve_by_id(session, pres_ex_id) + await pres_ex_record.save_error_state( + session, + reason=f"created problem report: {description}", + ) report = problem_report_for_record(pres_ex_record, description) - await pres_ex_record.save_error_state( - session, - reason=f"created problem report: {description}", - ) except StorageNotFoundError as err: # other party does not care about meta-problems raise web.HTTPNotFound(reason=err.roll_up) from err except StorageError as err: From 273fc71378f360dcfd856d0f275c99f75020e6ef Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 22 Feb 2022 10:57:27 +0100 Subject: [PATCH 028/872] feat: query connections by their_public_did Signed-off-by: Timo Glastra --- aries_cloudagent/protocols/connections/v1_0/routes.py | 3 +++ .../protocols/connections/v1_0/tests/test_routes.py | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/protocols/connections/v1_0/routes.py b/aries_cloudagent/protocols/connections/v1_0/routes.py index ae65d52f99..ac16fa67d3 100644 --- a/aries_cloudagent/protocols/connections/v1_0/routes.py +++ b/aries_cloudagent/protocols/connections/v1_0/routes.py @@ -186,6 +186,8 @@ class ConnectionsListQueryStringSchema(OpenAPISchema): ), ) their_did = fields.Str(description="Their DID", required=False, **INDY_DID) + their_public_did = fields.Str( + description="Their Public DID", required=False, **INDY_DID) their_role = fields.Str( description="Their role in the connection protocol", required=False, @@ -332,6 +334,7 @@ async def connections_list(request: web.BaseRequest): "their_did", "request_id", "invitation_key", + "their_public_did" ): if param_name in request.query and request.query[param_name] != "": tag_filter[param_name] = request.query[param_name] diff --git a/aries_cloudagent/protocols/connections/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/connections/v1_0/tests/test_routes.py index be40c47e1d..dd5f0d26bb 100644 --- a/aries_cloudagent/protocols/connections/v1_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/connections/v1_0/tests/test_routes.py @@ -32,6 +32,7 @@ async def test_connections_list(self): "their_role": ConnRecord.Role.REQUESTER.rfc160, "connection_protocol": ConnRecord.Protocol.RFC_0160.aries_protocol, "invitation_key": "some-invitation-key", + "their_public_did": "a_public_did" } STATE_COMPLETED = ConnRecord.State.COMPLETED @@ -89,7 +90,7 @@ async def test_connections_list(self): await test_module.connections_list(self.request) mock_conn_rec.query.assert_called_once_with( ANY, - {"invitation_id": "dummy", "invitation_key": "some-invitation-key"}, + {"invitation_id": "dummy", "invitation_key": "some-invitation-key", "their_public_did": "a_public_did"}, post_filter_positive={ "their_role": [v for v in ConnRecord.Role.REQUESTER.value], "connection_protocol": ConnRecord.Protocol.RFC_0160.aries_protocol, From 75e9d0c3023a199cb2ce59df9ef0b9372392b161 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 22 Feb 2022 11:02:53 +0100 Subject: [PATCH 029/872] style: black formatting Signed-off-by: Timo Glastra --- aries_cloudagent/protocols/connections/v1_0/routes.py | 5 +++-- .../protocols/connections/v1_0/tests/test_routes.py | 8 ++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/aries_cloudagent/protocols/connections/v1_0/routes.py b/aries_cloudagent/protocols/connections/v1_0/routes.py index ac16fa67d3..b35a6b9067 100644 --- a/aries_cloudagent/protocols/connections/v1_0/routes.py +++ b/aries_cloudagent/protocols/connections/v1_0/routes.py @@ -187,7 +187,8 @@ class ConnectionsListQueryStringSchema(OpenAPISchema): ) their_did = fields.Str(description="Their DID", required=False, **INDY_DID) their_public_did = fields.Str( - description="Their Public DID", required=False, **INDY_DID) + description="Their Public DID", required=False, **INDY_DID + ) their_role = fields.Str( description="Their role in the connection protocol", required=False, @@ -334,7 +335,7 @@ async def connections_list(request: web.BaseRequest): "their_did", "request_id", "invitation_key", - "their_public_did" + "their_public_did", ): if param_name in request.query and request.query[param_name] != "": tag_filter[param_name] = request.query[param_name] diff --git a/aries_cloudagent/protocols/connections/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/connections/v1_0/tests/test_routes.py index dd5f0d26bb..b769191c25 100644 --- a/aries_cloudagent/protocols/connections/v1_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/connections/v1_0/tests/test_routes.py @@ -32,7 +32,7 @@ async def test_connections_list(self): "their_role": ConnRecord.Role.REQUESTER.rfc160, "connection_protocol": ConnRecord.Protocol.RFC_0160.aries_protocol, "invitation_key": "some-invitation-key", - "their_public_did": "a_public_did" + "their_public_did": "a_public_did", } STATE_COMPLETED = ConnRecord.State.COMPLETED @@ -90,7 +90,11 @@ async def test_connections_list(self): await test_module.connections_list(self.request) mock_conn_rec.query.assert_called_once_with( ANY, - {"invitation_id": "dummy", "invitation_key": "some-invitation-key", "their_public_did": "a_public_did"}, + { + "invitation_id": "dummy", + "invitation_key": "some-invitation-key", + "their_public_did": "a_public_did", + }, post_filter_positive={ "their_role": [v for v in ConnRecord.Role.REQUESTER.value], "connection_protocol": ConnRecord.Protocol.RFC_0160.aries_protocol, From 7bd1391528aa962dd2e62168c24a378cd0fdb154 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Tue, 22 Feb 2022 13:11:03 -0500 Subject: [PATCH 030/872] refactor: use constants for mediation metadata strings Signed-off-by: Daniel Bluhm --- .../v1_0/handlers/connection_request_handler.py | 16 ++++++---------- .../protocols/connections/v1_0/manager.py | 4 +++- .../coordinate_mediation/v1_0/manager.py | 2 ++ .../didexchange/v1_0/handlers/request_handler.py | 10 ++++------ .../protocols/out_of_band/v1_0/manager.py | 4 +++- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/aries_cloudagent/protocols/connections/v1_0/handlers/connection_request_handler.py b/aries_cloudagent/protocols/connections/v1_0/handlers/connection_request_handler.py index b1f26a85ae..38a7e385f5 100644 --- a/aries_cloudagent/protocols/connections/v1_0/handlers/connection_request_handler.py +++ b/aries_cloudagent/protocols/connections/v1_0/handlers/connection_request_handler.py @@ -1,12 +1,8 @@ """Connection request handler.""" -from .....messaging.base_handler import ( - BaseHandler, - BaseResponder, - RequestContext, -) from .....connections.models.conn_record import ConnRecord - +from .....messaging.base_handler import BaseHandler, BaseResponder, RequestContext +from ....coordinate_mediation.v1_0.manager import MediationManager from ..manager import ConnectionManager, ConnectionManagerError from ..messages.connection_request import ConnectionRequest from ..messages.problem_report import ConnectionProblemReport @@ -30,19 +26,19 @@ async def handle(self, context: RequestContext, responder: BaseResponder): profile = context.profile mgr = ConnectionManager(profile) + mediation_id = None if context.connection_record: async with profile.session() as session: mediation_metadata = await context.connection_record.metadata_get( - session, "mediation", {} + session, MediationManager.METADATA_KEY, {} ) - else: - mediation_metadata = {} + mediation_id = mediation_metadata.get(MediationManager.METADATA_ID) try: connection = await mgr.receive_request( context.message, context.message_receipt, - mediation_id=mediation_metadata.get("id"), + mediation_id=mediation_id, ) if connection.accept == ConnRecord.ACCEPT_AUTO: diff --git a/aries_cloudagent/protocols/connections/v1_0/manager.py b/aries_cloudagent/protocols/connections/v1_0/manager.py index 96e72b8b37..a1fb3af093 100644 --- a/aries_cloudagent/protocols/connections/v1_0/manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/manager.py @@ -248,7 +248,9 @@ async def create_invitation( # Save that this invitation was created with mediation async with self.profile.session() as session: await connection.metadata_set( - session, "mediation", {"id": mediation_record.mediation_id} + session, + MediationManager.METADATA_KEY, + {MediationManager.METADATA_ID: mediation_record.mediation_id}, ) if keylist_updates: diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py index bd86337700..8fb2d2a449 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py @@ -58,6 +58,8 @@ class MediationManager: DEFAULT_MEDIATOR_RECORD_TYPE = "default_mediator" SEND_REQ_AFTER_CONNECTION = "send_mediation_request_on_connection" SET_TO_DEFAULT_ON_GRANTED = "set_to_default_on_granted" + METADATA_KEY = "mediation" + METADATA_ID = "id" def __init__(self, profile: Profile): """Initialize Mediation Manager. diff --git a/aries_cloudagent/protocols/didexchange/v1_0/handlers/request_handler.py b/aries_cloudagent/protocols/didexchange/v1_0/handlers/request_handler.py index b7f036e11f..efcc02c64d 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/handlers/request_handler.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/handlers/request_handler.py @@ -2,9 +2,8 @@ from .....connections.models.conn_record import ConnRecord from .....messaging.base_handler import BaseHandler, BaseResponder, RequestContext - +from ....coordinate_mediation.v1_0.manager import MediationManager from ....problem_report.v1_0.message import ProblemReport - from ..manager import DIDXManager, DIDXManagerError from ..messages.request import DIDXRequest @@ -27,15 +26,14 @@ async def handle(self, context: RequestContext, responder: BaseResponder): profile = context.profile mgr = DIDXManager(profile) + mediation_id = None if context.connection_record: async with profile.session() as session: mediation_metadata = await context.connection_record.metadata_get( - session, "mediation", {} + session, MediationManager.METADATA_KEY, {} ) - else: - mediation_metadata = {} + mediation_id = mediation_metadata.get(MediationManager.METADATA_ID) - mediation_id = mediation_metadata.get("id") try: conn_rec = await mgr.receive_request( request=context.message, diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py index 7b8478e258..9fd4542c02 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py @@ -325,7 +325,9 @@ async def create_invitation( async with self.profile.session() as session: await conn_rec.metadata_set( - session, "mediation", {"id": mediation_record.mediation_id} + session, + MediationManager.METADATA_KEY, + {MediationManager.METADATA_ID: mediation_record.mediation_id}, ) if keylist_updates: From e4946e1ea54d7aa3d65e47a1d3d6783677f72baf Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Tue, 22 Feb 2022 13:11:34 -0500 Subject: [PATCH 031/872] fix: ensure auto conn responses are correctly mediated Signed-off-by: Daniel Bluhm --- .../connections/v1_0/handlers/connection_request_handler.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/protocols/connections/v1_0/handlers/connection_request_handler.py b/aries_cloudagent/protocols/connections/v1_0/handlers/connection_request_handler.py index 38a7e385f5..662e0bfbee 100644 --- a/aries_cloudagent/protocols/connections/v1_0/handlers/connection_request_handler.py +++ b/aries_cloudagent/protocols/connections/v1_0/handlers/connection_request_handler.py @@ -42,7 +42,9 @@ async def handle(self, context: RequestContext, responder: BaseResponder): ) if connection.accept == ConnRecord.ACCEPT_AUTO: - response = await mgr.create_response(connection) + response = await mgr.create_response( + connection, mediation_id=mediation_id + ) await responder.send_reply( response, connection_id=connection.connection_id ) From 69a542af93b79197ba25b38c5d18b72913aa695c Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Wed, 19 Jan 2022 14:05:25 -0800 Subject: [PATCH 032/872] close sessions before performing additional work Signed-off-by: Andrew Whitehead --- .../v2_0/formats/indy/handler.py | 29 ++++---- .../v2_0/formats/ld_proof/handler.py | 16 ++--- .../issue_credential/v2_0/manager.py | 72 +++++++++---------- 3 files changed, 59 insertions(+), 58 deletions(-) diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/formats/indy/handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/formats/indy/handler.py index 2257a91cd7..b536e69300 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/formats/indy/handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/formats/indy/handler.py @@ -97,25 +97,26 @@ async def get_detail_record(self, cred_ex_id: str) -> V20CredExRecordIndy: session, cred_ex_id ) - if len(records) > 1: - LOGGER.warning( - "Cred ex id %s has %d %s detail records: should be 1", - cred_ex_id, - len(records), - IndyCredFormatHandler.format.api, - ) - return records[0] if records else None + if len(records) > 1: + LOGGER.warning( + "Cred ex id %s has %d %s detail records: should be 1", + cred_ex_id, + len(records), + IndyCredFormatHandler.format.api, + ) + return records[0] if records else None async def _check_uniqueness(self, cred_ex_id: str): """Raise exception on evidence that cred ex already has cred issued to it.""" async with self.profile.session() as session: - if await IndyCredFormatHandler.format.detail.query_by_cred_ex_id( + exist = await IndyCredFormatHandler.format.detail.query_by_cred_ex_id( session, cred_ex_id - ): - raise V20CredFormatError( - f"{IndyCredFormatHandler.format.api} detail record already " - f"exists for cred ex id {cred_ex_id}" - ) + ) + if exist: + raise V20CredFormatError( + f"{IndyCredFormatHandler.format.api} detail record already " + f"exists for cred ex id {cred_ex_id}" + ) def get_format_identifier(self, message_type: str) -> str: """Get attachment format identifier for format and message combination. diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py index e2ede57342..796e71e67f 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py @@ -128,14 +128,14 @@ async def get_detail_record(self, cred_ex_id: str) -> V20CredExRecordLDProof: session, cred_ex_id ) - if len(records) > 1: - LOGGER.warning( - "Cred ex id %s has %d %s detail records: should be 1", - cred_ex_id, - len(records), - LDProofCredFormatHandler.format.api, - ) - return records[0] if records else None + if len(records) > 1: + LOGGER.warning( + "Cred ex id %s has %d %s detail records: should be 1", + cred_ex_id, + len(records), + LDProofCredFormatHandler.format.api, + ) + return records[0] if records else None def get_format_identifier(self, message_type: str) -> str: """Get attachment format identifier for format and message combination. diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/manager.py b/aries_cloudagent/protocols/issue_credential/v2_0/manager.py index a5931011d7..310deb1f92 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/manager.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/manager.py @@ -435,15 +435,15 @@ async def receive_request( ), ) - for format in cred_request_message.formats: - cred_format = V20CredFormat.Format.get(format.format) - if cred_format: - await cred_format.handler(self.profile).receive_request( - cred_ex_record, cred_request_message - ) + for format in cred_request_message.formats: + cred_format = V20CredFormat.Format.get(format.format) + if cred_format: + await cred_format.handler(self.profile).receive_request( + cred_ex_record, cred_request_message + ) - cred_ex_record.cred_request = cred_request_message - cred_ex_record.state = V20CredExRecord.STATE_REQUEST_RECEIVED + cred_ex_record.cred_request = cred_request_message + cred_ex_record.state = V20CredExRecord.STATE_REQUEST_RECEIVED async with self._profile.session() as session: await cred_ex_record.save(session, reason="receive v2.0 credential request") @@ -549,37 +549,37 @@ async def receive_credential( ) ) - cred_request_message = cred_ex_record.cred_request - req_formats = [ - V20CredFormat.Format.get(fmt.format) - for fmt in cred_request_message.formats - if V20CredFormat.Format.get(fmt.format) - ] - issue_formats = [ - V20CredFormat.Format.get(fmt.format) - for fmt in cred_issue_message.formats - if V20CredFormat.Format.get(fmt.format) - ] - handled_formats = [] - - # check that we didn't receive any formats not present in the request - if set(issue_formats) - set(req_formats): - raise V20CredManagerError( - "Received issue credential format(s) not present in credential " - f"request: {set(issue_formats) - set(req_formats)}" - ) + cred_request_message = cred_ex_record.cred_request + req_formats = [ + V20CredFormat.Format.get(fmt.format) + for fmt in cred_request_message.formats + if V20CredFormat.Format.get(fmt.format) + ] + issue_formats = [ + V20CredFormat.Format.get(fmt.format) + for fmt in cred_issue_message.formats + if V20CredFormat.Format.get(fmt.format) + ] + handled_formats = [] - for issue_format in issue_formats: - await issue_format.handler(self.profile).receive_credential( - cred_ex_record, cred_issue_message - ) - handled_formats.append(issue_format) + # check that we didn't receive any formats not present in the request + if set(issue_formats) - set(req_formats): + raise V20CredManagerError( + "Received issue credential format(s) not present in credential " + f"request: {set(issue_formats) - set(req_formats)}" + ) - if len(handled_formats) == 0: - raise V20CredManagerError("No supported credential formats received.") + for issue_format in issue_formats: + await issue_format.handler(self.profile).receive_credential( + cred_ex_record, cred_issue_message + ) + handled_formats.append(issue_format) + + if len(handled_formats) == 0: + raise V20CredManagerError("No supported credential formats received.") - cred_ex_record.cred_issue = cred_issue_message - cred_ex_record.state = V20CredExRecord.STATE_CREDENTIAL_RECEIVED + cred_ex_record.cred_issue = cred_issue_message + cred_ex_record.state = V20CredExRecord.STATE_CREDENTIAL_RECEIVED async with self._profile.session() as session: await cred_ex_record.save(session, reason="receive v2.0 credential issue") From 9720896d4d061b7cf38c53de8a97e06fb36a7ff6 Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Wed, 2 Feb 2022 15:39:29 -0800 Subject: [PATCH 033/872] fix --proposal flag for performance demo Signed-off-by: Andrew Whitehead --- demo/runners/performance.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/demo/runners/performance.py b/demo/runners/performance.py index e90c176087..aec0cb320b 100644 --- a/demo/runners/performance.py +++ b/demo/runners/performance.py @@ -73,6 +73,11 @@ async def handle_connections(self, payload): self.log("Connected") self._connection_ready.set_result(True) + async def handle_issue_credential(self, payload): + cred_ex_id = payload["credential_exchange_id"] + self.credential_state[cred_ex_id] = payload["state"] + self.credential_event.set() + async def handle_issue_credential_v2_0(self, payload): cred_ex_id = payload["cred_ex_id"] self.credential_state[cred_ex_id] = payload["state"] @@ -103,7 +108,7 @@ async def check_received_creds(self) -> Tuple[int, int]: pending = 0 total = len(self.credential_state) for result in self.credential_state.values(): - if result != "done": + if result != "done" and result != "credential_acked": pending += 1 if self.credential_event.is_set(): continue From 1897c0fcfdf969c4fc1c2314599412485de1ea2f Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Wed, 2 Feb 2022 15:39:58 -0800 Subject: [PATCH 034/872] logging cleanups in issue-credential protocols Signed-off-by: Andrew Whitehead --- .../v1_0/handlers/credential_issue_handler.py | 2 +- .../v1_0/handlers/credential_offer_handler.py | 2 +- .../v1_0/handlers/credential_proposal_handler.py | 2 +- .../v1_0/handlers/credential_request_handler.py | 2 +- .../protocols/issue_credential/v1_0/manager.py | 4 +++- .../v1_0/models/credential_exchange.py | 2 +- .../v2_0/handlers/cred_issue_handler.py | 2 +- .../v2_0/handlers/cred_offer_handler.py | 2 +- .../v2_0/handlers/cred_proposal_handler.py | 2 +- .../protocols/issue_credential/v2_0/manager.py | 8 +++++--- .../protocols/issue_credential/v2_0/routes.py | 15 ++++++++++++++- 11 files changed, 30 insertions(+), 13 deletions(-) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_issue_handler.py b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_issue_handler.py index 1e6404e521..34a6052b2e 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_issue_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_issue_handler.py @@ -62,7 +62,7 @@ async def handle(self, context: RequestContext, responder: BaseResponder): StorageError, ) as err: # treat failure to store as mangled on receipt hence protocol error - self._logger.exception(err) + self._logger.exception("Error storing issued credential") if cred_ex_record: async with profile.session() as session: await cred_ex_record.save_error_state( diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_offer_handler.py b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_offer_handler.py index cddc2e4010..c148f0632e 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_offer_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_offer_handler.py @@ -70,7 +70,7 @@ async def handle(self, context: RequestContext, responder: BaseResponder): LedgerError, StorageError, ) as err: - self._logger.exception(err) + self._logger.exception("Error responding to credential offer") if cred_ex_record: async with profile.session() as session: await cred_ex_record.save_error_state( diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_proposal_handler.py b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_proposal_handler.py index fe2a94bf72..502eee171b 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_proposal_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_proposal_handler.py @@ -72,7 +72,7 @@ async def handle(self, context: RequestContext, responder: BaseResponder): LedgerError, StorageError, ) as err: - self._logger.exception(err) + self._logger.exception("Error responding to credential proposal") if cred_ex_record: async with profile.session() as session: await cred_ex_record.save_error_state( diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_request_handler.py b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_request_handler.py index 39ff2e73b5..1ceacfd7b8 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_request_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_request_handler.py @@ -74,7 +74,7 @@ async def handle(self, context: RequestContext, responder: BaseResponder): LedgerError, StorageError, ) as err: - self._logger.exception(err) + self._logger.exception("Error responding to credential request") if cred_ex_record: async with profile.session() as session: await cred_ex_record.save_error_state( diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/manager.py b/aries_cloudagent/protocols/issue_credential/v1_0/manager.py index d94759ba05..0ae08a8614 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/manager.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/manager.py @@ -850,7 +850,9 @@ async def send_credential_ack( await cred_ex_record.delete_record(session) # all done: delete except StorageError as err: - LOGGER.exception(err) # holder still owes an ack: carry on + LOGGER.exception( + "Error updating credential exchange" + ) # holder still owes an ack: carry on responder = self._profile.inject_or(BaseResponder) if responder: diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py b/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py index e8118bf591..2fcbc2248d 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py @@ -219,7 +219,7 @@ async def save_error_state( log_override=log_override, ) except StorageError as err: - LOGGER.exception(err) + LOGGER.exception("Error saving credential exchange error state") @property def record_value(self) -> dict: diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_issue_handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_issue_handler.py index d33c44be5f..e14f09ff19 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_issue_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_issue_handler.py @@ -61,7 +61,7 @@ async def handle(self, context: RequestContext, responder: BaseResponder): V20CredManagerError, ) as err: # treat failure to store as mangled on receipt hence protocol error - self._logger.exception(err) + self._logger.exception("Error storing issued credential") if cred_ex_record: async with context.profile.session() as session: await cred_ex_record.save_error_state( diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_offer_handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_offer_handler.py index aaab690de6..cebaa68b8d 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_offer_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_offer_handler.py @@ -68,7 +68,7 @@ async def handle(self, context: RequestContext, responder: BaseResponder): StorageError, V20CredManagerError, ) as err: - self._logger.exception(err) + self._logger.exception("Error responding to credential offer") if cred_ex_record: async with profile.session() as session: await cred_ex_record.save_error_state( diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_proposal_handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_proposal_handler.py index c9f2be9e8a..81315bf4f4 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_proposal_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_proposal_handler.py @@ -69,7 +69,7 @@ async def handle(self, context: RequestContext, responder: BaseResponder): StorageError, V20CredManagerError, ) as err: - self._logger.exception(err) + self._logger.exception("Error responding to credential proposal") async with profile.session() as session: await cred_ex_record.save_error_state( session, diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/manager.py b/aries_cloudagent/protocols/issue_credential/v2_0/manager.py index 310deb1f92..b21d200856 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/manager.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/manager.py @@ -646,11 +646,13 @@ async def send_cred_ack( # FIXME - re-fetch record to check state, apply transactional update await cred_ex_record.save(session, reason="store credential v2.0") - if cred_ex_record.auto_remove: - await cred_ex_record.delete_record(session) # all done: delete + if cred_ex_record.auto_remove: + await self.delete_cred_ex_record(cred_ex_record.cred_ex_id) except StorageError as err: - LOGGER.exception(err) # holder still owes an ack: carry on + LOGGER.exception( + "Error sending credential ack" + ) # holder still owes an ack: carry on responder = self._profile.inject_or(BaseResponder) if responder: diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/routes.py b/aries_cloudagent/protocols/issue_credential/v2_0/routes.py index 27bfe4938b..b7d98758b0 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/routes.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/routes.py @@ -1,6 +1,7 @@ """Credential exchange admin routes.""" -from ....vc.ld_proofs.error import LinkedDataProofException +import logging + from json.decoder import JSONDecodeError from typing import Mapping @@ -33,6 +34,7 @@ ) from ....storage.error import StorageError, StorageNotFoundError from ....utils.tracing import trace_event, get_timer, AdminAPIMessageTracingSchema +from ....vc.ld_proofs.error import LinkedDataProofException from . import problem_report_for_record, report_problem from .manager import V20CredManager, V20CredManagerError @@ -47,6 +49,8 @@ from .formats.handler import V20CredFormatError from .formats.ld_proof.models.cred_detail import LDProofVCDetailSchema +LOGGER = logging.getLogger(__name__) + class V20IssueCredentialModuleResponseSchema(OpenAPISchema): """Response schema for v2.0 Issue Credential Module.""" @@ -658,6 +662,7 @@ async def credential_exchange_send(request: web.BaseRequest): V20CredManagerError, V20CredFormatError, ) as err: + LOGGER.exception("Error preparing credential offer") if cred_ex_record: async with profile.session() as session: await cred_ex_record.save_error_state(session, reason=err.roll_up) @@ -746,6 +751,7 @@ async def credential_exchange_send_proposal(request: web.BaseRequest): result = cred_ex_record.serialize() except (BaseModelError, StorageError) as err: + LOGGER.exception("Error preparing credential proposal") if cred_ex_record: async with profile.session() as session: await cred_ex_record.save_error_state(session, reason=err.roll_up) @@ -866,6 +872,7 @@ async def credential_exchange_create_free_offer(request: web.BaseRequest): V20CredFormatError, V20CredManagerError, ) as err: + LOGGER.exception("Error creating free credential offer") if cred_ex_record: async with profile.session() as session: await cred_ex_record.save_error_state(session, reason=err.roll_up) @@ -947,6 +954,7 @@ async def credential_exchange_send_free_offer(request: web.BaseRequest): V20CredFormatError, V20CredManagerError, ) as err: + LOGGER.exception("Error preparing free credential offer") if cred_ex_record: async with profile.session() as session: await cred_ex_record.save_error_state(session, reason=err.roll_up) @@ -1051,6 +1059,7 @@ async def credential_exchange_send_bound_offer(request: web.BaseRequest): V20CredFormatError, V20CredManagerError, ) as err: + LOGGER.exception("Error preparing bound credential offer") if cred_ex_record: async with profile.session() as session: await cred_ex_record.save_error_state(session, reason=err.roll_up) @@ -1155,6 +1164,7 @@ async def credential_exchange_send_free_request(request: web.BaseRequest): StorageError, V20CredManagerError, ) as err: + LOGGER.exception("Error preparing free credential request") if cred_ex_record: async with profile.session() as session: await cred_ex_record.save_error_state(session, reason=err.roll_up) @@ -1244,6 +1254,7 @@ async def credential_exchange_send_bound_request(request: web.BaseRequest): V20CredFormatError, V20CredManagerError, ) as err: + LOGGER.exception("Error preparing bound credential request") if cred_ex_record: async with profile.session() as session: await cred_ex_record.save_error_state(session, reason=err.roll_up) @@ -1329,6 +1340,7 @@ async def credential_exchange_issue(request: web.BaseRequest): V20CredFormatError, V20CredManagerError, ) as err: + LOGGER.exception("Error preparing issued credential") if cred_ex_record: async with profile.session() as session: await cred_ex_record.save_error_state(session, reason=err.roll_up) @@ -1408,6 +1420,7 @@ async def credential_exchange_store(request: web.BaseRequest): StorageError, V20CredManagerError, ) as err: # treat failure to store as mangled on receipt hence protocol error + LOGGER.exception("Error storing issued credential") if cred_ex_record: async with profile.session() as session: await cred_ex_record.save_error_state(session, reason=err.roll_up) From 79cdddc055b990f03925ffce5128f1f5411a8770 Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Wed, 2 Feb 2022 15:48:49 -0800 Subject: [PATCH 035/872] remove unused, broken session limiter Signed-off-by: Andrew Whitehead --- aries_cloudagent/transport/inbound/manager.py | 8 -------- aries_cloudagent/transport/inbound/tests/test_manager.py | 1 - 2 files changed, 9 deletions(-) diff --git a/aries_cloudagent/transport/inbound/manager.py b/aries_cloudagent/transport/inbound/manager.py index 3ae54122dc..65d081454d 100644 --- a/aries_cloudagent/transport/inbound/manager.py +++ b/aries_cloudagent/transport/inbound/manager.py @@ -1,6 +1,5 @@ """Inbound transport manager.""" -import asyncio import logging import uuid from collections import OrderedDict @@ -43,7 +42,6 @@ def __init__( self.registered_transports = {} self.running_transports = {} self.sessions = OrderedDict() - self.session_limit: asyncio.Semaphore = None self.task_queue = TaskQueue() self.undelivered_queue: DeliveryQueue = None @@ -68,8 +66,6 @@ async def setup(self): if self.profile.context.settings.get("transport.enable_undelivered_queue"): self.undelivered_queue = DeliveryQueue() - # self.session_limit = asyncio.Semaphore(50) - def register(self, config: InboundTransportConfiguration) -> str: """ Register transport module. @@ -163,8 +159,6 @@ async def create_session( client_info: An optional dict describing the client wire_format: Override the wire format for this session """ - if self.session_limit: - await self.session_limit if not wire_format: wire_format = self.profile.context.inject(BaseWireFormat) session = InboundSession( @@ -195,8 +189,6 @@ def closed_session(self, session: InboundSession): """ if session.session_id in self.sessions: del self.sessions[session.session_id] - if self.session_limit: - self.session_limit.release() if session.response_buffer: if self.return_inbound: self.return_inbound(session.profile, session.response_buffer) diff --git a/aries_cloudagent/transport/inbound/tests/test_manager.py b/aries_cloudagent/transport/inbound/tests/test_manager.py index f5ab7eeaa3..ae13051416 100644 --- a/aries_cloudagent/transport/inbound/tests/test_manager.py +++ b/aries_cloudagent/transport/inbound/tests/test_manager.py @@ -83,7 +83,6 @@ async def test_create_session(self): test_accept = True test_can_respond = True test_client_info = {"client": "info"} - mgr.session_limit = asyncio.Semaphore(16) session = await mgr.create_session( test_transport, accept_undelivered=test_accept, From 2e97a87934411e60602693edf902cb16eaa62939 Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Wed, 2 Feb 2022 15:48:56 -0800 Subject: [PATCH 036/872] fix warnings Signed-off-by: Andrew Whitehead --- aries_cloudagent/protocols/issue_credential/v1_0/manager.py | 2 +- .../issue_credential/v1_0/models/credential_exchange.py | 2 +- aries_cloudagent/protocols/issue_credential/v2_0/manager.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/manager.py b/aries_cloudagent/protocols/issue_credential/v1_0/manager.py index 0ae08a8614..0802dc4e99 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/manager.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/manager.py @@ -849,7 +849,7 @@ async def send_credential_ack( if cred_ex_record.auto_remove: await cred_ex_record.delete_record(session) # all done: delete - except StorageError as err: + except StorageError: LOGGER.exception( "Error updating credential exchange" ) # holder still owes an ack: carry on diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py b/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py index 2fcbc2248d..1a32ba2abc 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py @@ -218,7 +218,7 @@ async def save_error_state( log_params=log_params, log_override=log_override, ) - except StorageError as err: + except StorageError: LOGGER.exception("Error saving credential exchange error state") @property diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/manager.py b/aries_cloudagent/protocols/issue_credential/v2_0/manager.py index b21d200856..5dd4a7e347 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/manager.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/manager.py @@ -649,7 +649,7 @@ async def send_cred_ack( if cred_ex_record.auto_remove: await self.delete_cred_ex_record(cred_ex_record.cred_ex_id) - except StorageError as err: + except StorageError: LOGGER.exception( "Error sending credential ack" ) # holder still owes an ack: carry on From d06c75c1f88799178701d2b79a69dc5a78c52e66 Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Fri, 4 Feb 2022 19:02:55 -0800 Subject: [PATCH 037/872] support saving storage records with a predetermined ID (for tests); add for_update option to retrieval methods Signed-off-by: Andrew Whitehead --- .../messaging/models/base_record.py | 20 ++++++++++++++----- aries_cloudagent/storage/askar.py | 15 +++++++++----- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/aries_cloudagent/messaging/models/base_record.py b/aries_cloudagent/messaging/models/base_record.py index 07c56b2008..44ed51e4fe 100644 --- a/aries_cloudagent/messaging/models/base_record.py +++ b/aries_cloudagent/messaging/models/base_record.py @@ -89,6 +89,7 @@ def __init__( *, created_at: Union[str, datetime] = None, updated_at: Union[str, datetime] = None, + new_with_id: bool = False, ): """Initialize a new BaseRecord.""" if not self.RECORD_TYPE: @@ -99,6 +100,7 @@ def __init__( ) self._id = id self._last_state = state + self._new_with_id = new_with_id self.state = state self.created_at = datetime_to_str(created_at) self.updated_at = datetime_to_str(updated_at) @@ -218,7 +220,11 @@ async def clear_cached_key(cls, session: ProfileSession, cache_key: str): @classmethod async def retrieve_by_id( - cls: Type[RecordType], session: ProfileSession, record_id: str + cls: Type[RecordType], + session: ProfileSession, + record_id: str, + *, + for_update=False, ) -> RecordType: """ Retrieve a stored record by ID. @@ -230,7 +236,7 @@ async def retrieve_by_id( storage = session.inject(BaseStorage) result = await storage.get_record( - cls.RECORD_TYPE, record_id, {"retrieveTags": False} + cls.RECORD_TYPE, record_id, {"forUpdate": for_update, "retrieveTags": False} ) vals = json.loads(result.value) return cls.from_storage(record_id, vals) @@ -241,6 +247,8 @@ async def retrieve_by_tag_filter( session: ProfileSession, tag_filter: dict, post_filter: dict = None, + *, + for_update=False, ) -> RecordType: """ Retrieve a record by tag filter. @@ -256,7 +264,7 @@ async def retrieve_by_tag_filter( rows = await storage.find_all_records( cls.RECORD_TYPE, cls.prefix_tag_filter(tag_filter), - options={"retrieveTags": False}, + options={"forUpdate": for_update, "retrieveTags": False}, ) found = None for record in rows: @@ -349,15 +357,17 @@ async def save( try: self.updated_at = time_now() storage = session.inject(BaseStorage) - if self._id: + if self._id and not self._new_with_id: record = self.storage_record await storage.update_record(record, record.value, record.tags) new_record = False else: - self._id = str(uuid.uuid4()) + if not self._id: + self._id = str(uuid.uuid4()) self.created_at = self.updated_at await storage.add_record(self.storage_record) new_record = True + self._new_with_id = False finally: params = {self.RECORD_TYPE: self.serialize()} if log_params: diff --git a/aries_cloudagent/storage/askar.py b/aries_cloudagent/storage/askar.py index 75683d4af9..2a48cfb2a7 100644 --- a/aries_cloudagent/storage/askar.py +++ b/aries_cloudagent/storage/askar.py @@ -84,10 +84,11 @@ async def get_record( raise StorageError("Record type not provided") if not record_id: raise StorageError("Record ID not provided") - if not options: - options = {} + for_update = bool(options and options.get("forUpdate")) try: - item = await self._session.handle.fetch(record_type, record_id) + item = await self._session.handle.fetch( + record_type, record_id, for_update=for_update + ) except AskarError as err: raise StorageError("Error when fetching storage record") from err if not item: @@ -155,9 +156,10 @@ async def find_record( tag_query: Tags to query options: Dictionary of backend-specific options """ + for_update = bool(options and options.get("forUpdate")) try: results = await self._session.handle.fetch_all( - type_filter, tag_query, limit=2 + type_filter, tag_query, limit=2, for_update=for_update ) except AskarError as err: raise StorageError("Error when finding storage record") from err @@ -180,8 +182,11 @@ async def find_all_records( options: Mapping = None, ): """Retrieve all records matching a particular type filter and tag query.""" + for_update = bool(options and options.get("forUpdate")) results = [] - for row in await self._session.handle.fetch_all(type_filter, tag_query): + for row in await self._session.handle.fetch_all( + type_filter, tag_query, for_update=for_update + ): results.append( StorageRecord( type=row.category, From d7f248f1843f7f4a2a250080c6679471aa5af13a Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Fri, 4 Feb 2022 19:03:22 -0800 Subject: [PATCH 038/872] use transactions and add state checks in issue-credential 1.0 protocol Signed-off-by: Andrew Whitehead --- .../v1_0/handlers/credential_issue_handler.py | 2 +- .../v1_0/handlers/credential_offer_handler.py | 4 +- .../handlers/credential_request_handler.py | 2 +- .../issue_credential/v1_0/manager.py | 232 ++++++++++++------ .../v1_0/models/credential_exchange.py | 10 +- .../v1_0/tests/test_manager.py | 148 +++++++---- 6 files changed, 278 insertions(+), 120 deletions(-) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_issue_handler.py b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_issue_handler.py index 34a6052b2e..938d2a2a1f 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_issue_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_issue_handler.py @@ -50,7 +50,7 @@ async def handle(self, context: RequestContext, responder: BaseResponder): ) # Automatically move to next state if flag is set - if context.settings.get("debug.auto_store_credential"): + if cred_ex_record and context.settings.get("debug.auto_store_credential"): try: cred_ex_record = await credential_manager.store_credential( cred_ex_record diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_offer_handler.py b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_offer_handler.py index c148f0632e..af1f3844ae 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_offer_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_offer_handler.py @@ -52,7 +52,9 @@ async def handle(self, context: RequestContext, responder: BaseResponder): ) # If auto respond is turned on, automatically reply with credential request - if context.settings.get("debug.auto_respond_credential_offer"): + if cred_ex_record and context.settings.get( + "debug.auto_respond_credential_offer" + ): credential_request_message = None try: ( diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_request_handler.py b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_request_handler.py index 1ceacfd7b8..bc6d028c10 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_request_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_request_handler.py @@ -52,7 +52,7 @@ async def handle(self, context: RequestContext, responder: BaseResponder): ) # If auto_issue is enabled, respond immediately - if cred_ex_record.auto_issue: + if cred_ex_record and cred_ex_record.auto_issue: if ( cred_ex_record.credential_proposal_dict and cred_ex_record.credential_proposal_dict.credential_proposal diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/manager.py b/aries_cloudagent/protocols/issue_credential/v1_0/manager.py index 0802dc4e99..75fe7895ac 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/manager.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/manager.py @@ -346,35 +346,42 @@ async def receive_offer( cred_def_id=cred_def_id, ) - async with self._profile.session() as session: + async with self._profile.transaction() as txn: # Get credential exchange record (holder sent proposal first) # or create it (issuer sent offer first) try: cred_ex_record = await ( V10CredentialExchange.retrieve_by_connection_and_thread( - session, connection_id, message._thread_id + txn, connection_id, message._thread_id, for_update=True ) ) - cred_ex_record.credential_proposal_dict = credential_proposal_dict except StorageNotFoundError: # issuer sent this offer free of any proposal cred_ex_record = V10CredentialExchange( connection_id=connection_id, thread_id=message._thread_id, initiator=V10CredentialExchange.INITIATOR_EXTERNAL, role=V10CredentialExchange.ROLE_HOLDER, - credential_proposal_dict=credential_proposal_dict, auto_remove=not self._profile.settings.get( "preserve_exchange_records" ), trace=(message._trace is not None), ) + else: + if cred_ex_record.state != V10CredentialExchange.STATE_PROPOSAL_SENT: + raise CredentialManagerError( + f"Credential exchange {cred_ex_record.credential_exchange_id} " + f"in {cred_ex_record.state} state " + f"(must be {V10CredentialExchange.STATE_PROPOSAL_SENT})" + ) + cred_ex_record.credential_proposal_dict = credential_proposal_dict cred_ex_record.credential_offer = indy_offer cred_ex_record.state = V10CredentialExchange.STATE_OFFER_RECEIVED cred_ex_record.schema_id = schema_id cred_ex_record.credential_definition_id = cred_def_id - await cred_ex_record.save(session, reason="receive credential offer") + await cred_ex_record.save(txn, reason="receive credential offer") + await txn.commit() return cred_ex_record @@ -402,6 +409,8 @@ async def create_request( credential_definition_id = cred_ex_record.credential_definition_id cred_offer_ser = cred_ex_record._credential_offer.ser + cred_req_ser = None + cred_req_meta = None async def _create(): ledger_exec_inst = self._profile.inject(IndyLedgerRequestsExecutor) @@ -432,6 +441,8 @@ async def _create(): "create_request called multiple times for v1.0 credential exchange: %s", cred_ex_record.credential_exchange_id, ) + cred_req_ser = cred_ex_record._credential_request.ser + cred_req_meta = cred_ex_record.credential_request_metadata else: nonce = cred_offer_ser["nonce"] cache_key = ( @@ -448,27 +459,33 @@ async def _create(): await entry.set_result(cred_req_result, 3600) if not cred_req_result: cred_req_result = await _create() - - ( - cred_ex_record.credential_request, - cred_ex_record.credential_request_metadata, - ) = (cred_req_result["request"], cred_req_result["metadata"]) + cred_req_ser = cred_req_result["request"] + cred_req_meta = cred_req_result["metadata"] credential_request_message = CredentialRequest( - requests_attach=[ - CredentialRequest.wrap_indy_cred_req( - cred_ex_record._credential_request.ser - ) - ] + requests_attach=[CredentialRequest.wrap_indy_cred_req(cred_req_ser)] ) credential_request_message._thread = {"thid": cred_ex_record.thread_id} credential_request_message.assign_trace_decorator( self._profile.settings, cred_ex_record.trace ) - cred_ex_record.state = V10CredentialExchange.STATE_REQUEST_SENT - async with self._profile.session() as session: - await cred_ex_record.save(session, reason="create credential request") + async with self._profile.transaction() as txn: + cred_ex_record = await V10CredentialExchange.retrieve_by_id( + txn, cred_ex_record.credential_exchange_id, for_update=True + ) + if cred_ex_record.state != V10CredentialExchange.STATE_OFFER_RECEIVED: + raise CredentialManagerError( + f"Credential exchange {cred_ex_record.credential_exchange_id} " + f"in {cred_ex_record.state} state " + f"(must be {V10CredentialExchange.STATE_OFFER_RECEIVED})" + ) + + cred_ex_record.credential_request = cred_req_ser + cred_ex_record.credential_request_metadata = cred_req_meta + cred_ex_record.state = V10CredentialExchange.STATE_REQUEST_SENT + await cred_ex_record.save(txn, reason="create credential request") + await txn.commit() return (cred_ex_record, credential_request_message) @@ -486,28 +503,37 @@ async def receive_request(self, message: CredentialRequest, connection_id: str): assert len(message.requests_attach or []) == 1 credential_request = message.indy_cred_req(0) - async with self._profile.session() as session: + async with self._profile.transaction() as txn: try: cred_ex_record = await ( V10CredentialExchange.retrieve_by_connection_and_thread( - session, connection_id, message._thread_id + txn, connection_id, message._thread_id, for_update=True ) ) except StorageNotFoundError: try: cred_ex_record = await V10CredentialExchange.retrieve_by_tag_filter( - session, + txn, {"thread_id": message._thread_id}, {"connection_id": None}, + for_update=True, ) cred_ex_record.connection_id = connection_id except StorageNotFoundError: raise CredentialManagerError( "Indy issue credential format can't start from credential request" - ) + ) from None + if cred_ex_record.state != V10CredentialExchange.STATE_OFFER_SENT: + LOGGER.error( + "Skipping credential request; exchange state is %s (id=%s)", + cred_ex_record.state, + cred_ex_record.credential_exchange_id, + ) + return None cred_ex_record.credential_request = credential_request cred_ex_record.state = V10CredentialExchange.STATE_REQUEST_RECEIVED - await cred_ex_record.save(session, reason="receive credential request") + await cred_ex_record.save(txn, reason="receive credential request") + await txn.commit() return cred_ex_record @@ -540,6 +566,7 @@ async def issue_credential( schema_id = cred_ex_record.schema_id rev_reg = None + credential_ser = None if cred_ex_record.credential: LOGGER.warning( @@ -547,6 +574,7 @@ async def issue_credential( + "credential exchange record %s - abstaining", cred_ex_record.credential_exchange_id, ) + credential_ser = cred_ex_record._credential.ser else: cred_offer_ser = cred_ex_record._credential_offer.ser cred_req_ser = cred_ex_record._credential_request.ser @@ -623,7 +651,7 @@ async def issue_credential( raise CredentialManagerError( f"Cred def id {cred_ex_record.credential_definition_id} " "has no active revocation registry" - ) + ) from None del revoc credential_values = ( @@ -645,6 +673,7 @@ async def issue_credential( cred_ex_record.revoc_reg_id, tails_path, ) + credential_ser = json.loads(credential_json) # If the rev reg is now full if rev_reg and rev_reg.max_creds == int(cred_ex_record.revocation_id): @@ -687,12 +716,20 @@ async def issue_credential( raise - cred_ex_record.credential = json.loads(credential_json) - - cred_ex_record.state = V10CredentialExchange.STATE_ISSUED - async with self._profile.session() as session: - # FIXME - re-fetch record to check state, apply transactional update - await cred_ex_record.save(session, reason="issue credential") + async with self._profile.transaction() as txn: + cred_ex_record = await V10CredentialExchange.retrieve_by_id( + txn, cred_ex_record.credential_exchange_id, for_update=True + ) + if cred_ex_record.state != V10CredentialExchange.STATE_REQUEST_RECEIVED: + raise CredentialManagerError( + f"Credential exchange {cred_ex_record.credential_exchange_id} " + f"in {cred_ex_record.state} state " + f"(must be {V10CredentialExchange.STATE_REQUEST_RECEIVED})" + ) + cred_ex_record.state = V10CredentialExchange.STATE_ISSUED + cred_ex_record.credential = credential_ser + await cred_ex_record.save(txn, reason="issue credential") + await txn.commit() credential_message = CredentialIssue( comment=comment, @@ -722,20 +759,29 @@ async def receive_credential( assert len(message.credentials_attach or []) == 1 raw_credential = message.indy_credential(0) - # FIXME use transaction, fetch for_update - async with self._profile.session() as session: - cred_ex_record = await ( - V10CredentialExchange.retrieve_by_connection_and_thread( - session, - connection_id, - message._thread_id, + async with self._profile.transaction() as txn: + try: + cred_ex_record = await ( + V10CredentialExchange.retrieve_by_connection_and_thread( + txn, connection_id, message._thread_id, for_update=True + ) + ) + except StorageNotFoundError: + raise CredentialManagerError( + "No credential exchange record found for received credential" + ) from None + if cred_ex_record.state != V10CredentialExchange.STATE_REQUEST_SENT: + raise CredentialManagerError( + f"Credential exchange {cred_ex_record.credential_exchange_id} " + f"in {cred_ex_record.state} state " + f"(must be {V10CredentialExchange.STATE_REQUEST_SENT})" ) - ) - cred_ex_record.raw_credential = raw_credential cred_ex_record.state = V10CredentialExchange.STATE_CREDENTIAL_RECEIVED - await cred_ex_record.save(session, reason="receive credential") + await cred_ex_record.save(txn, reason="receive credential") + await txn.commit() + return cred_ex_record async def store_credential( @@ -753,7 +799,7 @@ async def store_credential( Updated credential exchange record """ - if cred_ex_record.state != (V10CredentialExchange.STATE_CREDENTIAL_RECEIVED): + if cred_ex_record.state != V10CredentialExchange.STATE_CREDENTIAL_RECEIVED: raise CredentialManagerError( f"Credential exchange {cred_ex_record.credential_exchange_id} " f"in {cred_ex_record.state} state " @@ -802,20 +848,29 @@ async def store_credential( rev_reg_def=revoc_reg_def, ) except IndyHolderError as e: - LOGGER.error(f"Error storing credential. {e.error_code}: {e.message}") + LOGGER.error("Error storing credential: %s: %s", e.error_code, e.message) raise e credential_json = await holder.get_credential(credential_id) credential = json.loads(credential_json) - cred_ex_record.credential_id = credential_id - cred_ex_record.credential = credential - cred_ex_record.revoc_reg_id = credential.get("rev_reg_id", None) - cred_ex_record.revocation_id = credential.get("cred_rev_id", None) + async with self._profile.transaction() as txn: + cred_ex_record = await V10CredentialExchange.retrieve_by_id( + txn, cred_ex_record.credential_exchange_id, for_update=True + ) + if cred_ex_record.state != V10CredentialExchange.STATE_CREDENTIAL_RECEIVED: + raise CredentialManagerError( + f"Credential exchange {cred_ex_record.credential_exchange_id} " + f"in {cred_ex_record.state} state " + f"(must be {V10CredentialExchange.STATE_CREDENTIAL_RECEIVED})" + ) - async with self._profile.session() as session: - # FIXME - re-fetch record to check state, apply transactional update - await cred_ex_record.save(session, reason="store credential") + cred_ex_record.credential_id = credential_id + cred_ex_record.credential = credential + cred_ex_record.revoc_reg_id = credential.get("rev_reg_id", None) + cred_ex_record.revocation_id = credential.get("cred_rev_id", None) + await cred_ex_record.save(txn, reason="store credential") + await txn.commit() return cred_ex_record @@ -840,13 +895,36 @@ async def send_credential_ack( self._profile.settings, cred_ex_record.trace ) - cred_ex_record.state = V10CredentialExchange.STATE_ACKED try: - async with self._profile.session() as session: - # FIXME - re-fetch record to check state, apply transactional update - await cred_ex_record.save(session, reason="ack credential") + async with self._profile.transaction() as txn: + try: + cred_ex_record = await V10CredentialExchange.retrieve_by_id( + txn, cred_ex_record.credential_exchange_id, for_update=True + ) + except StorageNotFoundError: + LOGGER.warning( + "Skipping credential exchange ack, record not found: '%s'", + cred_ex_record.credential_exchange_id, + ) + return None + + if ( + cred_ex_record.state + != V10CredentialExchange.STATE_CREDENTIAL_RECEIVED + ): + LOGGER.warning( + "Skipping credential exchange ack, state is '%s' for record '%s'", + cred_ex_record.state, + cred_ex_record.credential_exchange_id, + ) + return None + + cred_ex_record.state = V10CredentialExchange.STATE_ACKED + await cred_ex_record.save(txn, reason="ack credential") + await txn.commit() - if cred_ex_record.auto_remove: + if cred_ex_record.auto_remove: + async with self._profile.session() as session: await cred_ex_record.delete_record(session) # all done: delete except StorageError: @@ -866,7 +944,7 @@ async def send_credential_ack( cred_ex_record.thread_id, ) - return cred_ex_record, credential_ack_message + return credential_ack_message async def receive_credential_ack( self, message: CredentialAck, connection_id: str @@ -878,18 +956,25 @@ async def receive_credential_ack( credential exchange record, retrieved and updated """ - # FIXME use transaction, fetch for_update - async with self._profile.session() as session: - cred_ex_record = await ( - V10CredentialExchange.retrieve_by_connection_and_thread( - session, - connection_id, + async with self._profile.transaction() as txn: + try: + cred_ex_record = await ( + V10CredentialExchange.retrieve_by_connection_and_thread( + txn, connection_id, message._thread_id, for_update=True + ) + ) + except StorageNotFoundError: + LOGGER.warning( + "Skip ack message on credential exchange, record not found %s", message._thread_id, ) - ) + return None + if cred_ex_record.state == V10CredentialExchange.STATE_ACKED: + return None cred_ex_record.state = V10CredentialExchange.STATE_ACKED - await cred_ex_record.save(session, reason="credential acked") + await cred_ex_record.save(txn, reason="credential acked") + await txn.commit() if cred_ex_record.auto_remove: async with self._profile.session() as session: @@ -907,15 +992,19 @@ async def receive_problem_report( credential exchange record, retrieved and updated """ - # FIXME use transaction, fetch for_update - async with self._profile.session() as session: - cred_ex_record = await ( - V10CredentialExchange.retrieve_by_connection_and_thread( - session, - connection_id, + async with self._profile.transaction() as txn: + try: + cred_ex_record = await ( + V10CredentialExchange.retrieve_by_connection_and_thread( + txn, connection_id, message._thread_id, for_update=True + ) + ) + except StorageNotFoundError: + LOGGER.warning( + "Skip problem report on credential exchange, record not found %s", message._thread_id, ) - ) + return None cred_ex_record.state = V10CredentialExchange.STATE_ABANDONED code = message.description.get( @@ -923,6 +1012,7 @@ async def receive_problem_report( ProblemReportReason.ISSUANCE_ABANDONED.value, ) cred_ex_record.error_msg = f"{code}: {message.description.get('en', code)}" - await cred_ex_record.save(session, reason="received problem report") + await cred_ex_record.save(txn, reason="received problem report") + await txn.commit() return cred_ex_record diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py b/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py index 1a32ba2abc..5df1eedd74 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py @@ -262,18 +262,24 @@ def record_value(self) -> dict: @classmethod async def retrieve_by_connection_and_thread( - cls, session: ProfileSession, connection_id: str, thread_id: str + cls, + session: ProfileSession, + connection_id: str, + thread_id: str, + *, + for_update=False, ) -> "V10CredentialExchange": """Retrieve a credential exchange record by connection and thread ID.""" cache_key = f"credential_exchange_ctidx::{connection_id}::{thread_id}" record_id = await cls.get_cached_key(session, cache_key) if record_id: - record = await cls.retrieve_by_id(session, record_id) + record = await cls.retrieve_by_id(session, record_id, for_update=for_update) else: record = await cls.retrieve_by_tag_filter( session, {"thread_id": thread_id}, {"connection_id": connection_id} if connection_id else None, + for_update=for_update, ) await cls.set_cached_key(session, cache_key, record.credential_exchange_id) return record diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py index 91a34af9ac..68ad3d7c3b 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py @@ -55,6 +55,9 @@ async def setUp(self): setattr( self.profile, "session", async_mock.MagicMock(return_value=self.session) ) + setattr( + self.profile, "transaction", async_mock.MagicMock(return_value=self.session) + ) Ledger = async_mock.MagicMock() self.ledger = Ledger() @@ -278,12 +281,14 @@ async def test_create_free_offer(self): credential_proposal=preview, cred_def_id=CRED_DEF_ID, schema_id=None ) - exchange = V10CredentialExchange( + stored_exchange = V10CredentialExchange( credential_exchange_id="dummy-cxid", credential_definition_id=CRED_DEF_ID, role=V10CredentialExchange.ROLE_ISSUER, credential_proposal_dict=proposal.serialize(), + new_with_id=True, ) + await stored_exchange.save(self.session) with async_mock.patch.object( V10CredentialExchange, "save", autospec=True @@ -313,25 +318,27 @@ async def test_create_free_offer(self): await self.session.storage.add_record(cred_def_record) (ret_exchange, ret_offer) = await self.manager.create_offer( - cred_ex_record=exchange, + cred_ex_record=stored_exchange, counter_proposal=None, comment=comment, ) - assert ret_exchange is exchange + assert ret_exchange is stored_exchange save_ex.assert_called_once() issuer.create_credential_offer.assert_called_once_with(CRED_DEF_ID) - assert exchange.credential_exchange_id == ret_exchange._id # cover property - assert exchange.thread_id == ret_offer._thread_id - assert exchange.credential_definition_id == CRED_DEF_ID - assert exchange.role == V10CredentialExchange.ROLE_ISSUER - assert exchange.schema_id == SCHEMA_ID - assert exchange.state == V10CredentialExchange.STATE_OFFER_SENT - assert exchange._credential_offer.ser == INDY_OFFER + assert ( + stored_exchange.credential_exchange_id == ret_exchange._id + ) # cover property + assert stored_exchange.thread_id == ret_offer._thread_id + assert stored_exchange.credential_definition_id == CRED_DEF_ID + assert stored_exchange.role == V10CredentialExchange.ROLE_ISSUER + assert stored_exchange.schema_id == SCHEMA_ID + assert stored_exchange.state == V10CredentialExchange.STATE_OFFER_SENT + assert stored_exchange._credential_offer.ser == INDY_OFFER (ret_exchange, ret_offer) = await self.manager.create_offer( - cred_ex_record=exchange, + cred_ex_record=stored_exchange, counter_proposal=None, comment=comment, ) # once more to cover case where offer is available in cache @@ -352,12 +359,14 @@ async def test_create_free_offer_attr_mismatch(self): credential_proposal=preview, cred_def_id=CRED_DEF_ID, schema_id=None ) - exchange = V10CredentialExchange( + stored_exchange = V10CredentialExchange( credential_exchange_id="dummy-cxid", credential_definition_id=CRED_DEF_ID, role=V10CredentialExchange.ROLE_ISSUER, credential_proposal_dict=proposal.serialize(), + new_with_id=True, ) + await stored_exchange.save(self.session) with async_mock.patch.object( V10CredentialExchange, "save", autospec=True @@ -388,7 +397,7 @@ async def test_create_free_offer_attr_mismatch(self): with self.assertRaises(CredentialManagerError): await self.manager.create_offer( - cred_ex_record=exchange, + cred_ex_record=stored_exchange, counter_proposal=None, comment=comment, ) @@ -407,11 +416,13 @@ async def test_create_bound_offer(self): ) ) proposal = CredentialProposal(credential_proposal=preview) - exchange = V10CredentialExchange( + stored_exchange = V10CredentialExchange( credential_exchange_id="dummy-cxid", credential_proposal_dict=proposal.serialize(), role=V10CredentialExchange.ROLE_ISSUER, + new_with_id=True, ) + await stored_exchange.save(self.session) with async_mock.patch.object( V10CredentialExchange, "save", autospec=True @@ -443,21 +454,21 @@ async def test_create_bound_offer(self): await self.session.storage.add_record(cred_def_record) (ret_exchange, ret_offer) = await self.manager.create_offer( - cred_ex_record=exchange, + cred_ex_record=stored_exchange, counter_proposal=None, comment=comment, ) - assert ret_exchange is exchange + assert ret_exchange is stored_exchange save_ex.assert_called_once() issuer.create_credential_offer.assert_called_once_with(CRED_DEF_ID) - assert exchange.thread_id == ret_offer._thread_id - assert exchange.schema_id == SCHEMA_ID - assert exchange.credential_definition_id == CRED_DEF_ID - assert exchange.role == V10CredentialExchange.ROLE_ISSUER - assert exchange.state == V10CredentialExchange.STATE_OFFER_SENT - assert exchange._credential_offer.ser == INDY_OFFER + assert stored_exchange.thread_id == ret_offer._thread_id + assert stored_exchange.schema_id == SCHEMA_ID + assert stored_exchange.credential_definition_id == CRED_DEF_ID + assert stored_exchange.role == V10CredentialExchange.ROLE_ISSUER + assert stored_exchange.state == V10CredentialExchange.STATE_OFFER_SENT + assert stored_exchange._credential_offer.ser == INDY_OFFER # additionally check that credential preview was passed through assert ret_offer.credential_preview.attributes == preview.attributes @@ -476,11 +487,13 @@ async def test_create_bound_offer_no_cred_def(self): ) ) proposal = CredentialProposal(credential_proposal=preview) - exchange = V10CredentialExchange( + stored_exchange = V10CredentialExchange( credential_exchange_id="dummy-cxid", credential_proposal_dict=proposal.serialize(), role=V10CredentialExchange.ROLE_ISSUER, + new_with_id=True, ) + await stored_exchange.save(self.session) with async_mock.patch.object( V10CredentialExchange, "save", autospec=True @@ -498,7 +511,7 @@ async def test_create_bound_offer_no_cred_def(self): with self.assertRaises(CredentialManagerError): await self.manager.create_offer( - cred_ex_record=exchange, + cred_ex_record=stored_exchange, counter_proposal=None, comment=comment, ) @@ -529,9 +542,12 @@ async def test_receive_offer_proposed(self): credential_proposal_dict=proposal.serialize(), initiator=V10CredentialExchange.INITIATOR_EXTERNAL, role=V10CredentialExchange.ROLE_HOLDER, + state=V10CredentialExchange.STATE_PROPOSAL_SENT, schema_id=SCHEMA_ID, thread_id=thread_id, + new_with_id=True, ) + await stored_exchange.save(self.session) with async_mock.patch.object( V10CredentialExchange, "save", autospec=True @@ -604,7 +620,9 @@ async def test_create_request(self): state=V10CredentialExchange.STATE_OFFER_RECEIVED, schema_id=SCHEMA_ID, thread_id=thread_id, + new_with_id=True, ) + await stored_exchange.save(self.session) self.cache = InMemoryCache() self.context.injector.bind_instance(BaseCache, self.cache) @@ -646,10 +664,9 @@ async def test_create_request(self): stored_exchange.state = V10CredentialExchange.STATE_OFFER_RECEIVED stored_exchange.credential_request = INDY_CRED_REQ ( - ret_existing_exchange, + _ret_existing_exchange, ret_existing_request, ) = await self.manager.create_request(stored_exchange, holder_did) - assert ret_existing_exchange == ret_exchange assert ret_existing_request._thread_id == thread_id async def test_create_request_no_cache(self): @@ -667,7 +684,9 @@ async def test_create_request_no_cache(self): state=V10CredentialExchange.STATE_OFFER_RECEIVED, schema_id=SCHEMA_ID, thread_id=thread_id, + new_with_id=True, ) + await stored_exchange.save(self.session) with async_mock.patch.object( V10CredentialExchange, "save", autospec=True @@ -712,7 +731,9 @@ async def test_create_request_bad_state(self): state=V10CredentialExchange.STATE_PROPOSAL_SENT, schema_id=SCHEMA_ID, thread_id=thread_id, + new_with_id=True, ) + await stored_exchange.save(self.session) with self.assertRaises(CredentialManagerError): await self.manager.create_request(stored_exchange, holder_did) @@ -725,7 +746,10 @@ async def test_receive_request(self): connection_id=connection_id, initiator=V10CredentialExchange.INITIATOR_EXTERNAL, role=V10CredentialExchange.ROLE_ISSUER, + state=V10CredentialExchange.STATE_OFFER_SENT, + new_with_id=True, ) + await stored_exchange.save(self.session) request = CredentialRequest( requests_attach=[CredentialRequest.wrap_indy_cred_req(INDY_CRED_REQ)] @@ -741,7 +765,7 @@ async def test_receive_request(self): exchange = await self.manager.receive_request(request, connection_id) retrieve_ex.assert_called_once_with( - self.session, connection_id, request._thread_id + self.session, connection_id, request._thread_id, for_update=True ) save_ex.assert_called_once() @@ -753,7 +777,10 @@ async def test_receive_request_no_connection_cred_request(self): credential_exchange_id="dummy-cxid", initiator=V10CredentialExchange.INITIATOR_EXTERNAL, role=V10CredentialExchange.ROLE_ISSUER, + state=V10CredentialExchange.STATE_OFFER_SENT, + new_with_id=True, ) + await stored_exchange.save(self.session) request = CredentialRequest( requests_attach=[CredentialRequest.wrap_indy_cred_req(INDY_CRED_REQ)] @@ -773,10 +800,13 @@ async def test_receive_request_no_connection_cred_request(self): cx_rec = await self.manager.receive_request(request, "test_conn_id") mock_retrieve.assert_called_once_with( - self.session, "test_conn_id", request._thread_id + self.session, "test_conn_id", request._thread_id, for_update=True ) mock_retrieve_tag_filter.assert_called_once_with( - self.session, {"thread_id": request._thread_id}, {"connection_id": None} + self.session, + {"thread_id": request._thread_id}, + {"connection_id": None}, + for_update=True, ) mock_save.assert_called_once() assert cx_rec.state == V10CredentialExchange.STATE_REQUEST_RECEIVED @@ -788,7 +818,10 @@ async def test_receive_request_no_cred_ex_with_offer_found(self): credential_exchange_id="dummy-cxid", initiator=V10CredentialExchange.INITIATOR_EXTERNAL, role=V10CredentialExchange.ROLE_ISSUER, + state=V10CredentialExchange.STATE_OFFER_SENT, + new_with_id=True, ) + await stored_exchange.save(self.session) request = CredentialRequest( requests_attach=[CredentialRequest.wrap_indy_cred_req(INDY_CRED_REQ)] @@ -809,12 +842,13 @@ async def test_receive_request_no_cred_ex_with_offer_found(self): cx_rec = await self.manager.receive_request(request, "test_conn_id") mock_retrieve.assert_called_once_with( - self.session, "test_conn_id", request._thread_id + self.session, "test_conn_id", request._thread_id, for_update=True ) mock_retrieve_tag_filter.assert_called_once_with( self.session, {"thread_id": request._thread_id}, {"connection_id": None}, + for_update=True, ) async def test_issue_credential(self): @@ -840,7 +874,9 @@ async def test_issue_credential(self): role=V10CredentialExchange.ROLE_ISSUER, state=V10CredentialExchange.STATE_REQUEST_RECEIVED, thread_id=thread_id, + new_with_id=True, ) + await stored_exchange.save(self.session) issuer = async_mock.MagicMock() cred = {"indy": "credential"} @@ -892,6 +928,7 @@ async def test_issue_credential(self): # cover case with existing cred stored_exchange.credential = cred stored_exchange.state = V10CredentialExchange.STATE_REQUEST_RECEIVED + await stored_exchange.save(self.session) ( ret_existing_exchange, ret_existing_cred, @@ -926,7 +963,9 @@ async def test_issue_credential_non_revocable(self): role=V10CredentialExchange.ROLE_ISSUER, state=V10CredentialExchange.STATE_REQUEST_RECEIVED, thread_id=thread_id, + new_with_id=True, ) + await stored_exchange.save(self.session) issuer = async_mock.MagicMock() cred = {"indy": "credential"} @@ -1000,7 +1039,9 @@ async def test_issue_credential_fills_rr(self): state=V10CredentialExchange.STATE_REQUEST_RECEIVED, thread_id=thread_id, revocation_id="1000", + new_with_id=True, ) + await stored_exchange.save(self.session) issuer = async_mock.MagicMock() cred = {"indy": "credential"} @@ -1075,7 +1116,9 @@ async def test_issue_credential_request_bad_state(self): state=V10CredentialExchange.STATE_PROPOSAL_SENT, schema_id=SCHEMA_ID, thread_id=thread_id, + new_with_id=True, ) + await stored_exchange.save(self.session) with self.assertRaises(CredentialManagerError): await self.manager.issue_credential(stored_exchange) @@ -1103,7 +1146,9 @@ async def test_issue_credential_no_active_rr_no_retries(self): role=V10CredentialExchange.ROLE_ISSUER, state=V10CredentialExchange.STATE_REQUEST_RECEIVED, thread_id=thread_id, + new_with_id=True, ) + await stored_exchange.save(self.session) issuer = async_mock.MagicMock() cred = {"indy": "credential"} @@ -1167,7 +1212,9 @@ async def test_issue_credential_no_active_rr_retry(self): role=V10CredentialExchange.ROLE_ISSUER, state=V10CredentialExchange.STATE_REQUEST_RECEIVED, thread_id=thread_id, + new_with_id=True, ) + await stored_exchange.save(self.session) issuer = async_mock.MagicMock() cred = {"indy": "credential"} @@ -1235,7 +1282,9 @@ async def test_issue_credential_rr_full(self): role=V10CredentialExchange.ROLE_ISSUER, state=V10CredentialExchange.STATE_REQUEST_RECEIVED, thread_id=thread_id, + new_with_id=True, ) + await stored_exchange.save(self.session) issuer = async_mock.MagicMock() cred = {"indy": "credential"} @@ -1284,7 +1333,10 @@ async def test_receive_credential(self): connection_id=connection_id, initiator=V10CredentialExchange.INITIATOR_EXTERNAL, role=V10CredentialExchange.ROLE_ISSUER, + state=V10CredentialExchange.STATE_REQUEST_SENT, + new_with_id=True, ) + await stored_exchange.save(self.session) issue = CredentialIssue( credentials_attach=[CredentialIssue.wrap_indy_credential(INDY_CRED)] @@ -1300,7 +1352,7 @@ async def test_receive_credential(self): exchange = await self.manager.receive_credential(issue, connection_id) retrieve_ex.assert_called_once_with( - self.session, connection_id, issue._thread_id + self.session, connection_id, issue._thread_id, for_update=True ) save_ex.assert_called_once() @@ -1337,7 +1389,9 @@ async def test_store_credential(self): state=V10CredentialExchange.STATE_CREDENTIAL_RECEIVED, thread_id=thread_id, auto_remove=True, + new_with_id=True, ) + await stored_exchange.save(self.session) cred_id = "cred-id" holder = async_mock.MagicMock() @@ -1406,7 +1460,9 @@ async def test_store_credential_bad_state(self): role=V10CredentialExchange.ROLE_HOLDER, state=V10CredentialExchange.STATE_OFFER_RECEIVED, thread_id=thread_id, + new_with_id=True, ) + await stored_exchange.save(self.session) cred_id = "cred-id" with self.assertRaises(CredentialManagerError): @@ -1435,7 +1491,9 @@ async def test_store_credential_no_preview(self): role=V10CredentialExchange.ROLE_HOLDER, state=V10CredentialExchange.STATE_CREDENTIAL_RECEIVED, thread_id=thread_id, + new_with_id=True, ) + await stored_exchange.save(self.session) cred_def = async_mock.MagicMock() self.ledger.get_credential_definition = async_mock.CoroutineMock( @@ -1503,7 +1561,9 @@ async def test_store_credential_holder_store_indy_error(self): role=V10CredentialExchange.ROLE_HOLDER, state=V10CredentialExchange.STATE_CREDENTIAL_RECEIVED, thread_id=thread_id, + new_with_id=True, ) + await stored_exchange.save(self.session) cred_def = async_mock.MagicMock() self.ledger.get_credential_definition = async_mock.CoroutineMock( @@ -1535,12 +1595,15 @@ async def test_send_credential_ack(self): credential_exchange_id="dummy-cxid", connection_id=connection_id, initiator=V10CredentialExchange.INITIATOR_SELF, + state=V10CredentialExchange.STATE_CREDENTIAL_RECEIVED, thread_id="thid", parent_thread_id="pthid", role=V10CredentialExchange.ROLE_ISSUER, trace=False, auto_remove=True, + new_with_id=True, ) + await stored_exchange.save(self.session) with async_mock.patch.object( V10CredentialExchange, "save", autospec=True @@ -1552,16 +1615,15 @@ async def test_send_credential_ack(self): test_module.LOGGER, "warning", async_mock.MagicMock() ) as mock_log_warning: mock_delete_ex.side_effect = test_module.StorageError() - (_, ack) = await self.manager.send_credential_ack(stored_exchange) + ack = await self.manager.send_credential_ack(stored_exchange) assert ack._thread mock_log_exception.assert_called_once() # cover exception log-and-continue mock_log_warning.assert_called_once() # no BaseResponder mock_responder = MockResponder() # cover with responder self.context.injector.bind_instance(BaseResponder, mock_responder) - (cx_rec, ack) = await self.manager.send_credential_ack(stored_exchange) + ack = await self.manager.send_credential_ack(stored_exchange) assert ack._thread - assert cx_rec.state == V10CredentialExchange.STATE_ACKED async def test_receive_credential_ack(self): connection_id = "connection-id" @@ -1570,7 +1632,9 @@ async def test_receive_credential_ack(self): connection_id=connection_id, initiator=V10CredentialExchange.INITIATOR_SELF, role=V10CredentialExchange.ROLE_ISSUER, + new_with_id=True, ) + await stored_exchange.save(self.session) ack = CredentialAck() @@ -1587,7 +1651,7 @@ async def test_receive_credential_ack(self): ret_exchange = await self.manager.receive_credential_ack(ack, connection_id) retrieve_ex.assert_called_once_with( - self.session, connection_id, ack._thread_id + self.session, connection_id, ack._thread_id, for_update=True ) save_ex.assert_called_once() @@ -1601,7 +1665,9 @@ async def test_receive_problem_report(self): connection_id=connection_id, initiator=V10CredentialExchange.INITIATOR_SELF, role=V10CredentialExchange.ROLE_ISSUER, + new_with_id=True, ) + await stored_exchange.save(self.session) problem = CredentialProblemReport( description={ "code": test_module.ProblemReportReason.ISSUANCE_ABANDONED.value, @@ -1622,7 +1688,7 @@ async def test_receive_problem_report(self): problem, connection_id ) retrieve_ex.assert_called_once_with( - self.session, connection_id, problem._thread_id + self.session, connection_id, problem._thread_id, for_update=True ) save_ex.assert_called_once() @@ -1630,12 +1696,6 @@ async def test_receive_problem_report(self): async def test_receive_problem_report_x(self): connection_id = "connection-id" - stored_exchange = V10CredentialExchange( - credential_exchange_id="dummy-cxid", - initiator=V10CredentialExchange.INITIATOR_SELF, - role=V10CredentialExchange.ROLE_ISSUER, - state=V10CredentialExchange.STATE_REQUEST_RECEIVED, - ) problem = CredentialProblemReport( description={ "code": test_module.ProblemReportReason.ISSUANCE_ABANDONED.value, @@ -1650,8 +1710,8 @@ async def test_receive_problem_report_x(self): ) as retrieve_ex: retrieve_ex.side_effect = test_module.StorageNotFoundError("No such record") - with self.assertRaises(test_module.StorageNotFoundError): - await self.manager.receive_problem_report(problem, connection_id) + exch = await self.manager.receive_problem_report(problem, connection_id) + assert exch is None async def test_retrieve_records(self): self.cache = InMemoryCache() From 6eeba51054e5010c36efa21eb009682865bb3a8c Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Wed, 23 Feb 2022 16:41:27 -0800 Subject: [PATCH 039/872] pin markupsafe at version 2.0.1 Signed-off-by: Andrew Whitehead --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 6d98ce200c..2429236466 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,6 +8,7 @@ base58~=2.1.0 deepmerge~=0.3.0 ecdsa~=0.16.1 Markdown~=3.1.1 +markupsafe==2.0.1 marshmallow==3.5.1 msgpack~=1.0 prompt_toolkit~=2.0.9 From 766145f69c08c2d330e16d62e2aa22b3b11e9572 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 24 Feb 2022 13:41:32 +0100 Subject: [PATCH 040/872] feat: accept taa using config Signed-off-by: Timo Glastra --- aries_cloudagent/config/argparse.py | 14 +++++ aries_cloudagent/config/ledger.py | 54 +++++++++++++++---- aries_cloudagent/config/tests/test_ledger.py | 56 +++++++++++++++++--- 3 files changed, 106 insertions(+), 18 deletions(-) diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index 5d2e55af4e..027fa9f4cd 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -800,6 +800,17 @@ def add_arguments(self, parser: ArgumentParser): " HyperLedger Indy ledgers." ), ) + parser.add_argument( + "--accept-taa", + type=str, + nargs=2, + metavar=("", ""), + env_var="ACAPY_ACCEPT_TAA", + help=( + "Specify the acceptance mechanism and taa version for which to accept the transaction author agreement." + "If not provided, the TAA must be accepted through the TTY or the admin API." + ), + ) def get_settings(self, args: Namespace) -> dict: """Extract ledger settings.""" @@ -838,6 +849,9 @@ def get_settings(self, args: Namespace) -> dict: settings["ledger.keepalive"] = args.ledger_keepalive if args.ledger_socks_proxy: settings["ledger.socks_proxy"] = args.ledger_socks_proxy + if args.accept_taa: + settings["ledger.taa_acceptance_mechanism"] = args.accept_taa[0] + settings["ledger.taa_acceptance_version"] = args.accept_taa[1] return settings diff --git a/aries_cloudagent/config/ledger.py b/aries_cloudagent/config/ledger.py index cdd2d9efc5..78a2d5e17e 100644 --- a/aries_cloudagent/config/ledger.py +++ b/aries_cloudagent/config/ledger.py @@ -4,6 +4,7 @@ import logging import re import sys +from typing import Optional import uuid import markdown @@ -140,7 +141,7 @@ async def ledger_config( not taa_accepted or taa_info["taa_record"]["digest"] != taa_accepted["digest"] ): - if not await accept_taa(ledger, taa_info, provision): + if not await accept_taa(ledger, profile, taa_info, provision): return False # Publish endpoints if necessary - skipped if TAA is required but not accepted @@ -162,13 +163,7 @@ async def ledger_config( return True -async def accept_taa(ledger: BaseLedger, taa_info, provision: bool = False) -> bool: - """Perform TAA acceptance.""" - - if not sys.stdout.isatty(): - LOGGER.warning("Cannot accept TAA without interactive terminal") - return False - +async def select_aml_tty(taa_info, provision: bool = False) -> Optional[str]: mechanisms = taa_info["aml_record"]["aml"] allow_opts = OrderedDict( [ @@ -230,16 +225,53 @@ async def accept_taa(ledger: BaseLedger, taa_info, provision: bool = False) -> b try: opt = await prompt_toolkit.prompt(opts_text, async_=True) except EOFError: - return False + return None if not opt: opt = "1" opt = opt.strip() if opt in ("x", "X"): - return False + return None if opt in num_mechanisms: mechanism = num_mechanisms[opt] break - await ledger.accept_txn_author_agreement(taa_info["taa_record"], mechanism) + return mechanism + + +async def accept_taa(ledger: BaseLedger, profile: Profile, taa_info, provision: bool = False, ) -> bool: + """Perform TAA acceptance.""" + + mechanisms = taa_info["aml_record"]["aml"] + mechanism = None + + taa_acceptance_mechanism = profile.settings.get("ledger.taa_acceptance_mechanism") + taa_acceptance_version = profile.settings.get("ledger.taa_acceptance_version") + + # If configured, accept the TAA automatically + if taa_acceptance_mechanism: + taa_record_version = taa_info["taa_record"]["version"] + if taa_acceptance_version != taa_record_version: + raise LedgerError( + f"TAA version ({taa_record_version}) is different from TAA accept version ({taa_acceptance_version}) from configuration. Update the TAA version in the config to accept the TAA." + ) + + if taa_acceptance_mechanism not in mechanisms: + raise LedgerError( + f"TAA acceptance mechanism '{taa_acceptance_mechanism}' is not a valid acceptance mechanism. Valid mechanisms are: {str(list(mechanisms.keys()))}" + ) + + mechanism = taa_acceptance_mechanism + # If tty is available use it (allows to accept newer TAA than configured) + elif sys.stdout.isatty(): + mechanism = await select_aml_tty(taa_info, provision) + else: + LOGGER.warning( + "Cannot accept TAA without interactive terminal or taa accept config" + ) + if not mechanism: + return False + + LOGGER.debug(f"Accepting the TAA using mechanism '{mechanism}'") + await ledger.accept_txn_author_agreement(taa_info["taa_record"], mechanism) return True diff --git a/aries_cloudagent/config/tests/test_ledger.py b/aries_cloudagent/config/tests/test_ledger.py index bb6d2ecaa6..63c2517319 100644 --- a/aries_cloudagent/config/tests/test_ledger.py +++ b/aries_cloudagent/config/tests/test_ledger.py @@ -1,6 +1,6 @@ from os import remove from tempfile import NamedTemporaryFile - +import pytest from asynctest import TestCase as AsyncTestCase, mock as async_mock from .. import argparse @@ -643,14 +643,24 @@ async def test_load_multiple_genesis_transactions_from_config_io_x(self): ) @async_mock.patch("sys.stdout") - async def test_ledger_accept_taa_not_tty(self, mock_stdout): + async def test_ledger_accept_taa_not_tty_not_accept_config(self, mock_stdout): mock_stdout.isatty = async_mock.MagicMock(return_value=False) + mock_profile = InMemoryProfile.test_profile() + + taa_info = { + "taa_record": {"version": "1.0", "text": "Agreement"}, + "aml_record": {"aml": ["wallet_agreement", "on_file"]}, + } + + assert not await test_module.accept_taa(None, mock_profile, taa_info, provision=False) + - assert not await test_module.accept_taa(None, None, provision=False) @async_mock.patch("sys.stdout") - async def test_ledger_accept_taa(self, mock_stdout): + async def test_ledger_accept_taa_tty(self, mock_stdout): mock_stdout.isatty = async_mock.MagicMock(return_value=True) + mock_profile = InMemoryProfile.test_profile() + taa_info = { "taa_record": {"version": "1.0", "text": "Agreement"}, @@ -663,7 +673,7 @@ async def test_ledger_accept_taa(self, mock_stdout): test_module.prompt_toolkit, "prompt", async_mock.CoroutineMock() ) as mock_prompt: mock_prompt.side_effect = EOFError() - assert not await test_module.accept_taa(None, taa_info, provision=False) + assert not await test_module.accept_taa(None, mock_profile, taa_info, provision=False) with async_mock.patch.object( test_module, "use_asyncio_event_loop", async_mock.MagicMock() @@ -671,7 +681,7 @@ async def test_ledger_accept_taa(self, mock_stdout): test_module.prompt_toolkit, "prompt", async_mock.CoroutineMock() ) as mock_prompt: mock_prompt.return_value = "x" - assert not await test_module.accept_taa(None, taa_info, provision=False) + assert not await test_module.accept_taa(None, mock_profile, taa_info, provision=False) with async_mock.patch.object( test_module, "use_asyncio_event_loop", async_mock.MagicMock() @@ -682,7 +692,39 @@ async def test_ledger_accept_taa(self, mock_stdout): accept_txn_author_agreement=async_mock.CoroutineMock() ) mock_prompt.return_value = "" - assert await test_module.accept_taa(mock_ledger, taa_info, provision=False) + assert await test_module.accept_taa(mock_ledger, mock_profile, taa_info, provision=False) + + async def test_ledger_accept_taa_tty(self): + taa_info = { + "taa_record": {"version": "1.0", "text": "Agreement"}, + "aml_record": {"aml": {"wallet_agreement": "", "on_file": ""}}, + } + + # Incorrect version + with pytest.raises(LedgerError): + mock_profile = InMemoryProfile.test_profile({ + "ledger.taa_acceptance_mechanism": "wallet_agreement", + "ledger.taa_acceptance_version": "1.5" + }) + assert not await test_module.accept_taa(None, mock_profile, taa_info, provision=False) + + # Incorrect mechanism + with pytest.raises(LedgerError): + mock_profile = InMemoryProfile.test_profile({ + "ledger.taa_acceptance_mechanism": "not_exist", + "ledger.taa_acceptance_version": "1.0" + }) + assert not await test_module.accept_taa(None, mock_profile, taa_info, provision=False) + + # Valid + mock_profile = InMemoryProfile.test_profile({ + "ledger.taa_acceptance_mechanism": "on_file", + "ledger.taa_acceptance_version": "1.0" + }) + mock_ledger = async_mock.MagicMock( + accept_txn_author_agreement=async_mock.CoroutineMock() + ) + assert await test_module.accept_taa(mock_ledger, mock_profile, taa_info, provision=False) async def test_ledger_config(self): """Test required argument parsing.""" From a0c9ffba925955effddb2d43957e469bf95a0d05 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 24 Feb 2022 13:41:48 +0100 Subject: [PATCH 041/872] style: black formatting Signed-off-by: Timo Glastra --- aries_cloudagent/config/ledger.py | 7 ++- aries_cloudagent/config/tests/test_ledger.py | 63 +++++++++++++------- 2 files changed, 46 insertions(+), 24 deletions(-) diff --git a/aries_cloudagent/config/ledger.py b/aries_cloudagent/config/ledger.py index 78a2d5e17e..088e7ffa71 100644 --- a/aries_cloudagent/config/ledger.py +++ b/aries_cloudagent/config/ledger.py @@ -238,7 +238,12 @@ async def select_aml_tty(taa_info, provision: bool = False) -> Optional[str]: return mechanism -async def accept_taa(ledger: BaseLedger, profile: Profile, taa_info, provision: bool = False, ) -> bool: +async def accept_taa( + ledger: BaseLedger, + profile: Profile, + taa_info, + provision: bool = False, +) -> bool: """Perform TAA acceptance.""" mechanisms = taa_info["aml_record"]["aml"] diff --git a/aries_cloudagent/config/tests/test_ledger.py b/aries_cloudagent/config/tests/test_ledger.py index 63c2517319..f9c4175f03 100644 --- a/aries_cloudagent/config/tests/test_ledger.py +++ b/aries_cloudagent/config/tests/test_ledger.py @@ -652,16 +652,15 @@ async def test_ledger_accept_taa_not_tty_not_accept_config(self, mock_stdout): "aml_record": {"aml": ["wallet_agreement", "on_file"]}, } - assert not await test_module.accept_taa(None, mock_profile, taa_info, provision=False) - - + assert not await test_module.accept_taa( + None, mock_profile, taa_info, provision=False + ) @async_mock.patch("sys.stdout") async def test_ledger_accept_taa_tty(self, mock_stdout): mock_stdout.isatty = async_mock.MagicMock(return_value=True) mock_profile = InMemoryProfile.test_profile() - taa_info = { "taa_record": {"version": "1.0", "text": "Agreement"}, "aml_record": {"aml": ["wallet_agreement", "on_file"]}, @@ -673,7 +672,9 @@ async def test_ledger_accept_taa_tty(self, mock_stdout): test_module.prompt_toolkit, "prompt", async_mock.CoroutineMock() ) as mock_prompt: mock_prompt.side_effect = EOFError() - assert not await test_module.accept_taa(None, mock_profile, taa_info, provision=False) + assert not await test_module.accept_taa( + None, mock_profile, taa_info, provision=False + ) with async_mock.patch.object( test_module, "use_asyncio_event_loop", async_mock.MagicMock() @@ -681,7 +682,9 @@ async def test_ledger_accept_taa_tty(self, mock_stdout): test_module.prompt_toolkit, "prompt", async_mock.CoroutineMock() ) as mock_prompt: mock_prompt.return_value = "x" - assert not await test_module.accept_taa(None, mock_profile, taa_info, provision=False) + assert not await test_module.accept_taa( + None, mock_profile, taa_info, provision=False + ) with async_mock.patch.object( test_module, "use_asyncio_event_loop", async_mock.MagicMock() @@ -692,7 +695,9 @@ async def test_ledger_accept_taa_tty(self, mock_stdout): accept_txn_author_agreement=async_mock.CoroutineMock() ) mock_prompt.return_value = "" - assert await test_module.accept_taa(mock_ledger, mock_profile, taa_info, provision=False) + assert await test_module.accept_taa( + mock_ledger, mock_profile, taa_info, provision=False + ) async def test_ledger_accept_taa_tty(self): taa_info = { @@ -702,29 +707,41 @@ async def test_ledger_accept_taa_tty(self): # Incorrect version with pytest.raises(LedgerError): - mock_profile = InMemoryProfile.test_profile({ - "ledger.taa_acceptance_mechanism": "wallet_agreement", - "ledger.taa_acceptance_version": "1.5" - }) - assert not await test_module.accept_taa(None, mock_profile, taa_info, provision=False) + mock_profile = InMemoryProfile.test_profile( + { + "ledger.taa_acceptance_mechanism": "wallet_agreement", + "ledger.taa_acceptance_version": "1.5", + } + ) + assert not await test_module.accept_taa( + None, mock_profile, taa_info, provision=False + ) # Incorrect mechanism with pytest.raises(LedgerError): - mock_profile = InMemoryProfile.test_profile({ - "ledger.taa_acceptance_mechanism": "not_exist", - "ledger.taa_acceptance_version": "1.0" - }) - assert not await test_module.accept_taa(None, mock_profile, taa_info, provision=False) - + mock_profile = InMemoryProfile.test_profile( + { + "ledger.taa_acceptance_mechanism": "not_exist", + "ledger.taa_acceptance_version": "1.0", + } + ) + assert not await test_module.accept_taa( + None, mock_profile, taa_info, provision=False + ) + # Valid - mock_profile = InMemoryProfile.test_profile({ - "ledger.taa_acceptance_mechanism": "on_file", - "ledger.taa_acceptance_version": "1.0" - }) + mock_profile = InMemoryProfile.test_profile( + { + "ledger.taa_acceptance_mechanism": "on_file", + "ledger.taa_acceptance_version": "1.0", + } + ) mock_ledger = async_mock.MagicMock( accept_txn_author_agreement=async_mock.CoroutineMock() ) - assert await test_module.accept_taa(mock_ledger, mock_profile, taa_info, provision=False) + assert await test_module.accept_taa( + mock_ledger, mock_profile, taa_info, provision=False + ) async def test_ledger_config(self): """Test required argument parsing.""" From 7c241aacffaa4334e924c79444aa2d427ff25deb Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 24 Feb 2022 13:58:48 +0100 Subject: [PATCH 042/872] style: flake errors Signed-off-by: Timo Glastra --- aries_cloudagent/config/argparse.py | 5 +++-- aries_cloudagent/config/ledger.py | 9 +++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index 027fa9f4cd..a8b6ae5110 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -807,8 +807,9 @@ def add_arguments(self, parser: ArgumentParser): metavar=("", ""), env_var="ACAPY_ACCEPT_TAA", help=( - "Specify the acceptance mechanism and taa version for which to accept the transaction author agreement." - "If not provided, the TAA must be accepted through the TTY or the admin API." + "Specify the acceptance mechanism and taa version for which to accept" + " the transaction author agreement. If not provided, the TAA must" + " be accepted through the TTY or the admin API." ), ) diff --git a/aries_cloudagent/config/ledger.py b/aries_cloudagent/config/ledger.py index 088e7ffa71..d98b9ad0e5 100644 --- a/aries_cloudagent/config/ledger.py +++ b/aries_cloudagent/config/ledger.py @@ -164,6 +164,7 @@ async def ledger_config( async def select_aml_tty(taa_info, provision: bool = False) -> Optional[str]: + """Select acceptance mechanism from AML.""" mechanisms = taa_info["aml_record"]["aml"] allow_opts = OrderedDict( [ @@ -257,12 +258,16 @@ async def accept_taa( taa_record_version = taa_info["taa_record"]["version"] if taa_acceptance_version != taa_record_version: raise LedgerError( - f"TAA version ({taa_record_version}) is different from TAA accept version ({taa_acceptance_version}) from configuration. Update the TAA version in the config to accept the TAA." + f"TAA version ({taa_record_version}) is different from TAA accept " + f"version ({taa_acceptance_version}) from configuration. Update the " + "TAA version in the config to accept the TAA." ) if taa_acceptance_mechanism not in mechanisms: raise LedgerError( - f"TAA acceptance mechanism '{taa_acceptance_mechanism}' is not a valid acceptance mechanism. Valid mechanisms are: {str(list(mechanisms.keys()))}" + f"TAA acceptance mechanism '{taa_acceptance_mechanism}' is not a " + "valid acceptance mechanism. Valid mechanisms are: " + + str(list(mechanisms.keys())) ) mechanism = taa_acceptance_mechanism From 7803a12aaa036039c5c053ab5395c536855d5370 Mon Sep 17 00:00:00 2001 From: Roman Reinert Date: Thu, 24 Feb 2022 16:24:29 +0000 Subject: [PATCH 043/872] make 'verified' attribute of V20PresExRecord conform to schema Signed-off-by: Roman Reinert --- .../protocols/present_proof/v2_0/formats/dif/handler.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/handler.py b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/handler.py index 33f1b653d0..65527dccec 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/handler.py @@ -1,5 +1,6 @@ """V2.0 present-proof dif presentation-exchange format handler.""" +import json import logging from marshmallow import RAISE @@ -475,5 +476,5 @@ async def verify_pres(self, pres_ex_record: V20PresExRecord) -> V20PresExRecord: document_loader=self._profile.inject(DocumentLoader), challenge=challenge, ) - pres_ex_record.verified = pres_ver_result.verified + pres_ex_record.verified = json.dumps(pres_ver_result.verified) return pres_ex_record From 5851f2cb2f0156e6f473901bfbc763996140ba83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Humbert?= Date: Fri, 25 Feb 2022 15:57:16 +0100 Subject: [PATCH 044/872] Update Endorser documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update Endorser.md documentation to reflect the recent addition of `--auto-promote-author-did` parameter. Signed-off-by: Clément Humbert --- Endorser.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Endorser.md b/Endorser.md index c108fec4d8..54f545fce3 100644 --- a/Endorser.md +++ b/Endorser.md @@ -57,5 +57,7 @@ Endorsement: --auto-create-revocation-transactions For Authors, specify whether to automatically create transactions for a cred def's revocation registry. (If not specified, the controller must invoke the endpoints required to create the revocation registry and assign to the cred def.) [env var: ACAPY_CREATE_REVOCATION_TRANSACTIONS] + --auto-promote-author-did + For Authors, specify whether to automatically promote a DID to the wallet public DID after writing to the ledger. ``` From 748c3fa317ca014b771daf2fd6c9187161e860c2 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Tue, 1 Mar 2022 08:20:02 -0800 Subject: [PATCH 045/872] Performance deme updates Signed-off-by: Ian Costanzo --- demo/README.md | 16 ++++++++++++++++ demo/runners/performance.py | 21 +++++++++++++++++++-- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/demo/README.md b/demo/README.md index a3db767adf..3ed7a073b9 100644 --- a/demo/README.md +++ b/demo/README.md @@ -396,6 +396,22 @@ The script starts both agents, runs the performance test, spits out performance A second version of the performance test can be run by adding the parameter `--routing` to the invocation above. The parameter triggers the example to run with Alice using a routing agent such that all messages pass through the routing agent between Alice and Faber. This is a good, simple example of how routing can be implemented with DIDComm agents. +You can also run the demo against a postgres database using the following: + +```bash +./run_demo performance --arg-file demo/postgres-indy-args.yml +``` + +(Obvs you need to be running a postgres database - the command to start postgres is in the yml file provided above.) + +You can tweak the number of credentials issued using the `--cound` and --batch` prameters, and you can run against an askar database using the `--askar` option. + +An example full set of options is: + +```bash +./run_demo performance --arg-file demo/postgres-indy-args.yml -c 10000 -b 10 --wallet-type askar +``` + ## Coding Challenge: Adding ACME Now that you have a solid foundation in using ACA-Py, time for a coding challenge. In this challenge, we extend the Alice-Faber command line demo by adding in ACME Corp, a place where Alice wants to work. The demo adds: diff --git a/demo/runners/performance.py b/demo/runners/performance.py index e90c176087..855d1867cd 100644 --- a/demo/runners/performance.py +++ b/demo/runners/performance.py @@ -266,7 +266,9 @@ async def main( revocation: bool = False, tails_server_base_url: str = None, issue_count: int = 300, + batch_size: int = 30, wallet_type: str = None, + arg_file: str = None, ): if multi_ledger: @@ -295,6 +297,7 @@ async def main( multitenant=multitenant, mediation=mediation, wallet_type=wallet_type, + arg_file=arg_file, ) await alice.listen_webhooks(start_port + 2) @@ -307,6 +310,7 @@ async def main( multitenant=multitenant, mediation=mediation, wallet_type=wallet_type, + arg_file=arg_file, ) await faber.listen_webhooks(start_port + 5) await faber.register_did() @@ -371,8 +375,6 @@ async def main( await alice_mediator_agent.reset_timing() await faber_mediator_agent.reset_timing() - batch_size = 100 - semaphore = asyncio.Semaphore(threads) def done_propose(fut: asyncio.Task): @@ -591,6 +593,13 @@ async def check_received_pings(agent, issue_count, pb): default=300, help="Set the number of credentials to issue", ) + parser.add_argument( + "-b", + "--batch", + type=int, + default=100, + help="Set the batch size of credentials to issue", + ) parser.add_argument( "-p", "--port", @@ -655,6 +664,12 @@ async def check_received_pings(agent, issue_count, pb): metavar="", help="Set the agent wallet type", ) + parser.add_argument( + "--arg-file", + type=str, + metavar="", + help="Specify a file containing additional aca-py parameters", + ) args = parser.parse_args() if args.did_exchange and args.mediation: @@ -690,7 +705,9 @@ async def check_received_pings(agent, issue_count, pb): args.revocation, tails_server_base_url, args.count, + args.batch, args.wallet_type, + args.arg_file, ) ) except KeyboardInterrupt: From 19409c2400b4bee751910ad7eb63bbd569231786 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Tue, 1 Mar 2022 08:39:36 -0800 Subject: [PATCH 046/872] Update demo/README.md Co-authored-by: Andrew Whitehead Signed-off-by: Ian Costanzo --- demo/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/README.md b/demo/README.md index 3ed7a073b9..83c39d02d4 100644 --- a/demo/README.md +++ b/demo/README.md @@ -404,7 +404,7 @@ You can also run the demo against a postgres database using the following: (Obvs you need to be running a postgres database - the command to start postgres is in the yml file provided above.) -You can tweak the number of credentials issued using the `--cound` and --batch` prameters, and you can run against an askar database using the `--askar` option. +You can tweak the number of credentials issued using the `--count` and `--batch` parameters, and you can run against an Askar database using the `--wallet-type askar` option. An example full set of options is: From b962077067963f8448ae7e2e30876a37c9d87837 Mon Sep 17 00:00:00 2001 From: Micah Peltier Date: Fri, 4 Mar 2022 12:01:01 -0700 Subject: [PATCH 047/872] feat: add event and emitter Signed-off-by: Micah Peltier --- aries_cloudagent/core/conductor.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/core/conductor.py b/aries_cloudagent/core/conductor.py index 6ed174545f..9237327c81 100644 --- a/aries_cloudagent/core/conductor.py +++ b/aries_cloudagent/core/conductor.py @@ -70,6 +70,7 @@ from .util import STARTUP_EVENT_TOPIC, SHUTDOWN_EVENT_TOPIC LOGGER = logging.getLogger(__name__) +UNDELIVERABLE_EVENT_TOPIC = "acapy::outbound-message::undeliverable" class Conductor: @@ -697,7 +698,7 @@ def handle_not_delivered( ) -> OutboundSendStatus: """Handle a message that failed delivery via outbound transports.""" queued_for_inbound = self.inbound_transport_manager.return_undelivered(outbound) - + profile.notify(UNDELIVERABLE_EVENT_TOPIC, outbound) return ( OutboundSendStatus.WAITING_FOR_PICKUP if queued_for_inbound From 6c7c4354ffaceadac10191b3dd27094d541b4f00 Mon Sep 17 00:00:00 2001 From: Micah Peltier Date: Fri, 4 Mar 2022 13:02:28 -0700 Subject: [PATCH 048/872] feat: async-ing and awaiting necessary funcitons Signed-off-by: Micah Peltier --- aries_cloudagent/core/conductor.py | 10 +++++----- aries_cloudagent/transport/outbound/manager.py | 4 +++- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/aries_cloudagent/core/conductor.py b/aries_cloudagent/core/conductor.py index 9237327c81..51e39d80bf 100644 --- a/aries_cloudagent/core/conductor.py +++ b/aries_cloudagent/core/conductor.py @@ -658,7 +658,7 @@ async def queue_outbound( if self.outbound_queue: return await self._queue_external(profile, outbound) else: - return self._queue_internal(profile, outbound) + return await self._queue_internal(profile, outbound) async def _queue_external( self, @@ -682,7 +682,7 @@ async def _queue_external( return OutboundSendStatus.SENT_TO_EXTERNAL_QUEUE - def _queue_internal( + async def _queue_internal( self, profile: Profile, outbound: OutboundMessage ) -> OutboundSendStatus: """Save the message to an internal outbound queue.""" @@ -691,14 +691,14 @@ def _queue_internal( return OutboundSendStatus.QUEUED_FOR_DELIVERY except OutboundDeliveryError: LOGGER.warning("Cannot queue message for delivery, no supported transport") - return self.handle_not_delivered(profile, outbound) + return await self.handle_not_delivered(profile, outbound) - def handle_not_delivered( + async def handle_not_delivered( self, profile: Profile, outbound: OutboundMessage ) -> OutboundSendStatus: """Handle a message that failed delivery via outbound transports.""" queued_for_inbound = self.inbound_transport_manager.return_undelivered(outbound) - profile.notify(UNDELIVERABLE_EVENT_TOPIC, outbound) + await profile.notify(UNDELIVERABLE_EVENT_TOPIC, outbound) return ( OutboundSendStatus.WAITING_FOR_PICKUP if queued_for_inbound diff --git a/aries_cloudagent/transport/outbound/manager.py b/aries_cloudagent/transport/outbound/manager.py index 18fdf4b182..fe4e09745c 100644 --- a/aries_cloudagent/transport/outbound/manager.py +++ b/aries_cloudagent/transport/outbound/manager.py @@ -363,7 +363,9 @@ async def _process_loop(self): exc_info=queued.error, ) if self.handle_not_delivered and queued.message: - self.handle_not_delivered(queued.profile, queued.message) + await self.handle_not_delivered( + queued.profile, queued.message + ) continue # remove from buffer deliver = False From 0bb67482df14900b96962ac8d7ee661427dde559 Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Fri, 4 Mar 2022 16:33:36 -0800 Subject: [PATCH 049/872] fix usage of send_credential_ack Signed-off-by: Andrew Whitehead --- .../protocols/issue_credential/v1_0/manager.py | 2 +- .../protocols/issue_credential/v1_0/routes.py | 7 +++---- .../issue_credential/v1_0/tests/test_routes.py | 14 ++++---------- 3 files changed, 8 insertions(+), 15 deletions(-) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/manager.py b/aries_cloudagent/protocols/issue_credential/v1_0/manager.py index 75fe7895ac..bc11f4995e 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/manager.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/manager.py @@ -884,7 +884,7 @@ async def send_credential_ack( Delete credential exchange record if set to auto-remove. Returns: - Tuple: cred ex record, credential ack message for tracing. + credential ack message for tracing """ credential_ack_message = CredentialAck() diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/routes.py b/aries_cloudagent/protocols/issue_credential/v1_0/routes.py index fd8c7b3f59..abe70b2d96 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/routes.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/routes.py @@ -1183,10 +1183,9 @@ async def credential_exchange_store(request: web.BaseRequest): ) try: # protocol owes an ack - ( - cred_ex_record, - credential_ack_message, - ) = await credential_manager.send_credential_ack(cred_ex_record) + credential_ack_message = await credential_manager.send_credential_ack( + cred_ex_record + ) result = cred_ex_record.serialize() # pick up state done except ( diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_routes.py index 8744ffe449..0fe2cf8d78 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_routes.py @@ -1187,8 +1187,7 @@ async def test_credential_exchange_store(self): mock_cred_ex_record ) mock_credential_manager.return_value.send_credential_ack.return_value = ( - mock_cred_ex_record, - async_mock.MagicMock(), + async_mock.MagicMock() ) await test_module.credential_exchange_store(self.request) @@ -1224,8 +1223,7 @@ async def test_credential_exchange_store_bad_cred_id_json(self): mock_cred_ex_record ) mock_credential_manager.return_value.send_credential_ack.return_value = ( - mock_cred_ex_record, - async_mock.MagicMock(), + async_mock.MagicMock() ) await test_module.credential_exchange_store(self.request) @@ -1278,8 +1276,7 @@ async def test_credential_exchange_store_no_conn_record(self): mock_cred_ex ) mock_credential_manager.return_value.send_credential_ack.return_value = ( - mock_cred_ex, - async_mock.MagicMock(), + async_mock.MagicMock() ) with self.assertRaises(test_module.web.HTTPBadRequest): @@ -1339,10 +1336,7 @@ async def test_credential_exchange_store_x(self): return_value=mock_cred_ex_record ), send_credential_ack=async_mock.CoroutineMock( - return_value=( - mock_cred_ex_record, - async_mock.MagicMock(), - ) + return_value=async_mock.MagicMock() ), ) From 9f5aeda5c7d275cacc218fb7440af077557541c5 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 8 Mar 2022 08:41:46 +0000 Subject: [PATCH 050/872] docs: ontario supported features attribution Signed-off-by: Timo Glastra --- SupportedRFCs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SupportedRFCs.md b/SupportedRFCs.md index d54a138269..685e06451a 100644 --- a/SupportedRFCs.md +++ b/SupportedRFCs.md @@ -11,7 +11,7 @@ welcome! If you have any questions, please contact us on the #aries channel on **Last Update**: 2021-12-22, Release 0.7.3 > The checklist version of this document was created as a joint effort -> between [Northern Block](https://northernblock.io/) and [Animo Solutions](https://animo.id/). +> between [Northern Block](https://northernblock.io/), [Animo Solutions](https://animo.id/) and the Ontario government, on behalf of the Ontario government. ## AIP Support and Interoperability From 647210e0f85bf8c47f9590a05859efa342af60b6 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 8 Mar 2022 08:43:02 +0000 Subject: [PATCH 051/872] docs: supported features discord link Signed-off-by: Timo Glastra --- SupportedRFCs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SupportedRFCs.md b/SupportedRFCs.md index 685e06451a..029923b427 100644 --- a/SupportedRFCs.md +++ b/SupportedRFCs.md @@ -6,7 +6,7 @@ and an overview of the ACA-Py feature set. This document is manually updated and as such, may not be up to date with the most recent release of ACA-Py or the repository `main` branch. Reminders (and PRs!) to update this page are welcome! If you have any questions, please contact us on the #aries channel on -[Hyperledger Rocketchat](https://chat.hyperledger.org) or through an issue in this repo. +[Hyperledger Discord](https://discord.gg/hyperledger) or through an issue in this repo. **Last Update**: 2021-12-22, Release 0.7.3 From 8a72a294de52b7315e2b5dc041d2da42ee0fd95f Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Mon, 7 Mar 2022 17:00:59 -0700 Subject: [PATCH 052/872] feat: Add cred_def_id to claim_def transaction metadata When a transaction is returned during a credential definition, the `cred_def_id` is never returned back to the caller (neither in the response or in the webhooks), however, the schema ID is returned when a cred_def or a schema transaction is created. Signed-off-by: Colton Wolkins (Indicio work address) --- aries_cloudagent/messaging/credential_definitions/routes.py | 1 + 1 file changed, 1 insertion(+) diff --git a/aries_cloudagent/messaging/credential_definitions/routes.py b/aries_cloudagent/messaging/credential_definitions/routes.py index d547027d6d..7c62546834 100644 --- a/aries_cloudagent/messaging/credential_definitions/routes.py +++ b/aries_cloudagent/messaging/credential_definitions/routes.py @@ -251,6 +251,7 @@ async def credential_definitions_send_credential_definition(request: web.BaseReq meta_data = { "context": { "schema_id": schema_id, + "cred_def_id": cred_def_id, "support_revocation": support_revocation, "novel": novel, "tag": tag, From d4f71742382bdc9fd90e669fa2a66e1bdcb1d064 Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Tue, 8 Mar 2022 09:26:24 -0700 Subject: [PATCH 053/872] chore: consolidate cred_def metadata / cleanup Signed-off-by: Colton Wolkins (Indicio work address) --- aries_cloudagent/messaging/credential_definitions/routes.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/aries_cloudagent/messaging/credential_definitions/routes.py b/aries_cloudagent/messaging/credential_definitions/routes.py index 7c62546834..26694483e4 100644 --- a/aries_cloudagent/messaging/credential_definitions/routes.py +++ b/aries_cloudagent/messaging/credential_definitions/routes.py @@ -248,10 +248,12 @@ async def credential_definitions_send_credential_definition(request: web.BaseReq except (IndyIssuerError, LedgerError) as e: raise web.HTTPBadRequest(reason=e.message) from e + issuer_did = cred_def_id.split(":")[0] meta_data = { "context": { "schema_id": schema_id, "cred_def_id": cred_def_id, + "issuer_did": issuer_did, "support_revocation": support_revocation, "novel": novel, "tag": tag, @@ -264,10 +266,6 @@ async def credential_definitions_send_credential_definition(request: web.BaseReq if not create_transaction_for_endorser: # Notify event - issuer_did = cred_def_id.split(":")[0] - meta_data["context"]["schema_id"] = schema_id - meta_data["context"]["cred_def_id"] = cred_def_id - meta_data["context"]["issuer_did"] = issuer_did meta_data["processing"]["auto_create_rev_reg"] = True await notify_cred_def_event(context.profile, cred_def_id, meta_data) From b3f8bba1823b1fa49e016a953ca11fde89dbdb5c Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Wed, 9 Mar 2022 09:01:25 -0800 Subject: [PATCH 054/872] cleanup Signed-off-by: Shaanjot Gill --- OutboundQueue.md | 53 ---- aries_cloudagent/config/argparse.py | 27 -- .../config/tests/test_argparse.py | 39 --- aries_cloudagent/core/conductor.py | 25 +- aries_cloudagent/core/tests/test_conductor.py | 288 +++++++++++++----- .../transport/outbound/queue/redis.py | 91 ------ .../outbound/queue/tests/test_redis.py | 111 ------- 7 files changed, 237 insertions(+), 397 deletions(-) delete mode 100644 OutboundQueue.md delete mode 100644 aries_cloudagent/transport/outbound/queue/redis.py delete mode 100644 aries_cloudagent/transport/outbound/queue/tests/test_redis.py diff --git a/OutboundQueue.md b/OutboundQueue.md deleted file mode 100644 index c8d9c175f2..0000000000 --- a/OutboundQueue.md +++ /dev/null @@ -1,53 +0,0 @@ -# Outbound Queues in ACA-py - -## Background - -By default, messages often stay in ACA-py memory for long periods of time without being delivered. As a result, when the ACA-py Python process is terminated unexpectedly, messages are lost. - -But with recent changes, outbound messages can now be sent to a message queue of your choice instead of being delivered by ACA-py. This queue is external to the ACA-py process, and can be configured for the durability requirements you want. This new concept of an "outbound queue" is intended to be an optional replacement to the current ACA-py outbound transport (i.e. option `-ot`, `--outbound-transport`). - -If you run an outbound queue, you will also need to run a new service, a delivery agent, to actually deliver the message. See more details below. - -## Usage Details - -A new set of commandline options have been added to provide a way for users to "opt in" to use of the outbound queue. These new options are as follows: - -- `-oq`, `--outbound-queue`: specifies the queue connection details. -- `-oqp`, `--outbound-queue-prefix`: defines a prefix to use when generating the topic key. -- `-oqc`, `--outbound-queue-class`: specify the location of a custom queue class. - -Only the first, `--outbound-queue`, is required if you would like to opt into the outbound queue to replace `--outbound-transport`. The input for this option takes the form `[protocol]://[host]:[port]`. So for example, if the queue I want to use is Redis, on host `myredis.mydomain.com` using the default port for Redis, the string would be as follows: `redis://myredis.mydomain.com:6379` - -The second option, `--outbound-queue-prefix`, specifies the queue topic prefix. The queue topic is generated in the following form: `{prefix}.outbound_transport`. The default value for this commandline option is the value `acapy`, so a queue key of `acapy.outbound_transport` is generated in the case of the default settings. ACA-py will send messages to the queue using this generated key as the topic. - -The third option, `--outbound-queue-class`, specifies the queue backend. By default, this is `aries_cloudagent.transport.outbound.queue.redis:RedisOutboundQueue`, which specifies ACA-py's builtin Redis `LIST` backend. Users can define their own class, inheriting from `BaseOutboundQueue`, to implement a queue backend of their choice. This commandline option is the official entrypoint of ACA-py's pluggable queue interface. Developers must specify a Python dotpath to a module importable in the current `PYTHONPATH`, followed by a colon, followed by the name of their custom class. - -## Delivery Agent - -When using `--outbound-queue` instead of `--outbound-transport`, ACA-py no longer delivers the messages to destinations. Instead, a delivery service ([a prototype can be found here](https://github.com/andrewwhitehead/aca-deliver)) would need to be run. This service should pick up a message from the queue and then deliver that message. - -When running `--outbound-queue`, ACA-py serializes messages to be sent to the queue by using MessagePack. MessagePack is a protocol to serialize content into a compact binary format. ACA-py generates keys in MessagePack as follows: -- `endpoint` - specifies the endpoint for the message. -- `headers` - specifies a set of key-value pairs representing message headers. -- `payload` - the raw binary content of the message. - -The delivery service will need to deserialize the binary content on the consuming end. The result will then be a key-value data structure (for example, a `dict` in Python). So the deseralized message, deserialized into a Python `dict` for example, would be in the following form: -``` -{ - "headers": {"Content-Type": "..."}, - "endpoint": "...", - "payload": "..." -} -``` -The delivery agent should process this message and deliver it to the recipient as appropriate. - -## Backend-Specific Notes - -### Redis - -Value for `--outbound-queue-class` to use this backend: -- `aries_cloudagent.transport.outbound.queue.redis:RedisOutboundQueue` - -This is a queue backend, using the `LIST` data type in Redis. When using Redis, the delivery service consuming this queue in order to send outbound messages over transport will need to pop from the left side of the queue (i.e. the Redis `LPOP` command) to get messages in the order they were sent. - -Users will need to configure [Redis persistence](https://redis.io/topics/persistence) to gain message durability benefits in their Redis deployment. Redis by default runs entirely in-memory, so it is subject to the same data loss characteristics as ACA-py unless you also configure it to run in persistence mode. diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index a8b6ae5110..f39659552d 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -1169,22 +1169,6 @@ def add_arguments(self, parser: ArgumentParser): "Supported outbound transport types are 'http' and 'ws'." ), ) - parser.add_argument( - "-oq", - "--outbound-queue", - dest="outbound_queue", - type=str, - env_var="ACAPY_OUTBOUND_TRANSPORT_QUEUE", - help=( - "Defines the location of the Outbound Queue Engine. This must be " - "a 'dotpath' to a Python module on the PYTHONPATH, followed by a " - "colon, followed by the name of a Python class that implements " - "BaseOutboundQueue. This commandline option is the official entry " - "point of ACA-py's pluggable queue interface. The default value is: " - "'aries_cloudagent.transport.outbound.queue.redis:RedisOutboundQueue'." - "" - ), - ) parser.add_argument( "-l", "--label", @@ -1264,19 +1248,8 @@ def get_settings(self, args: Namespace): settings["transport.inbound_configs"] = args.inbound_transports else: raise ArgsParseError("-it/--inbound-transport is required") - if not args.outbound_transports and not args.outbound_queue: - raise ArgsParseError( - "-ot/--outbound-transport or -oq/--outbound-queue is required" - ) - if args.outbound_transports and args.outbound_queue: - raise ArgsParseError( - "-ot/--outbound-transport and -oq/--outbound-queue are " - "not allowed together" - ) if args.outbound_transports: settings["transport.outbound_configs"] = args.outbound_transports - if args.outbound_queue: - settings["transport.outbound_queue"] = args.outbound_queue settings["transport.enable_undelivered_queue"] = args.enable_undelivered_queue diff --git a/aries_cloudagent/config/tests/test_argparse.py b/aries_cloudagent/config/tests/test_argparse.py index 4e76a681d2..0eeed5587f 100644 --- a/aries_cloudagent/config/tests/test_argparse.py +++ b/aries_cloudagent/config/tests/test_argparse.py @@ -126,45 +126,6 @@ async def test_upgrade_config(self): == "./aries_cloudagent/config/tests/test-acapy-upgrade-config.yml" ) - async def test_outbound_is_required(self): - """Test that either -ot or -oq are required""" - parser = argparse.create_argument_parser() - group = argparse.TransportGroup() - group.add_arguments(parser) - - result = parser.parse_args( - [ - "--inbound-transport", - "http", - "0.0.0.0", - "80", - ] - ) - - with self.assertRaises(argparse.ArgsParseError): - settings = group.get_settings(result) - - async def test_outbound_queue(self): - """Test outbound queue class path string.""" - parser = argparse.create_argument_parser() - group = argparse.TransportGroup() - group.add_arguments(parser) - - result = parser.parse_args( - [ - "--inbound-transport", - "http", - "0.0.0.0", - "80", - "--outbound-queue", - "my_queue.mod.path", - ] - ) - - settings = group.get_settings(result) - - assert settings.get("transport.outbound_queue") == "my_queue.mod.path" - async def test_general_settings_file(self): """Test file argument parsing.""" diff --git a/aries_cloudagent/core/conductor.py b/aries_cloudagent/core/conductor.py index 6ed174545f..31cd7c579b 100644 --- a/aries_cloudagent/core/conductor.py +++ b/aries_cloudagent/core/conductor.py @@ -213,6 +213,22 @@ async def setup(self): self.outbound_queue = get_outbound_queue(self.root_profile) + if ( + not self.outbound_queue + and self.outbound_transport_manager.registered_transports == {} + ): + LOGGER.exception("outbound_transport or outbound_queue is required") + raise + + if ( + self.outbound_queue + and self.outbound_transport_manager.registered_transports != {} + ): + LOGGER.exception( + "outbound_transport and outbound-queue are not allowed together" + ) + raise + # Admin API if context.settings.get("admin.enabled"): try: @@ -649,11 +665,10 @@ async def queue_outbound( self.admin_server.notify_fatal_error() raise del conn_mgr - # If ``self.outbound_queue`` is specified (usually set via - # outbound queue `-oq` commandline option), use that external - # queue. Else save the message to an internal queue. This - # internal queue usually results in the message to be sent over - # ACA-py `-ot` transport. + # If ``self.outbound_queue`` is specified (redis_queue plugin), + # use that external queue. Else save the message to an internal + # queue. This internal queue usually results in the message to + # be sent over ACA-py `-ot` transport. if self.outbound_queue: return await self._queue_external(profile, outbound) else: diff --git a/aries_cloudagent/core/tests/test_conductor.py b/aries_cloudagent/core/tests/test_conductor.py index d3f17d499b..4aa6dfc79e 100644 --- a/aries_cloudagent/core/tests/test_conductor.py +++ b/aries_cloudagent/core/tests/test_conductor.py @@ -120,7 +120,9 @@ async def test_startup(self): return_value=async_mock.MagicMock(value=f"v{__version__}") ), ): - + mock_outbound_mgr.return_value.registered_transports = { + "test": async_mock.MagicMock(schemes=["http"]) + } await conductor.setup() session = await conductor.root_profile.session() @@ -162,7 +164,9 @@ async def test_startup_admin_server_x(self): ) as mock_logger, async_mock.patch.object( test_module, "AdminServer", async_mock.MagicMock() ) as mock_admin_server: - + mock_outbound_mgr.return_value.registered_transports = { + "test": async_mock.MagicMock(schemes=["http"]) + } mock_admin_server.side_effect = ValueError() with self.assertRaises(ValueError): await conductor.setup() @@ -184,7 +188,9 @@ async def test_startup_no_public_did(self): return_value=async_mock.MagicMock(value=f"v{__version__}") ), ): - + mock_outbound_mgr.return_value.registered_transports = { + "test": async_mock.MagicMock(schemes=["http"]) + } await conductor.setup() mock_inbound_mgr.return_value.setup.assert_awaited_once() @@ -223,6 +229,9 @@ async def test_stats(self): async_mock.MagicMock(state=QueuedOutboundMessage.STATE_ENCODE), async_mock.MagicMock(state=QueuedOutboundMessage.STATE_DELIVER), ] + mock_outbound_mgr.return_value.registered_transports = { + "test": async_mock.MagicMock(schemes=["http"]) + } await conductor.setup() @@ -244,7 +253,13 @@ async def test_inbound_message_handler(self): builder: ContextBuilder = StubContextBuilder(self.test_settings) conductor = test_module.Conductor(builder) - await conductor.setup() + with async_mock.patch.object( + test_module, "OutboundTransportManager", autospec=True + ) as mock_outbound_mgr: + mock_outbound_mgr.return_value.registered_transports = { + "test": async_mock.MagicMock(schemes=["http"]) + } + await conductor.setup() with async_mock.patch.object( conductor.dispatcher, "queue_message", autospec=True @@ -268,7 +283,13 @@ async def test_inbound_message_handler_ledger_x(self): builder: ContextBuilder = StubContextBuilder(self.test_settings_admin) conductor = test_module.Conductor(builder) - await conductor.setup() + with async_mock.patch.object( + test_module, "OutboundTransportManager", autospec=True + ) as mock_outbound_mgr: + mock_outbound_mgr.return_value.registered_transports = { + "test": async_mock.MagicMock(schemes=["http"]) + } + await conductor.setup() with async_mock.patch.object( conductor.dispatcher, "queue_message", autospec=True @@ -294,8 +315,16 @@ async def test_outbound_message_handler_return_route(self): conductor = test_module.Conductor(builder) test_to_verkey = "test-to-verkey" test_from_verkey = "test-from-verkey" - - await conductor.setup() + with async_mock.patch.object( + test_module, + "get_outbound_queue", + async_mock.MagicMock( + return_value=async_mock.MagicMock( + enqueue_message=async_mock.CoroutineMock() + ) + ), + ) as mock_get_outbound_queue: + await conductor.setup() payload = "{}" message = OutboundMessage(payload=payload) @@ -321,7 +350,9 @@ async def test_outbound_message_handler_with_target(self): with async_mock.patch.object( test_module, "OutboundTransportManager", autospec=True ) as mock_outbound_mgr: - + mock_outbound_mgr.return_value.registered_transports = { + "test": async_mock.MagicMock(schemes=["http"]) + } await conductor.setup() payload = "{}" @@ -345,7 +376,9 @@ async def test_outbound_message_handler_with_connection(self): ) as mock_outbound_mgr, async_mock.patch.object( test_module, "ConnectionManager", autospec=True ) as conn_mgr: - + mock_outbound_mgr.return_value.registered_transports = { + "test": async_mock.MagicMock(schemes=["http"]) + } await conductor.setup() payload = "{}" @@ -373,7 +406,9 @@ async def test_outbound_message_handler_with_verkey_no_target(self): with async_mock.patch.object( test_module, "OutboundTransportManager", autospec=True ) as mock_outbound_mgr: - + mock_outbound_mgr.return_value.registered_transports = { + "test": async_mock.MagicMock(schemes=["http"]) + } await conductor.setup() payload = "{}" @@ -435,44 +470,41 @@ async def test_handle_nots(self): await conductor.queue_outbound(conductor.root_profile, message) mock_run_task.assert_called_once() - async def test_handle_outbound_queue(self): - builder: ContextBuilder = StubContextBuilder(self.test_settings) - conductor = test_module.Conductor(builder) - encoded_outbound_message_mock = async_mock.MagicMock(payload="message_payload") - - payload = "{}" - message = OutboundMessage( - payload=payload, - connection_id="dummy-conn-id", - target=async_mock.MagicMock(endpoint="endpoint"), - reply_to_verkey=TestDIDs.test_verkey, - ) - - await conductor.setup() - conductor.outbound_queue = async_mock.MagicMock( - enqueue_message=async_mock.CoroutineMock() - ) - conductor.outbound_transport_manager = async_mock.MagicMock( - encode_outbound_message=async_mock.CoroutineMock( - return_value=encoded_outbound_message_mock - ) - ) - - await conductor.queue_outbound(conductor.root_profile, message) - - conductor.outbound_transport_manager.encode_outbound_message.assert_called_once_with( - conductor.root_profile, message, message.target - ) - conductor.outbound_queue.enqueue_message.assert_called_once_with( - encoded_outbound_message_mock.payload, message.target.endpoint - ) - + # async def test_handle_outbound_queue(self): + # builder: ContextBuilder = StubContextBuilder(self.test_settings) + # conductor = test_module.Conductor(builder) + # encoded_outbound_message_mock = async_mock.MagicMock(payload="message_payload") + + # payload = "{}" + # message = OutboundMessage( + # payload=payload, + # connection_id="dummy-conn-id", + # target=async_mock.MagicMock(endpoint="endpoint"), + # reply_to_verkey=TestDIDs.test_verkey, + # ) + # with async_mock.patch.object( + # test_module, "get_outbound_queue", async_mock.MagicMock() + # ) as mock_get_outbound_queue: + # await conductor.setup() + # conductor.outbound_queue = async_mock.MagicMock( + # enqueue_message=async_mock.CoroutineMock() + # ) + + # await conductor.queue_outbound(conductor.root_profile, message) + # conductor.outbound_queue.enqueue_message.assert_called_once_with( + # encoded_outbound_message_mock.payload, message.target.endpoint + # ) async def test_handle_not_returned_ledger_x(self): builder: ContextBuilder = StubContextBuilder(self.test_settings_admin) conductor = test_module.Conductor(builder) - await conductor.setup() - + with async_mock.patch.object( + test_module, "OutboundTransportManager", autospec=True + ) as mock_outbound_mgr: + mock_outbound_mgr.return_value.registered_transports = { + "test": async_mock.MagicMock(schemes=["http"]) + } + await conductor.setup() with async_mock.patch.object( conductor.dispatcher, "run_task", async_mock.MagicMock() ) as mock_dispatch_run, async_mock.patch.object( @@ -501,7 +533,13 @@ async def test_queue_outbound_ledger_x(self): builder: ContextBuilder = StubContextBuilder(self.test_settings_admin) conductor = test_module.Conductor(builder) - await conductor.setup() + with async_mock.patch.object( + test_module, "OutboundTransportManager", autospec=True + ) as mock_outbound_mgr: + mock_outbound_mgr.return_value.registered_transports = { + "test": async_mock.MagicMock(schemes=["http"]) + } + await conductor.setup() with async_mock.patch.object( test_module, "ConnectionManager", autospec=True @@ -532,8 +570,13 @@ async def test_admin(self): builder: ContextBuilder = StubContextBuilder(self.test_settings) builder.update_settings({"admin.enabled": "1"}) conductor = test_module.Conductor(builder) - - await conductor.setup() + with async_mock.patch.object( + test_module, "OutboundTransportManager", autospec=True + ) as mock_outbound_mgr: + mock_outbound_mgr.return_value.registered_transports = { + "test": async_mock.MagicMock(schemes=["http"]) + } + await conductor.setup() admin = conductor.context.inject(BaseAdminServer) assert admin is conductor.admin_server @@ -571,8 +614,13 @@ async def test_admin_startx(self): } ) conductor = test_module.Conductor(builder) - - await conductor.setup() + with async_mock.patch.object( + test_module, "OutboundTransportManager", autospec=True + ) as mock_outbound_mgr: + mock_outbound_mgr.return_value.registered_transports = { + "test": async_mock.MagicMock(schemes=["http"]) + } + await conductor.setup() admin = conductor.context.inject(BaseAdminServer) assert admin is conductor.admin_server @@ -622,7 +670,9 @@ async def test_setup_collector(self): ) as mock_outbound_mgr, async_mock.patch.object( test_module, "LoggingConfigurator", autospec=True ) as mock_logger: - + mock_outbound_mgr.return_value.registered_transports = { + "test": async_mock.MagicMock(schemes=["http"]) + } await conductor.setup() async def test_start_static(self): @@ -638,7 +688,12 @@ async def test_start_static(self): async_mock.CoroutineMock( return_value=async_mock.MagicMock(value=f"v{__version__}") ), - ): + ), async_mock.patch.object( + test_module, "OutboundTransportManager", autospec=True + ) as mock_outbound_mgr: + mock_outbound_mgr.return_value.registered_transports = { + "test": async_mock.MagicMock(schemes=["http"]) + } await conductor.setup() session = await conductor.root_profile.session() @@ -661,11 +716,16 @@ async def test_start_x_in(self): test_module, "ConnectionManager" ) as mock_mgr, async_mock.patch.object( test_module, "InboundTransportManager" - ) as mock_intx_mgr: + ) as mock_intx_mgr, async_mock.patch.object( + test_module, "OutboundTransportManager", autospec=True + ) as mock_outbound_mgr: mock_intx_mgr.return_value = async_mock.MagicMock( setup=async_mock.CoroutineMock(), start=async_mock.CoroutineMock(side_effect=KeyError("trouble")), ) + mock_outbound_mgr.return_value.registered_transports = { + "test": async_mock.MagicMock(schemes=["http"]) + } await conductor.setup() mock_mgr.return_value.create_static_connection = async_mock.CoroutineMock() with self.assertRaises(KeyError): @@ -684,6 +744,7 @@ async def test_start_x_out(self): mock_outx_mgr.return_value = async_mock.MagicMock( setup=async_mock.CoroutineMock(), start=async_mock.CoroutineMock(side_effect=KeyError("trouble")), + registered_transports={"test": async_mock.MagicMock(schemes=["http"])}, ) await conductor.setup() mock_mgr.return_value.create_static_connection = async_mock.CoroutineMock() @@ -708,7 +769,13 @@ async def test_dispatch_complete_non_fatal_x(self): }, ) - await conductor.setup() + with async_mock.patch.object( + test_module, "OutboundTransportManager", autospec=True + ) as mock_outbound_mgr: + mock_outbound_mgr.return_value.registered_transports = { + "test": async_mock.MagicMock(schemes=["http"]) + } + await conductor.setup() with async_mock.patch.object( conductor.admin_server, "notify_fatal_error", async_mock.MagicMock() @@ -738,7 +805,13 @@ async def test_dispatch_complete_fatal_x(self): }, ) - await conductor.setup() + with async_mock.patch.object( + test_module, "OutboundTransportManager", autospec=True + ) as mock_outbound_mgr: + mock_outbound_mgr.return_value.registered_transports = { + "test": async_mock.MagicMock(schemes=["http"]) + } + await conductor.setup() with async_mock.patch.object( conductor.admin_server, "notify_fatal_error", async_mock.MagicMock() @@ -757,8 +830,6 @@ async def test_print_invite_connection(self): ) conductor = test_module.Conductor(builder) - await conductor.setup() - with async_mock.patch( "sys.stdout", new=StringIO() ) as captured, async_mock.patch.object( @@ -767,7 +838,12 @@ async def test_print_invite_connection(self): async_mock.CoroutineMock( return_value=async_mock.MagicMock(value=f"v{__version__}") ), - ): + ), async_mock.patch.object( + test_module, "OutboundTransportManager", autospec=True + ) as mock_outbound_mgr: + mock_outbound_mgr.return_value.registered_transports = { + "test": async_mock.MagicMock(schemes=["http"]) + } await conductor.setup() session = await conductor.root_profile.session() @@ -788,7 +864,13 @@ async def test_clear_default_mediator(self): builder.update_settings({"mediation.clear": True}) conductor = test_module.Conductor(builder) - await conductor.setup() + with async_mock.patch.object( + test_module, "OutboundTransportManager", autospec=True + ) as mock_outbound_mgr: + mock_outbound_mgr.return_value.registered_transports = { + "test": async_mock.MagicMock(schemes=["http"]) + } + await conductor.setup() with async_mock.patch.object( test_module, @@ -812,7 +894,13 @@ async def test_set_default_mediator(self): builder.update_settings({"mediation.default_id": "test-id"}) conductor = test_module.Conductor(builder) - await conductor.setup() + with async_mock.patch.object( + test_module, "OutboundTransportManager", autospec=True + ) as mock_outbound_mgr: + mock_outbound_mgr.return_value.registered_transports = { + "test": async_mock.MagicMock(schemes=["http"]) + } + await conductor.setup() with async_mock.patch.object( test_module, @@ -846,7 +934,13 @@ async def test_set_default_mediator_x(self): builder.update_settings({"mediation.default_id": "test-id"}) conductor = test_module.Conductor(builder) - await conductor.setup() + with async_mock.patch.object( + test_module, "OutboundTransportManager", autospec=True + ) as mock_outbound_mgr: + mock_outbound_mgr.return_value.registered_transports = { + "test": async_mock.MagicMock(schemes=["http"]) + } + await conductor.setup() with async_mock.patch.object( MediationRecord, @@ -877,7 +971,13 @@ async def test_webhook_router(self): test_endpoint = "http://example" test_attempts = 2 - await conductor.setup() + with async_mock.patch.object( + test_module, "OutboundTransportManager", autospec=True + ) as mock_outbound_mgr: + mock_outbound_mgr.return_value.registered_transports = { + "test": async_mock.MagicMock(schemes=["http"]) + } + await conductor.setup() with async_mock.patch.object( conductor.outbound_transport_manager, "enqueue_webhook" ) as mock_enqueue: @@ -914,7 +1014,9 @@ async def test_shutdown_multitenant_profiles(self): ) as mock_outbound_mgr, async_mock.patch.object( test_module, "LoggingConfigurator", autospec=True ) as mock_logger: - + mock_outbound_mgr.return_value.registered_transports = { + "test": async_mock.MagicMock(schemes=["http"]) + } await conductor.setup() multitenant_mgr = conductor.context.inject(BaseMultitenantManager) @@ -968,7 +1070,13 @@ async def test_mediator_invitation_0160(self, mock_from_url, _): conductor = test_module.Conductor( self.__get_mediator_config("test-invite", True) ) - await conductor.setup() + with async_mock.patch.object( + test_module, "OutboundTransportManager", autospec=True + ) as mock_outbound_mgr: + mock_outbound_mgr.return_value.registered_transports = { + "test": async_mock.MagicMock(schemes=["http"]) + } + await conductor.setup() mock_conn_record = async_mock.MagicMock() @@ -1006,7 +1114,13 @@ async def test_mediator_invitation_0434(self, mock_from_url, _): conductor = test_module.Conductor( self.__get_mediator_config("test-invite", False) ) - await conductor.setup() + with async_mock.patch.object( + test_module, "OutboundTransportManager", autospec=True + ) as mock_outbound_mgr: + mock_outbound_mgr.return_value.registered_transports = { + "test": async_mock.MagicMock(schemes=["http"]) + } + await conductor.setup() conn_record = ConnRecord( invitation_key="3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx", @@ -1055,7 +1169,13 @@ async def test_mediation_invitation_should_use_stored_invitation( conductor = test_module.Conductor( self.__get_mediator_config(invite_string, True) ) - await conductor.setup() + with async_mock.patch.object( + test_module, "OutboundTransportManager", autospec=True + ) as mock_outbound_mgr: + mock_outbound_mgr.return_value.registered_transports = { + "test": async_mock.MagicMock(schemes=["http"]) + } + await conductor.setup() mock_conn_record = async_mock.MagicMock() mocked_store = get_invite_store_mock(invite_string) patched_invite_store.return_value = mocked_store @@ -1102,7 +1222,13 @@ async def test_mediation_invitation_should_not_create_connection_for_old_invitat conductor = test_module.Conductor( self.__get_mediator_config(invite_string, True) ) - await conductor.setup() + with async_mock.patch.object( + test_module, "OutboundTransportManager", autospec=True + ) as mock_outbound_mgr: + mock_outbound_mgr.return_value.registered_transports = { + "test": async_mock.MagicMock(schemes=["http"]) + } + await conductor.setup() invite_store_mock = get_invite_store_mock(invite_string, True) patched_invite_store.return_value = invite_store_mock @@ -1137,7 +1263,13 @@ async def test_mediator_invitation_x(self, _): conductor = test_module.Conductor( self.__get_mediator_config("test-invite", True) ) - await conductor.setup() + with async_mock.patch.object( + test_module, "OutboundTransportManager", autospec=True + ) as mock_outbound_mgr: + mock_outbound_mgr.return_value.registered_transports = { + "test": async_mock.MagicMock(schemes=["http"]) + } + await conductor.setup() with async_mock.patch.object( test_module.ConnectionInvitation, @@ -1169,7 +1301,12 @@ async def test_setup_ledger_both_multiple_and_base(self): async_mock.CoroutineMock(), ) as mock_multiple_genesis_load, async_mock.patch.object( test_module, "get_genesis_transactions", async_mock.CoroutineMock() - ) as mock_genesis_load: + ) as mock_genesis_load, async_mock.patch.object( + test_module, "OutboundTransportManager", autospec=True + ) as mock_outbound_mgr: + mock_outbound_mgr.return_value.registered_transports = { + "test": async_mock.MagicMock(schemes=["http"]) + } await conductor.setup() mock_multiple_genesis_load.assert_called_once() mock_genesis_load.assert_called_once() @@ -1181,7 +1318,12 @@ async def test_setup_ledger_only_base(self): with async_mock.patch.object( test_module, "get_genesis_transactions", async_mock.CoroutineMock() - ) as mock_genesis_load: + ) as mock_genesis_load, async_mock.patch.object( + test_module, "OutboundTransportManager", autospec=True + ) as mock_outbound_mgr: + mock_outbound_mgr.return_value.registered_transports = { + "test": async_mock.MagicMock(schemes=["http"]) + } await conductor.setup() mock_genesis_load.assert_called_once() @@ -1202,7 +1344,9 @@ async def test_startup_x_version_mismatch(self): return_value=async_mock.MagicMock(value=f"v0.6.0") ), ): - + mock_outbound_mgr.return_value.registered_transports = { + "test": async_mock.MagicMock(schemes=["http"]) + } await conductor.setup() session = await conductor.root_profile.session() @@ -1237,7 +1381,9 @@ async def test_startup_x_no_storage_version(self): "find_record", async_mock.CoroutineMock(side_effect=StorageNotFoundError()), ): - + mock_outbound_mgr.return_value.registered_transports = { + "test": async_mock.MagicMock(schemes=["http"]) + } await conductor.setup() session = await conductor.root_profile.session() diff --git a/aries_cloudagent/transport/outbound/queue/redis.py b/aries_cloudagent/transport/outbound/queue/redis.py deleted file mode 100644 index 79f596ffca..0000000000 --- a/aries_cloudagent/transport/outbound/queue/redis.py +++ /dev/null @@ -1,91 +0,0 @@ -"""Redis outbound transport.""" - -from typing import Union - -import aioredis -import msgpack - -from ....core.profile import Profile -from .base import BaseOutboundQueue, OutboundQueueConfigurationError, OutboundQueueError - - -class RedisOutboundQueue(BaseOutboundQueue): - """Redis outbound transport class.""" - - config_key = "redis_queue" - - def __init__(self, root_profile: Profile) -> None: - """Set initial state.""" - try: - plugin_config = root_profile.settings["plugin_config"] or {} - config = plugin_config[self.config_key] - self.connection = config["connection"] - except KeyError as error: - raise OutboundQueueConfigurationError( - "Configuration missing for redis queue" - ) from error - - self.prefix = config.get("prefix", "acapy") - self.pool = aioredis.ConnectionPool.from_url( - self.connection, max_connections=10 - ) - self.redis = aioredis.Redis(connection_pool=self.pool) - - def __str__(self): - """Return string representation of the outbound queue.""" - return ( - f"RedisOutboundQueue(" - f"connection={self.connection}, " - f"prefix={self.prefix}" - f")" - ) - - async def start(self): - """Start the transport.""" - # aioredis will lazily connect but we can eagerly trigger connection with: - # await self.redis.ping() - # Calling this on enter to `async with` just before another queue - # operation is made does not make sense and we should just let aioredis - # do lazy connection. - - async def stop(self): - """Stop the transport.""" - # aioredis cleans up automatically but we can clean up manually with: - # await self.pool.disconnect() - # However, calling this on exit of `async with` does not make sense and - # we should just let aioredis handle the connection lifecycle. - - async def push(self, key: bytes, message: bytes): - """Push a ``message`` to redis on ``key``.""" - try: - return await self.redis.rpush(key, message) - except aioredis.RedisError as error: - raise OutboundQueueError("Unexpected redis client exception") from error - - async def enqueue_message( - self, - payload: Union[str, bytes], - endpoint: str, - ): - """Prepare and send message to external redis. - - Args: - payload: message payload in string or byte format - endpoint: URI endpoint for delivery - """ - if not endpoint: - raise OutboundQueueError("No endpoint provided") - if isinstance(payload, bytes): - content_type = "application/ssi-agent-wire" - else: - content_type = "application/json" - payload = payload.encode(encoding="utf-8") - message = msgpack.packb( - { - "headers": {"Content-Type": content_type}, - "endpoint": endpoint, - "payload": payload, - } - ) - key = f"{self.prefix}.outbound_transport".encode() - return await self.push(key, message) diff --git a/aries_cloudagent/transport/outbound/queue/tests/test_redis.py b/aries_cloudagent/transport/outbound/queue/tests/test_redis.py deleted file mode 100644 index ebdb147957..0000000000 --- a/aries_cloudagent/transport/outbound/queue/tests/test_redis.py +++ /dev/null @@ -1,111 +0,0 @@ -import os -import string - -import aioredis -from asynctest import mock as async_mock -import msgpack -import pytest - -from .....config.settings import Settings -from .....core.in_memory.profile import InMemoryProfile -from ..base import OutboundQueueConfigurationError, OutboundQueueError -from ..redis import RedisOutboundQueue - - -ENDPOINT = "http://localhost:9000" -KEYNAME = "acapy.outbound_transport" - -REDIS_CONF = os.environ.get("TEST_REDIS_CONFIG", None) - - -@pytest.fixture -async def mock_redis(): - with async_mock.patch( - "aioredis.ConnectionPool.from_url", async_mock.MagicMock() - ), async_mock.patch("aioredis.Redis", async_mock.MagicMock()): - yield - - -@pytest.fixture -def profile(): - def _profile_factory(connection=None, prefix=None): - return InMemoryProfile.test_profile( - settings={ - "plugin_config": { - RedisOutboundQueue.config_key: { - "connection": connection or "connection", - "prefix": prefix or "acapy", - } - } - } - ) - - yield _profile_factory - - -@pytest.fixture -def queue(profile, mock_redis): - yield RedisOutboundQueue(profile()) - - -@pytest.fixture -def mock_rpush(queue): - pushed = [] - - async def _mock_rpush(key, message): - pushed.append((key, message)) - - queue.redis.rpush = _mock_rpush - yield pushed - - -def test_init(mock_redis, profile): - q = RedisOutboundQueue(profile()) - q.prefix == "acapy" - q.connection = "connection" - assert str(q) - - -def test_init_x(mock_redis): - with pytest.raises(OutboundQueueConfigurationError): - RedisOutboundQueue(InMemoryProfile.test_profile()) - - -@pytest.mark.asyncio -async def test_enqueue_message_str(queue, mock_rpush): - await queue.enqueue_message( - payload=string.ascii_letters + string.digits, - endpoint=ENDPOINT, - ) - [(key, message)] = mock_rpush - assert ( - msgpack.unpackb(message).get("headers", {}).get("Content-Type") - == "application/json" - ) - - -@pytest.mark.asyncio -async def test_enqueue_message_bytes(queue, mock_rpush): - await queue.enqueue_message( - payload=bytes(range(0, 256)), - endpoint=ENDPOINT, - ) - [(key, message)] = mock_rpush - assert ( - msgpack.unpackb(message).get("headers", {}).get("Content-Type") - == "application/ssi-agent-wire" - ) - - -@pytest.mark.asyncio -async def test_enqueue_message_x_redis_error(queue): - queue.redis.rpush = async_mock.CoroutineMock(side_effect=aioredis.RedisError) - with pytest.raises(OutboundQueueError): - await queue.enqueue_message(payload="", endpoint=ENDPOINT) - - -@pytest.mark.asyncio -async def test_enqueue_message_x_no_endpoint(queue): - queue.redis.rpush = async_mock.CoroutineMock(side_effect=aioredis.RedisError) - with pytest.raises(OutboundQueueError): - await queue.enqueue_message(payload="", endpoint=None) From a77f6599f22e70786704be01fc0bc172d161adb2 Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Wed, 9 Mar 2022 11:00:42 -0700 Subject: [PATCH 055/872] fix: Send schema ID back when a duplicate schema is published When publishing a schema that has already been published, we should respond back with the schema ID, rather than return an error about how the `signed_txn` key does not exist on the schema_def object. Signed-off-by: Colton Wolkins (Indicio work address) --- aries_cloudagent/messaging/schemas/routes.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/aries_cloudagent/messaging/schemas/routes.py b/aries_cloudagent/messaging/schemas/routes.py index 5c7c6eb7fb..d713541b08 100644 --- a/aries_cloudagent/messaging/schemas/routes.py +++ b/aries_cloudagent/messaging/schemas/routes.py @@ -261,6 +261,10 @@ async def schemas_send_schema(request: web.BaseRequest): await notify_schema_event(context.profile, schema_id, meta_data) return web.json_response({"schema_id": schema_id, "schema": schema_def}) + # If the transaction is for the endorser, but the schema has already been created, + # then we send back the schema since the transaction will fail to be created. + elif "signed_txn" not in schema_def: + return web.json_response({"sent": {"schema_id": schema_id, "schema": schema_def}}) else: transaction_mgr = TransactionManager(context.profile) try: From a9c4244c0f435ea9ec7c76fe4dcaa1288949c597 Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Wed, 9 Mar 2022 11:06:06 -0700 Subject: [PATCH 056/872] fix: Send cred_def_id back when a duplicate cred_def is published When publishing a credential definition that has already been published, we should respond back with the `cred_def_id`, rather than return an error about how the `signed_txn` key does not exist on the cred_def object. Signed-off-by: Colton Wolkins (Indicio work address) --- aries_cloudagent/messaging/credential_definitions/routes.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/aries_cloudagent/messaging/credential_definitions/routes.py b/aries_cloudagent/messaging/credential_definitions/routes.py index 26694483e4..222ec92c39 100644 --- a/aries_cloudagent/messaging/credential_definitions/routes.py +++ b/aries_cloudagent/messaging/credential_definitions/routes.py @@ -271,6 +271,10 @@ async def credential_definitions_send_credential_definition(request: web.BaseReq return web.json_response({"credential_definition_id": cred_def_id}) + # If the transaction is for the endorser, but the schema has already been created, + # then we send back the schema since the transaction will fail to be created. + elif "signed_txn" not in cred_def: + return web.json_response({"sent": {"credential_definition_id": cred_def_id}}) else: meta_data["processing"]["auto_create_rev_reg"] = context.settings.get_value( "endorser.auto_create_rev_reg" From 5ca0fd2bd6798f5cbcdea3057784e5faf0450b7c Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Wed, 9 Mar 2022 11:22:35 -0700 Subject: [PATCH 057/872] fix: Add sent key on creddef & schema to standardize response The OpenAPI Spec schema indicates that "sent" is a possible field, however, no code paths (aside from the changes in the last two commits) returned it. Seeing as the last two commits return it during an error case, the changes here now return the same data during a successful case. This should make it easier for controllers to grab the `schema_id` or the `cred_def_id` under both circumstances. Signed-off-by: Colton Wolkins (Indicio work address) --- aries_cloudagent/messaging/credential_definitions/routes.py | 5 ++++- aries_cloudagent/messaging/schemas/routes.py | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/messaging/credential_definitions/routes.py b/aries_cloudagent/messaging/credential_definitions/routes.py index 222ec92c39..cae32f95e5 100644 --- a/aries_cloudagent/messaging/credential_definitions/routes.py +++ b/aries_cloudagent/messaging/credential_definitions/routes.py @@ -304,7 +304,10 @@ async def credential_definitions_send_credential_definition(request: web.BaseReq await outbound_handler(transaction_request, connection_id=connection_id) - return web.json_response({"txn": transaction.serialize()}) + return web.json_response({ + "sent": {"credential_definition_id": cred_def_id}, + "txn": transaction.serialize(), + }) @docs( diff --git a/aries_cloudagent/messaging/schemas/routes.py b/aries_cloudagent/messaging/schemas/routes.py index d713541b08..09415370a7 100644 --- a/aries_cloudagent/messaging/schemas/routes.py +++ b/aries_cloudagent/messaging/schemas/routes.py @@ -290,7 +290,10 @@ async def schemas_send_schema(request: web.BaseRequest): await outbound_handler(transaction_request, connection_id=connection_id) - return web.json_response({"txn": transaction.serialize()}) + return web.json_response({ + "sent": {"schema_id": schema_id, "schema": schema_def}, + "txn": transaction.serialize(), + }) @docs( From e2bd59aad561fece79dc6dde5bf9fdbd9b5fa39f Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Wed, 9 Mar 2022 11:23:42 -0800 Subject: [PATCH 058/872] test update Signed-off-by: Shaanjot Gill --- aries_cloudagent/core/tests/test_conductor.py | 120 +++++++++++++----- 1 file changed, 91 insertions(+), 29 deletions(-) diff --git a/aries_cloudagent/core/tests/test_conductor.py b/aries_cloudagent/core/tests/test_conductor.py index 4aa6dfc79e..52819d4563 100644 --- a/aries_cloudagent/core/tests/test_conductor.py +++ b/aries_cloudagent/core/tests/test_conductor.py @@ -187,10 +187,15 @@ async def test_startup_no_public_did(self): async_mock.CoroutineMock( return_value=async_mock.MagicMock(value=f"v{__version__}") ), - ): - mock_outbound_mgr.return_value.registered_transports = { - "test": async_mock.MagicMock(schemes=["http"]) - } + ), async_mock.patch.object( + test_module, "get_outbound_queue", async_mock.MagicMock() + ) as mock_get_outbound_queue: + mock_outbound_mgr.return_value.registered_transports = {} + mock_get_outbound_queue.return_value = async_mock.MagicMock( + enqueue_message=async_mock.CoroutineMock(), + start=async_mock.CoroutineMock(), + stop=async_mock.CoroutineMock(), + ) await conductor.setup() mock_inbound_mgr.return_value.setup.assert_awaited_once() @@ -470,30 +475,60 @@ async def test_handle_nots(self): await conductor.queue_outbound(conductor.root_profile, message) mock_run_task.assert_called_once() - # async def test_handle_outbound_queue(self): - # builder: ContextBuilder = StubContextBuilder(self.test_settings) - # conductor = test_module.Conductor(builder) - # encoded_outbound_message_mock = async_mock.MagicMock(payload="message_payload") - - # payload = "{}" - # message = OutboundMessage( - # payload=payload, - # connection_id="dummy-conn-id", - # target=async_mock.MagicMock(endpoint="endpoint"), - # reply_to_verkey=TestDIDs.test_verkey, - # ) - # with async_mock.patch.object( - # test_module, "get_outbound_queue", async_mock.MagicMock() - # ) as mock_get_outbound_queue: - # await conductor.setup() - # conductor.outbound_queue = async_mock.MagicMock( - # enqueue_message=async_mock.CoroutineMock() - # ) - - # await conductor.queue_outbound(conductor.root_profile, message) - # conductor.outbound_queue.enqueue_message.assert_called_once_with( - # encoded_outbound_message_mock.payload, message.target.endpoint - # ) + async def test_handle_outbound_queue(self): + builder: ContextBuilder = StubContextBuilder(self.test_settings) + conductor = test_module.Conductor(builder) + encoded_outbound_message_mock = async_mock.MagicMock(payload="message_payload") + + payload = "{}" + message = OutboundMessage( + payload=payload, + connection_id="dummy-conn-id", + target=async_mock.MagicMock(endpoint="endpoint"), + reply_to_verkey=TestDIDs.test_verkey, + ) + with async_mock.patch.object( + test_module, "get_outbound_queue", async_mock.MagicMock() + ) as mock_get_outbound_queue: + mock_get_outbound_queue.return_value = async_mock.MagicMock( + enqueue_message=async_mock.CoroutineMock() + ) + await conductor.setup() + await conductor.queue_outbound(conductor.root_profile, message) + conductor.outbound_queue.enqueue_message.assert_called_once() + + async def test_handle_outbound_queue_and_transport_missing(self): + builder: ContextBuilder = StubContextBuilder(self.test_settings) + conductor = test_module.Conductor(builder) + with async_mock.patch.object(test_module, "LOGGER") as mock_logger: + with self.assertRaises(RuntimeError): + await conductor.setup() + mock_logger.exception.assert_called_once_with( + "outbound_transport or outbound_queue is required" + ) + + async def test_handle_outbound_queue_and_transport_both(self): + builder: ContextBuilder = StubContextBuilder(self.test_settings) + conductor = test_module.Conductor(builder) + with async_mock.patch.object( + test_module, "LOGGER" + ) as mock_logger, async_mock.patch.object( + test_module, "OutboundTransportManager", autospec=True + ) as mock_outbound_mgr, async_mock.patch.object( + test_module, "get_outbound_queue", async_mock.MagicMock() + ) as mock_get_outbound_queue: + mock_outbound_mgr.return_value.registered_transports = { + "test": async_mock.MagicMock(schemes=["http"]) + } + mock_get_outbound_queue.return_value = async_mock.MagicMock( + enqueue_message=async_mock.CoroutineMock() + ) + with self.assertRaises(RuntimeError): + await conductor.setup() + mock_logger.exception.assert_called_once_with( + "outbound_transport and outbound-queue are not allowed together" + ) + async def test_handle_not_returned_ledger_x(self): builder: ContextBuilder = StubContextBuilder(self.test_settings_admin) conductor = test_module.Conductor(builder) @@ -731,7 +766,7 @@ async def test_start_x_in(self): with self.assertRaises(KeyError): await conductor.start() - async def test_start_x_out(self): + async def test_start_x_out_a(self): builder: ContextBuilder = StubContextBuilder(self.test_settings) builder.update_settings({"debug.test_suite_endpoint": True}) conductor = test_module.Conductor(builder) @@ -751,6 +786,33 @@ async def test_start_x_out(self): with self.assertRaises(KeyError): await conductor.start() + async def test_start_x_out_b(self): + builder: ContextBuilder = StubContextBuilder(self.test_settings) + builder.update_settings({"debug.test_suite_endpoint": True}) + conductor = test_module.Conductor(builder) + + with async_mock.patch.object( + test_module, "ConnectionManager" + ) as mock_mgr, async_mock.patch.object( + test_module, "OutboundTransportManager" + ) as mock_outx_mgr, async_mock.patch.object( + test_module, "get_outbound_queue", async_mock.MagicMock() + ) as mock_get_outbound_queue: + mock_get_outbound_queue.return_value = async_mock.MagicMock( + enqueue_message=async_mock.CoroutineMock(), + start=async_mock.CoroutineMock(side_effect=KeyError("trouble")), + stop=async_mock.CoroutineMock(), + ) + mock_outx_mgr.return_value = async_mock.MagicMock( + setup=async_mock.CoroutineMock(), + start=async_mock.CoroutineMock(), + registered_transports={}, + ) + await conductor.setup() + mock_mgr.return_value.create_static_connection = async_mock.CoroutineMock() + with self.assertRaises(KeyError): + await conductor.start() + async def test_dispatch_complete_non_fatal_x(self): builder: ContextBuilder = StubContextBuilder(self.test_settings_admin) conductor = test_module.Conductor(builder) From 44e4828e072b80c162a7ce33e396826139dccf95 Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Wed, 9 Mar 2022 15:52:04 -0800 Subject: [PATCH 059/872] revert change to send_credential_ack return value Signed-off-by: Andrew Whitehead --- .../v1_0/handlers/credential_issue_handler.py | 2 +- .../handlers/tests/test_credential_issue_handler.py | 12 ++++++++++-- .../protocols/issue_credential/v1_0/manager.py | 11 ++++++----- .../protocols/issue_credential/v1_0/routes.py | 7 ++++--- .../issue_credential/v1_0/tests/test_manager.py | 6 ++++-- .../issue_credential/v1_0/tests/test_routes.py | 11 +++++++---- 6 files changed, 32 insertions(+), 17 deletions(-) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_issue_handler.py b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_issue_handler.py index 938d2a2a1f..9048311cfe 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_issue_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_issue_handler.py @@ -76,7 +76,7 @@ async def handle(self, context: RequestContext, responder: BaseResponder): ) ) - credential_ack_message = await credential_manager.send_credential_ack( + (_, credential_ack_message) = await credential_manager.send_credential_ack( cred_ex_record ) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_issue_handler.py b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_issue_handler.py index 8e07df1ce7..f7c8d490c4 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_issue_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_issue_handler.py @@ -45,7 +45,10 @@ async def test_called_auto_store(self): receive_credential=async_mock.CoroutineMock(), store_credential=async_mock.CoroutineMock(), send_credential_ack=async_mock.CoroutineMock( - return_value="credential_ack_message" + return_value=( + async_mock.CoroutineMock(), + async_mock.CoroutineMock(), + ) ), ) request_context.message = CredentialIssue() @@ -78,7 +81,12 @@ async def test_called_auto_store_x(self): store_credential=async_mock.CoroutineMock( side_effect=test_module.IndyHolderError() ), - send_credential_ack=async_mock.CoroutineMock(), + send_credential_ack=async_mock.CoroutineMock( + return_value=( + async_mock.CoroutineMock(), + async_mock.CoroutineMock(), + ) + ), ) request_context.message = CredentialIssue() diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/manager.py b/aries_cloudagent/protocols/issue_credential/v1_0/manager.py index bc11f4995e..7934d1725d 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/manager.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/manager.py @@ -877,14 +877,15 @@ async def store_credential( async def send_credential_ack( self, cred_ex_record: V10CredentialExchange, - ): + ) -> Tuple[V10CredentialExchange, CredentialAck]: """ Create, send, and return ack message for input credential exchange record. Delete credential exchange record if set to auto-remove. Returns: - credential ack message for tracing + a tuple of the updated credential exchange record + and the credential ack message for tracing """ credential_ack_message = CredentialAck() @@ -906,7 +907,7 @@ async def send_credential_ack( "Skipping credential exchange ack, record not found: '%s'", cred_ex_record.credential_exchange_id, ) - return None + return (cred_ex_record, None) if ( cred_ex_record.state @@ -917,7 +918,7 @@ async def send_credential_ack( cred_ex_record.state, cred_ex_record.credential_exchange_id, ) - return None + return (cred_ex_record, None) cred_ex_record.state = V10CredentialExchange.STATE_ACKED await cred_ex_record.save(txn, reason="ack credential") @@ -944,7 +945,7 @@ async def send_credential_ack( cred_ex_record.thread_id, ) - return credential_ack_message + return (cred_ex_record, credential_ack_message) async def receive_credential_ack( self, message: CredentialAck, connection_id: str diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/routes.py b/aries_cloudagent/protocols/issue_credential/v1_0/routes.py index abe70b2d96..fd8c7b3f59 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/routes.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/routes.py @@ -1183,9 +1183,10 @@ async def credential_exchange_store(request: web.BaseRequest): ) try: # protocol owes an ack - credential_ack_message = await credential_manager.send_credential_ack( - cred_ex_record - ) + ( + cred_ex_record, + credential_ack_message, + ) = await credential_manager.send_credential_ack(cred_ex_record) result = cred_ex_record.serialize() # pick up state done except ( diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py index 68ad3d7c3b..6e3df6387e 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py @@ -1615,15 +1615,17 @@ async def test_send_credential_ack(self): test_module.LOGGER, "warning", async_mock.MagicMock() ) as mock_log_warning: mock_delete_ex.side_effect = test_module.StorageError() - ack = await self.manager.send_credential_ack(stored_exchange) + (exch, ack) = await self.manager.send_credential_ack(stored_exchange) assert ack._thread mock_log_exception.assert_called_once() # cover exception log-and-continue mock_log_warning.assert_called_once() # no BaseResponder + assert exch.state == V10CredentialExchange.STATE_ACKED mock_responder = MockResponder() # cover with responder self.context.injector.bind_instance(BaseResponder, mock_responder) - ack = await self.manager.send_credential_ack(stored_exchange) + (exch, ack) = await self.manager.send_credential_ack(stored_exchange) assert ack._thread + assert exch.state == V10CredentialExchange.STATE_ACKED async def test_receive_credential_ack(self): connection_id = "connection-id" diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_routes.py index 0fe2cf8d78..280f19ce05 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_routes.py @@ -1187,7 +1187,8 @@ async def test_credential_exchange_store(self): mock_cred_ex_record ) mock_credential_manager.return_value.send_credential_ack.return_value = ( - async_mock.MagicMock() + mock_cred_ex_record, + async_mock.MagicMock(), ) await test_module.credential_exchange_store(self.request) @@ -1223,7 +1224,8 @@ async def test_credential_exchange_store_bad_cred_id_json(self): mock_cred_ex_record ) mock_credential_manager.return_value.send_credential_ack.return_value = ( - async_mock.MagicMock() + mock_cred_ex_record, + async_mock.MagicMock(), ) await test_module.credential_exchange_store(self.request) @@ -1276,7 +1278,8 @@ async def test_credential_exchange_store_no_conn_record(self): mock_cred_ex ) mock_credential_manager.return_value.send_credential_ack.return_value = ( - async_mock.MagicMock() + mock_cred_ex, + async_mock.MagicMock(), ) with self.assertRaises(test_module.web.HTTPBadRequest): @@ -1336,7 +1339,7 @@ async def test_credential_exchange_store_x(self): return_value=mock_cred_ex_record ), send_credential_ack=async_mock.CoroutineMock( - return_value=async_mock.MagicMock() + return_value=(mock_cred_ex_record, async_mock.MagicMock()) ), ) From eefe1a21ac546066a313f94ad6793a59a5a774c8 Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Thu, 10 Mar 2022 11:38:28 -0700 Subject: [PATCH 060/872] fix: Standardize SchemaID/CredDefID responses for non-authors Add the `sent` key to the response for non-authors to follow the OpenAPI Schema Specification. Due to the fact that removing the old values would be a breaking change, I have left those values in for the time being to allow for a migration path to the new `sent` key. Signed-off-by: Colton Wolkins (Indicio work address) --- aries_cloudagent/messaging/credential_definitions/routes.py | 5 ++++- aries_cloudagent/messaging/schemas/routes.py | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/messaging/credential_definitions/routes.py b/aries_cloudagent/messaging/credential_definitions/routes.py index cae32f95e5..e479afa2f3 100644 --- a/aries_cloudagent/messaging/credential_definitions/routes.py +++ b/aries_cloudagent/messaging/credential_definitions/routes.py @@ -269,7 +269,10 @@ async def credential_definitions_send_credential_definition(request: web.BaseReq meta_data["processing"]["auto_create_rev_reg"] = True await notify_cred_def_event(context.profile, cred_def_id, meta_data) - return web.json_response({"credential_definition_id": cred_def_id}) + return web.json_response({ + "sent": {"credential_definition_id": cred_def_id}, + "credential_definition_id": cred_def_id, + }) # If the transaction is for the endorser, but the schema has already been created, # then we send back the schema since the transaction will fail to be created. diff --git a/aries_cloudagent/messaging/schemas/routes.py b/aries_cloudagent/messaging/schemas/routes.py index 09415370a7..b76de56399 100644 --- a/aries_cloudagent/messaging/schemas/routes.py +++ b/aries_cloudagent/messaging/schemas/routes.py @@ -259,7 +259,10 @@ async def schemas_send_schema(request: web.BaseRequest): if not create_transaction_for_endorser: # Notify event await notify_schema_event(context.profile, schema_id, meta_data) - return web.json_response({"schema_id": schema_id, "schema": schema_def}) + return web.json_response({ + "sent": {"schema_id": schema_id, "schema": schema_def}, + "schema_id": schema_id, "schema": schema_def + }) # If the transaction is for the endorser, but the schema has already been created, # then we send back the schema since the transaction will fail to be created. From aedd70e455b7e3c8b4f0d34948c67272afac9766 Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Thu, 10 Mar 2022 11:45:13 -0700 Subject: [PATCH 061/872] chore: Fix black errors Signed-off-by: Colton Wolkins (Indicio work address) --- .../credential_definitions/routes.py | 20 +++++++++------ aries_cloudagent/messaging/schemas/routes.py | 25 ++++++++++++------- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/aries_cloudagent/messaging/credential_definitions/routes.py b/aries_cloudagent/messaging/credential_definitions/routes.py index e479afa2f3..1603c76cd1 100644 --- a/aries_cloudagent/messaging/credential_definitions/routes.py +++ b/aries_cloudagent/messaging/credential_definitions/routes.py @@ -269,10 +269,12 @@ async def credential_definitions_send_credential_definition(request: web.BaseReq meta_data["processing"]["auto_create_rev_reg"] = True await notify_cred_def_event(context.profile, cred_def_id, meta_data) - return web.json_response({ - "sent": {"credential_definition_id": cred_def_id}, - "credential_definition_id": cred_def_id, - }) + return web.json_response( + { + "sent": {"credential_definition_id": cred_def_id}, + "credential_definition_id": cred_def_id, + } + ) # If the transaction is for the endorser, but the schema has already been created, # then we send back the schema since the transaction will fail to be created. @@ -307,10 +309,12 @@ async def credential_definitions_send_credential_definition(request: web.BaseReq await outbound_handler(transaction_request, connection_id=connection_id) - return web.json_response({ - "sent": {"credential_definition_id": cred_def_id}, - "txn": transaction.serialize(), - }) + return web.json_response( + { + "sent": {"credential_definition_id": cred_def_id}, + "txn": transaction.serialize(), + } + ) @docs( diff --git a/aries_cloudagent/messaging/schemas/routes.py b/aries_cloudagent/messaging/schemas/routes.py index b76de56399..9a44dddbcf 100644 --- a/aries_cloudagent/messaging/schemas/routes.py +++ b/aries_cloudagent/messaging/schemas/routes.py @@ -259,15 +259,20 @@ async def schemas_send_schema(request: web.BaseRequest): if not create_transaction_for_endorser: # Notify event await notify_schema_event(context.profile, schema_id, meta_data) - return web.json_response({ - "sent": {"schema_id": schema_id, "schema": schema_def}, - "schema_id": schema_id, "schema": schema_def - }) + return web.json_response( + { + "sent": {"schema_id": schema_id, "schema": schema_def}, + "schema_id": schema_id, + "schema": schema_def, + } + ) # If the transaction is for the endorser, but the schema has already been created, # then we send back the schema since the transaction will fail to be created. elif "signed_txn" not in schema_def: - return web.json_response({"sent": {"schema_id": schema_id, "schema": schema_def}}) + return web.json_response( + {"sent": {"schema_id": schema_id, "schema": schema_def}} + ) else: transaction_mgr = TransactionManager(context.profile) try: @@ -293,10 +298,12 @@ async def schemas_send_schema(request: web.BaseRequest): await outbound_handler(transaction_request, connection_id=connection_id) - return web.json_response({ - "sent": {"schema_id": schema_id, "schema": schema_def}, - "txn": transaction.serialize(), - }) + return web.json_response( + { + "sent": {"schema_id": schema_id, "schema": schema_def}, + "txn": transaction.serialize(), + } + ) @docs( From 002d371a32ac725394e14e01a7ae45439a1a7232 Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Thu, 10 Mar 2022 12:11:30 -0700 Subject: [PATCH 062/872] ci: Fix CredDef & Publish Schema tests after recent changes Signed-off-by: Colton Wolkins (Indicio work address) --- .../tests/test_routes.py | 12 +++++++++-- .../messaging/schemas/tests/test_routes.py | 20 ++++++++++++++++++- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/aries_cloudagent/messaging/credential_definitions/tests/test_routes.py b/aries_cloudagent/messaging/credential_definitions/tests/test_routes.py index a1e72ddf6e..953f185d73 100644 --- a/aries_cloudagent/messaging/credential_definitions/tests/test_routes.py +++ b/aries_cloudagent/messaging/credential_definitions/tests/test_routes.py @@ -81,7 +81,10 @@ async def test_send_credential_definition(self): ) assert result == mock_response.return_value mock_response.assert_called_once_with( - {"credential_definition_id": CRED_DEF_ID} + { + "sent": {"credential_definition_id": CRED_DEF_ID}, + "credential_definition_id": CRED_DEF_ID, + } ) async def test_send_credential_definition_create_transaction_for_endorser(self): @@ -126,7 +129,12 @@ async def test_send_credential_definition_create_transaction_for_endorser(self): ) ) assert result == mock_response.return_value - mock_response.assert_called_once_with({"txn": {"...": "..."}}) + mock_response.assert_called_once_with( + { + "sent": {"credential_definition_id": CRED_DEF_ID}, + "txn": {"...": "..."}, + } + ) async def test_send_credential_definition_create_transaction_for_endorser_storage_x( self, diff --git a/aries_cloudagent/messaging/schemas/tests/test_routes.py b/aries_cloudagent/messaging/schemas/tests/test_routes.py index ff445535ff..74b9f79e82 100644 --- a/aries_cloudagent/messaging/schemas/tests/test_routes.py +++ b/aries_cloudagent/messaging/schemas/tests/test_routes.py @@ -70,6 +70,13 @@ async def test_send_schema(self): assert result == mock_response.return_value mock_response.assert_called_once_with( { + "sent": { + "schema_id": SCHEMA_ID, + "schema": { + "schema": "def", + "signed_txn": "...", + }, + }, "schema_id": SCHEMA_ID, "schema": { "schema": "def", @@ -116,7 +123,18 @@ async def test_send_schema_create_transaction_for_endorser(self): ) result = await test_module.schemas_send_schema(self.request) assert result == mock_response.return_value - mock_response.assert_called_once_with({"txn": {"...": "..."}}) + mock_response.assert_called_once_with( + { + "sent": { + "schema_id": SCHEMA_ID, + "schema": { + "schema": "def", + "signed_txn": "...", + }, + }, + "txn": {"...": "..."}, + } + ) async def test_send_schema_create_transaction_for_endorser_storage_x(self): self.request.json = async_mock.CoroutineMock( From d86915e007ee6628a1c91789891fc464fcfb5d8d Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sun, 13 Mar 2022 20:12:54 +0100 Subject: [PATCH 063/872] fix: always notify if revocation record exists Signed-off-by: Timo Glastra --- .../revocation_notification/v1_0/routes.py | 8 +++--- .../v1_0/tests/test_routes.py | 27 ------------------- 2 files changed, 3 insertions(+), 32 deletions(-) diff --git a/aries_cloudagent/protocols/revocation_notification/v1_0/routes.py b/aries_cloudagent/protocols/revocation_notification/v1_0/routes.py index 83ba81fe63..cdefdaf642 100644 --- a/aries_cloudagent/protocols/revocation_notification/v1_0/routes.py +++ b/aries_cloudagent/protocols/revocation_notification/v1_0/routes.py @@ -32,7 +32,6 @@ async def on_revocation_published(profile: Profile, event: Event): """Handle issuer revoke event.""" LOGGER.debug("Sending notification of revocation to recipient: %s", event.payload) - should_notify = profile.settings.get("revocation.notify", False) responder = profile.inject(BaseResponder) crids = event.payload.get("crids") or [] @@ -46,10 +45,9 @@ async def on_revocation_published(profile: Profile, event: Event): for record in records: await record.delete_record(session) - if should_notify: - await responder.send( - record.to_message(), connection_id=record.connection_id - ) + await responder.send( + record.to_message(), connection_id=record.connection_id + ) except StorageNotFoundError: LOGGER.info( diff --git a/aries_cloudagent/protocols/revocation_notification/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/revocation_notification/v1_0/tests/test_routes.py index 6fe38c848b..b4805d8059 100644 --- a/aries_cloudagent/protocols/revocation_notification/v1_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/revocation_notification/v1_0/tests/test_routes.py @@ -50,7 +50,6 @@ async def test_on_revocation_published(profile: Profile, responder: MockResponde event = Event(topic, {"rev_reg_id": "mock", "crids": ["mock"]}) assert isinstance(profile.settings, Settings) - profile.settings["revocation.notify"] = True with mock.patch.object(test_module, "RevNotificationRecord", MockRec): await test_module.on_revocation_published(profile, event) @@ -60,32 +59,6 @@ async def test_on_revocation_published(profile: Profile, responder: MockResponde assert responder.messages -@pytest.mark.asyncio -async def test_on_revocation_published_no_notify( - profile: Profile, responder: MockResponder -): - """Test revocation published event handler.""" - mock_rec = mock.MagicMock() - mock_rec.cred_rev_id = "mock" - mock_rec.delete_record = mock.CoroutineMock() - - MockRec = mock.MagicMock() - MockRec.query_by_rev_reg_id = mock.CoroutineMock(return_value=[mock_rec]) - - topic = f"{REVOCATION_EVENT_PREFIX}{REVOCATION_PUBLISHED_EVENT}::mock" - event = Event(topic, {"rev_reg_id": "mock", "crids": ["mock"]}) - - assert isinstance(profile.settings, Settings) - profile.settings["revocation.notify"] = False - - with mock.patch.object(test_module, "RevNotificationRecord", MockRec): - await test_module.on_revocation_published(profile, event) - - MockRec.query_by_rev_reg_id.assert_called_once() - mock_rec.delete_record.assert_called_once() - assert not responder.messages - - @pytest.mark.asyncio async def test_on_revocation_published_x_not_found( profile: Profile, responder: MockResponder From 48d49a66bed7927be60be89ea7f0abf5f9226189 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Mon, 14 Mar 2022 08:59:37 -0700 Subject: [PATCH 064/872] fix Signed-off-by: Shaanjot Gill --- .../protocols/present_proof/v2_0/formats/indy/handler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aries_cloudagent/protocols/present_proof/v2_0/formats/indy/handler.py b/aries_cloudagent/protocols/present_proof/v2_0/formats/indy/handler.py index 6158f3b96c..884638a026 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/formats/indy/handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/formats/indy/handler.py @@ -97,7 +97,7 @@ def get_format_data( async def create_bound_request( self, pres_ex_record: V20PresExRecord, - request_data: dict = None, + request_data: dict = {}, ) -> Tuple[V20PresFormat, AttachDecorator]: """ Create a presentation request bound to a proposal. From 440d530f7890f917cc007575c229a079f60d3730 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Mon, 14 Mar 2022 15:32:07 -0700 Subject: [PATCH 065/872] update Signed-off-by: Shaanjot Gill --- .../v2_0/formats/indy/handler.py | 21 ++++++++----- .../present_proof/v2_0/tests/test_manager.py | 30 ++++++++++++++++++- 2 files changed, 42 insertions(+), 9 deletions(-) diff --git a/aries_cloudagent/protocols/present_proof/v2_0/formats/indy/handler.py b/aries_cloudagent/protocols/present_proof/v2_0/formats/indy/handler.py index 884638a026..5313dde87d 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/formats/indy/handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/formats/indy/handler.py @@ -97,7 +97,7 @@ def get_format_data( async def create_bound_request( self, pres_ex_record: V20PresExRecord, - request_data: dict = {}, + request_data: dict = None, ) -> Tuple[V20PresFormat, AttachDecorator]: """ Create a presentation request bound to a proposal. @@ -114,21 +114,26 @@ async def create_bound_request( indy_proof_request = pres_ex_record.pres_proposal.attachment( IndyPresExchangeHandler.format ) - indy_proof_request["name"] = request_data.get("name") or "proof-request" - indy_proof_request["version"] = request_data.get("version") or "1.0" - indy_proof_request["nonce"] = ( - request_data.get("nonce") or await generate_pr_nonce() - ) + if request_data: + indy_proof_request["name"] = request_data.get("name", "proof-request") + indy_proof_request["version"] = request_data.get("version", "1.0") + indy_proof_request["nonce"] = ( + request_data.get("nonce") or await generate_pr_nonce() + ) + else: + indy_proof_request["name"] = "proof-request" + indy_proof_request["version"] = "1.0" + indy_proof_request["nonce"] = await generate_pr_nonce() return self.get_format_data(PRES_20_REQUEST, indy_proof_request) async def create_pres( self, pres_ex_record: V20PresExRecord, - request_data: dict = {}, + request_data: dict = None, ) -> Tuple[V20PresFormat, AttachDecorator]: """Create a presentation.""" requested_credentials = {} - if request_data == {}: + if not request_data: try: proof_request = pres_ex_record.pres_request indy_proof_request = proof_request.attachment( diff --git a/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py b/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py index bf293f3cb6..cb84391890 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py @@ -551,7 +551,7 @@ async def test_receive_proposal(self): assert px_rec.state == V20PresExRecord.STATE_PROPOSAL_RECEIVED - async def test_create_bound_request(self): + async def test_create_bound_request_a(self): comment = "comment" proposal = V20PresProposal( @@ -585,6 +585,34 @@ async def test_create_bound_request(self): assert ret_px_rec is px_rec px_rec.save.assert_called_once() + async def test_create_bound_request_b(self): + comment = "comment" + + proposal = V20PresProposal( + formats=[ + V20PresFormat( + attach_id="indy", + format_=ATTACHMENT_FORMAT[PRES_20_PROPOSAL][ + V20PresFormat.Format.INDY.api + ], + ) + ], + proposals_attach=[ + AttachDecorator.data_base64(INDY_PROOF_REQ_NAME, ident="indy") + ], + ) + px_rec = V20PresExRecord( + pres_proposal=proposal.serialize(), + role=V20PresExRecord.ROLE_VERIFIER, + ) + px_rec.save = async_mock.CoroutineMock() + (ret_px_rec, pres_req_msg) = await self.manager.create_bound_request( + pres_ex_record=px_rec, + comment=comment, + ) + assert ret_px_rec is px_rec + px_rec.save.assert_called_once() + async def test_create_bound_request_no_format(self): px_rec = V20PresExRecord( pres_proposal=V20PresProposal( From 9c65346b78c2fb2de17c1900f101939446174bc3 Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Mon, 14 Mar 2022 15:33:34 -0700 Subject: [PATCH 066/872] fetch stored credentials prior to sending ack response in issue-credential 2.0 Signed-off-by: Andrew Whitehead --- .../protocols/issue_credential/v2_0/routes.py | 43 +++++++++++++------ 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/routes.py b/aries_cloudagent/protocols/issue_credential/v2_0/routes.py index b7d98758b0..c8e1fea7f7 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/routes.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/routes.py @@ -396,19 +396,31 @@ def _formats_filters(filt_spec: Mapping) -> Mapping: ) -async def _get_result_with_details( +async def _get_attached_credentials( profile: Profile, cred_ex_record: V20CredExRecord ) -> Mapping: - """Get credential exchange result with detail records.""" - result = {"cred_ex_record": cred_ex_record.serialize()} + """Fetch the detail records attached to a credential exchange.""" + result = {} for fmt in V20CredFormat.Format: detail_record = await fmt.handler(profile).get_detail_record( cred_ex_record.cred_ex_id ) + if detail_record: + result[fmt.api] = detail_record + + return result - result[fmt.api] = detail_record.serialize() if detail_record else None +def _format_result_with_details( + cred_ex_record: V20CredExRecord, details: Mapping +) -> Mapping: + """Get credential exchange result with detail records.""" + result = {"cred_ex_record": cred_ex_record.serialize()} + for fmt in V20CredFormat.Format: + ident = fmt.api + detail_record = details.get(ident) + result[ident] = detail_record.serialize() if detail_record else None return result @@ -450,7 +462,8 @@ async def credential_exchange_list(request: web.BaseRequest): results = [] for cxr in cred_ex_records: - result = await _get_result_with_details(profile, cxr) + details = await _get_attached_credentials(profile, cxr) + result = _format_result_with_details(cxr, details) results.append(result) except (StorageError, BaseModelError) as err: @@ -486,8 +499,8 @@ async def credential_exchange_retrieve(request: web.BaseRequest): async with profile.session() as session: cred_ex_record = await V20CredExRecord.retrieve_by_id(session, cred_ex_id) - result = await _get_result_with_details(context.profile, cred_ex_record) - + details = await _get_attached_credentials(profile, cred_ex_record) + result = _format_result_with_details(cred_ex_record, details) except StorageNotFoundError as err: # no such cred ex record: not protocol error, user fat-fingered id raise web.HTTPNotFound(reason=err.roll_up) from err @@ -1321,8 +1334,9 @@ async def credential_exchange_issue(request: web.BaseRequest): connection_id = cred_ex_record.connection_id conn_record = await ConnRecord.retrieve_by_id(session, connection_id) - if not conn_record.is_ready: - raise web.HTTPForbidden(reason=f"Connection {connection_id} not ready") + + if not conn_record.is_ready: + raise web.HTTPForbidden(reason=f"Connection {connection_id} not ready") cred_manager = V20CredManager(profile) (cred_ex_record, cred_issue_message) = await cred_manager.issue_credential( @@ -1330,7 +1344,8 @@ async def credential_exchange_issue(request: web.BaseRequest): comment=comment, ) - result = await _get_result_with_details(profile, cred_ex_record) + details = await _get_attached_credentials(profile, cred_ex_record) + result = _format_result_with_details(cred_ex_record, details) except ( BaseModelError, @@ -1433,14 +1448,16 @@ async def credential_exchange_store(request: web.BaseRequest): ) try: + # fetch these early, before potential removal + details = await _get_attached_credentials(profile, cred_ex_record) + + # the record may be auto-removed here ( cred_ex_record, cred_ack_message, ) = await cred_manager.send_cred_ack(cred_ex_record) - # We first need to retrieve the the cred_ex_record with detail record - # as the record may be auto removed - result = await _get_result_with_details(profile, cred_ex_record) + result = _format_result_with_details(cred_ex_record, details) except ( BaseModelError, From c5ffa46dbbbff35a5980ff26b3254b12117502ce Mon Sep 17 00:00:00 2001 From: Adam Burdett Date: Tue, 15 Mar 2022 16:05:44 -0600 Subject: [PATCH 067/872] multitenant key derivation method Signed-off-by: Adam Burdett --- aries_cloudagent/config/argparse.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index a8b6ae5110..8c203a04e0 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -1623,7 +1623,7 @@ def add_arguments(self, parser: ArgumentParser): help=( 'Specify multitenancy configuration ("wallet_type" and "wallet_name"). ' 'For example: "{"wallet_type":"askar-profile","wallet_name":' - '"askar-profile-name"}"' + '"askar-profile-name", "key":"key", "key_derivation_method":"RAW"}"' '"wallet_name" is only used when "wallet_type" is "askar-profile"' ), ) @@ -1656,6 +1656,16 @@ def get_settings(self, args: Namespace): settings["multitenant.wallet_name"] = multitenancyConfig.get( "wallet_name" ) + + if multitenancyConfig.get("key"): + settings["multitenant.key"] = multitenancyConfig.get( + "key" + ) + + if multitenancyConfig.get("key_derivation_method"): + settings["multitenant.key_derivation_method"] = multitenancyConfig.get( + "key_derivation_method" + ) return settings From ac3f21c681e7359bbc64322e5805928f8249dbb0 Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Wed, 16 Mar 2022 08:49:07 -0700 Subject: [PATCH 068/872] set revoc_reg_id, revocation_id on issued credential exchange Signed-off-by: Andrew Whitehead --- .../protocols/issue_credential/v1_0/manager.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/manager.py b/aries_cloudagent/protocols/issue_credential/v1_0/manager.py index 7934d1725d..74dd313a8d 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/manager.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/manager.py @@ -566,7 +566,9 @@ async def issue_credential( schema_id = cred_ex_record.schema_id rev_reg = None + rev_reg_id = None credential_ser = None + cred_rev_id = None if cred_ex_record.credential: LOGGER.warning( @@ -575,6 +577,8 @@ async def issue_credential( cred_ex_record.credential_exchange_id, ) credential_ser = cred_ex_record._credential.ser + cred_rev_id = cred_ex_record.revocation_id + rev_reg_id = cred_ex_record.revoc_reg_id else: cred_offer_ser = cred_ex_record._credential_offer.ser cred_req_ser = cred_ex_record._credential_request.ser @@ -599,8 +603,7 @@ async def issue_credential( cred_ex_record.credential_definition_id ) rev_reg = await active_rev_reg_rec.get_registry() - cred_ex_record.revoc_reg_id = active_rev_reg_rec.revoc_reg_id - + rev_reg_id = rev_reg.registry_id tails_path = rev_reg.tails_local_path await rev_reg.get_or_fetch_local_tails_path() @@ -661,22 +664,19 @@ async def issue_credential( ) issuer = self._profile.inject(IndyIssuer) try: - ( - credential_json, - cred_ex_record.revocation_id, - ) = await issuer.create_credential( + (credential_json, cred_rev_id) = await issuer.create_credential( schema, cred_offer_ser, cred_req_ser, credential_values, cred_ex_record.credential_exchange_id, - cred_ex_record.revoc_reg_id, + rev_reg_id, tails_path, ) credential_ser = json.loads(credential_json) # If the rev reg is now full - if rev_reg and rev_reg.max_creds == int(cred_ex_record.revocation_id): + if rev_reg and rev_reg.max_creds == int(cred_rev_id): async with self._profile.session() as session: await active_rev_reg_rec.set_state( session, @@ -728,6 +728,8 @@ async def issue_credential( ) cred_ex_record.state = V10CredentialExchange.STATE_ISSUED cred_ex_record.credential = credential_ser + cred_ex_record.revoc_reg_id = rev_reg_id + cred_ex_record.revocation_id = cred_rev_id await cred_ex_record.save(txn, reason="issue credential") await txn.commit() From 86f92dde82c3bd2bc95abc36cdc872ca66b5b871 Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Wed, 16 Mar 2022 12:29:25 -0700 Subject: [PATCH 069/872] tolerate multiple calls to create_request, issue_credential Signed-off-by: Andrew Whitehead --- .../issue_credential/v1_0/manager.py | 114 +++++++++--------- .../v1_0/tests/test_manager.py | 16 +-- 2 files changed, 60 insertions(+), 70 deletions(-) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/manager.py b/aries_cloudagent/protocols/issue_credential/v1_0/manager.py index 74dd313a8d..b4b9291be4 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/manager.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/manager.py @@ -400,13 +400,6 @@ async def create_request( A tuple (credential exchange record, credential request message) """ - if cred_ex_record.state != V10CredentialExchange.STATE_OFFER_RECEIVED: - raise CredentialManagerError( - f"Credential exchange {cred_ex_record.credential_exchange_id} " - f"in {cred_ex_record.state} state " - f"(must be {V10CredentialExchange.STATE_OFFER_RECEIVED})" - ) - credential_definition_id = cred_ex_record.credential_definition_id cred_offer_ser = cred_ex_record._credential_offer.ser cred_req_ser = None @@ -436,14 +429,14 @@ async def _create(): "metadata": json.loads(metadata_json), } - if cred_ex_record.credential_request: + if cred_ex_record.state == V10CredentialExchange.STATE_REQUEST_SENT: LOGGER.warning( "create_request called multiple times for v1.0 credential exchange: %s", cred_ex_record.credential_exchange_id, ) cred_req_ser = cred_ex_record._credential_request.ser cred_req_meta = cred_ex_record.credential_request_metadata - else: + elif cred_ex_record.state == V10CredentialExchange.STATE_OFFER_RECEIVED: nonce = cred_offer_ser["nonce"] cache_key = ( f"credential_request::{credential_definition_id}::{holder_did}::{nonce}" @@ -462,6 +455,29 @@ async def _create(): cred_req_ser = cred_req_result["request"] cred_req_meta = cred_req_result["metadata"] + async with self._profile.transaction() as txn: + cred_ex_record = await V10CredentialExchange.retrieve_by_id( + txn, cred_ex_record.credential_exchange_id, for_update=True + ) + if cred_ex_record.state != V10CredentialExchange.STATE_OFFER_RECEIVED: + raise CredentialManagerError( + f"Credential exchange {cred_ex_record.credential_exchange_id} " + f"in {cred_ex_record.state} state " + f"(must be {V10CredentialExchange.STATE_OFFER_RECEIVED})" + ) + + cred_ex_record.credential_request = cred_req_ser + cred_ex_record.credential_request_metadata = cred_req_meta + cred_ex_record.state = V10CredentialExchange.STATE_REQUEST_SENT + await cred_ex_record.save(txn, reason="create credential request") + await txn.commit() + else: + raise CredentialManagerError( + f"Credential exchange {cred_ex_record.credential_exchange_id} " + f"in {cred_ex_record.state} state " + f"(must be {V10CredentialExchange.STATE_OFFER_RECEIVED})" + ) + credential_request_message = CredentialRequest( requests_attach=[CredentialRequest.wrap_indy_cred_req(cred_req_ser)] ) @@ -470,23 +486,6 @@ async def _create(): self._profile.settings, cred_ex_record.trace ) - async with self._profile.transaction() as txn: - cred_ex_record = await V10CredentialExchange.retrieve_by_id( - txn, cred_ex_record.credential_exchange_id, for_update=True - ) - if cred_ex_record.state != V10CredentialExchange.STATE_OFFER_RECEIVED: - raise CredentialManagerError( - f"Credential exchange {cred_ex_record.credential_exchange_id} " - f"in {cred_ex_record.state} state " - f"(must be {V10CredentialExchange.STATE_OFFER_RECEIVED})" - ) - - cred_ex_record.credential_request = cred_req_ser - cred_ex_record.credential_request_metadata = cred_req_meta - cred_ex_record.state = V10CredentialExchange.STATE_REQUEST_SENT - await cred_ex_record.save(txn, reason="create credential request") - await txn.commit() - return (cred_ex_record, credential_request_message) async def receive_request(self, message: CredentialRequest, connection_id: str): @@ -557,31 +556,22 @@ async def issue_credential( """ - if cred_ex_record.state != V10CredentialExchange.STATE_REQUEST_RECEIVED: - raise CredentialManagerError( - f"Credential exchange {cred_ex_record.credential_exchange_id} " - f"in {cred_ex_record.state} state " - f"(must be {V10CredentialExchange.STATE_REQUEST_RECEIVED})" - ) - - schema_id = cred_ex_record.schema_id - rev_reg = None - rev_reg_id = None credential_ser = None - cred_rev_id = None if cred_ex_record.credential: LOGGER.warning( - "issue_credential called multiple times for " - + "credential exchange record %s - abstaining", + "issue_credential called multiple times for v1.0 credential exchange %s", cred_ex_record.credential_exchange_id, ) credential_ser = cred_ex_record._credential.ser - cred_rev_id = cred_ex_record.revocation_id - rev_reg_id = cred_ex_record.revoc_reg_id - else: + + elif cred_ex_record.state == V10CredentialExchange.STATE_REQUEST_RECEIVED: + rev_reg = None + rev_reg_id = None + cred_rev_id = None cred_offer_ser = cred_ex_record._credential_offer.ser cred_req_ser = cred_ex_record._credential_request.ser + schema_id = cred_ex_record.schema_id ledger_exec_inst = self._profile.inject(IndyLedgerRequestsExecutor) ledger = ( await ledger_exec_inst.get_ledger_for_identifier( @@ -716,28 +706,32 @@ async def issue_credential( raise - async with self._profile.transaction() as txn: - cred_ex_record = await V10CredentialExchange.retrieve_by_id( - txn, cred_ex_record.credential_exchange_id, for_update=True - ) - if cred_ex_record.state != V10CredentialExchange.STATE_REQUEST_RECEIVED: - raise CredentialManagerError( - f"Credential exchange {cred_ex_record.credential_exchange_id} " - f"in {cred_ex_record.state} state " - f"(must be {V10CredentialExchange.STATE_REQUEST_RECEIVED})" + async with self._profile.transaction() as txn: + cred_ex_record = await V10CredentialExchange.retrieve_by_id( + txn, cred_ex_record.credential_exchange_id, for_update=True ) - cred_ex_record.state = V10CredentialExchange.STATE_ISSUED - cred_ex_record.credential = credential_ser - cred_ex_record.revoc_reg_id = rev_reg_id - cred_ex_record.revocation_id = cred_rev_id - await cred_ex_record.save(txn, reason="issue credential") - await txn.commit() + if cred_ex_record.state != V10CredentialExchange.STATE_REQUEST_RECEIVED: + raise CredentialManagerError( + f"Credential exchange {cred_ex_record.credential_exchange_id} " + f"in {cred_ex_record.state} state " + f"(must be {V10CredentialExchange.STATE_REQUEST_RECEIVED})" + ) + cred_ex_record.state = V10CredentialExchange.STATE_ISSUED + cred_ex_record.credential = credential_ser + cred_ex_record.revoc_reg_id = rev_reg_id + cred_ex_record.revocation_id = cred_rev_id + await cred_ex_record.save(txn, reason="issue credential") + await txn.commit() + else: + raise CredentialManagerError( + f"Credential exchange {cred_ex_record.credential_exchange_id} " + f"in {cred_ex_record.state} state " + f"(must be {V10CredentialExchange.STATE_REQUEST_RECEIVED})" + ) credential_message = CredentialIssue( comment=comment, - credentials_attach=[ - CredentialIssue.wrap_indy_credential(cred_ex_record._credential.ser) - ], + credentials_attach=[CredentialIssue.wrap_indy_credential(credential_ser)], ) credential_message._thread = {"thid": cred_ex_record.thread_id} credential_message.assign_trace_decorator( diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py index 6e3df6387e..24287a6523 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py @@ -661,12 +661,11 @@ async def test_create_request(self): await self.manager.create_request(stored_exchange, holder_did) # cover case with existing cred req - stored_exchange.state = V10CredentialExchange.STATE_OFFER_RECEIVED - stored_exchange.credential_request = INDY_CRED_REQ ( - _ret_existing_exchange, + ret_existing_exchange, ret_existing_request, - ) = await self.manager.create_request(stored_exchange, holder_did) + ) = await self.manager.create_request(ret_exchange, holder_did) + assert ret_existing_exchange == ret_exchange assert ret_existing_request._thread_id == thread_id async def test_create_request_no_cache(self): @@ -895,9 +894,9 @@ async def test_issue_credential(self): ) as save_ex: revoc.return_value.get_active_issuer_rev_reg_record = async_mock.CoroutineMock( return_value=async_mock.MagicMock( # active_rev_reg_rec - revoc_reg_id=REV_REG_ID, get_registry=async_mock.CoroutineMock( return_value=async_mock.MagicMock( # rev_reg + registry_id=REV_REG_ID, tails_local_path="dummy-path", get_or_fetch_local_tails_path=async_mock.CoroutineMock(), ) @@ -926,14 +925,11 @@ async def test_issue_credential(self): assert ret_cred_issue._thread_id == thread_id # cover case with existing cred - stored_exchange.credential = cred - stored_exchange.state = V10CredentialExchange.STATE_REQUEST_RECEIVED - await stored_exchange.save(self.session) ( ret_existing_exchange, ret_existing_cred, ) = await self.manager.issue_credential( - stored_exchange, comment=comment, retries=0 + ret_exchange, comment=comment, retries=0 ) assert ret_existing_exchange == ret_exchange assert ret_existing_cred._thread_id == thread_id @@ -1061,9 +1057,9 @@ async def test_issue_credential_fills_rr(self): get_active_issuer_rev_reg_record=( async_mock.CoroutineMock( return_value=async_mock.MagicMock( # active_rev_reg_rec - revoc_reg_id=REV_REG_ID, get_registry=async_mock.CoroutineMock( return_value=async_mock.MagicMock( # rev_reg + registry_id=REV_REG_ID, tails_local_path="dummy-path", max_creds=1000, get_or_fetch_local_tails_path=( From acd943fead82aa892b15698b13753a80367e6a1b Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Wed, 16 Mar 2022 17:21:29 -0700 Subject: [PATCH 070/872] Qualify did exch connection lookup by role Signed-off-by: Ian Costanzo --- aries_cloudagent/connections/models/conn_record.py | 5 ++++- aries_cloudagent/protocols/didexchange/v1_0/manager.py | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/aries_cloudagent/connections/models/conn_record.py b/aries_cloudagent/connections/models/conn_record.py index 672d9359e0..eb0342970a 100644 --- a/aries_cloudagent/connections/models/conn_record.py +++ b/aries_cloudagent/connections/models/conn_record.py @@ -174,6 +174,7 @@ def __eq__(self, other: Union[str, "ConnRecord.State"]) -> bool: "invitation_key", "their_public_did", "invitation_msg_id", + "their_role", } RECORD_TYPE = "connection" @@ -373,7 +374,7 @@ async def find_existing_connection( @classmethod async def retrieve_by_request_id( - cls, session: ProfileSession, request_id: str + cls, session: ProfileSession, request_id: str, their_role: str = None ) -> "ConnRecord": """Retrieve a connection record from our previous request ID. @@ -382,6 +383,8 @@ async def retrieve_by_request_id( request_id: The ID of the originating connection request """ tag_filter = {"request_id": request_id} + if their_role: + tag_filter["their_role"] = their_role return await cls.retrieve_by_tag_filter(session, tag_filter) @classmethod diff --git a/aries_cloudagent/protocols/didexchange/v1_0/manager.py b/aries_cloudagent/protocols/didexchange/v1_0/manager.py index 282bbb8335..a1456b6f49 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/manager.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/manager.py @@ -705,7 +705,7 @@ async def accept_response( try: async with self.profile.session() as session: conn_rec = await ConnRecord.retrieve_by_request_id( - session, response._thread_id + session, response._thread_id, their_role="inviter" ) except StorageNotFoundError: pass @@ -812,7 +812,7 @@ async def accept_complete( try: async with self.profile.session() as session: conn_rec = await ConnRecord.retrieve_by_request_id( - session, complete._thread_id + session, complete._thread_id, their_role="invitee" ) except StorageNotFoundError: raise DIDXManagerError( From 8e93135aee30c0ce4790d1570b24cff70e56bca3 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Wed, 16 Mar 2022 18:52:14 -0600 Subject: [PATCH 071/872] chore: add pre-commit configurations Signed-off-by: Daniel Bluhm --- .commitlint.config.js | 3 +++ .pre-commit-config.yaml | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 .commitlint.config.js create mode 100644 .pre-commit-config.yaml diff --git a/.commitlint.config.js b/.commitlint.config.js new file mode 100644 index 0000000000..c34aa79d07 --- /dev/null +++ b/.commitlint.config.js @@ -0,0 +1,3 @@ +module.exports = { + extends: ['@commitlint/config-conventional'] +}; diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000000..1c72e986f7 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,19 @@ +--- +repos: + - repo: https://github.com/alessandrojcm/commitlint-pre-commit-hook + rev: v2.2.0 + hooks: + - id: commitlint + stages: [commit-msg] + args: ["--config", ".commitlint.config.js"] + additional_dependencies: ['@commitlint/config-conventional'] + - repo: https://github.com/psf/black + rev: 20.8b1 + hooks: + - id: black + stages: [commit] + - repo: https://gitlab.com/pycqa/flake8 + rev: 3.9.0 + hooks: + - id: flake8 + stages: [commit] From bdde30ebe703ae2c20c1b9fbb1dd95dc547c5a28 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Wed, 16 Mar 2022 19:03:49 -0600 Subject: [PATCH 072/872] feat: add pre-commit as dev dep and instructions Signed-off-by: Daniel Bluhm --- CONTRIBUTING.md | 18 ++++++++++++++++++ requirements.dev.txt | 1 + 2 files changed, 19 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4d3508f609..51d59a2c0b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,3 +12,21 @@ end-user and developer demos in the repo should include updates or extensions to If you would like to propose a significant change, please open an issue first to discuss the work with the community. Contributions are made pursuant to the Developer's Certificate of Origin, available at [https://developercertificate.org](https://developercertificate.org), and licensed under the Apache License, version 2.0 (Apache-2.0). + +## Development Tools + +### Pre-commit + +A configuration for [pre-commit](https://pre-commit.com/) is included in this repository. This is an optional tool to help contributors commit code that follows the formatting requirements enforced by the CI pipeline. Additionally, it can be used to help contributors write descriptive commit messages that can be parsed by changelog generators. + +On each commit, pre-commit hooks will run that verify the committed code complies with flake8 and is formatted with black. To install the flake8 and black checks: + +``` +$ pre-commit install +``` + +To install the commit message linter: + +``` +$ pre-commit install --hook-type commit-msg +``` diff --git a/requirements.dev.txt b/requirements.dev.txt index 792ac2d13b..22433dcadc 100644 --- a/requirements.dev.txt +++ b/requirements.dev.txt @@ -9,6 +9,7 @@ flake8==3.9.0 flake8-docstrings==1.5.0 flake8-rst==0.7.2 pydocstyle==3.0.0 +pre-commit sphinx==1.8.4 sphinx-rtd-theme>=0.4.3 From 780ae76cf1ef113def00578e95542a0e077a90d3 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Wed, 16 Mar 2022 18:42:43 -0700 Subject: [PATCH 073/872] Use connection role enums Signed-off-by: Ian Costanzo --- .../protocols/didexchange/v1_0/manager.py | 40 ++++++++++++++----- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/aries_cloudagent/protocols/didexchange/v1_0/manager.py b/aries_cloudagent/protocols/didexchange/v1_0/manager.py index a1456b6f49..8489cd4964 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/manager.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/manager.py @@ -702,13 +702,22 @@ async def accept_response( conn_rec = None if response._thread: # identify the request by the thread ID - try: - async with self.profile.session() as session: + async with self.profile.session() as session: + try: conn_rec = await ConnRecord.retrieve_by_request_id( - session, response._thread_id, their_role="inviter" + session, response._thread_id, + their_role=ConnRecord.Role.RESPONDER.rfc23, ) - except StorageNotFoundError: - pass + except StorageNotFoundError: + pass + if not conn_rec: + try: + conn_rec = await ConnRecord.retrieve_by_request_id( + session, response._thread_id, + their_role=ConnRecord.Role.RESPONDER.rfc160, + ) + except StorageNotFoundError: + pass if not conn_rec and receipt.sender_did: # identify connection by the DID they used for us @@ -809,12 +818,25 @@ async def accept_complete( conn_rec = None # identify the request by the thread ID - try: - async with self.profile.session() as session: + async with self.profile.session() as session: + try: conn_rec = await ConnRecord.retrieve_by_request_id( - session, complete._thread_id, their_role="invitee" + session, complete._thread_id, + their_role=ConnRecord.Role.REQUESTER.rfc23, ) - except StorageNotFoundError: + except StorageNotFoundError: + pass + + if not conn_rec: + try: + conn_rec = await ConnRecord.retrieve_by_request_id( + session, complete._thread_id, + their_role=ConnRecord.Role.REQUESTER.rfc160, + ) + except StorageNotFoundError: + pass + + if not conn_rec: raise DIDXManagerError( "No corresponding connection request found", error_code=ProblemReportReason.COMPLETE_NOT_ACCEPTED.value, From 6f8644e451f6a146ed6d7345e41a465580fda1ed Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Wed, 16 Mar 2022 18:45:33 -0700 Subject: [PATCH 074/872] Fix black formatting Signed-off-by: Ian Costanzo --- .../protocols/didexchange/v1_0/manager.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/aries_cloudagent/protocols/didexchange/v1_0/manager.py b/aries_cloudagent/protocols/didexchange/v1_0/manager.py index 8489cd4964..c7c9e51eaa 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/manager.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/manager.py @@ -705,7 +705,8 @@ async def accept_response( async with self.profile.session() as session: try: conn_rec = await ConnRecord.retrieve_by_request_id( - session, response._thread_id, + session, + response._thread_id, their_role=ConnRecord.Role.RESPONDER.rfc23, ) except StorageNotFoundError: @@ -713,7 +714,8 @@ async def accept_response( if not conn_rec: try: conn_rec = await ConnRecord.retrieve_by_request_id( - session, response._thread_id, + session, + response._thread_id, their_role=ConnRecord.Role.RESPONDER.rfc160, ) except StorageNotFoundError: @@ -821,7 +823,8 @@ async def accept_complete( async with self.profile.session() as session: try: conn_rec = await ConnRecord.retrieve_by_request_id( - session, complete._thread_id, + session, + complete._thread_id, their_role=ConnRecord.Role.REQUESTER.rfc23, ) except StorageNotFoundError: @@ -830,7 +833,8 @@ async def accept_complete( if not conn_rec: try: conn_rec = await ConnRecord.retrieve_by_request_id( - session, complete._thread_id, + session, + complete._thread_id, their_role=ConnRecord.Role.REQUESTER.rfc160, ) except StorageNotFoundError: From 46edc7827c815674c9c2d433e88703eeca4309dd Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Thu, 17 Mar 2022 10:17:04 -0700 Subject: [PATCH 075/872] Add an integration test for mixed proof with a revocable cred and a non-revocable cred Signed-off-by: Ian Costanzo --- demo/features/0454-present-proof.feature | 22 ++++++++++- ...tion_DL_age_over_19_v2_with_health_id.json | 24 ++++++++++++ ...uest_DL_age_over_19_v2_with_health_id.json | 38 +++++++++++++++++++ 3 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 demo/features/data/presentation_DL_age_over_19_v2_with_health_id.json create mode 100644 demo/features/data/proof_request_DL_age_over_19_v2_with_health_id.json diff --git a/demo/features/0454-present-proof.feature b/demo/features/0454-present-proof.feature index 2092b46007..ad8d16e3f9 100644 --- a/demo/features/0454-present-proof.feature +++ b/demo/features/0454-present-proof.feature @@ -40,7 +40,7 @@ Feature: RFC 0454 Aries agent present proof @T001.2-RFC0454 @GHA Scenario Outline: Present Proof json-ld where the prover does not propose a presentation of the proof and is acknowledged - Given we have "2" agents + Given we have "3" agents | name | role | capabilities | | Acme | issuer | | | Faber | verifier | | @@ -96,3 +96,23 @@ Feature: RFC 0454 Aries agent present proof | Faber | --revocation --public-did | | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | | Acme | --revocation --public-did --mediation | | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | | Acme | --revocation --public-did --multitenant | --multitenant | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | + + @T003-RFC0454 + Scenario Outline: Present Proof for multiple credentials where the one is revocable and one isn't + Given we have "4" agents + | name | role | capabilities | + | Acme1 | issuer1 | | + | Acme2 | issuer2 | | + | Faber | verifier | | + | Bob | prover | | + And "" and "Bob" have an existing connection + And "Bob" has an issued credential from "" + And "" and "Bob" have an existing connection + And "Bob" has an issued credential from "" + And "Faber" and "Bob" have an existing connection + When "Faber" sends a request for proof presentation to "Bob" + Then "Faber" has the proof verified + + Examples: + | issuer1 | Acme1_capabilities | issuer2 | Acme2_capabilities | Bob_capabilities | Schema_name_1 | Credential_data_1 | Schema_name_2 | Credential_data_2 | Proof_request | + | Acme1 | --revocation --public-did | Acme2 | --public-did | | driverslicense_v2 | Data_DL_MaxValues | health_id | Data_DL_MaxValues | DL_age_over_19_v2_with_health_id | diff --git a/demo/features/data/presentation_DL_age_over_19_v2_with_health_id.json b/demo/features/data/presentation_DL_age_over_19_v2_with_health_id.json new file mode 100644 index 0000000000..af35bfdaee --- /dev/null +++ b/demo/features/data/presentation_DL_age_over_19_v2_with_health_id.json @@ -0,0 +1,24 @@ +{ + "presentation": { + "comment": "This is a comment for the send presentation.", + "requested_attributes": { + "address_attrs": { + "cred_type_name": "Schema_DriversLicense_v2", + "revealed": true, + "cred_id": "replace_me" + }, + "health_attrs": { + "cred_type_name": "Schema_Health_ID", + "revealed": true, + "cred_id": "replace_me" + } + }, + "requested_predicates": { + "age": { + "cred_type_name": "Schema_DriversLicense_v2", + "cred_id": "replace me" + } + }, + "self_attested_attributes": {} + } +} \ No newline at end of file diff --git a/demo/features/data/proof_request_DL_age_over_19_v2_with_health_id.json b/demo/features/data/proof_request_DL_age_over_19_v2_with_health_id.json new file mode 100644 index 0000000000..e52e1e245a --- /dev/null +++ b/demo/features/data/proof_request_DL_age_over_19_v2_with_health_id.json @@ -0,0 +1,38 @@ +{ + "presentation_proposal": { + "requested_attributes": { + "address_attrs": { + "name": "address", + "restrictions": [ + { + "schema_name": "Schema_DriversLicense_v2", + "schema_version": "1.0.1" + } + ] + }, + "health_attrs": { + "name": "health_id_num", + "restrictions": [ + { + "schema_name": "Schema_Health_ID", + "schema_version": "1.0.0" + } + ] + } + }, + "requested_predicates": { + "age": { + "name": "age", + "p_type": ">", + "p_value": 19, + "restrictions": [ + { + "schema_name": "Schema_DriversLicense_v2", + "schema_version": "1.0.1" + } + ] + } + }, + "version": "0.1.0" + } +} \ No newline at end of file From 98f935485f09dd16a7f8003258393a8a77961021 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Thu, 17 Mar 2022 12:03:33 -0700 Subject: [PATCH 076/872] Check cred_def revocability when validating proof Signed-off-by: Ian Costanzo --- aries_cloudagent/indy/sdk/verifier.py | 4 + aries_cloudagent/indy/verifier.py | 123 ++++++++++++++------------ 2 files changed, 70 insertions(+), 57 deletions(-) diff --git a/aries_cloudagent/indy/sdk/verifier.py b/aries_cloudagent/indy/sdk/verifier.py index f7695a9e0e..b9e087aa82 100644 --- a/aries_cloudagent/indy/sdk/verifier.py +++ b/aries_cloudagent/indy/sdk/verifier.py @@ -47,6 +47,8 @@ async def verify_presentation( rev_reg_entries: revocation registry entries """ + LOGGER.debug(f">>> received presentation: {pres}") + LOGGER.debug(f">>> for pres_req: {pres_req}") try: self.non_revoc_intervals(pres_req, pres, credential_definitions) await self.check_timestamps(self.profile, pres_req, pres, rev_reg_defs) @@ -58,6 +60,8 @@ async def verify_presentation( ) return False + LOGGER.debug(f">>> verifying presentation: {pres}") + LOGGER.debug(f">>> for pres_req: {pres_req}") try: verified = await indy.anoncreds.verifier_verify_proof( json.dumps(pres_req), diff --git a/aries_cloudagent/indy/verifier.py b/aries_cloudagent/indy/verifier.py index d8a2d61a10..2206653ff7 100644 --- a/aries_cloudagent/indy/verifier.py +++ b/aries_cloudagent/indy/verifier.py @@ -107,24 +107,28 @@ async def check_timestamps( """ now = int(time()) non_revoc_intervals = indy_proof_req2non_revoc_intervals(pres_req) + LOGGER.debug(f">>> got non-revoc intervals: {non_revoc_intervals}") # timestamp for irrevocable credential + cred_defs = [] for (index, ident) in enumerate(pres["identifiers"]): + LOGGER.debug(f">>> got (index, ident): ({index},{ident})") + cred_def_id = ident["cred_def_id"] + ledger_exec_inst = profile.inject(IndyLedgerRequestsExecutor) + ledger = ( + await ledger_exec_inst.get_ledger_for_identifier( + cred_def_id, + txn_record_type=GET_CRED_DEF, + ) + )[1] + async with ledger: + cred_def = await ledger.get_credential_definition(cred_def_id) + cred_defs.append(cred_def) if ident.get("timestamp"): - cred_def_id = ident["cred_def_id"] - ledger_exec_inst = profile.inject(IndyLedgerRequestsExecutor) - ledger = ( - await ledger_exec_inst.get_ledger_for_identifier( - cred_def_id, - txn_record_type=GET_CRED_DEF, + if not cred_def["value"].get("revocation"): + raise ValueError( + f"Timestamp in presentation identifier #{index} " + f"for irrevocable cred def id {cred_def_id}" ) - )[1] - async with ledger: - cred_def = await ledger.get_credential_definition(cred_def_id) - if not cred_def["value"].get("revocation"): - raise ValueError( - f"Timestamp in presentation identifier #{index} " - f"for irrevocable cred def id {cred_def_id}" - ) # timestamp in the future too far in the past for ident in pres["identifiers"]: @@ -157,43 +161,73 @@ async def check_timestamps( if "name" in req_attr: if uuid in revealed_attrs: index = revealed_attrs[uuid]["sub_proof_index"] + if cred_defs[index]["value"].get("revocation"): + timestamp = pres["identifiers"][index].get("timestamp") + if (timestamp is not None) ^ bool(non_revoc_intervals.get(uuid)): + LOGGER.debug(f">>> uuid: {uuid}") + LOGGER.debug(f">>> revealed_attrs[uuid]: {revealed_attrs[uuid]}") + raise ValueError( + f"Timestamp on sub-proof #{index} " + f"is {'superfluous' if timestamp else 'missing'} " + f"vs. requested attribute {uuid}" + ) + if non_revoc_intervals.get(uuid) and not ( + non_revoc_intervals[uuid].get("from", 0) + < timestamp + < non_revoc_intervals[uuid].get("to", now) + ): + LOGGER.info( + f"Timestamp {timestamp} from ledger for item" + f"{uuid} falls outside non-revocation interval " + f"{non_revoc_intervals[uuid]}" + ) + elif uuid not in self_attested: + raise ValueError( + f"Presentation attributes mismatch requested attribute {uuid}" + ) + + elif "names" in req_attr: + group_spec = revealed_groups.get(uuid) + if ( + group_spec is None + or "sub_proof_index" not in group_spec + or "values" not in group_spec + ): + raise ValueError(f"Missing requested attribute group {uuid}") + index = group_spec["sub_proof_index"] + if cred_defs[index]["value"].get("revocation"): timestamp = pres["identifiers"][index].get("timestamp") if (timestamp is not None) ^ bool(non_revoc_intervals.get(uuid)): raise ValueError( f"Timestamp on sub-proof #{index} " f"is {'superfluous' if timestamp else 'missing'} " - f"vs. requested attribute {uuid}" + f"vs. requested attribute group {uuid}" ) if non_revoc_intervals.get(uuid) and not ( non_revoc_intervals[uuid].get("from", 0) < timestamp < non_revoc_intervals[uuid].get("to", now) ): - LOGGER.info( + LOGGER.warning( f"Timestamp {timestamp} from ledger for item" f"{uuid} falls outside non-revocation interval " f"{non_revoc_intervals[uuid]}" ) - elif uuid not in self_attested: - raise ValueError( - f"Presentation attributes mismatch requested attribute {uuid}" - ) - elif "names" in req_attr: - group_spec = revealed_groups.get(uuid) - if ( - group_spec is None - or "sub_proof_index" not in group_spec - or "values" not in group_spec - ): - raise ValueError(f"Missing requested attribute group {uuid}") - index = group_spec["sub_proof_index"] + for (uuid, req_pred) in pres_req["requested_predicates"].items(): + pred_spec = preds.get(uuid) + if pred_spec is None or "sub_proof_index" not in pred_spec: + raise ValueError( + f"Presentation predicates mismatch requested predicate {uuid}" + ) + index = pred_spec["sub_proof_index"] + if cred_defs[index]["value"].get("revocation"): timestamp = pres["identifiers"][index].get("timestamp") if (timestamp is not None) ^ bool(non_revoc_intervals.get(uuid)): raise ValueError( f"Timestamp on sub-proof #{index} " f"is {'superfluous' if timestamp else 'missing'} " - f"vs. requested attribute group {uuid}" + f"vs. requested predicate {uuid}" ) if non_revoc_intervals.get(uuid) and not ( non_revoc_intervals[uuid].get("from", 0) @@ -201,36 +235,11 @@ async def check_timestamps( < non_revoc_intervals[uuid].get("to", now) ): LOGGER.warning( - f"Timestamp {timestamp} from ledger for item" - f"{uuid} falls outside non-revocation interval " + f"Best-effort timestamp {timestamp} " + "from ledger falls outside non-revocation interval " f"{non_revoc_intervals[uuid]}" ) - for (uuid, req_pred) in pres_req["requested_predicates"].items(): - pred_spec = preds.get(uuid) - if pred_spec is None or "sub_proof_index" not in pred_spec: - raise ValueError( - f"Presentation predicates mismatch requested predicate {uuid}" - ) - index = pred_spec["sub_proof_index"] - timestamp = pres["identifiers"][index].get("timestamp") - if (timestamp is not None) ^ bool(non_revoc_intervals.get(uuid)): - raise ValueError( - f"Timestamp on sub-proof #{index} " - f"is {'superfluous' if timestamp else 'missing'} " - f"vs. requested predicate {uuid}" - ) - if non_revoc_intervals.get(uuid) and not ( - non_revoc_intervals[uuid].get("from", 0) - < timestamp - < non_revoc_intervals[uuid].get("to", now) - ): - LOGGER.warning( - f"Best-effort timestamp {timestamp} " - "from ledger falls outside non-revocation interval " - f"{non_revoc_intervals[uuid]}" - ) - async def pre_verify(self, pres_req: dict, pres: dict): """ Check for essential components and tampering in presentation. From 96000e1dbd827473836a4a7702aa44b5cf3bc921 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Thu, 17 Mar 2022 12:10:33 -0700 Subject: [PATCH 077/872] Black formatting fixes Signed-off-by: Ian Costanzo --- aries_cloudagent/indy/verifier.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/indy/verifier.py b/aries_cloudagent/indy/verifier.py index 2206653ff7..cf474bebf3 100644 --- a/aries_cloudagent/indy/verifier.py +++ b/aries_cloudagent/indy/verifier.py @@ -163,9 +163,13 @@ async def check_timestamps( index = revealed_attrs[uuid]["sub_proof_index"] if cred_defs[index]["value"].get("revocation"): timestamp = pres["identifiers"][index].get("timestamp") - if (timestamp is not None) ^ bool(non_revoc_intervals.get(uuid)): + if (timestamp is not None) ^ bool( + non_revoc_intervals.get(uuid) + ): LOGGER.debug(f">>> uuid: {uuid}") - LOGGER.debug(f">>> revealed_attrs[uuid]: {revealed_attrs[uuid]}") + LOGGER.debug( + f">>> revealed_attrs[uuid]: {revealed_attrs[uuid]}" + ) raise ValueError( f"Timestamp on sub-proof #{index} " f"is {'superfluous' if timestamp else 'missing'} " From 28e15ec39854f3b66161996f1760b99ec379b507 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 18 Mar 2022 15:13:07 +0100 Subject: [PATCH 078/872] feat: add service decorator Signed-off-by: Timo Glastra --- aries_cloudagent/messaging/agent_message.py | 25 +++++ .../messaging/decorators/default.py | 2 + .../messaging/decorators/service_decorator.py | 105 ++++++++++++++++++ 3 files changed, 132 insertions(+) create mode 100644 aries_cloudagent/messaging/decorators/service_decorator.py diff --git a/aries_cloudagent/messaging/agent_message.py b/aries_cloudagent/messaging/agent_message.py index 60b0534433..ea39f14712 100644 --- a/aries_cloudagent/messaging/agent_message.py +++ b/aries_cloudagent/messaging/agent_message.py @@ -21,6 +21,7 @@ from .decorators.default import DecoratorSet from .decorators.signature_decorator import SignatureDecorator # TODO deprecated from .decorators.thread_decorator import ThreadDecorator +from .decorators.service_decorator import ServiceDecorator from .decorators.trace_decorator import ( TraceDecorator, TraceReport, @@ -249,6 +250,30 @@ async def verify_signatures(self, wallet: BaseWallet) -> bool: return False return True + @property + def _service(self) -> ServiceDecorator: + """ + Accessor for the message's service decorator. + + Returns: + The ServiceDecorator for this message + + """ + return self._decorators.get("service") + + @_service.setter + def _service(self, val: Union[ServiceDecorator, dict]): + """ + Setter for the message's service decorator. + + Args: + val: ServiceDecorator or dict to set as the service + """ + if val is None: + self._decorators.pop("service", None) + else: + self._decorators["service"] = val + @property def _thread(self) -> ThreadDecorator: """ diff --git a/aries_cloudagent/messaging/decorators/default.py b/aries_cloudagent/messaging/decorators/default.py index 0d6d9f27c1..bc8c314466 100644 --- a/aries_cloudagent/messaging/decorators/default.py +++ b/aries_cloudagent/messaging/decorators/default.py @@ -8,6 +8,7 @@ from .trace_decorator import TraceDecorator from .timing_decorator import TimingDecorator from .transport_decorator import TransportDecorator +from .service_decorator import ServiceDecorator DEFAULT_MODELS = { "l10n": LocalizationDecorator, @@ -16,6 +17,7 @@ "trace": TraceDecorator, "timing": TimingDecorator, "transport": TransportDecorator, + "service": ServiceDecorator, } diff --git a/aries_cloudagent/messaging/decorators/service_decorator.py b/aries_cloudagent/messaging/decorators/service_decorator.py new file mode 100644 index 0000000000..b59de93565 --- /dev/null +++ b/aries_cloudagent/messaging/decorators/service_decorator.py @@ -0,0 +1,105 @@ +""" +A message decorator for services. + +A service decorator adds routing information to a message so agent can respond without +needing to perform a handshake. +""" + +from typing import List, Optional + +from marshmallow import EXCLUDE, fields + +from ..models.base import BaseModel, BaseModelSchema +from ..valid import INDY_RAW_PUBLIC_KEY + + +class ServiceDecorator(BaseModel): + """Class representing service decorator.""" + + class Meta: + """ServiceDecorator metadata.""" + + schema_class = "ServiceDecoratorSchema" + + def __init__( + self, + *, + endpoint: str, + recipient_keys: List[str], + routing_keys: Optional[List[str]] = None, + ): + """ + Initialize a ServiceDecorator instance. + + Args: + endpoint: Endpoint which this agent can be reached at + recipient_keys: List of recipient keys + routing_keys: List of routing keys + + """ + super().__init__() + self._endpoint = endpoint + self._recipient_keys = recipient_keys + self._routing_keys = routing_keys + + @property + def endpoint(self): + """ + Accessor for service endpoint. + + Returns: + This service's `serviceEndpoint` + + """ + return self._endpoint + + @property + def recipient_keys(self): + """ + Accessor for recipient keys. + + Returns: + This service's `recipientKeys` + + """ + return self._recipient_keys + + @property + def routing_keys(self): + """ + Accessor for routing keys. + + Returns: + This service's `routingKeys` + + """ + return self._routing_keys + + +class ServiceDecoratorSchema(BaseModelSchema): + """Thread decorator schema used in serialization/deserialization.""" + + class Meta: + """ServiceDecoratorSchema metadata.""" + + model_class = ServiceDecorator + unknown = EXCLUDE + + recipient_keys = fields.List( + fields.Str(description="Recipient public key", **INDY_RAW_PUBLIC_KEY), + data_key="recipientKeys", + required=True, + description="List of recipient keys", + ) + endpoint = fields.Str( + data_key="serviceEndpoint", + required=True, + description="Service endpoint at which to reach this agent", + example="http://192.168.56.101:8020", + ) + routing_keys = fields.List( + fields.Str(description="Routing key", **INDY_RAW_PUBLIC_KEY), + data_key="routingKeys", + required=False, + description="List of routing keys", + ) From 7151545009b95c03630150b04a23dd46c4f9d6ca Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 18 Mar 2022 15:44:43 +0100 Subject: [PATCH 079/872] fix: small type updates Signed-off-by: Timo Glastra --- .../protocols/connections/v1_0/manager.py | 12 ++++++------ .../protocols/didexchange/v1_0/manager.py | 9 +++++---- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/aries_cloudagent/protocols/connections/v1_0/manager.py b/aries_cloudagent/protocols/connections/v1_0/manager.py index a1fb3af093..f9806e58fd 100644 --- a/aries_cloudagent/protocols/connections/v1_0/manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/manager.py @@ -2,7 +2,7 @@ import logging -from typing import Coroutine, Sequence, Tuple +from typing import Coroutine, Optional, Sequence, Tuple from ....cache.base import BaseCache from ....config.base import InjectionError @@ -281,11 +281,11 @@ async def create_invitation( async def receive_invitation( self, invitation: ConnectionInvitation, - their_public_did: str = None, - auto_accept: bool = None, - alias: str = None, - mediation_id: str = None, - mediation_record: MediationRecord = None, + their_public_did: Optional[str] = None, + auto_accept: Optional[bool] = None, + alias: Optional[str] = None, + mediation_id: Optional[str] = None, + mediation_record: Optional[MediationRecord] = None, ) -> ConnRecord: """ Create a new connection record to track a received invitation. diff --git a/aries_cloudagent/protocols/didexchange/v1_0/manager.py b/aries_cloudagent/protocols/didexchange/v1_0/manager.py index 282bbb8335..af88808037 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/manager.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/manager.py @@ -2,6 +2,7 @@ import json import logging +from typing import Optional from ....connections.models.conn_record import ConnRecord from ....connections.models.diddoc import DIDDoc @@ -67,10 +68,10 @@ def profile(self) -> Profile: async def receive_invitation( self, invitation: OOBInvitationMessage, - their_public_did: str = None, - auto_accept: bool = None, - alias: str = None, - mediation_id: str = None, + their_public_did: Optional[str] = None, + auto_accept: Optional[bool] = None, + alias: Optional[str] = None, + mediation_id: Optional[str] = None, ) -> ConnRecord: # leave in didexchange as it uses a responder: not out-of-band """ Create a new connection record to track a received invitation. From fd0e51f8920d95bec2152bd89417620f843aa56b Mon Sep 17 00:00:00 2001 From: Thomas Diesler Date: Fri, 18 Mar 2022 16:30:04 +0100 Subject: [PATCH 080/872] [#1674] Add basic DOCKER_ENV logging for run_demo signed-off-by: Thomas Diesler --- demo/run_demo | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/demo/run_demo b/demo/run_demo index ce2c193804..b9c5714ded 100755 --- a/demo/run_demo +++ b/demo/run_demo @@ -188,7 +188,7 @@ if [ -z "$AGENT_ENDPOINT" ] && [ "$RUNMODE" == "docker" ]; then fi fi -echo $DOCKERHOST +echo "DOCKERHOST=$DOCKERHOST" DOCKER_ENV="-e LOG_LEVEL=${LOG_LEVEL} -e RUNMODE=${RUNMODE} -e DOCKERHOST=${DOCKERHOST}" if ! [ -z "$AGENT_PORT" ]; then @@ -241,6 +241,8 @@ if ! [ -z "${ENABLE_PYDEVD_PYCHARM}" ]; then DOCKER_ENV="${DOCKER_ENV} -e ENABLE_PYDEVD_PYCHARM=${ENABLE_PYDEVD_PYCHARM} -e PYDEVD_PYCHARM_CONTROLLER_PORT=${PYDEVD_PYCHARM_CONTROLLER_PORT} -e PYDEVD_PYCHARM_AGENT_PORT=${PYDEVD_PYCHARM_AGENT_PORT}" fi +echo "DOCKER_ENV=$DOCKER_ENV" + # on Windows, docker run needs to be prefixed by winpty if [ "$OSTYPE" = "msys" ]; then DOCKER="winpty docker" From 978226d53e31615e5a29e5019a41b0d99549170b Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Fri, 18 Mar 2022 08:41:12 -0700 Subject: [PATCH 081/872] updates Signed-off-by: Shaanjot Gill --- .../protocols/out_of_band/v1_0/manager.py | 135 +++++++++--------- .../out_of_band/v1_0/tests/test_manager.py | 28 +++- .../protocols/present_proof/dif/pres_exch.py | 2 +- .../present_proof/dif/pres_exch_handler.py | 37 +++-- .../dif/tests/test_pres_exch_handler.py | 11 +- .../present_proof/v2_0/formats/dif/handler.py | 11 +- .../v2_0/formats/dif/tests/test_handler.py | 45 +----- .../protocols/present_proof/v2_0/manager.py | 3 +- 8 files changed, 141 insertions(+), 131 deletions(-) diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py index 9fd4542c02..ec157bf1b6 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py @@ -4,7 +4,7 @@ import json import logging -from typing import Mapping, Sequence +from typing import Mapping, Sequence, Optional from ....connections.base_manager import BaseConnectionManager from ....connections.models.conn_record import ConnRecord @@ -477,69 +477,7 @@ async def receive_invitation( and num_included_req_attachments == 0 and use_existing_connection ): - await self.create_handshake_reuse_message( - invi_msg=invitation, - conn_record=conn_rec, - ) - try: - await asyncio.wait_for( - self.check_reuse_msg_state( - conn_rec=conn_rec, - ), - 15, - ) - async with self.profile.session() as session: - await conn_rec.metadata_delete( - session=session, key="reuse_msg_id" - ) - - msg_state = await conn_rec.metadata_get( - session, "reuse_msg_state" - ) - - if msg_state == "not_accepted": - conn_rec = None - else: - async with self.profile.session() as session: - await conn_rec.metadata_delete( - session=session, key="reuse_msg_state" - ) - # refetch connection for accurate state after handshake - conn_rec = await ConnRecord.retrieve_by_id( - session=session, record_id=conn_rec.connection_id - ) - except asyncio.TimeoutError: - # If no reuse_accepted or problem_report message was received within - # the 15s timeout then a new connection to be created - async with self.profile.session() as session: - sent_reuse_msg_id = await conn_rec.metadata_get( - session=session, key="reuse_msg_id" - ) - await conn_rec.metadata_delete( - session=session, key="reuse_msg_id" - ) - await conn_rec.metadata_delete( - session=session, key="reuse_msg_state" - ) - conn_rec.state = ConnRecord.State.ABANDONED.rfc160 - await conn_rec.save( - session, reason="No HandshakeReuseAccept message received" - ) - # Emit webhook - await self.profile.notify( - REUSE_ACCEPTED_WEBHOOK_TOPIC, - { - "thread_id": sent_reuse_msg_id, - "connection_id": conn_rec.connection_id, - "state": "rejected", - "comment": ( - "No HandshakeReuseAccept message received, " - f"connection {conn_rec.connection_id} ", - f"and invitation {invitation._id}", - ), - }, - ) - conn_rec = None + conn_rec = await self.send_reuse_message(invitation, conn_rec) # Inverse of the following cases # Handshake_Protocol not included # Request_Attachment included @@ -609,6 +547,8 @@ async def receive_invitation( # Request Attach if len(invitation.requests_attach) >= 1 and conn_rec is not None: req_attach = invitation.requests_attach[0] + if use_existing_connection: + conn_rec = await self.send_reuse_message(invitation, conn_rec) if isinstance(req_attach, AttachDecorator): if req_attach.data is not None: unq_req_attach_type = DIDCommPrefix.unqualify( @@ -768,6 +708,73 @@ async def _process_pres_request_v1( ) ) + async def send_reuse_message( + self, invitation: InvitationMessage, conn_rec: ConnRecord + ) -> Optional[ConnRecord]: + """ + Create and wait for handshake reuse message. + + Args: + invitation: invitation message + conn_rec: connection record + """ + await self.create_handshake_reuse_message( + invi_msg=invitation, + conn_record=conn_rec, + ) + try: + await asyncio.wait_for( + self.check_reuse_msg_state( + conn_rec=conn_rec, + ), + 15, + ) + async with self.profile.session() as session: + await conn_rec.metadata_delete(session=session, key="reuse_msg_id") + + msg_state = await conn_rec.metadata_get(session, "reuse_msg_state") + + if msg_state == "not_accepted": + conn_rec = None + else: + async with self.profile.session() as session: + await conn_rec.metadata_delete( + session=session, key="reuse_msg_state" + ) + # refetch connection for accurate state after handshake + conn_rec = await ConnRecord.retrieve_by_id( + session=session, record_id=conn_rec.connection_id + ) + return conn_rec + except asyncio.TimeoutError: + # If no reuse_accepted or problem_report message was received within + # the 15s timeout then a new connection to be created + async with self.profile.session() as session: + sent_reuse_msg_id = await conn_rec.metadata_get( + session=session, key="reuse_msg_id" + ) + await conn_rec.metadata_delete(session=session, key="reuse_msg_id") + await conn_rec.metadata_delete(session=session, key="reuse_msg_state") + conn_rec.state = ConnRecord.State.ABANDONED.rfc160 + await conn_rec.save( + session, reason="No HandshakeReuseAccept message received" + ) + # Emit webhook + await self.profile.notify( + REUSE_ACCEPTED_WEBHOOK_TOPIC, + { + "thread_id": sent_reuse_msg_id, + "connection_id": conn_rec.connection_id, + "state": "rejected", + "comment": ( + "No HandshakeReuseAccept message received, " + f"connection {conn_rec.connection_id} ", + f"and invitation {invitation._id}", + ), + }, + ) + return None + async def _process_pres_request_v2( self, req_attach: AttachDecorator, diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py index 297d10c59b..144e9301cb 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py @@ -1351,7 +1351,19 @@ async def test_receive_invitation_attachment_x(self): ) as didx_mgr_receive_invitation, async_mock.patch( "aries_cloudagent.protocols.out_of_band.v1_0.manager.InvitationMessage", autospec=True, - ) as inv_message_cls: + ) as inv_message_cls, async_mock.patch.object( + OutOfBandManager, + "create_handshake_reuse_message", + autospec=True, + ) as oob_mgr_create_reuse_msg, async_mock.patch.object( + OutOfBandManager, + "check_reuse_msg_state", + autospec=True, + ) as oob_mgr_check_reuse_state, async_mock.patch.object( + OutOfBandManager, + "send_reuse_message", + autospec=True, + ) as oob_mgr_send_reuse_message: mock_oob_invi = async_mock.MagicMock( services=[TestConfig.test_did], @@ -1373,7 +1385,19 @@ async def test_receive_invitation_req_pres_v1_0_attachment_x(self): ) as didx_mgr_receive_invitation, async_mock.patch( "aries_cloudagent.protocols.out_of_band.v1_0.manager.InvitationMessage", autospec=True, - ) as inv_message_cls: + ) as inv_message_cls, async_mock.patch.object( + OutOfBandManager, + "create_handshake_reuse_message", + autospec=True, + ) as oob_mgr_create_reuse_msg, async_mock.patch.object( + OutOfBandManager, + "check_reuse_msg_state", + autospec=True, + ) as oob_mgr_check_reuse_state, async_mock.patch.object( + OutOfBandManager, + "send_reuse_message", + autospec=True, + ) as oob_mgr_send_reuse_message: mock_oob_invi = async_mock.MagicMock( handshake_protocols=[ pfx.qualify(HSProto.RFC23.name) for pfx in DIDCommPrefix diff --git a/aries_cloudagent/protocols/present_proof/dif/pres_exch.py b/aries_cloudagent/protocols/present_proof/dif/pres_exch.py index 4a2ef289cf..dafe0b8857 100644 --- a/aries_cloudagent/protocols/present_proof/dif/pres_exch.py +++ b/aries_cloudagent/protocols/present_proof/dif/pres_exch.py @@ -646,7 +646,7 @@ class Meta: ), example=( { - "oneOf": [ + "oneof_filter": [ [ {"uri": "https://www.w3.org/Test1#Test1"}, {"uri": "https://www.w3.org/Test2#Test2"}, diff --git a/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py b/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py index 86c8372206..2824b0cf54 100644 --- a/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py +++ b/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py @@ -10,6 +10,7 @@ """ import pytz import re +import logging from datetime import datetime from dateutil.parser import parse as dateutil_parser @@ -61,6 +62,7 @@ PRESENTATION_SUBMISSION_JSONLD_TYPE = "PresentationSubmission" PYTZ_TIMEZONE_PATTERN = re.compile(r"(([a-zA-Z]+)(?:\/)([a-zA-Z]+))") LIST_INDEX_PATTERN = re.compile(r"\[(\W+)\]|\[(\d+)\]") +LOGGER = logging.getLogger(__name__) class DIFPresExchError(BaseError): @@ -789,8 +791,11 @@ def exclusive_minimum_check(self, val: any, _filter: Filter) -> bool: given_date = self.string_to_timezone_aware_datetime(str(val)) return given_date > to_compare_date else: - if self.is_numeric(val): + try: + val = self.is_numeric(val) return val > _filter.exclusive_min + except DIFPresExchError as err: + LOGGER.error(err) return False except (TypeError, ValueError, ParserError): return False @@ -817,8 +822,11 @@ def exclusive_maximum_check(self, val: any, _filter: Filter) -> bool: given_date = self.string_to_timezone_aware_datetime(str(val)) return given_date < to_compare_date else: - if self.is_numeric(val): + try: + val = self.is_numeric(val) return val < _filter.exclusive_max + except DIFPresExchError as err: + LOGGER.error(err) return False except (TypeError, ValueError, ParserError): return False @@ -845,8 +853,11 @@ def maximum_check(self, val: any, _filter: Filter) -> bool: given_date = self.string_to_timezone_aware_datetime(str(val)) return given_date <= to_compare_date else: - if self.is_numeric(val): + try: + val = self.is_numeric(val) return val <= _filter.maximum + except DIFPresExchError as err: + LOGGER.error(err) return False except (TypeError, ValueError, ParserError): return False @@ -873,8 +884,11 @@ def minimum_check(self, val: any, _filter: Filter) -> bool: given_date = self.string_to_timezone_aware_datetime(str(val)) return given_date >= to_compare_date else: - if self.is_numeric(val): + try: + val = self.is_numeric(val) return val >= _filter.minimum + except DIFPresExchError as err: + LOGGER.error(err) return False except (TypeError, ValueError, ParserError): return False @@ -1147,7 +1161,7 @@ async def apply_requirements( nested_result=nested_result, exclude=exclude ) - def is_numeric(self, val: any) -> bool: + def is_numeric(self, val: any): """ Check if val is an int or float. @@ -1155,11 +1169,18 @@ def is_numeric(self, val: any) -> bool: val: to check Return: bool + Raises: + DIFPresExchError: Provided value has invalid/incompatible type + """ if isinstance(val, float) or isinstance(val, int): - return True - else: - return False + return val + elif isinstance(val, str): + if val.isdigit(): + return float(val) + raise DIFPresExchError( + "Invalid type provided for comparision/numeric operation." + ) async def merge_nested_results( self, nested_result: Sequence[dict], exclude: dict diff --git a/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py b/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py index 67a56badb0..5c8fc2039b 100644 --- a/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py +++ b/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py @@ -1669,9 +1669,14 @@ def test_subject_is_issuer(self, setup_tuple, profile): @pytest.mark.asyncio def test_is_numeric(self, profile): dif_pres_exch_handler = DIFPresExchHandler(profile) - assert dif_pres_exch_handler.is_numeric("test") is False - assert dif_pres_exch_handler.is_numeric(1) is True - assert dif_pres_exch_handler.is_numeric(2 + 3j) is False + with pytest.raises(DIFPresExchError): + dif_pres_exch_handler.is_numeric("test") + assert dif_pres_exch_handler.is_numeric(1) == 1 + assert dif_pres_exch_handler.is_numeric(2.20) == 2.20 + assert dif_pres_exch_handler.is_numeric("2.20") == 2.20 + assert dif_pres_exch_handler.is_numeric("2") == 2 + with pytest.raises(DIFPresExchError): + dif_pres_exch_handler.is_numeric(2 + 3j) @pytest.mark.asyncio def test_filter_no_match(self, profile): diff --git a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/handler.py b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/handler.py index 33f1b653d0..1a196fe270 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/handler.py @@ -459,16 +459,11 @@ async def verify_pres(self, pres_ex_record: V20PresExRecord) -> V20PresExRecord: pres_request = pres_ex_record.pres_request.attachment( DIFPresFormatHandler.format ) + challenge = None if "options" in pres_request: - challenge = pres_request["options"].get("challenge") - else: - raise V20PresFormatHandlerError( - "No options [challenge] set for the presentation request" - ) + challenge = pres_request["options"].get("challenge", str(uuid4())) if not challenge: - raise V20PresFormatHandlerError( - "No challenge is set for the presentation request" - ) + challenge = str(uuid4()) pres_ver_result = await verify_presentation( presentation=dif_proof, suites=await self._get_all_suites(wallet=wallet), diff --git a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py index 4ac318c506..1b648f2aeb 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py @@ -975,50 +975,7 @@ async def test_verify_pres_no_challenge(self): error_msg="error", ) - with self.assertRaises(V20PresFormatHandlerError): - await self.handler.verify_pres(record) - - async def test_verify_pres_invalid_challenge(self): - test_pd = deepcopy(DIF_PRES_REQUEST_B) - del test_pd["options"] - dif_pres_request = V20PresRequest( - formats=[ - V20PresFormat( - attach_id="dif", - format_=ATTACHMENT_FORMAT[PRES_20_REQUEST][ - V20PresFormat.Format.DIF.api - ], - ) - ], - request_presentations_attach=[ - AttachDecorator.data_json(test_pd, ident="dif") - ], - ) - dif_pres = V20Pres( - formats=[ - V20PresFormat( - attach_id="dif", - format_=ATTACHMENT_FORMAT[PRES_20][V20PresFormat.Format.DIF.api], - ) - ], - presentations_attach=[AttachDecorator.data_json(DIF_PRES, ident="dif")], - ) - record = V20PresExRecord( - pres_ex_id="pxid", - thread_id="thid", - connection_id="conn_id", - initiator="init", - role="role", - state="state", - pres_request=dif_pres_request, - pres=dif_pres, - verified="false", - auto_present=True, - error_msg="error", - ) - - with self.assertRaises(V20PresFormatHandlerError): - await self.handler.verify_pres(record) + assert await self.handler.verify_pres(record) async def test_create_pres_cred_limit_disclosure_no_bbs(self): test_pd = deepcopy(DIF_PRES_REQUEST_B) diff --git a/aries_cloudagent/protocols/present_proof/v2_0/manager.py b/aries_cloudagent/protocols/present_proof/v2_0/manager.py index 688f4ca35f..7693942921 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/manager.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/manager.py @@ -339,7 +339,8 @@ async def receive_pres(self, message: V20Pres, conn_record: ConnRecord): ) pres_ex_record.pres = message pres_ex_record.state = V20PresExRecord.STATE_PRESENTATION_RECEIVED - + if not pres_ex_record.connection_id: + pres_ex_record.connection_id = conn_record.connection_id async with self._profile.session() as session: await pres_ex_record.save(session, reason="receive v2.0 presentation") From 84d7a0d2b343cde3c5832c032ece7ce1361ade4b Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Fri, 18 Mar 2022 08:47:08 -0700 Subject: [PATCH 082/872] is_numeric fix Signed-off-by: Shaanjot Gill --- .../protocols/present_proof/dif/pres_exch_handler.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py b/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py index 2824b0cf54..fcc703d20b 100644 --- a/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py +++ b/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py @@ -1168,7 +1168,7 @@ def is_numeric(self, val: any): Args: val: to check Return: - bool + numeric value Raises: DIFPresExchError: Provided value has invalid/incompatible type @@ -1177,6 +1177,8 @@ def is_numeric(self, val: any): return val elif isinstance(val, str): if val.isdigit(): + return int(val) + elif val.isdecimal(): return float(val) raise DIFPresExchError( "Invalid type provided for comparision/numeric operation." From 047b99b9da699423fe43ab2c69fcee2079abd89f Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Fri, 18 Mar 2022 08:53:19 -0700 Subject: [PATCH 083/872] is_numeric fix Signed-off-by: Shaanjot Gill --- .../protocols/present_proof/dif/pres_exch_handler.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py b/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py index fcc703d20b..638ce0fa3e 100644 --- a/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py +++ b/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py @@ -1178,8 +1178,11 @@ def is_numeric(self, val: any): elif isinstance(val, str): if val.isdigit(): return int(val) - elif val.isdecimal(): - return float(val) + else: + try: + return float(val) + except ValueError: + pass raise DIFPresExchError( "Invalid type provided for comparision/numeric operation." ) From e6fe5803f4727039b230801100f799ef07730d41 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Fri, 18 Mar 2022 09:38:10 -0700 Subject: [PATCH 084/872] retrigger checks Signed-off-by: Shaanjot Gill From fcb464c1117e5405db9e1d09a864b1ab3ba034aa Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sun, 20 Mar 2022 21:56:57 +0100 Subject: [PATCH 085/872] feat: connectionless working with auto-accept Signed-off-by: Timo Glastra --- aries_cloudagent/core/conductor.py | 21 +- aries_cloudagent/core/dispatcher.py | 21 +- aries_cloudagent/core/oob_processor.py | 300 +++++ aries_cloudagent/messaging/request_context.py | 2 +- .../v1_0/handlers/credential_ack_handler.py | 21 +- .../v1_0/handlers/credential_issue_handler.py | 24 +- .../v1_0/handlers/credential_offer_handler.py | 36 +- .../credential_problem_report_handler.py | 12 +- .../handlers/credential_proposal_handler.py | 9 +- .../handlers/credential_request_handler.py | 19 +- .../issue_credential/v1_0/manager.py | 73 +- .../v1_0/models/credential_exchange.py | 14 +- .../protocols/out_of_band/v1_0/manager.py | 1114 +++++++---------- .../out_of_band/v1_0/models/invitation.py | 13 +- .../out_of_band/v1_0/models/oob_record.py | 172 +++ .../protocols/out_of_band/v1_0/routes.py | 2 +- .../v1_0/handlers/presentation_ack_handler.py | 16 +- .../v1_0/handlers/presentation_handler.py | 19 +- .../presentation_problem_report_handler.py | 12 +- .../handlers/presentation_proposal_handler.py | 9 +- .../handlers/presentation_request_handler.py | 29 +- .../protocols/present_proof/v1_0/manager.py | 60 +- .../protocols/present_proof/v1_0/routes.py | 2 - aries_cloudagent/transport/inbound/receipt.py | 25 + aries_cloudagent/transport/pack_format.py | 1 + aries_cloudagent/transport/wire_format.py | 1 + 26 files changed, 1300 insertions(+), 727 deletions(-) create mode 100644 aries_cloudagent/core/oob_processor.py create mode 100644 aries_cloudagent/protocols/out_of_band/v1_0/models/oob_record.py diff --git a/aries_cloudagent/core/conductor.py b/aries_cloudagent/core/conductor.py index 6ed174545f..82ef06bea3 100644 --- a/aries_cloudagent/core/conductor.py +++ b/aries_cloudagent/core/conductor.py @@ -65,6 +65,7 @@ from ..vc.ld_proofs.document_loader import DocumentLoader from ..version import __version__, RECORD_TYPE_ACAPY_VERSION from ..wallet.did_info import DIDInfo +from .oob_processor import OobMessageProcessor from .dispatcher import Dispatcher from .util import STARTUP_EVENT_TOPIC, SHUTDOWN_EVENT_TOPIC @@ -206,6 +207,12 @@ async def setup(self): BaseMultitenantManager, MultitenantManagerProvider(self.root_profile) ) + # Bind oob message processor to be able to receive and process un-encrypted messages + context.injector.bind_instance( + OobMessageProcessor, + OobMessageProcessor(inbound_message_router=self.inbound_message_router), + ) + # Bind default PyLD document loader context.injector.bind_instance( DocumentLoader, DocumentLoader(self.root_profile) @@ -631,8 +638,10 @@ async def queue_outbound( message: An outbound message to be sent inbound: The inbound message that produced this response, if available """ + has_target = outbound.target or outbound.target_list + # populate connection target(s) - if not outbound.target and not outbound.target_list and outbound.connection_id: + if not has_target and outbound.connection_id: conn_mgr = ConnectionManager(profile) try: outbound.target_list = await self.dispatcher.run_task( @@ -649,6 +658,16 @@ async def queue_outbound( self.admin_server.notify_fatal_error() raise del conn_mgr + + # Find oob/connectionless target we can send the message to + elif not has_target and outbound.reply_thread_id: + message_processor = profile.inject(OobMessageProcessor) + outbound.target = await self.dispatcher.run_task( + message_processor.find_oob_target_for_outbound_message( + profile, outbound + ) + ) + # If ``self.outbound_queue`` is specified (usually set via # outbound queue `-oq` commandline option), use that external # queue. Else save the message to an internal queue. This diff --git a/aries_cloudagent/core/dispatcher.py b/aries_cloudagent/core/dispatcher.py index 98df420472..57ef012021 100644 --- a/aries_cloudagent/core/dispatcher.py +++ b/aries_cloudagent/core/dispatcher.py @@ -14,6 +14,8 @@ from aiohttp.web import HTTPException + +from ..connections.models.conn_record import ConnRecord from ..core.profile import Profile from ..messaging.agent_message import AgentMessage from ..messaging.base_message import BaseMessage @@ -173,11 +175,20 @@ async def handle_message( context.injector.bind_instance(BaseResponder, responder) - connection_mgr = ConnectionManager(profile) - connection = await connection_mgr.find_inbound_connection( - inbound_message.receipt - ) - del connection_mgr + # When processing oob attach message we supply the connection id + # associated with the inbound message + if inbound_message.connection_id: + async with self.profile.session() as session: + connection = await ConnRecord.retrieve_by_id( + session, inbound_message.connection_id + ) + else: + connection_mgr = ConnectionManager(profile) + connection = await connection_mgr.find_inbound_connection( + inbound_message.receipt + ) + del connection_mgr + if connection: inbound_message.connection_id = connection.connection_id diff --git a/aries_cloudagent/core/oob_processor.py b/aries_cloudagent/core/oob_processor.py new file mode 100644 index 0000000000..1e126ca5d4 --- /dev/null +++ b/aries_cloudagent/core/oob_processor.py @@ -0,0 +1,300 @@ +"""Oob message processor and functions.""" + +import json +import logging +from typing import Any, Callable, Dict, Optional, cast + + +from ..ledger.base import BaseLedger +from ..connections.models.conn_record import ConnRecord +from ..connections.models.connection_target import ConnectionTarget +from ..did.did_key import DIDKey +from ..messaging.decorators.service_decorator import ServiceDecorator +from ..messaging.request_context import RequestContext +from ..protocols.out_of_band.v1_0.messages.service import Service +from ..protocols.out_of_band.v1_0.models.oob_record import OobRecord +from ..storage.error import StorageNotFoundError +from ..transport.inbound.message import InboundMessage +from ..transport.outbound.message import OutboundMessage +from ..transport.wire_format import JsonWireFormat +from .profile import Profile + +LOGGER = logging.getLogger(__name__) + + +class OobMessageProcessor: + """Out of band message processor.""" + + def __init__( + self, + inbound_message_router: Callable[ + [Profile, InboundMessage, Optional[bool]], None + ], + ) -> None: + """ + Initialize an inbound OOB message processor + + Args: + inbound_message_router: Method to create a new inbound session + + """ + self._inbound_message_router = inbound_message_router + self.wire_format = JsonWireFormat() + + async def find_oob_target_for_outbound_message( + self, profile: Profile, outbound_message: OutboundMessage + ) -> Optional[ConnectionTarget]: + try: + async with profile.session() as session: + # Try to find the oob record for the outbound message: + oob_record = await OobRecord.retrieve_by_tag_filter( + session, {"attach_thread_id": outbound_message.reply_thread_id} + ) + + their_service = oob_record.their_service + their_service = ServiceDecorator.deserialize(their_service) + + # FIXME: integrate with mediation + our_service = ServiceDecorator( + recipient_keys=[oob_record.our_recipient_key], + endpoint=profile.settings.get("default_endpoint"), + routing_keys=[], + ) + + # Attach ~service decorator so other message can respond + message = json.loads(outbound_message.payload) + if not message.get("~service"): + message["~service"] = our_service.serialize() + + # TODO: state is somewhat done, but we need it for connectionless exchange + # if is_first_response: + message["~thread"] = { + **message.get("~thread", {}), + "pthid": oob_record.invi_msg_id, + } + + outbound_message.payload = json.dumps(message) + + return ConnectionTarget( + endpoint=their_service.endpoint, + recipient_keys=their_service.recipient_keys, + routing_keys=their_service.routing_keys, + sender_key=oob_record.our_recipient_key, + ) + except StorageNotFoundError: + return None + + async def find_oob_record_for_inbound_message( + self, context: RequestContext + ) -> Optional[OobRecord]: + message_type = context.message._type + oob_record = None + + async with context.profile.session() as session: + # First try to find the oob record based on the associated pthid + if context.message_receipt.parent_thread_id: + try: + LOGGER.debug( + f"Retrieving OOB record using pthid {context.message_receipt.parent_thread_id} for message type {message_type}" + ) + oob_record = await OobRecord.retrieve_by_tag_filter( + session, + {"invi_msg_id": context.message_receipt.parent_thread_id}, + ) + except StorageNotFoundError: + # Fine if record is not found + pass + # Otherwise try to find it using the attach thread id. This is only needed + # for connectionless exchanges where every handlers needs the context of the oob + # record for verification. We could attach the oob_record to all messages, even if + # we have a connection, but it would add another query to all inbound messages. + if ( + not oob_record + and not context.connection_record + and context.message_receipt.thread_id + and context.message_receipt.recipient_verkey + ): + try: + LOGGER.debug( + f"Retrieving OOB record using thid {context.message_receipt.thread_id} and recipient verkey {context.message_receipt.recipient_verkey} for message type {message_type}" + ) + oob_record = await OobRecord.retrieve_by_tag_filter( + session, + { + "attach_thread_id": context.message_receipt.thread_id, + "our_recipient_key": context.message_receipt.recipient_verkey, + }, + ) + except StorageNotFoundError: + # Fine if record is not found + pass + + # If not oob record was found we can return early without oob record + if not oob_record: + return None + + LOGGER.debug( + f"Found out of band record for inbound message with type {message_type}: %s", + oob_record, + ) + + # If the connection does not match with the connection id associated with the + # oob record we don't want to associate the oob record to the current context + # This is not the case if the state is await response, in this case we might want + # to update the connection id on the oob record + if ( + # Only if we created the invitation + oob_record.role == OobRecord.ROLE_SENDER + # If connection is present and not same as oob_record conn id + and context.connection_record + and context.connection_record.connection_id != oob_record.connection_id + ): + LOGGER.debug( + f"Oob record connection id {oob_record.connection_id} is different from inbound message connection {context.connection_record.connection_id}", + ) + # Mismatch in connection id's in only allowed in state await response (connection id can change bc of reuse) + if oob_record.state != OobRecord.STATE_AWAIT_RESPONSE: + LOGGER.debug( + f"Inbound message has incorrect connection_id {context.connection_record.connection_id}. Oob record {oob_record.oob_id} associated with connection id {oob_record.connection_id}" + ) + return None + + # If the state is await response, and there are attachments we want to update the connection id + # on the oob record. In case no request_attach is present, this is handled by the reuse handlers + if ( + oob_record.invitation.requests_attach + and oob_record.state == OobRecord.STATE_AWAIT_RESPONSE + ): + LOGGER.debug( + f"Removing stale connection {oob_record.connection_id} due to connection reuse" + ) + # Remove stale connection due to connection reuse + if oob_record.connection_id: + async with context.profile.session() as session: + old_conn_record = await ConnRecord.retrieve_by_id( + session, oob_record.connection_id + ) + await old_conn_record.delete_record(session) + + oob_record.connection_id = context.connection_record.connection_id + + # If no attach_thread_id is stored yet we need to match the current message thread_id against the attached messages + # in the oob invitation + if not oob_record.attach_thread_id and oob_record.invitation.requests_attach: + # Check if the current message thread_id corresponds to one of the invitation ~thread.thid + allowed_thread_ids = [ + self._get_thread_id(attachment.content) + for attachment in oob_record.invitation.requests_attach + ] + + if not context.message_receipt.thread_id in allowed_thread_ids: + LOGGER.debug( + f"Inbound message is for not allowed thread {context.message_receipt.thread_id}. Allowed threads are {allowed_thread_ids}" + ) + return None + + oob_record.attach_thread_id = context.message_receipt.thread_id + elif ( + oob_record.attach_thread_id + and context.message_receipt.thread_id != oob_record.attach_thread_id + ): + LOGGER.debug( + f"Inbound message thread id {context.message_receipt.thread_id} does not match oob record thread id {oob_record.attach_thread_id}" + ) + return None + + their_service = ( + cast( + ServiceDecorator, + ServiceDecorator.deserialize(oob_record.their_service), + ) + if oob_record.their_service + else None + ) + + # Verify the sender key is present in their service in our record + # If we don't have the sender verkey stored yet we can allow any key + if ( + their_service + # FIXME: does this mean anyone with anoncreds can send a message? + and context.message_receipt.sender_verkey + and context.message_receipt.sender_verkey + not in their_service.recipient_keys + ): + LOGGER.debug( + "Inbound message sender verkey does not match stored service on oob record" + ) + return None + + # If the message has a ~service decorator we save it in the oob record so we can reply to this message + if context._message._service: + # TODO: what should we do if the keys don't match? I would say for now we require the complete + # oob exchange to use the same keys + oob_record.their_service = context.message._service.serialize() + + async with context.profile.session() as session: + await oob_record.save(session, reason="Update their service in oob record") + + return oob_record + + async def handle_message( + self, profile: Profile, message: Dict[str, Any], oob_record: OobRecord + ): + """Message handler for inbound messages.""" + + message_str = json.dumps(message) + + async with profile.session() as session: + message_dict, receipt = await self.wire_format.parse_message( + session, message_str + ) + + inbound_message = InboundMessage( + payload=message_dict, + connection_id=oob_record.connection_id, + receipt=receipt, + ) + # Create ~service from the oob service + + if not oob_record.connection_id: + service = oob_record.invitation.services[0] + + if isinstance(service, str): + async with session.inject(BaseLedger) as ledger: + endpoint = await ledger.get_endpoint_for_did(service) + verkey = await ledger.get_key_for_did(service) + + service_decorator = ServiceDecorator( + endpoint=endpoint, + recipient_keys=[verkey], + routing_keys=[], + ) + else: + service_decorator = self._service_decorator_from_service(service) + + oob_record.their_service = service_decorator.serialize() + + oob_record.attach_thread_id = inbound_message.receipt.thread_id + + await oob_record.save(session) + + self._inbound_message_router(profile, inbound_message, False) + + def _get_thread_id(self, message: Dict[str, Any]) -> str: + return message.get("~thread", {}).get("thid") or message.get("@id") + + def _service_decorator_from_service(self, service: Service) -> ServiceDecorator: + # Create ~service decorator from the oob service + recipient_keys = [ + DIDKey.from_did(did_key).public_key_b58 + for did_key in service.recipient_keys + ] + routing_keys = [ + DIDKey.from_did(did_key).public_key_b58 for did_key in service.routing_keys + ] + + return ServiceDecorator( + endpoint=service.service_endpoint, + recipient_keys=recipient_keys, + routing_keys=routing_keys, + ) diff --git a/aries_cloudagent/messaging/request_context.py b/aries_cloudagent/messaging/request_context.py index 97e0d1f9ef..715591cb2d 100644 --- a/aries_cloudagent/messaging/request_context.py +++ b/aries_cloudagent/messaging/request_context.py @@ -61,7 +61,7 @@ def connection_ready(self, active: bool): self._connection_ready = active @property - def connection_record(self) -> ConnRecord: + def connection_record(self) -> Optional[ConnRecord]: """Accessor for the related connection record.""" return self._connection_record diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_ack_handler.py b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_ack_handler.py index 26faeb8915..da5712e7ab 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_ack_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_ack_handler.py @@ -1,5 +1,6 @@ """Credential ack message handler.""" +from .....core.oob_processor import OobMessageProcessor from .....messaging.base_handler import BaseHandler, HandlerException from .....messaging.request_context import RequestContext from .....messaging.responder import BaseResponder @@ -29,12 +30,26 @@ async def handle(self, context: RequestContext, responder: BaseResponder): context.message.serialize(as_string=True), ) - if not context.connection_ready: - raise HandlerException("No connection established for credential ack") + # If connection is present it must be ready for use + if context.connection_record and not context.connection_ready: + raise HandlerException("Connection used for credential ack not ready") + + # Find associated oob record + oob_processor = context.inject(OobMessageProcessor) + oob_record = await oob_processor.find_oob_record_for_inbound_message(context) + + # Either connection or oob context must be present + if not context.connection_record and not oob_record: + raise HandlerException( + "No connection or associated connectionless exchange found for credential ack" + ) credential_manager = CredentialManager(context.profile) await credential_manager.receive_credential_ack( - context.message, context.connection_record.connection_id + context.message, + context.connection_record.connection_id + if context.connection_record + else None, ) trace_event( diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_issue_handler.py b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_issue_handler.py index 9048311cfe..f34560c624 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_issue_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_issue_handler.py @@ -1,5 +1,9 @@ """Credential issue message handler.""" +from aries_cloudagent.protocols.connections.v1_0.handlers.tests.test_request_handler import ( + connection_record, +) +from .....core.oob_processor import OobMessageProcessor from .....indy.holder import IndyHolderError from .....messaging.base_handler import BaseHandler, HandlerException from .....messaging.models.base import BaseModelError @@ -34,12 +38,26 @@ async def handle(self, context: RequestContext, responder: BaseResponder): "Received credential message: %s", context.message.serialize(as_string=True) ) - if not context.connection_ready: - raise HandlerException("No connection established for credential issue") + # If connection is present it must be ready for use + if context.connection_record and not context.connection_ready: + raise HandlerException("Connection used for credential not ready") + + # Find associated oob record + oob_processor = context.inject(OobMessageProcessor) + oob_record = await oob_processor.find_oob_record_for_inbound_message(context) + + # Either connection or oob context must be present + if not context.connection_record and not oob_record: + raise HandlerException( + "No connection or associated connectionless exchange found for credential" + ) credential_manager = CredentialManager(profile) cred_ex_record = await credential_manager.receive_credential( - context.message, context.connection_record.connection_id + context.message, + context.connection_record.connection_id + if context.connection_record + else None, ) # mgr only finds, saves record: on exception, saving state null is hopeless r_time = trace_event( diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_offer_handler.py b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_offer_handler.py index af1f3844ae..ded7722293 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_offer_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_offer_handler.py @@ -1,5 +1,8 @@ """Credential offer message handler.""" +from base58 import b58decode, b58encode + +from .....core.oob_processor import OobMessageProcessor from .....indy.holder import IndyHolderError from .....ledger.error import LedgerError from .....messaging.base_handler import BaseHandler, HandlerException @@ -36,12 +39,29 @@ async def handle(self, context: RequestContext, responder: BaseResponder): context.message.serialize(as_string=True), ) - if not context.connection_ready: - raise HandlerException("No connection established for credential offer") + # If connection is present it must be ready for use + if context.connection_record and not context.connection_ready: + raise HandlerException("Connection used for credential offer not ready") + + # Find associated oob record + oob_processor = context.inject(OobMessageProcessor) + oob_record = await oob_processor.find_oob_record_for_inbound_message(context) + + # Either connection or oob context must be present + if not context.connection_record and not oob_record: + raise HandlerException( + "No connection or associated connectionless exchange found for credential offer" + ) + + connection_id = ( + context.connection_record.connection_id + if context.connection_record + else None + ) credential_manager = CredentialManager(profile) cred_ex_record = await credential_manager.receive_offer( - context.message, context.connection_record.connection_id + context.message, connection_id ) # mgr only finds, saves record: on exception, saving state null is hopeless r_time = trace_event( @@ -51,6 +71,14 @@ async def handle(self, context: RequestContext, responder: BaseResponder): perf_counter=r_time, ) + if context.connection_record: + holder_did = context.connection_record.my_did + else: + # Transform recipient key into did + holder_did = b58encode(b58decode(oob_record.our_recipient_key)[:16]).decode( + "utf-8" + ) + # If auto respond is turned on, automatically reply with credential request if cred_ex_record and context.settings.get( "debug.auto_respond_credential_offer" @@ -62,7 +90,7 @@ async def handle(self, context: RequestContext, responder: BaseResponder): credential_request_message, ) = await credential_manager.create_request( cred_ex_record=cred_ex_record, - holder_did=context.connection_record.my_did, + holder_did=holder_did, ) await responder.send_reply(credential_request_message) except ( diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_problem_report_handler.py b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_problem_report_handler.py index bb88d6614f..24d2b25079 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_problem_report_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_problem_report_handler.py @@ -1,6 +1,6 @@ """Credential problem report message handler.""" -from .....messaging.base_handler import BaseHandler +from .....messaging.base_handler import BaseHandler, HandlerException from .....messaging.request_context import RequestContext from .....messaging.responder import BaseResponder from .....storage.error import StorageError, StorageNotFoundError @@ -26,6 +26,16 @@ async def handle(self, context: RequestContext, responder: BaseResponder): ) assert isinstance(context.message, CredentialProblemReport) + # If connection is present it must be ready for use + if context.connection_record and not context.connection_ready: + raise HandlerException( + "Connection used for credential problem report not ready" + ) + elif not context.connection_record: + raise HandlerException( + "Connectionless not supported for credential problem report" + ) + credential_manager = CredentialManager(context.profile) try: await credential_manager.receive_problem_report( diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_proposal_handler.py b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_proposal_handler.py index 502eee171b..b42338aa10 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_proposal_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_proposal_handler.py @@ -37,8 +37,13 @@ async def handle(self, context: RequestContext, responder: BaseResponder): context.message.serialize(as_string=True), ) - if not context.connection_ready: - raise HandlerException("No connection established for credential proposal") + # If connection is present it must be ready for use + if context.connection_record and not context.connection_ready: + raise HandlerException("Connection used for credential proposal not ready") + elif not context.connection_record: + raise HandlerException( + "Connectionless not supported for credential proposal" + ) credential_manager = CredentialManager(profile) cred_ex_record = await credential_manager.receive_proposal( diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_request_handler.py b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_request_handler.py index bc6d028c10..eecbf2e348 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_request_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_request_handler.py @@ -1,5 +1,6 @@ """Credential request message handler.""" +from .....core.oob_processor import OobMessageProcessor from .....indy.issuer import IndyIssuerError from .....ledger.error import LedgerError from .....messaging.base_handler import BaseHandler, HandlerException @@ -36,12 +37,24 @@ async def handle(self, context: RequestContext, responder: BaseResponder): context.message.serialize(as_string=True), ) - if not context.connection_ready: - raise HandlerException("No connection established for credential request") + # If connection is present it must be ready for use + if context.connection_record and not context.connection_ready: + raise HandlerException("Connection used for credential request not ready") + + # Find associated oob record. If the credential offer was created as an oob attachment + # the presentation exchange record won't have a connection id (yet) + oob_processor = context.inject(OobMessageProcessor) + oob_record = await oob_processor.find_oob_record_for_inbound_message(context) + + # Either connection or oob context must be present + if not context.connection_record and not oob_record: + raise HandlerException( + "No connection or associated connectionless exchange found for credential request" + ) credential_manager = CredentialManager(profile) cred_ex_record = await credential_manager.receive_request( - context.message, context.connection_record.connection_id + context.message, context.connection_record, oob_record ) # mgr only finds, saves record: on exception, saving state null is hopeless r_time = trace_event( diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/manager.py b/aries_cloudagent/protocols/issue_credential/v1_0/manager.py index 7934d1725d..f1d0423a9b 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/manager.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/manager.py @@ -4,7 +4,7 @@ import json import logging -from typing import Mapping, Tuple +from typing import Mapping, Optional, Tuple from ....cache.base import BaseCache from ....core.error import BaseError @@ -27,7 +27,9 @@ from ....revocation.util import notify_revocation_reg_event from ....storage.base import BaseStorage from ....storage.error import StorageError, StorageNotFoundError +from ....connections.models.conn_record import ConnRecord +from ...out_of_band.v1_0.models.oob_record import OobRecord from .messages.credential_ack import CredentialAck from .messages.credential_issue import CredentialIssue from .messages.credential_offer import CredentialOffer @@ -325,7 +327,7 @@ async def _create(cred_def_id): return (cred_ex_record, credential_offer_message) async def receive_offer( - self, message: CredentialOffer, connection_id: str + self, message: CredentialOffer, connection_id: Optional[str] ) -> V10CredentialExchange: """ Receive a credential offer. @@ -352,7 +354,11 @@ async def receive_offer( try: cred_ex_record = await ( V10CredentialExchange.retrieve_by_connection_and_thread( - txn, connection_id, message._thread_id, for_update=True + txn, + connection_id, + message._thread_id, + role=V10CredentialExchange.ROLE_HOLDER, + for_update=True, ) ) except StorageNotFoundError: # issuer sent this offer free of any proposal @@ -375,6 +381,7 @@ async def receive_offer( ) cred_ex_record.credential_proposal_dict = credential_proposal_dict + cred_ex_record.credential_offer_dict = message cred_ex_record.credential_offer = indy_offer cred_ex_record.state = V10CredentialExchange.STATE_OFFER_RECEIVED cred_ex_record.schema_id = schema_id @@ -465,7 +472,10 @@ async def _create(): credential_request_message = CredentialRequest( requests_attach=[CredentialRequest.wrap_indy_cred_req(cred_req_ser)] ) - credential_request_message._thread = {"thid": cred_ex_record.thread_id} + # Assign thid (and optionally pthid) to message + credential_request_message.assign_thread_from( + cred_ex_record.credential_offer_dict + ) credential_request_message.assign_trace_decorator( self._profile.settings, cred_ex_record.trace ) @@ -489,7 +499,12 @@ async def _create(): return (cred_ex_record, credential_request_message) - async def receive_request(self, message: CredentialRequest, connection_id: str): + async def receive_request( + self, + message: CredentialRequest, + connection_record: Optional[ConnRecord], + oob_record: Optional[OobRecord], + ): """ Receive a credential request. @@ -503,26 +518,26 @@ async def receive_request(self, message: CredentialRequest, connection_id: str): assert len(message.requests_attach or []) == 1 credential_request = message.indy_cred_req(0) + # connection_id is None in the record if this is in response to + # an request~attach from an OOB message. If so, we do not want to filter + # the record by connection_id. + connection_id = None if oob_record else connection_record.connection_id + async with self._profile.transaction() as txn: try: cred_ex_record = await ( V10CredentialExchange.retrieve_by_connection_and_thread( - txn, connection_id, message._thread_id, for_update=True - ) - ) - except StorageNotFoundError: - try: - cred_ex_record = await V10CredentialExchange.retrieve_by_tag_filter( txn, - {"thread_id": message._thread_id}, - {"connection_id": None}, + connection_id, + message._thread_id, + role=V10CredentialExchange.ROLE_ISSUER, for_update=True, ) - cred_ex_record.connection_id = connection_id - except StorageNotFoundError: - raise CredentialManagerError( - "Indy issue credential format can't start from credential request" - ) from None + ) + except StorageNotFoundError: + raise CredentialManagerError( + "Indy issue credential format can't start from credential request" + ) from None if cred_ex_record.state != V10CredentialExchange.STATE_OFFER_SENT: LOGGER.error( "Skipping credential request; exchange state is %s (id=%s)", @@ -530,6 +545,10 @@ async def receive_request(self, message: CredentialRequest, connection_id: str): cred_ex_record.credential_exchange_id, ) return None + + if connection_record: + cred_ex_record.connection_id = connection_record.connection_id + cred_ex_record.credential_request = credential_request cred_ex_record.state = V10CredentialExchange.STATE_REQUEST_RECEIVED await cred_ex_record.save(txn, reason="receive credential request") @@ -745,7 +764,7 @@ async def issue_credential( return (cred_ex_record, credential_message) async def receive_credential( - self, message: CredentialIssue, connection_id: str + self, message: CredentialIssue, connection_id: Optional[str] ) -> V10CredentialExchange: """ Receive a credential from an issuer. @@ -763,7 +782,11 @@ async def receive_credential( try: cred_ex_record = await ( V10CredentialExchange.retrieve_by_connection_and_thread( - txn, connection_id, message._thread_id, for_update=True + txn, + connection_id, + message._thread_id, + role=V10CredentialExchange.ROLE_HOLDER, + for_update=True, ) ) except StorageNotFoundError: @@ -948,8 +971,8 @@ async def send_credential_ack( return (cred_ex_record, credential_ack_message) async def receive_credential_ack( - self, message: CredentialAck, connection_id: str - ) -> V10CredentialExchange: + self, message: CredentialAck, connection_id: Optional[str] + ) -> Optional[V10CredentialExchange]: """ Receive credential ack from holder. @@ -961,7 +984,11 @@ async def receive_credential_ack( try: cred_ex_record = await ( V10CredentialExchange.retrieve_by_connection_and_thread( - txn, connection_id, message._thread_id, for_update=True + txn, + connection_id, + message._thread_id, + role=V10CredentialExchange.ROLE_ISSUER, + for_update=True, ) ) except StorageNotFoundError: diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py b/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py index 5df1eedd74..a91d5c03b6 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py @@ -2,7 +2,7 @@ import logging -from typing import Any, Mapping, Union +from typing import Any, Mapping, Optional, Union from marshmallow import fields, validate @@ -264,21 +264,27 @@ def record_value(self) -> dict: async def retrieve_by_connection_and_thread( cls, session: ProfileSession, - connection_id: str, + connection_id: Optional[str], thread_id: str, + role: Optional[str] = None, *, for_update=False, ) -> "V10CredentialExchange": """Retrieve a credential exchange record by connection and thread ID.""" - cache_key = f"credential_exchange_ctidx::{connection_id}::{thread_id}" + cache_key = f"credential_exchange_ctidx::{connection_id}::{thread_id}::{role}" record_id = await cls.get_cached_key(session, cache_key) if record_id: record = await cls.retrieve_by_id(session, record_id, for_update=for_update) else: + post_filter = {} + if role: + post_filter["role"] = role + if connection_id: + post_filter["connection_id"] = connection_id record = await cls.retrieve_by_tag_filter( session, {"thread_id": thread_id}, - {"connection_id": connection_id} if connection_id else None, + post_filter, for_update=for_update, ) await cls.set_cached_key(session, cache_key, record.credential_exchange_id) diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py index 9fd4542c02..12309e2b97 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py @@ -1,56 +1,43 @@ """Classes to manage connections.""" import asyncio -import json import logging +import re +from typing import Mapping, Optional, Sequence +import uuid -from typing import Mapping, Sequence +from aries_cloudagent.core.event_bus import EventBus from ....connections.base_manager import BaseConnectionManager from ....connections.models.conn_record import ConnRecord from ....connections.util import mediation_record_if_id from ....core.error import BaseError +from ....core.oob_processor import OobMessageProcessor from ....core.profile import Profile from ....did.did_key import DIDKey -from ....indy.holder import IndyHolder -from ....indy.models.xform import indy_proof_req_preview2indy_requested_creds from ....messaging.decorators.attach_decorator import AttachDecorator from ....messaging.responder import BaseResponder from ....multitenant.base import BaseMultitenantManager from ....storage.error import StorageNotFoundError from ....transport.inbound.receipt import MessageReceipt from ....wallet.base import BaseWallet -from ....wallet.util import b64_to_bytes from ....wallet.key_type import KeyType - -from ...coordinate_mediation.v1_0.manager import MediationManager from ...connections.v1_0.manager import ConnectionManager from ...connections.v1_0.messages.connection_invitation import ConnectionInvitation +from ...coordinate_mediation.v1_0.manager import MediationManager from ...didcomm_prefix import DIDCommPrefix from ...didexchange.v1_0.manager import DIDXManager -from ...issue_credential.v1_0.manager import CredentialManager as V10CredManager -from ...issue_credential.v1_0.messages.credential_offer import ( - CredentialOffer as V10CredOffer, -) -from ...issue_credential.v1_0.message_types import CREDENTIAL_OFFER from ...issue_credential.v1_0.models.credential_exchange import V10CredentialExchange -from ...issue_credential.v2_0.manager import V20CredManager -from ...issue_credential.v2_0.messages.cred_offer import V20CredOffer -from ...issue_credential.v2_0.message_types import CRED_20_OFFER from ...issue_credential.v2_0.models.cred_ex_record import V20CredExRecord -from ...present_proof.v1_0.manager import PresentationManager -from ...present_proof.v1_0.message_types import PRESENTATION_REQUEST from ...present_proof.v1_0.models.presentation_exchange import V10PresentationExchange -from ...present_proof.v2_0.manager import V20PresManager -from ...present_proof.v2_0.message_types import PRES_20_REQUEST from ...present_proof.v2_0.models.pres_exchange import V20PresExRecord - from .messages.invitation import HSProto, InvitationMessage from .messages.problem_report import OOBProblemReport from .messages.reuse import HandshakeReuse from .messages.reuse_accept import HandshakeReuseAccept from .messages.service import Service as ServiceMessage from .models.invitation import InvitationRecord +from .models.oob_record import OobRecord LOGGER = logging.getLogger(__name__) REUSE_WEBHOOK_TOPIC = "acapy::webhook::connection_reuse" @@ -159,11 +146,20 @@ async def create_invitation( "Cannot store metadata on public invitations" ) + if attachments and multi_use: + raise OutOfBandManagerError( + "Cannot create multi use invitation with attachments" + ) + + invitation_message_id = str(uuid.uuid4()) + message_attachments = [] for atch in attachments or []: a_type = atch.get("type") a_id = atch.get("id") + message = None + if a_type == "credential-offer": try: async with self.profile.session() as session: @@ -171,22 +167,15 @@ async def create_invitation( session, a_id, ) - message_attachments.append( - InvitationMessage.wrap_message( - cred_ex_rec.credential_offer_dict.serialize() - ) - ) + message = cred_ex_rec.credential_offer_dict.serialize() + except StorageNotFoundError: async with self.profile.session() as session: cred_ex_rec = await V20CredExRecord.retrieve_by_id( session, a_id, ) - message_attachments.append( - InvitationMessage.wrap_message( - cred_ex_rec.cred_offer.serialize() - ) - ) + message = cred_ex_rec.cred_offer.serialize() elif a_type == "present-proof": try: async with self.profile.session() as session: @@ -194,25 +183,24 @@ async def create_invitation( session, a_id, ) - message_attachments.append( - InvitationMessage.wrap_message( - pres_ex_rec.presentation_request_dict.serialize() - ) - ) + message = pres_ex_rec.presentation_request_dict.serialize() except StorageNotFoundError: async with self.profile.session() as session: pres_ex_rec = await V20PresExRecord.retrieve_by_id( session, a_id, ) - message_attachments.append( - InvitationMessage.wrap_message( - pres_ex_rec.pres_request.serialize() - ) - ) + message = pres_ex_rec.pres_request.serialize() else: raise OutOfBandManagerError(f"Unknown attachment type: {a_type}") + # Assign pthid to the attached message + message["~thread"] = { + **message.get("~thread", {}), + "pthid": invitation_message_id, + } + message_attachments.append(InvitationMessage.wrap_message(message)) + handshake_protocols = [ DIDCommPrefix.qualify_current(hsp.name) for hsp in hs_protos or [] ] or None @@ -220,6 +208,9 @@ async def create_invitation( hs_protos[0].name if hs_protos and len(hs_protos) >= 1 else None ) + our_recipient_key = None + conn_rec = None + if public: if not self.profile.settings.get("public_invites"): raise OutOfBandManagerError("Public invitations are not enabled") @@ -233,6 +224,7 @@ async def create_invitation( ) invi_msg = InvitationMessage( # create invitation message + _id=invitation_message_id, label=my_label or self.profile.settings.get("default_label"), handshake_protocols=handshake_protocols, requests_attach=message_attachments, @@ -242,66 +234,76 @@ async def create_invitation( public_did.verkey, keylist_updates ) + our_recipient_key = public_did.verkey + endpoint, *_ = await self.resolve_invitation(public_did.did) invi_url = invi_msg.to_url(endpoint) - conn_rec = ConnRecord( # create connection record - invitation_key=public_did.verkey, - invitation_msg_id=invi_msg._id, - their_role=ConnRecord.Role.REQUESTER.rfc23, - state=ConnRecord.State.INVITATION.rfc23, - accept=ConnRecord.ACCEPT_AUTO if accept else ConnRecord.ACCEPT_MANUAL, - alias=alias, - connection_protocol=connection_protocol, - ) - - async with self.profile.session() as session: - await conn_rec.save(session, reason="Created new invitation") - await conn_rec.attach_invitation(session, invi_msg) - if multitenant_mgr and wallet_id: # add mapping for multitenant relay await multitenant_mgr.add_key( wallet_id, public_did.verkey, skip_if_exists=True ) - else: - invitation_mode = ( - ConnRecord.INVITATION_MODE_MULTI - if multi_use - else ConnRecord.INVITATION_MODE_ONCE - ) + # Only create connection record if hanshake_protocols is defined + if handshake_protocols: + conn_rec = ConnRecord( # create connection record + invitation_key=public_did.verkey, + invitation_msg_id=invi_msg._id, + their_role=ConnRecord.Role.REQUESTER.rfc23, + state=ConnRecord.State.INVITATION.rfc23, + accept=ConnRecord.ACCEPT_AUTO + if accept + else ConnRecord.ACCEPT_MANUAL, + alias=alias, + connection_protocol=connection_protocol, + ) + + async with self.profile.session() as session: + await conn_rec.save(session, reason="Created new invitation") + await conn_rec.attach_invitation(session, invi_msg) + else: if not my_endpoint: my_endpoint = self.profile.settings.get("default_endpoint") - # Create and store new invitation key - + # Create and store new key for exchange async with self.profile.session() as session: wallet = session.inject(BaseWallet) connection_key = await wallet.create_signing_key(KeyType.ED25519) keylist_updates = await mediation_mgr.add_key( connection_key.verkey, keylist_updates ) + + our_recipient_key = connection_key.verkey # Add mapping for multitenant relay if multitenant_mgr and wallet_id: await multitenant_mgr.add_key(wallet_id, connection_key.verkey) # Initializing InvitationMessage here to include # invitation_msg_id in webhook poyload - invi_msg = InvitationMessage() - # Create connection record - conn_rec = ConnRecord( - invitation_key=connection_key.verkey, - their_role=ConnRecord.Role.REQUESTER.rfc23, - state=ConnRecord.State.INVITATION.rfc23, - accept=ConnRecord.ACCEPT_AUTO if accept else ConnRecord.ACCEPT_MANUAL, - invitation_mode=invitation_mode, - alias=alias, - connection_protocol=connection_protocol, - invitation_msg_id=invi_msg._id, - ) + invi_msg = InvitationMessage(_id=invitation_message_id) - async with self.profile.session() as session: - await conn_rec.save(session, reason="Created new connection") + if handshake_protocols: + invitation_mode = ( + ConnRecord.INVITATION_MODE_MULTI + if multi_use + else ConnRecord.INVITATION_MODE_ONCE + ) + # Create connection record + conn_rec = ConnRecord( + invitation_key=connection_key.verkey, + their_role=ConnRecord.Role.REQUESTER.rfc23, + state=ConnRecord.State.INVITATION.rfc23, + accept=ConnRecord.ACCEPT_AUTO + if accept + else ConnRecord.ACCEPT_MANUAL, + invitation_mode=invitation_mode, + alias=alias, + connection_protocol=connection_protocol, + invitation_msg_id=invi_msg._id, + ) + + async with self.profile.session() as session: + await conn_rec.save(session, reason="Created new connection") routing_keys = [] # The base wallet can act as a mediator for all tenants @@ -364,16 +366,28 @@ async def create_invitation( invi_url = invi_msg.to_url() # Update connection record + if conn_rec: + async with self.profile.session() as session: + await conn_rec.attach_invitation(session, invi_msg) - async with self.profile.session() as session: - await conn_rec.attach_invitation(session, invi_msg) + if metadata: + for key, value in metadata.items(): + await conn_rec.metadata_set(session, key, value) - if metadata: - async with self.profile.session() as session: - for key, value in metadata.items(): - await conn_rec.metadata_set(session, key, value) + oob_record = OobRecord( + role=OobRecord.ROLE_SENDER, + state=OobRecord.STATE_AWAIT_RESPONSE, + connection_id=conn_rec.connection_id if conn_rec else None, + invi_msg_id=invi_msg._id, + invitation=invi_msg, + our_recipient_key=our_recipient_key, + ) + + async with self.profile.session() as session: + await oob_record.save(session, reason="Created new oob invitation") return InvitationRecord( # for return via admin API, not storage + oob_id=oob_record.oob_id, state=InvitationRecord.STATE_INITIAL, invi_msg_id=invi_msg._id, invitation=invi_msg, @@ -384,10 +398,10 @@ async def receive_invitation( self, invitation: InvitationMessage, use_existing_connection: bool = True, - auto_accept: bool = None, - alias: str = None, - mediation_id: str = None, - ) -> ConnRecord: + auto_accept: Optional[bool] = None, + alias: Optional[str] = None, + mediation_id: Optional[str] = None, + ) -> OobRecord: """ Receive an out of band invitation message. @@ -416,558 +430,371 @@ async def receive_invitation( raise OutOfBandManagerError( "Invitation must specify handshake_protocols, requests_attach, or both" ) + # Get the single service item oob_service_item = invitation.services[0] - if isinstance(oob_service_item, ServiceMessage): - service = oob_service_item - public_did = None - else: - # If it's in the did format, we need to convert to a full service block - # An existing connection can only be reused based on a public DID - # in an out-of-band message (RFC 0434). - service_did = oob_service_item + # Get the DID public did, if any + public_did = None + if isinstance(oob_service_item, str): + public_did = oob_service_item.split(":")[-1] - # TODO: resolve_invitation should resolve key_info objects - # or something else that includes the key type. We now assume - # ED25519 keys - endpoint, recipient_keys, routing_keys = await self.resolve_invitation( - service_did - ) - public_did = service_did.split(":")[-1] - service = ServiceMessage.deserialize( - { - "id": "#inline", - "type": "did-communication", - "recipientKeys": [ - DIDKey.from_public_key_b58(key, KeyType.ED25519).did - for key in recipient_keys - ], - "routingKeys": [ - DIDKey.from_public_key_b58(key, KeyType.ED25519).did - for key in routing_keys - ], - "serviceEndpoint": endpoint, - } - ) + conn_rec = None - unq_handshake_protos = [ - HSProto.get(hsp) - for hsp in dict.fromkeys( - [ - DIDCommPrefix.unqualify(proto) - for proto in invitation.handshake_protocols - ] + # Find existing connection - only if started by an invitation with Public DID + # and use_existing_connection is true + if ( + public_did is not None and use_existing_connection + ): # invite has public DID: seek existing connection + LOGGER.debug( + f"Trying to find existing connection for oob invitation with did {public_did}" ) - ] - - # Reuse Connection - only if started by an invitation with Public DID - conn_rec = None - if public_did is not None: # invite has public DID: seek existing connection async with self._profile.session() as session: conn_rec = await ConnRecord.find_existing_connection( session=session, their_public_did=public_did ) - if conn_rec is not None: - num_included_protocols = len(unq_handshake_protos) - num_included_req_attachments = len(invitation.requests_attach) - # With handshake protocol, request attachment; use existing connection - if ( - num_included_protocols >= 1 - and num_included_req_attachments == 0 - and use_existing_connection - ): - await self.create_handshake_reuse_message( - invi_msg=invitation, - conn_record=conn_rec, - ) - try: - await asyncio.wait_for( - self.check_reuse_msg_state( - conn_rec=conn_rec, - ), - 15, - ) - async with self.profile.session() as session: - await conn_rec.metadata_delete( - session=session, key="reuse_msg_id" - ) - - msg_state = await conn_rec.metadata_get( - session, "reuse_msg_state" - ) - if msg_state == "not_accepted": - conn_rec = None - else: - async with self.profile.session() as session: - await conn_rec.metadata_delete( - session=session, key="reuse_msg_state" - ) - # refetch connection for accurate state after handshake - conn_rec = await ConnRecord.retrieve_by_id( - session=session, record_id=conn_rec.connection_id - ) - except asyncio.TimeoutError: - # If no reuse_accepted or problem_report message was received within - # the 15s timeout then a new connection to be created - async with self.profile.session() as session: - sent_reuse_msg_id = await conn_rec.metadata_get( - session=session, key="reuse_msg_id" - ) - await conn_rec.metadata_delete( - session=session, key="reuse_msg_id" - ) - await conn_rec.metadata_delete( - session=session, key="reuse_msg_state" - ) - conn_rec.state = ConnRecord.State.ABANDONED.rfc160 - await conn_rec.save( - session, reason="No HandshakeReuseAccept message received" - ) - # Emit webhook - await self.profile.notify( - REUSE_ACCEPTED_WEBHOOK_TOPIC, - { - "thread_id": sent_reuse_msg_id, - "connection_id": conn_rec.connection_id, - "state": "rejected", - "comment": ( - "No HandshakeReuseAccept message received, " - f"connection {conn_rec.connection_id} ", - f"and invitation {invitation._id}", - ), - }, - ) - conn_rec = None - # Inverse of the following cases - # Handshake_Protocol not included - # Request_Attachment included - # Use_Existing_Connection Yes - # Handshake_Protocol included - # Request_Attachment included - # Use_Existing_Connection Yes - elif not ( - ( - num_included_protocols == 0 - and num_included_req_attachments >= 1 - and use_existing_connection - ) - or ( - num_included_protocols >= 1 - and num_included_req_attachments >= 1 - and use_existing_connection - ) - ): - conn_rec = None - if conn_rec is None: - if not unq_handshake_protos: - raise OutOfBandManagerError( - "No existing connection exists and handshake_protocol is missing" - ) - # Create a new connection - for proto in unq_handshake_protos: - if proto is HSProto.RFC23: - didx_mgr = DIDXManager(self.profile) - conn_rec = await didx_mgr.receive_invitation( - invitation=invitation, - their_public_did=public_did, - auto_accept=auto_accept, - alias=alias, - mediation_id=mediation_id, - ) - elif proto is HSProto.RFC160: - service.recipient_keys = [ - DIDKey.from_did(key).public_key_b58 - for key in service.recipient_keys or [] - ] - service.routing_keys = [ - DIDKey.from_did(key).public_key_b58 - for key in service.routing_keys - ] or [] - connection_invitation = ConnectionInvitation.deserialize( - { - "@id": invitation._id, - "@type": DIDCommPrefix.qualify_current(proto.name), - "label": invitation.label, - "recipientKeys": service.recipient_keys, - "serviceEndpoint": service.service_endpoint, - "routingKeys": service.routing_keys, - } - ) - conn_mgr = ConnectionManager(self.profile) - conn_rec = await conn_mgr.receive_invitation( - invitation=connection_invitation, - their_public_did=public_did, - auto_accept=auto_accept, - alias=alias, - mediation_id=mediation_id, - ) - if conn_rec is not None: - break - - # Request Attach - if len(invitation.requests_attach) >= 1 and conn_rec is not None: - req_attach = invitation.requests_attach[0] - if isinstance(req_attach, AttachDecorator): - if req_attach.data is not None: - unq_req_attach_type = DIDCommPrefix.unqualify( - req_attach.content["@type"] - ) - if unq_req_attach_type == PRESENTATION_REQUEST: - await self._process_pres_request_v1( - req_attach=req_attach, - service=service, - conn_rec=conn_rec, - trace=(invitation._trace is not None), - ) - elif unq_req_attach_type == PRES_20_REQUEST: - await self._process_pres_request_v2( - req_attach=req_attach, - service=service, - conn_rec=conn_rec, - trace=(invitation._trace is not None), - ) - elif unq_req_attach_type == CREDENTIAL_OFFER: - if auto_accept or self.profile.settings.get( - "debug.auto_accept_invites" - ): - try: - conn_rec = await asyncio.wait_for( - self.conn_rec_is_active(conn_rec.connection_id), - 7, - ) - except asyncio.TimeoutError: - LOGGER.warning( - "Connection not ready to receive credential, " - f"For connection_id:{conn_rec.connection_id} and " - f"invitation_msg_id {invitation._id}", - ) - await self._process_cred_offer_v1( - req_attach=req_attach, - conn_rec=conn_rec, - trace=(invitation._trace is not None), - ) - elif unq_req_attach_type == CRED_20_OFFER: - if auto_accept or self.profile.settings.get( - "debug.auto_accept_invites" - ): - try: - conn_rec = await asyncio.wait_for( - self.conn_rec_is_active(conn_rec.connection_id), - 7, - ) - except asyncio.TimeoutError: - LOGGER.warning( - "Connection not ready to receive credential, " - f"For connection_id:{conn_rec.connection_id} and " - f"invitation_msg_id {invitation._id}", - ) - await self._process_cred_offer_v2( - req_attach=req_attach, - conn_rec=conn_rec, - trace=(invitation._trace is not None), - ) - else: - raise OutOfBandManagerError( - ( - "Unsupported requests~attach type " - f"{req_attach.content['@type']}: must unqualify to" - f"{PRESENTATION_REQUEST} or {PRES_20_REQUEST}" - f"{CREDENTIAL_OFFER} or {CRED_20_OFFER}" - ) - ) - else: - raise OutOfBandManagerError("requests~attach is not properly formatted") + oob_record = OobRecord( + role=OobRecord.ROLE_RECEIVER, + invi_msg_id=invitation._id, + invitation=invitation, + state=OobRecord.STATE_INITIAL, + connection_id=conn_rec.connection_id if conn_rec else None, + ) - return conn_rec + # Save record + async with self.profile.session() as session: + await oob_record.save(session) - async def _process_pres_request_v1( - self, - req_attach: AttachDecorator, - service: ServiceMessage, - conn_rec: ConnRecord, - trace: bool, - ): - """ - Create exchange for v1 pres request attachment, auto-present if configured. + # Try to reuse the connection. If not accepted sets the conn_rec to None + if conn_rec and not invitation.requests_attach: + oob_record = await self._handle_hanshake_reuse(oob_record, conn_rec) + conn_rec = None - Args: - req_attach: request attachment on invitation - service: service message from invitation - conn_rec: connection record - trace: trace setting for presentation exchange record - """ - pres_mgr = PresentationManager(self.profile) - pres_request_msg = req_attach.content - indy_proof_request = json.loads( - b64_to_bytes( - pres_request_msg["request_presentations~attach"][0]["data"]["base64"] + LOGGER.warning( + f"Connection reuse request finished with state {oob_record.state}" ) - ) - oob_invi_service = service.serialize() - pres_request_msg["~service"] = { - "recipientKeys": oob_invi_service.get("recipientKeys"), - "routingKeys": oob_invi_service.get("routingKeys"), - "serviceEndpoint": oob_invi_service.get("serviceEndpoint"), - } - pres_ex_record = V10PresentationExchange( - connection_id=conn_rec.connection_id, - thread_id=pres_request_msg["@id"], - initiator=V10PresentationExchange.INITIATOR_EXTERNAL, - role=V10PresentationExchange.ROLE_PROVER, - presentation_request=indy_proof_request, - presentation_request_dict=pres_request_msg, - auto_present=self.profile.context.settings.get( - "debug.auto_respond_presentation_request" - ), - trace=trace, - ) - pres_ex_record = await pres_mgr.receive_request(pres_ex_record) - if pres_ex_record.auto_present: - try: - async with self.profile.session() as session: - req_creds = await indy_proof_req_preview2indy_requested_creds( - indy_proof_req=indy_proof_request, - preview=None, - holder=session.inject(IndyHolder), - ) - except ValueError as err: - LOGGER.exception( - "Unable to auto-respond to presentation request " - f"{pres_ex_record.presentation_exchange_id}, prover" - " could still build proof manually" - ) - raise OutOfBandManagerError( - "Cannot auto-respond to presentation request attachment" - ) from err - - (pres_ex_record, presentation_message) = await pres_mgr.create_presentation( - presentation_exchange_record=pres_ex_record, - requested_credentials=req_creds, - comment=( - "auto-presented for proof request nonce={}".format( - indy_proof_request["nonce"] - ) - ), + # If reuse is accepted we can return as the oob exchange is complete + # TODO: update the state to DONE + # TODO: Should we remove the oob record if the reuse has been accepted? + if oob_record.state == OobRecord.STATE_ACCEPTED: + return oob_record + + # Try to create a connection. Either if the reuse failed or we didn't have a connection yet + # Throws an error if connection could not be created + # TODO: do we need to wait for the connection to be active? (see below) + if not conn_rec and invitation.handshake_protocols: + oob_record = await self._perform_handshake( + oob_record=oob_record, + alias=alias, + auto_accept=auto_accept, + mediation_id=mediation_id, ) - responder = self.profile.inject_or(BaseResponder) - if responder: - await responder.send( - message=presentation_message, - target_list=await self.fetch_connection_targets( - connection=conn_rec - ), - ) - else: - raise OutOfBandManagerError( - ( - "Configuration sets auto_present false: cannot " - "respond automatically to presentation requests" + LOGGER.debug( + f"Performed handshake with connection {oob_record.connection_id}" + ) + # re-fetch connection record + async with self.profile.session() as session: + conn_rec = await ConnRecord.retrieve_by_id( + session, oob_record.connection_id ) + + # Handle any attachments + if invitation.requests_attach: + LOGGER.debug( + f"Process attached messages for oob exchange {oob_record.oob_id} (connection_id {oob_record.connection_id})" ) + if oob_record.connection_id: + # Wait for connection to become active. + # FIXME: this should ideally be handled using an event handler. Once the connection is ready + # we start processing the attached messages. For now we use the timeout method + # TODO: what if not ready within the timeout? + await self._wait_for_conn_rec_active(oob_record.connection_id) + if not conn_rec: + # Create and store new key for connectionless exchange + async with self.profile.session() as session: + wallet = session.inject(BaseWallet) + connection_key = await wallet.create_signing_key(KeyType.ED25519) + oob_record.our_recipient_key = connection_key.verkey + await oob_record.save(session) - async def _process_pres_request_v2( - self, - req_attach: AttachDecorator, - service: ServiceMessage, - conn_rec: ConnRecord, - trace: bool, - ): + await self._respond_request_attach(oob_record) + + # TODO: remove record? not possible with connectionless + oob_record.state = OobRecord.STATE_DONE + + return oob_record + + async def _respond_request_attach(self, oob_record: OobRecord): + invitation = oob_record.invitation + req_attach = invitation.requests_attach[0] + + if not isinstance(req_attach, AttachDecorator): + raise OutOfBandManagerError("requests~attach is not properly formatted") + + message_processor = self.profile.inject(OobMessageProcessor) + + LOGGER.warning("Handle inbound oob message") + + # TODO: should we add somethign to get the outcome of processing the message? + # Success will happen through protocol specific webhooks + await message_processor.handle_message( + self.profile, + req_attach.content, + oob_record=oob_record, + ) + + async def _wait_for_reuse_response( + self, oob_id: str, timeout: int = 15 + ) -> OobRecord: """ - Create exchange for v2 pres request attachment, auto-present if configured. + Wait for reuse response message state. Either by receiving a reuse accepted or problem + report. If no answer is received withing the timeout, the state will be set to reuse_not_acceted Args: - req_attach: request attachment on invitation - service: service message from invitation - conn_rec: connection record - trace: trace setting for presentation exchange record + oob_id: Identifier of the oob record + timeout: The timeout in seconds to wait for the reuse state [default=15] + + Returns: + """ - pres_mgr = V20PresManager(self.profile) - pres_request_msg = req_attach.content - oob_invi_service = service.serialize() - pres_request_msg["~service"] = { - "recipientKeys": oob_invi_service.get("recipientKeys"), - "routingKeys": oob_invi_service.get("routingKeys"), - "serviceEndpoint": oob_invi_service.get("serviceEndpoint"), - } - pres_ex_record = V20PresExRecord( - connection_id=conn_rec.connection_id, - thread_id=pres_request_msg["@id"], - initiator=V20PresExRecord.INITIATOR_EXTERNAL, - role=V20PresExRecord.ROLE_PROVER, - pres_request=pres_request_msg, - auto_present=self.profile.context.settings.get( - "debug.auto_respond_presentation_request" - ), - trace=trace, + OOB_REUSE_RESPONSE_STATE = re.compile( + "^acapy::record::out_of_band::(reuse_accepted|reuse_not_accepted)$" ) - pres_ex_record = await pres_mgr.receive_pres_request(pres_ex_record) - if pres_ex_record.auto_present: - (pres_ex_record, pres_msg) = await pres_mgr.create_pres( - pres_ex_record=pres_ex_record, - comment=( - f"auto-presented for proof requests" - f", pres_ex_record: {pres_ex_record.pres_ex_id}" - ), - ) - responder = self.profile.inject_or(BaseResponder) - if responder: - await responder.send( - message=pres_msg, - target_list=await self.fetch_connection_targets( - connection=conn_rec - ), - ) - else: - raise OutOfBandManagerError( - ( - "Configuration set auto_present false: cannot " - "respond automatically to presentation requests" - ) - ) + async def _wait_for_state() -> OobRecord: + event = self.profile.inject(EventBus) + with event.wait_for_event( + self.profile, + OOB_REUSE_RESPONSE_STATE, + lambda event: event.payload.get("oob_id") == oob_id, + ) as await_event: + # After starting the listener first retrieve the record from storage. + # This rules out the scenario where the record was in the desired state + # Before starting the event listener + async with self.profile.session() as session: + oob_record = await OobRecord.retrieve_by_id(session, oob_id) - async def _process_cred_offer_v1( - self, - req_attach: AttachDecorator, - conn_rec: ConnRecord, - trace: bool, - ): - """ - Create exchange for v1 cred offer attachment, auto-offer if configured. + if oob_record.state in [ + OobRecord.STATE_ACCEPTED, + OobRecord.STATE_NOT_ACCEPTED, + ]: + return oob_record - Args: - req_attach: request attachment on invitation - service: service message from invitation - conn_rec: connection record - """ - cred_mgr = V10CredManager(self.profile) - cred_offer_msg = req_attach.content - cred_offer = V10CredOffer.deserialize(cred_offer_msg) - cred_offer.assign_trace_decorator(self.profile.settings, trace) - # receive credential offer - cred_ex_record = await cred_mgr.receive_offer( - message=cred_offer, connection_id=conn_rec.connection_id + LOGGER.debug(f"Wait for oob {oob_id} to receive reuse accepted mesage") + # Wait for oob_record to have reuse_accepted state + event = await await_event + return OobRecord.deserialize(event.payload) + + try: + oob_record = await asyncio.wait_for( + _wait_for_state(), + timeout, + ) + + return oob_record + except asyncio.TimeoutError: + async with self.profile.session() as session: + oob_record = await OobRecord.retrieve_by_id(session, oob_id) + return oob_record + + async def _wait_for_conn_rec_active( + self, connection_id: str, timeout: int = 7 + ) -> Optional[ConnRecord]: + CONNECTION_READY_EVENT = re.compile( + "^acapy::record::connections::(active|completed|response)$" ) - if self.profile.context.settings.get("debug.auto_respond_credential_offer"): - if conn_rec.is_ready: - (_, cred_request_message) = await cred_mgr.create_request( - cred_ex_record=cred_ex_record, - holder_did=conn_rec.my_did, - ) - responder = self.profile.inject_or(BaseResponder) - if responder: - await responder.send( - message=cred_request_message, - target_list=await self.fetch_connection_targets( - connection=conn_rec - ), + + LOGGER.debug(f"Wait for connection {connection_id} to become active") + + async def _wait_for_state() -> ConnRecord: + event = self.profile.inject(EventBus) + with event.wait_for_event( + self.profile, + CONNECTION_READY_EVENT, + lambda event: event.payload.get("connection_id") == connection_id, + ) as await_event: + # After starting the listener first retrieve the record from storage. + # This rules out the scenario where the record was in the desired state + # Before starting the event listener + async with self.profile.session() as session: + conn_record = await ConnRecord.retrieve_by_id( + session, connection_id ) - else: - raise OutOfBandManagerError( - ( - "Configuration sets auto_offer false: cannot " - "respond automatically to credential offers" - ) + if conn_record.is_ready: + return conn_record + + LOGGER.debug(f"Wait for connection {connection_id} to become active") + # Wait for connection record to be in state + event = await await_event + return ConnRecord.deserialize(event.payload) + + try: + return await asyncio.wait_for( + _wait_for_state(), + timeout, ) - async def _process_cred_offer_v2( - self, - req_attach: AttachDecorator, - conn_rec: ConnRecord, - trace: bool, - ): - """ - Create exchange for v1 cred offer attachment, auto-offer if configured. + except asyncio.TimeoutError: + LOGGER.warning(f"Connection for connection_id {connection_id} not ready") + return None - Args: - req_attach: request attachment on invitation - service: service message from invitation - conn_rec: connection record - """ - cred_mgr = V20CredManager(self.profile) - cred_offer_msg = req_attach.content - cred_offer = V20CredOffer.deserialize(cred_offer_msg) - cred_offer.assign_trace_decorator(self.profile.settings, trace) + async def _handle_hanshake_reuse( + self, oob_record: OobRecord, conn_record: ConnRecord + ) -> OobRecord: + # Send handshake reuse + oob_record = await self._create_handshake_reuse_message(oob_record, conn_record) - cred_ex_record = await cred_mgr.receive_offer( - cred_offer_message=cred_offer, connection_id=conn_rec.connection_id + # Wait for the reuse accepted message + oob_record = await self._wait_for_reuse_response(oob_record.oob_id) + LOGGER.debug( + f"Oob reuse for oob id {oob_record.oob_id} with connection {oob_record.connection_id} finished with state {oob_record.state}" ) - if self.profile.context.settings.get("debug.auto_respond_credential_offer"): - if conn_rec.is_ready: - (_, cred_request_message) = await cred_mgr.create_request( - cred_ex_record=cred_ex_record, - holder_did=conn_rec.my_did, - ) - responder = self.profile.inject_or(BaseResponder) - if responder: - await responder.send( - message=cred_request_message, - target_list=await self.fetch_connection_targets( - connection=conn_rec - ), - ) - else: - raise OutOfBandManagerError( - ( - "Configuration sets auto_offer false: cannot " - "respond automatically to credential offers" - ) + + if oob_record.state != OobRecord.STATE_ACCEPTED: + # Remove associated connection id as reuse has ben denied + oob_record.connection_id = None + + # TODO: replace webhook event with new oob webhook event + # Emit webhook if the reuse was not accepted + await self.profile.notify( + REUSE_ACCEPTED_WEBHOOK_TOPIC, + { + "thread_id": oob_record.reuse_msg_id, + "connection_id": conn_record.connection_id, + "state": "rejected", + "comment": ( + "No HandshakeReuseAccept message received, " + f"connection {conn_record.connection_id} ", + f"and invitation {oob_record.invitation._id}", + ), + }, ) - async def check_reuse_msg_state( + async with self.profile.session() as session: + await oob_record.save(session) + + return oob_record + + async def _perform_handshake( self, - conn_rec: ConnRecord, - ): - """ - Check reuse message state from the ConnRecord Metadata. + *, + oob_record: OobRecord, + alias: Optional[str] = None, + auto_accept: Optional[bool] = None, + mediation_id: Optional[str] = None, + ) -> OobRecord: + invitation = oob_record.invitation + + supported_handshake_protocols = [ + HSProto.get(hsp) + for hsp in dict.fromkeys( + [ + DIDCommPrefix.unqualify(proto) + for proto in invitation.handshake_protocols + ] + ) + ] - Args: - conn_rec: The required ConnRecord with updated metadata + # Get the single service item + service = invitation.services[0] + public_did = None + if isinstance(service, str): + # If it's in the did format, we need to convert to a full service block + # An existing connection can only be reused based on a public DID + # in an out-of-band message (RFC 0434). - Returns: + public_did = service.split(":")[-1] - """ - received = False - while not received: - msg_state = None - async with self.profile.session() as session: - msg_state = await conn_rec.metadata_get(session, "reuse_msg_state") - if msg_state != "initial": - received = True - return + # TODO: resolve_invitation should resolve key_info objects + # or something else that includes the key type. We now assume + # ED25519 keys + endpoint, recipient_keys, routing_keys = await self.resolve_invitation( + service + ) + service = ServiceMessage.deserialize( + { + "id": "#inline", + "type": "did-communication", + "recipientKeys": [ + DIDKey.from_public_key_b58(key, KeyType.ED25519).did + for key in recipient_keys + ], + "routingKeys": [ + DIDKey.from_public_key_b58(key, KeyType.ED25519).did + for key in routing_keys + ], + "serviceEndpoint": endpoint, + } + ) - async def conn_rec_is_active(self, conn_rec_id: str) -> ConnRecord: - """ - Return when ConnRecord state becomes active. + LOGGER.debug(f"Creating connection with public did {public_did}") + + conn_record = None + for protocol in supported_handshake_protocols: + # DIDExchange + if protocol is HSProto.RFC23: + didx_mgr = DIDXManager(self.profile) + conn_record = await didx_mgr.receive_invitation( + invitation=invitation, + their_public_did=public_did, + auto_accept=auto_accept, + alias=alias, + mediation_id=mediation_id, + ) + break + # 0160 Connection + elif protocol is HSProto.RFC160: + service.recipient_keys = [ + DIDKey.from_did(key).public_key_b58 + for key in service.recipient_keys or [] + ] + service.routing_keys = [ + DIDKey.from_did(key).public_key_b58 for key in service.routing_keys + ] or [] + connection_invitation = ConnectionInvitation.deserialize( + { + "@id": invitation._id, + "@type": DIDCommPrefix.qualify_current(protocol.name), + "label": invitation.label, + "recipientKeys": service.recipient_keys, + "serviceEndpoint": service.service_endpoint, + "routingKeys": service.routing_keys, + } + ) + conn_mgr = ConnectionManager(self.profile) + conn_record = await conn_mgr.receive_invitation( + invitation=connection_invitation, + their_public_did=public_did, + auto_accept=auto_accept, + alias=alias, + mediation_id=mediation_id, + ) + break - Args: - conn_rec: ConnRecord + if not conn_record: + raise OutOfBandManagerError( + f"Unable to create connection. Could not perform handshake using any of the handshake_protocols (supported {supported_handshake_protocols})" + ) - Returns: - ConnRecord + async with self.profile.session() as session: + oob_record.connection_id = conn_record.connection_id + await oob_record.save(session) - """ - while True: - async with self.profile.session() as session: - conn_rec = await ConnRecord.retrieve_by_id(session, conn_rec_id) - if conn_rec.is_ready: - return conn_rec - await asyncio.sleep(0.5) + return oob_record - async def create_handshake_reuse_message( + async def _create_handshake_reuse_message( self, - invi_msg: InvitationMessage, + oob_record: OobRecord, conn_record: ConnRecord, - ) -> None: + ) -> OobRecord: """ Create and Send a Handshake Reuse message under RFC 0434. Args: - invi_msg: OOB Invitation Message - service: Service block extracted from the OOB invitation + oob_record: OOB Record + conn_record: Connection record associated with the oob record Returns: @@ -977,26 +804,26 @@ async def create_handshake_reuse_message( """ try: - pthid = invi_msg._id reuse_msg = HandshakeReuse() - thid = reuse_msg._id - reuse_msg.assign_thread_id(thid=thid, pthid=pthid) + reuse_msg.assign_thread_id(thid=reuse_msg._id, pthid=oob_record.invi_msg_id) + connection_targets = await self.fetch_connection_targets( connection=conn_record ) - responder = self.profile.inject_or(BaseResponder) - if responder: - await responder.send( - message=reuse_msg, - target_list=connection_targets, - ) - async with self.profile.session() as session: - await conn_record.metadata_set( - session=session, key="reuse_msg_id", value=reuse_msg._id - ) - await conn_record.metadata_set( - session=session, key="reuse_msg_state", value="initial" - ) + + responder = self.profile.inject(BaseResponder) + await responder.send( + message=reuse_msg, + target_list=connection_targets, + ) + + async with self.profile.session() as session: + oob_record.reuse_msg_id = reuse_msg._id + oob_record.state = OobRecord.STATE_AWAIT_RESPONSE + await oob_record.save(session, reason="Storing reuse msg data") + + return oob_record + except Exception as err: raise OutOfBandManagerError( f"Error on creating and sending a handshake reuse message: {err}" @@ -1004,11 +831,11 @@ async def create_handshake_reuse_message( async def delete_stale_connection_by_invitation(self, invi_msg_id: str): """Delete unused connections, using existing an active connection instead.""" - tag_filter = {} - post_filter = {} - tag_filter["invitation_msg_id"] = invi_msg_id - post_filter["invitation_mode"] = "once" - post_filter["state"] = "invitation" + tag_filter = { + "invitation_msg_id": invi_msg_id, + } + post_filter = {"invitation_mode": "once", "state": "invitation"} + async with self.profile.session() as session: conn_records = await ConnRecord.query( session, @@ -1042,20 +869,30 @@ async def receive_reuse_message( """ invi_msg_id = reuse_msg._thread.pthid - reuse_msg_id = reuse_msg._thread.thid - responder = self.profile.inject_or(BaseResponder) + reuse_msg_id = reuse_msg._thread_id + reuse_accept_msg = HandshakeReuseAccept() reuse_accept_msg.assign_thread_id(thid=reuse_msg_id, pthid=invi_msg_id) connection_targets = await self.fetch_connection_targets(connection=conn_rec) - if responder: - await responder.send( - message=reuse_accept_msg, - target_list=connection_targets, - ) + + responder = self.profile.inject(BaseResponder) + # Update ConnRecord's invi_msg_id async with self._profile.session() as session: + oob_record = await OobRecord.retrieve_by_tag_filter( + session, + {"invi_msg_id": invi_msg_id}, + {"state": OobRecord.STATE_AWAIT_RESPONSE}, + ) + + oob_record.state = OobRecord.STATE_ACCEPTED + oob_record.reuse_msg_id = reuse_msg_id + oob_record.connection_id = conn_rec.connection_id + await oob_record.save(session) + conn_rec.invitation_msg_id = invi_msg_id await conn_rec.save(session, reason="Assigning new invitation_msg_id") + # Delete the ConnRecord created; re-use existing connection await self.delete_stale_connection_by_invitation(invi_msg_id) # Emit webhook @@ -1065,12 +902,17 @@ async def receive_reuse_message( "thread_id": reuse_msg_id, "connection_id": conn_rec.connection_id, "comment": ( - f"Connection {conn_rec.connection_id} is being reused ", - f"for invitation {invi_msg_id}", + f"Connection {conn_rec.connection_id} is being reused " + f"for invitation {invi_msg_id}" ), }, ) + await responder.send( + message=reuse_accept_msg, + target_list=connection_targets, + ) + async def receive_reuse_accepted_message( self, reuse_accepted_msg: HandshakeReuseAccept, @@ -1080,7 +922,7 @@ async def receive_reuse_accepted_message( """ Receive and process a HandshakeReuseAccept message under RFC 0434. - Process a `HandshakeReuseAccept` message by updating the ConnRecord metadata + Process a `HandshakeReuseAccept` message by updating the OobRecord state to `accepted`. Args: @@ -1096,19 +938,20 @@ async def receive_reuse_accepted_message( """ invi_msg_id = reuse_accepted_msg._thread.pthid thread_reuse_msg_id = reuse_accepted_msg._thread.thid + try: async with self.profile.session() as session: - conn_reuse_msg_id = await conn_record.metadata_get( - session=session, key="reuse_msg_id" - ) - assert thread_reuse_msg_id == conn_reuse_msg_id - await conn_record.metadata_set( - session=session, key="reuse_msg_state", value="accepted" + oob_record = await OobRecord.retrieve_by_tag_filter( + session, + {"invi_msg_id": invi_msg_id, "reuse_msg_id": thread_reuse_msg_id}, ) + + oob_record.state = OobRecord.STATE_ACCEPTED conn_record.invitation_msg_id = invi_msg_id await conn_record.save( session, reason="Assigning new invitation_msg_id" ) + await oob_record.save(session, reason="Reuse accepted") # Emit webhook await self.profile.notify( REUSE_ACCEPTED_WEBHOOK_TOPIC, @@ -1117,8 +960,8 @@ async def receive_reuse_accepted_message( "connection_id": conn_record.connection_id, "state": "accepted", "comment": ( - f"Connection {conn_record.connection_id} is being reused ", - f"for invitation {invi_msg_id}", + f"Connection {conn_record.connection_id} is being reused " + f"for invitation {invi_msg_id}" ), }, ) @@ -1132,8 +975,8 @@ async def receive_reuse_accepted_message( "state": "rejected", "comment": ( "Unable to process HandshakeReuseAccept message, " - f"connection {conn_record.connection_id} ", - f"and invitation {invi_msg_id}", + f"connection {conn_record.connection_id} " + f"and invitation {invi_msg_id}" ), }, ) @@ -1155,7 +998,7 @@ async def receive_problem_report( """ Receive and process a ProblemReport message from the inviter to invitee. - Process a `ProblemReport` message by updating the ConnRecord metadata + Process a `ProblemReport` message by updating the OobRecord state to `not_accepted`. Args: @@ -1169,17 +1012,16 @@ async def receive_problem_report( HandshakeReuseAccept message """ + invi_msg_id = problem_report._thread.pthid + thread_reuse_msg_id = problem_report._thread.thid try: - invi_msg_id = problem_report._thread.pthid - thread_reuse_msg_id = problem_report._thread.thid async with self.profile.session() as session: - conn_reuse_msg_id = await conn_record.metadata_get( - session=session, key="reuse_msg_id" - ) - assert thread_reuse_msg_id == conn_reuse_msg_id - await conn_record.metadata_set( - session=session, key="reuse_msg_state", value="not_accepted" + oob_record = await OobRecord.retrieve_by_tag_filter( + session, + {"invi_msg_id": invi_msg_id, "reuse_msg_id": thread_reuse_msg_id}, ) + oob_record.state = OobRecord.STATE_NOT_ACCEPTED + await oob_record.save(session) except Exception as e: raise OutOfBandManagerError( ( diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/models/invitation.py b/aries_cloudagent/protocols/out_of_band/v1_0/models/invitation.py index 513fceb9c1..062466f05c 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/models/invitation.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/models/invitation.py @@ -35,6 +35,7 @@ def __init__( invi_msg_id: str = None, invitation: Union[InvitationMessage, Mapping] = None, # invitation message invitation_url: str = None, + oob_id: str = None, public_did: str = None, # backward-compat: BaseRecord.from_storage() trace: bool = False, **kwargs, @@ -46,6 +47,7 @@ def __init__( self.invi_msg_id = invi_msg_id self._invitation = InvitationMessage.serde(invitation) self.invitation_url = invitation_url + self.oob_id = oob_id self.trace = trace @property @@ -69,11 +71,7 @@ def record_value(self) -> dict: return { **{ prop: getattr(self, prop) - for prop in ( - "invitation_url", - "state", - "trace", - ) + for prop in ("invitation_url", "state", "trace", "oob_id") }, **{ prop: getattr(self, f"_{prop}").ser @@ -110,6 +108,11 @@ class Meta: description="Invitation message identifier", example=UUIDFour.EXAMPLE, ) + oob_id = fields.Str( + required=False, + description="Out of band record identifier", + example=UUIDFour.EXAMPLE, + ) invitation = fields.Nested( InvitationMessageSchema(), required=False, diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/models/oob_record.py b/aries_cloudagent/protocols/out_of_band/v1_0/models/oob_record.py new file mode 100644 index 0000000000..8110a4dea9 --- /dev/null +++ b/aries_cloudagent/protocols/out_of_band/v1_0/models/oob_record.py @@ -0,0 +1,172 @@ +"""Record for out of band invitations.""" + +from typing import Any, Mapping, Optional, Union + +from marshmallow import fields + +from aries_cloudagent.messaging.decorators.service_decorator import ( + ServiceDecorator, + ServiceDecoratorSchema, +) + +from .....messaging.models.base_record import BaseExchangeRecord, BaseExchangeSchema +from .....messaging.valid import UUIDFour + +from ..messages.invitation import InvitationMessage, InvitationMessageSchema + + +class OobRecord(BaseExchangeRecord): + """Represents an out of band record.""" + + class Meta: + """OobRecord metadata.""" + + schema_class = "OobRecordSchema" + + RECORD_TYPE = "oob_record" + RECORD_ID_NAME = "oob_id" + RECORD_TOPIC = "oob_record" + TAG_NAMES = { + "invi_msg_id", + "attach_thread_id", + "our_recipient_key", + "connection_id", + "reuse_msg_id", + } + + STATE_INITIAL = "initial" + STATE_AWAIT_RESPONSE = "await_response" + STATE_NOT_ACCEPTED = "reuse_not_accepted" + STATE_ACCEPTED = "reuse_accepted" + STATE_DONE = "done" + + ROLE_SENDER = "sender" + ROLE_RECEIVER = "receiver" + + def __init__( + self, + *, + state: str, + invi_msg_id: str, + role: str, + invitation: Union[InvitationMessage, Mapping[str, Any]], + their_service: Optional[ServiceDecorator] = None, + connection_id: Optional[str] = None, + reuse_msg_id: Optional[str] = None, + oob_id: Optional[str] = None, + attach_thread_id: Optional[str] = None, + our_recipient_key: Optional[str] = None, + trace: bool = False, + **kwargs, + ): + """Initialize a new OobRecord.""" + super().__init__(oob_id, state, trace=trace, **kwargs) + self._id = oob_id + self.state = state + self.invi_msg_id = invi_msg_id + self.role = role + self._invitation = InvitationMessage.serde(invitation) + self.connection_id = connection_id + self.reuse_msg_id = reuse_msg_id + self.their_service = their_service + self.attach_thread_id = attach_thread_id + self.our_recipient_key = our_recipient_key + self.trace = trace + + @property + def oob_id(self) -> str: + """Accessor for the ID associated with this exchange.""" + return self._id + + @property + def invitation(self) -> InvitationMessage: + """Accessor; get deserialized view.""" + return None if self._invitation is None else self._invitation.de + + @invitation.setter + def invitation(self, value): + """Setter; store de/serialized views.""" + self._invitation = InvitationMessage.serde(value) + + @property + def record_value(self) -> dict: + """Accessor for the JSON record value generated for this invitation.""" + return { + **{ + prop: getattr(self, prop) + for prop in ( + "state", + "their_service", + "connection_id", + "role", + ) + }, + **{ + prop: getattr(self, f"_{prop}").ser + for prop in ("invitation",) + if getattr(self, prop) is not None + }, + } + + def __eq__(self, other: Any) -> bool: + """Comparison between records.""" + return super().__eq__(other) + + +class OobRecordSchema(BaseExchangeSchema): + """Schema to allow serialization/deserialization of invitation records.""" + + class Meta: + """OobRecordSchema metadata.""" + + model_class = OobRecord + + oob_id = fields.Str( + required=True, + description="Oob record identifier", + example=UUIDFour.EXAMPLE, + ) + state = fields.Str( + required=True, + description="Out of band message exchange state", + example=OobRecord.STATE_AWAIT_RESPONSE, + ) + invi_msg_id = fields.Str( + required=True, + description="Invitation message identifier", + example=UUIDFour.EXAMPLE, + ) + invitation = fields.Nested( + InvitationMessageSchema(), + required=True, + description="Out of band invitation message", + ) + + their_service = fields.Nested( + ServiceDecoratorSchema(), + required=False, + ) + + connection_id = fields.Str( + description="Connection record identifier", + required=False, + example=UUIDFour.EXAMPLE, + ) + + attach_thread_id = fields.Str( + description="Connection record identifier", + required=False, + example=UUIDFour.EXAMPLE, + ) + + our_recipient_key = fields.Str( + description="Recipient key used for oob invitation", + required=False, + example=UUIDFour.EXAMPLE, + ) + + role = fields.Str( + description="OOB Role", + required=False, + example=OobRecord.ROLE_RECEIVER, + ) diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/routes.py b/aries_cloudagent/protocols/out_of_band/v1_0/routes.py index a4cca26875..f96f859f86 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/routes.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/routes.py @@ -177,7 +177,7 @@ async def invitation_create(request: web.BaseRequest): mediation_id=mediation_id, ) except (StorageNotFoundError, ValidationError, OutOfBandManagerError) as e: - raise web.HTTPBadRequest(reason=str(e)) + raise web.HTTPBadRequest(reason=e.roll_up) return web.json_response(invi_rec.serialize()) diff --git a/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_ack_handler.py b/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_ack_handler.py index 0a1d01232c..bf00d19580 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_ack_handler.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_ack_handler.py @@ -1,5 +1,6 @@ """Presentation ack message handler.""" +from .....core.oob_processor import OobMessageProcessor from .....messaging.base_handler import BaseHandler, HandlerException from .....messaging.request_context import RequestContext from .....messaging.responder import BaseResponder @@ -29,8 +30,19 @@ async def handle(self, context: RequestContext, responder: BaseResponder): context.message.serialize(as_string=True), ) - if not context.connection_ready: - raise HandlerException("No connection established for presentation ack") + # If connection is present it must be ready for use + if context.connection_record and not context.connection_ready: + raise HandlerException("Connection used for presentation ack not ready") + + # Find associated oob record + oob_processor = context.inject(OobMessageProcessor) + oob_record = await oob_processor.find_oob_record_for_inbound_message(context) + + # Either connection or oob context must be present + if not context.connection_record and not oob_record: + raise HandlerException( + "No connection or associated connectionless exchange found for presentation ack" + ) presentation_manager = PresentationManager(context.profile) await presentation_manager.receive_presentation_ack( diff --git a/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_handler.py b/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_handler.py index 40c4d73dc6..fdceec89c2 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_handler.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_handler.py @@ -1,7 +1,8 @@ """Presentation message handler.""" +from .....core.oob_processor import OobMessageProcessor from .....ledger.error import LedgerError -from .....messaging.base_handler import BaseHandler +from .....messaging.base_handler import BaseHandler, HandlerException from .....messaging.models.base import BaseModelError from .....messaging.request_context import RequestContext from .....messaging.responder import BaseResponder @@ -35,10 +36,24 @@ async def handle(self, context: RequestContext, responder: BaseResponder): context.message.serialize(as_string=True), ) + # If connection is present it must be ready for use + if context.connection_record and not context.connection_ready: + raise HandlerException("Connection used for presentation not ready") + + # Find associated oob record. If the proof request was created as an oob attachment + # the presentation exchange record won't have a connection id (yet) + oob_processor = context.inject(OobMessageProcessor) + oob_record = await oob_processor.find_oob_record_for_inbound_message(context) + + # Either connection or oob context must be present + if not context.connection_record and not oob_record: + raise HandlerException( + "No connection or associated connectionless exchange found for presentation" + ) presentation_manager = PresentationManager(profile) presentation_exchange_record = await presentation_manager.receive_presentation( - context.message, context.connection_record + context.message, context.connection_record, oob_record ) # mgr saves record state null if need be and possible r_time = trace_event( diff --git a/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_problem_report_handler.py b/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_problem_report_handler.py index e20d496c01..fb5874145a 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_problem_report_handler.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_problem_report_handler.py @@ -1,6 +1,6 @@ """Presentation problem report message handler.""" -from .....messaging.base_handler import BaseHandler +from .....messaging.base_handler import BaseHandler, HandlerException from .....messaging.request_context import RequestContext from .....messaging.responder import BaseResponder from .....storage.error import StorageError, StorageNotFoundError @@ -26,6 +26,16 @@ async def handle(self, context: RequestContext, responder: BaseResponder): ) assert isinstance(context.message, PresentationProblemReport) + # If connection is present it must be ready for use + if context.connection_record and not context.connection_ready: + raise HandlerException( + "Connection used for presentation problem report not ready" + ) + elif not context.connection_record: + raise HandlerException( + "Connectionless not supported for presentation problem report" + ) + presentation_manager = PresentationManager(context.profile) try: await presentation_manager.receive_problem_report( diff --git a/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_proposal_handler.py b/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_proposal_handler.py index 89708a4368..7977626453 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_proposal_handler.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_proposal_handler.py @@ -37,9 +37,14 @@ async def handle(self, context: RequestContext, responder: BaseResponder): context.message.serialize(as_string=True), ) - if not context.connection_ready: + if not context.connection_record: raise HandlerException( - "No connection established for presentation proposal" + "Connectionless not supported for presentation proposal" + ) + # If connection is present it must be ready for use + elif not context.connection_ready: + raise HandlerException( + "Connection used for presentation proposal not ready" ) presentation_manager = PresentationManager(profile) diff --git a/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_request_handler.py b/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_request_handler.py index 21979940ee..581de65553 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_request_handler.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_request_handler.py @@ -1,5 +1,6 @@ """Presentation request message handler.""" +from .....core.oob_processor import OobMessageProcessor from .....indy.holder import IndyHolder, IndyHolderError from .....indy.models.xform import indy_proof_req_preview2indy_requested_creds from .....ledger.error import LedgerError @@ -40,8 +41,25 @@ async def handle(self, context: RequestContext, responder: BaseResponder): context.message.serialize(as_string=True), ) - if not context.connection_ready: - raise HandlerException("No connection established for presentation request") + # If connection is present it must be ready for use + if context.connection_record and not context.connection_ready: + raise HandlerException("Connection used for presentation request not ready") + + # Find associated oob record + oob_processor = context.inject(OobMessageProcessor) + oob_record = await oob_processor.find_oob_record_for_inbound_message(context) + + # Either connection or oob context must be present + if not context.connection_record and not oob_record: + raise HandlerException( + "No connection or associated connectionless exchange found for presentation request" + ) + + connection_id = ( + context.connection_record.connection_id + if context.connection_record + else None + ) presentation_manager = PresentationManager(profile) @@ -56,11 +74,14 @@ async def handle(self, context: RequestContext, responder: BaseResponder): ) = await V10PresentationExchange.retrieve_by_tag_filter( session, {"thread_id": context.message._thread_id}, - {"connection_id": context.connection_record.connection_id}, + { + "role": V10PresentationExchange.ROLE_PROVER, + "connection_id": connection_id, + }, ) # holder initiated via proposal except StorageNotFoundError: # verifier sent this request free of any proposal presentation_exchange_record = V10PresentationExchange( - connection_id=context.connection_record.connection_id, + connection_id=connection_id, thread_id=context.message._thread_id, initiator=V10PresentationExchange.INITIATOR_EXTERNAL, role=V10PresentationExchange.ROLE_PROVER, diff --git a/aries_cloudagent/protocols/present_proof/v1_0/manager.py b/aries_cloudagent/protocols/present_proof/v1_0/manager.py index 10354d3d29..5b5cf4a909 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/manager.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/manager.py @@ -2,14 +2,15 @@ import json import logging +from typing import Optional +from ...out_of_band.v1_0.models.oob_record import OobRecord from ....connections.models.conn_record import ConnRecord from ....core.error import BaseError from ....core.profile import Profile from ....indy.verifier import IndyVerifier from ....messaging.decorators.attach_decorator import AttachDecorator from ....messaging.responder import BaseResponder -from ....storage.error import StorageNotFoundError from ..indy.pres_exch_handler import IndyPresExchHandler @@ -276,7 +277,10 @@ async def create_presentation( ], ) - presentation_message._thread = {"thid": presentation_exchange_record.thread_id} + # Assign thid (and optionally pthid) to message + presentation_message.assign_thread_from( + presentation_exchange_record.presentation_request_dict + ) presentation_message.assign_trace_decorator( self._profile.settings, presentation_exchange_record.trace ) @@ -294,7 +298,10 @@ async def create_presentation( return presentation_exchange_record, presentation_message async def receive_presentation( - self, message: Presentation, connection_record: ConnRecord + self, + message: Presentation, + connection_record: Optional[ConnRecord], + oob_record: Optional[OobRecord], ): """ Receive a presentation, from message in context on manager creation. @@ -306,25 +313,27 @@ async def receive_presentation( presentation = message.indy_proof() thread_id = message._thread_id - connection_id_filter = ( - {"connection_id": connection_record.connection_id} - if connection_record is not None - else None - ) + # connection_id is None in the record if this is in response to + # a request~attach from an OOB message. If so, we do not want to filter + # the record by connection_id. + connection_id = None if oob_record else connection_record.connection_id + async with self._profile.session() as session: - try: - ( - presentation_exchange_record - ) = await V10PresentationExchange.retrieve_by_tag_filter( - session, {"thread_id": thread_id}, connection_id_filter - ) - except StorageNotFoundError: - # Proof Request not bound to any connection: requests_attach in OOB msg - ( - presentation_exchange_record - ) = await V10PresentationExchange.retrieve_by_tag_filter( - session, {"thread_id": thread_id}, None + # Find by thread_id and role. Verify connection id later + presentation_exchange_record = ( + await V10PresentationExchange.retrieve_by_tag_filter( + session, + {"thread_id": thread_id}, + { + "role": V10PresentationExchange.ROLE_VERIFIER, + "connection_id": connection_id, + }, ) + ) + + # Save connection id (if it wasn't already present) + if connection_record: + presentation_exchange_record.connection_id = connection_record.connection_id # Check for bait-and-switch in presented attribute values vs. proposal if presentation_exchange_record.presentation_proposal_dict: @@ -442,6 +451,7 @@ async def send_presentation_ack( await responder.send_reply( presentation_ack_message, + # connection_id can be none in case of connectionless connection_id=presentation_exchange_record.connection_id, ) else: @@ -451,7 +461,7 @@ async def send_presentation_ack( ) async def receive_presentation_ack( - self, message: PresentationAck, connection_record: ConnRecord + self, message: PresentationAck, connection_record: Optional[ConnRecord] ): """ Receive a presentation ack, from message in context on manager creation. @@ -460,13 +470,19 @@ async def receive_presentation_ack( presentation exchange record, retrieved and updated """ + connection_id = connection_record.connection_id if connection_record else None + async with self._profile.session() as session: ( presentation_exchange_record ) = await V10PresentationExchange.retrieve_by_tag_filter( session, {"thread_id": message._thread_id}, - {"connection_id": connection_record.connection_id}, + { + # connection_id can be null in connectionless + "connection_id": connection_id, + "role": V10PresentationExchange.ROLE_PROVER, + }, ) presentation_exchange_record.state = ( diff --git a/aries_cloudagent/protocols/present_proof/v1_0/routes.py b/aries_cloudagent/protocols/present_proof/v1_0/routes.py index ddc90d4ef3..985ce700db 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/routes.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/routes.py @@ -496,8 +496,6 @@ async def presentation_exchange_create_request(request: web.BaseRequest): # other party does not care about our false protocol start raise web.HTTPBadRequest(reason=err.roll_up) - await outbound_handler(presentation_request_message, connection_id=None) - trace_event( context.settings, presentation_request_message, diff --git a/aries_cloudagent/transport/inbound/receipt.py b/aries_cloudagent/transport/inbound/receipt.py index 653bbc37d0..f36412458e 100644 --- a/aries_cloudagent/transport/inbound/receipt.py +++ b/aries_cloudagent/transport/inbound/receipt.py @@ -1,6 +1,7 @@ """Classes for representing message receipt details.""" from datetime import datetime +from typing import Optional class MessageReceipt: @@ -25,6 +26,7 @@ def __init__( sender_did: str = None, sender_verkey: str = None, thread_id: str = None, + parent_thread_id: str = None, ): """Initialize the message delivery instance.""" self._connection_id = connection_id @@ -37,6 +39,7 @@ def __init__( self._sender_did = sender_did self._sender_verkey = sender_verkey self._thread_id = thread_id + self._parent_thread_id = parent_thread_id @property def connection_id(self) -> str: @@ -266,6 +269,28 @@ def thread_id(self, thread: str): """ self._thread_id = thread + @property + def parent_thread_id(self) -> Optional[str]: + """ + Accessor for the identifier of the message parent thread. + + Returns: + The delivery parent thread ID + + """ + return self._parent_thread_id + + @parent_thread_id.setter + def parent_thread_id(self, thread: Optional[str]): + """ + Setter for the message parent thread identifier. + + Args: + thread: The new parent thread identifier + + """ + self._parent_thread_id = thread + def __repr__(self) -> str: """ Provide a human readable representation of this object. diff --git a/aries_cloudagent/transport/pack_format.py b/aries_cloudagent/transport/pack_format.py index 2423e47a40..f982748098 100644 --- a/aries_cloudagent/transport/pack_format.py +++ b/aries_cloudagent/transport/pack_format.py @@ -91,6 +91,7 @@ async def parse_message( receipt.thread_id = ( thread_dec and thread_dec.get("thid") or message_dict.get("@id") ) + receipt.parent_thread_id = thread_dec.get("pthid") if thread_dec else None # handle transport decorator transport_dec = message_dict.get("~transport") diff --git a/aries_cloudagent/transport/wire_format.py b/aries_cloudagent/transport/wire_format.py index d03a8eb40a..f70521dd4a 100644 --- a/aries_cloudagent/transport/wire_format.py +++ b/aries_cloudagent/transport/wire_format.py @@ -134,6 +134,7 @@ async def parse_message( receipt.thread_id = ( thread_dec and thread_dec.get("thid") or message_dict.get("@id") ) + receipt.parent_thread_id = thread_dec.get("pthid") if thread_dec else None # handle transport decorator transport_dec = message_dict.get("~transport") From 10b065330acc500336e4b126ead7f3ae38d34104 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sun, 20 Mar 2022 22:22:29 +0100 Subject: [PATCH 086/872] feat: connectionless proof exchange using rest endpoints Signed-off-by: Timo Glastra --- .../protocols/out_of_band/v1_0/manager.py | 2 +- .../v1_0/models/presentation_exchange.py | 4 ++-- .../protocols/present_proof/v1_0/routes.py | 24 ++++++++++++------- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py index 12309e2b97..914015d9e7 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py @@ -463,6 +463,7 @@ async def receive_invitation( ) # Save record + # TODO: I think we can remove this save. Other paths will save the record async with self.profile.session() as session: await oob_record.save(session) @@ -483,7 +484,6 @@ async def receive_invitation( # Try to create a connection. Either if the reuse failed or we didn't have a connection yet # Throws an error if connection could not be created - # TODO: do we need to wait for the connection to be active? (see below) if not conn_rec and invitation.handshake_protocols: oob_record = await self._perform_handshake( oob_record=oob_record, diff --git a/aries_cloudagent/protocols/present_proof/v1_0/models/presentation_exchange.py b/aries_cloudagent/protocols/present_proof/v1_0/models/presentation_exchange.py index bce40f896d..65690e329d 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/models/presentation_exchange.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/models/presentation_exchange.py @@ -2,7 +2,7 @@ import logging -from typing import Any, Mapping, Union +from typing import Any, Mapping, Optional, Union from marshmallow import fields, validate @@ -60,7 +60,7 @@ def __init__( self, *, presentation_exchange_id: str = None, - connection_id: str = None, + connection_id: Optional[str] = None, thread_id: str = None, initiator: str = None, role: str = None, diff --git a/aries_cloudagent/protocols/present_proof/v1_0/routes.py b/aries_cloudagent/protocols/present_proof/v1_0/routes.py index 985ce700db..b5a3930d0f 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/routes.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/routes.py @@ -720,14 +720,20 @@ async def presentation_exchange_send_presentation(request: web.BaseRequest): ) ) - connection_id = pres_ex_record.connection_id - try: - connection_record = await ConnRecord.retrieve_by_id(session, connection_id) - except StorageNotFoundError as err: - raise web.HTTPBadRequest(reason=err.roll_up) from err + # Fetch connection if exchange has record + connection_record = None + if pres_ex_record.connection_id: + try: + connection_record = await ConnRecord.retrieve_by_id( + session, pres_ex_record.connection_id + ) + except StorageNotFoundError as err: + raise web.HTTPBadRequest(reason=err.roll_up) from err - if not connection_record.is_ready: - raise web.HTTPForbidden(reason=f"Connection {connection_id} not ready") + if connection_record and not connection_record.is_ready: + raise web.HTTPForbidden( + reason=f"Connection {connection_record.connection_id} not ready" + ) try: presentation_manager = PresentationManager(profile) @@ -768,7 +774,9 @@ async def presentation_exchange_send_presentation(request: web.BaseRequest): context.settings, trace_msg, ) - await outbound_handler(presentation_message, connection_id=connection_id) + await outbound_handler( + presentation_message, connection_id=pres_ex_record.connection_id + ) trace_event( context.settings, From 1f8a609ba5a1de649c44521ebb892146d13cc153 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 21 Mar 2022 09:20:41 +0100 Subject: [PATCH 087/872] feat: connectionless cred exchange using rest endpoints Signed-off-by: Timo Glastra --- .../v1_0/handlers/credential_offer_handler.py | 6 +- .../v1_0/models/credential_exchange.py | 2 +- .../protocols/issue_credential/v1_0/routes.py | 116 +++++++++++++----- aries_cloudagent/wallet/util.py | 9 ++ 4 files changed, 95 insertions(+), 38 deletions(-) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_offer_handler.py b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_offer_handler.py index ded7722293..987a3feead 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_offer_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_offer_handler.py @@ -1,7 +1,7 @@ """Credential offer message handler.""" -from base58 import b58decode, b58encode +from .....wallet.util import default_did_from_verkey from .....core.oob_processor import OobMessageProcessor from .....indy.holder import IndyHolderError from .....ledger.error import LedgerError @@ -75,9 +75,7 @@ async def handle(self, context: RequestContext, responder: BaseResponder): holder_did = context.connection_record.my_did else: # Transform recipient key into did - holder_did = b58encode(b58decode(oob_record.our_recipient_key)[:16]).decode( - "utf-8" - ) + holder_did = default_did_from_verkey(oob_record.our_recipient_key) # If auto respond is turned on, automatically reply with credential request if cred_ex_record and context.settings.get( diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py b/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py index a91d5c03b6..6ef3a3af61 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py @@ -57,7 +57,7 @@ def __init__( self, *, credential_exchange_id: str = None, - connection_id: str = None, + connection_id: Optional[str] = None, thread_id: str = None, parent_thread_id: str = None, initiator: str = None, diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/routes.py b/aries_cloudagent/protocols/issue_credential/v1_0/routes.py index fd8c7b3f59..381ce0401f 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/routes.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/routes.py @@ -11,6 +11,8 @@ from json.decoder import JSONDecodeError from marshmallow import fields, validate +from ...out_of_band.v1_0.models.oob_record import OobRecord +from ....wallet.util import default_did_from_verkey from ....admin.request_context import AdminRequestContext from ....connections.models.conn_record import ConnRecord from ....core.profile import Profile @@ -972,30 +974,48 @@ async def credential_exchange_send_request(request: web.BaseRequest): cred_ex_record = None connection_record = None - try: - async with profile.session() as session: + + async with profile.session() as session: + try: + cred_ex_record = await V10CredentialExchange.retrieve_by_id( + session, credential_exchange_id + ) + except StorageNotFoundError as err: + raise web.HTTPNotFound(reason=err.roll_up) from err + + # Fetch connection if exchange has record + connection_record = None + if cred_ex_record.connection_id: try: - cred_ex_record = await V10CredentialExchange.retrieve_by_id( - session, credential_exchange_id + connection_record = await ConnRecord.retrieve_by_id( + session, cred_ex_record.connection_id ) - connection_id = cred_ex_record.connection_id except StorageNotFoundError as err: - raise web.HTTPNotFound(reason=err.roll_up) from err + raise web.HTTPBadRequest(reason=err.roll_up) from err - connection_record = await ConnRecord.retrieve_by_id( + if connection_record and not connection_record.is_ready: + raise web.HTTPForbidden( + reason=f"Connection {connection_record.connection_id} not ready" + ) + + if connection_record: + holder_did = connection_record.my_did + else: + # Need to get the holder DID from the out of band record + async with profile.session() as session: + oob_record = await OobRecord.retrieve_by_tag_filter( session, - connection_id, + {"invi_msg_id": cred_ex_record.credential_offer_dict._thread.pthid}, ) - if not connection_record.is_ready: - raise web.HTTPForbidden(reason=f"Connection {connection_id} not ready") + # Transform recipient key into did + holder_did = default_did_from_verkey(oob_record.our_recipient_key) + try: credential_manager = CredentialManager(profile) ( cred_ex_record, credential_request_message, - ) = await credential_manager.create_request( - cred_ex_record, connection_record.my_did - ) + ) = await credential_manager.create_request(cred_ex_record, holder_did) result = cred_ex_record.serialize() @@ -1017,7 +1037,9 @@ async def credential_exchange_send_request(request: web.BaseRequest): outbound_handler, ) - await outbound_handler(credential_request_message, connection_id=connection_id) + await outbound_handler( + credential_request_message, connection_id=cred_ex_record.connection_id + ) trace_event( context.settings, @@ -1060,20 +1082,31 @@ async def credential_exchange_issue(request: web.BaseRequest): cred_ex_record = None connection_record = None - try: - async with profile.session() as session: + + async with profile.session() as session: + try: + cred_ex_record = await V10CredentialExchange.retrieve_by_id( + session, credential_exchange_id + ) + except StorageNotFoundError as err: + raise web.HTTPNotFound(reason=err.roll_up) from err + + # Fetch connection if exchange has record + connection_record = None + if cred_ex_record.connection_id: try: - cred_ex_record = await V10CredentialExchange.retrieve_by_id( - session, credential_exchange_id + connection_record = await ConnRecord.retrieve_by_id( + session, cred_ex_record.connection_id ) except StorageNotFoundError as err: - raise web.HTTPNotFound(reason=err.roll_up) from err - connection_id = cred_ex_record.connection_id + raise web.HTTPBadRequest(reason=err.roll_up) from err - connection_record = await ConnRecord.retrieve_by_id(session, connection_id) - if not connection_record.is_ready: - raise web.HTTPForbidden(reason=f"Connection {connection_id} not ready") + if connection_record and not connection_record.is_ready: + raise web.HTTPForbidden( + reason=f"Connection {connection_record.connection_id} not ready" + ) + try: credential_manager = CredentialManager(profile) ( cred_ex_record, @@ -1100,7 +1133,9 @@ async def credential_exchange_issue(request: web.BaseRequest): outbound_handler, ) - await outbound_handler(credential_issue_message, connection_id=connection_id) + await outbound_handler( + credential_issue_message, connection_id=cred_ex_record.connection_id + ) trace_event( context.settings, @@ -1146,20 +1181,30 @@ async def credential_exchange_store(request: web.BaseRequest): cred_ex_record = None connection_record = None - try: - async with profile.session() as session: + + async with profile.session() as session: + try: + cred_ex_record = await V10CredentialExchange.retrieve_by_id( + session, credential_exchange_id + ) + except StorageNotFoundError as err: + raise web.HTTPNotFound(reason=err.roll_up) from err + + # Fetch connection if exchange has record + if cred_ex_record.connection_id: try: - cred_ex_record = await V10CredentialExchange.retrieve_by_id( - session, credential_exchange_id + connection_record = await ConnRecord.retrieve_by_id( + session, cred_ex_record.connection_id ) except StorageNotFoundError as err: - raise web.HTTPNotFound(reason=err.roll_up) from err + raise web.HTTPBadRequest(reason=err.roll_up) from err - connection_id = cred_ex_record.connection_id - connection_record = await ConnRecord.retrieve_by_id(session, connection_id) - if not connection_record.is_ready: - raise web.HTTPForbidden(reason=f"Connection {connection_id} not ready") + if connection_record and not connection_record.is_ready: + raise web.HTTPForbidden( + reason=f"Connection {connection_record.connection_id} not ready" + ) + try: credential_manager = CredentialManager(profile) cred_ex_record = await credential_manager.store_credential( cred_ex_record, @@ -1234,6 +1279,11 @@ async def credential_exchange_problem_report(request: web.BaseRequest): cred_ex_record = await V10CredentialExchange.retrieve_by_id( session, credential_exchange_id ) + + if not cred_ex_record.connection_id: + raise web.HTTPBadRequest( + reason="No connection associated with credential exchange." + ) report = problem_report_for_record(cred_ex_record, description) await cred_ex_record.save_error_state( session, diff --git a/aries_cloudagent/wallet/util.py b/aries_cloudagent/wallet/util.py index 2ec43fde9a..f87e6a53da 100644 --- a/aries_cloudagent/wallet/util.py +++ b/aries_cloudagent/wallet/util.py @@ -86,6 +86,15 @@ def full_verkey(did: str, abbr_verkey: str) -> str: ) +def default_did_from_verkey(verkey: str) -> str: + """Given a verkey, return the default indy did. + + By default the did is the first 16 bytes of the verkey. + """ + did = bytes_to_b58(b58_to_bytes(verkey)[:16]) + return did + + def abbr_verkey(full_verkey: str, did: str = None) -> str: """Given a full verkey and DID, return the abbreviated verkey.""" did_len = len(b58_to_bytes(did.split(":")[-1])) if did else 16 From 12b197f5ece53faf9210a8359473511edb9126fd Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 21 Mar 2022 10:18:04 +0100 Subject: [PATCH 088/872] fix: support aip1 style connectionless proof Signed-off-by: Timo Glastra --- .../v1_0/handlers/presentation_handler.py | 12 +----- .../protocols/present_proof/v1_0/manager.py | 39 ++++++++++++++----- .../protocols/present_proof/v1_0/routes.py | 4 +- 3 files changed, 34 insertions(+), 21 deletions(-) diff --git a/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_handler.py b/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_handler.py index fdceec89c2..d2082e3ee0 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_handler.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_handler.py @@ -40,20 +40,10 @@ async def handle(self, context: RequestContext, responder: BaseResponder): if context.connection_record and not context.connection_ready: raise HandlerException("Connection used for presentation not ready") - # Find associated oob record. If the proof request was created as an oob attachment - # the presentation exchange record won't have a connection id (yet) - oob_processor = context.inject(OobMessageProcessor) - oob_record = await oob_processor.find_oob_record_for_inbound_message(context) - - # Either connection or oob context must be present - if not context.connection_record and not oob_record: - raise HandlerException( - "No connection or associated connectionless exchange found for presentation" - ) presentation_manager = PresentationManager(profile) presentation_exchange_record = await presentation_manager.receive_presentation( - context.message, context.connection_record, oob_record + context.message, context.connection_record ) # mgr saves record state null if need be and possible r_time = trace_event( diff --git a/aries_cloudagent/protocols/present_proof/v1_0/manager.py b/aries_cloudagent/protocols/present_proof/v1_0/manager.py index 5b5cf4a909..446e4faa76 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/manager.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/manager.py @@ -11,7 +11,7 @@ from ....indy.verifier import IndyVerifier from ....messaging.decorators.attach_decorator import AttachDecorator from ....messaging.responder import BaseResponder - +from ....storage.error import StorageNotFoundError from ..indy.pres_exch_handler import IndyPresExchHandler from .messages.presentation_ack import PresentationAck @@ -298,10 +298,7 @@ async def create_presentation( return presentation_exchange_record, presentation_message async def receive_presentation( - self, - message: Presentation, - connection_record: Optional[ConnRecord], - oob_record: Optional[OobRecord], + self, message: Presentation, connection_record: Optional[ConnRecord] ): """ Receive a presentation, from message in context on manager creation. @@ -313,10 +310,11 @@ async def receive_presentation( presentation = message.indy_proof() thread_id = message._thread_id - # connection_id is None in the record if this is in response to - # a request~attach from an OOB message. If so, we do not want to filter - # the record by connection_id. - connection_id = None if oob_record else connection_record.connection_id + # Normally we only set the connection_id to None if an oob record is present + # But present proof supports the old-style AIP-1 connectionless exchange that + # bypasses the oob record. So we can't verify if an oob record is associated with the + # exchange because it is possible that there is None + connection_id = connection_record.connection_id if connection_record else None async with self._profile.session() as session: # Find by thread_id and role. Verify connection id later @@ -440,6 +438,29 @@ async def send_presentation_ack( """ responder = self._profile.inject_or(BaseResponder) + if not presentation_exchange_record.connection_id: + # Find associated oob record. If this presentation exchange is created + # without oob (aip1 style connectionless) we can't send a presentation ack + # because we don't have their service + try: + pthid = ( + presentation_exchange_record.presentation_request_dict._thread.pthid + ) + except AttributeError: + raise PresentationManagerError( + "Unable to send connectionless presentation ack without associated oob record" + ) + try: + async with self._profile.session() as session: + await OobRecord.retrieve_by_tag_filter( + session, + {"invi_msg_id": pthid}, + ) + except StorageNotFoundError: + raise PresentationManagerError( + "Unable to send connectionless presentation ack without associated oob record" + ) + if responder: presentation_ack_message = PresentationAck() presentation_ack_message._thread = { diff --git a/aries_cloudagent/protocols/present_proof/v1_0/routes.py b/aries_cloudagent/protocols/present_proof/v1_0/routes.py index b5a3930d0f..5103a27ea6 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/routes.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/routes.py @@ -36,7 +36,7 @@ from ....wallet.error import WalletNotFoundError from . import problem_report_for_record, report_problem -from .manager import PresentationManager +from .manager import PresentationManager, PresentationManagerError from .message_types import ATTACH_DECO_IDS, PRESENTATION_REQUEST, SPEC_URI from .messages.presentation_problem_report import ProblemReportReason from .messages.presentation_proposal import PresentationProposal @@ -846,6 +846,8 @@ async def presentation_exchange_verify_presentation(request: web.BaseRequest): pres_ex_record, outbound_handler, ) + except PresentationManagerError as err: + return web.HTTPBadRequest(reason=err.roll_up) trace_event( context.settings, From cc27dc8bbad92973e09c7f7e0fade13605a39dfb Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 21 Mar 2022 11:25:16 +0100 Subject: [PATCH 089/872] fix: fixmes and todos Signed-off-by: Timo Glastra --- aries_cloudagent/core/oob_processor.py | 72 ++-------- .../protocols/out_of_band/v1_0/manager.py | 130 +++++++++++++----- .../out_of_band/v1_0/models/oob_record.py | 3 + 3 files changed, 116 insertions(+), 89 deletions(-) diff --git a/aries_cloudagent/core/oob_processor.py b/aries_cloudagent/core/oob_processor.py index 1e126ca5d4..11c3494ad8 100644 --- a/aries_cloudagent/core/oob_processor.py +++ b/aries_cloudagent/core/oob_processor.py @@ -54,19 +54,12 @@ async def find_oob_target_for_outbound_message( their_service = oob_record.their_service their_service = ServiceDecorator.deserialize(their_service) - # FIXME: integrate with mediation - our_service = ServiceDecorator( - recipient_keys=[oob_record.our_recipient_key], - endpoint=profile.settings.get("default_endpoint"), - routing_keys=[], - ) - # Attach ~service decorator so other message can respond message = json.loads(outbound_message.payload) if not message.get("~service"): - message["~service"] = our_service.serialize() + message["~service"] = oob_record.our_service.serialize() - # TODO: state is somewhat done, but we need it for connectionless exchange + # OOB_TODO: state is somewhat done, but we need it for connectionless exchange # if is_first_response: message["~thread"] = { **message.get("~thread", {}), @@ -214,12 +207,18 @@ async def find_oob_record_for_inbound_message( # Verify the sender key is present in their service in our record # If we don't have the sender verkey stored yet we can allow any key - if ( - their_service - # FIXME: does this mean anyone with anoncreds can send a message? - and context.message_receipt.sender_verkey - and context.message_receipt.sender_verkey - not in their_service.recipient_keys + if their_service and ( + # We either want the sender key to be present and present in their_service + ( + context.message_receipt.sender_verkey + and context.message_receipt.sender_verkey + not in their_service.recipient_keys + ) + # Or we don't want the sender or recipient key to be present (in case of oob message handler) + or ( + not context.message_receipt.sender_verkey + and not context.message_receipt.recipient_verkey + ) ): LOGGER.debug( "Inbound message sender verkey does not match stored service on oob record" @@ -228,8 +227,6 @@ async def find_oob_record_for_inbound_message( # If the message has a ~service decorator we save it in the oob record so we can reply to this message if context._message._service: - # TODO: what should we do if the keys don't match? I would say for now we require the complete - # oob exchange to use the same keys oob_record.their_service = context.message._service.serialize() async with context.profile.session() as session: @@ -254,47 +251,8 @@ async def handle_message( connection_id=oob_record.connection_id, receipt=receipt, ) - # Create ~service from the oob service - - if not oob_record.connection_id: - service = oob_record.invitation.services[0] - - if isinstance(service, str): - async with session.inject(BaseLedger) as ledger: - endpoint = await ledger.get_endpoint_for_did(service) - verkey = await ledger.get_key_for_did(service) - - service_decorator = ServiceDecorator( - endpoint=endpoint, - recipient_keys=[verkey], - routing_keys=[], - ) - else: - service_decorator = self._service_decorator_from_service(service) - - oob_record.their_service = service_decorator.serialize() - - oob_record.attach_thread_id = inbound_message.receipt.thread_id - - await oob_record.save(session) self._inbound_message_router(profile, inbound_message, False) - def _get_thread_id(self, message: Dict[str, Any]) -> str: + def get_thread_id(self, message: Dict[str, Any]) -> str: return message.get("~thread", {}).get("thid") or message.get("@id") - - def _service_decorator_from_service(self, service: Service) -> ServiceDecorator: - # Create ~service decorator from the oob service - recipient_keys = [ - DIDKey.from_did(did_key).public_key_b58 - for did_key in service.recipient_keys - ] - routing_keys = [ - DIDKey.from_did(did_key).public_key_b58 for did_key in service.routing_keys - ] - - return ServiceDecorator( - endpoint=service.service_endpoint, - recipient_keys=recipient_keys, - routing_keys=routing_keys, - ) diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py index 914015d9e7..1269d901b0 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py @@ -3,11 +3,12 @@ import asyncio import logging import re -from typing import Mapping, Optional, Sequence +from typing import Mapping, Optional, Sequence, Union import uuid -from aries_cloudagent.core.event_bus import EventBus +from ....messaging.decorators.service_decorator import ServiceDecorator +from ....core.event_bus import EventBus from ....connections.base_manager import BaseConnectionManager from ....connections.models.conn_record import ConnRecord from ....connections.util import mediation_record_if_id @@ -38,6 +39,7 @@ from .messages.service import Service as ServiceMessage from .models.invitation import InvitationRecord from .models.oob_record import OobRecord +from .messages.service import Service LOGGER = logging.getLogger(__name__) REUSE_WEBHOOK_TOPIC = "acapy::webhook::connection_reuse" @@ -209,6 +211,7 @@ async def create_invitation( ) our_recipient_key = None + our_service = None conn_rec = None if public: @@ -261,7 +264,12 @@ async def create_invitation( async with self.profile.session() as session: await conn_rec.save(session, reason="Created new invitation") await conn_rec.attach_invitation(session, invi_msg) - + else: + our_service = ServiceDecorator( + recipient_keys=[our_recipient_key], + endpoint=endpoint, + routing_keys=[], + ) else: if not my_endpoint: my_endpoint = self.profile.settings.get("default_endpoint") @@ -324,19 +332,27 @@ async def create_invitation( my_endpoint = mediation_record.endpoint # Save that this invitation was created with mediation - - async with self.profile.session() as session: - await conn_rec.metadata_set( - session, - MediationManager.METADATA_KEY, - {MediationManager.METADATA_ID: mediation_record.mediation_id}, - ) + if conn_rec: + async with self.profile.session() as session: + await conn_rec.metadata_set( + session, + MediationManager.METADATA_KEY, + { + MediationManager.METADATA_ID: mediation_record.mediation_id + }, + ) if keylist_updates: responder = self.profile.inject_or(BaseResponder) await responder.send( keylist_updates, connection_id=mediation_record.connection_id ) + if not conn_rec: + our_service = ServiceDecorator( + recipient_keys=[our_recipient_key], + endpoint=my_endpoint, + routing_keys=routing_keys, + ) routing_keys = [ key if len(key.split(":")) == 3 @@ -381,6 +397,7 @@ async def create_invitation( invi_msg_id=invi_msg._id, invitation=invi_msg, our_recipient_key=our_recipient_key, + our_service=our_service, ) async with self.profile.session() as session: @@ -462,25 +479,23 @@ async def receive_invitation( connection_id=conn_rec.connection_id if conn_rec else None, ) - # Save record - # TODO: I think we can remove this save. Other paths will save the record - async with self.profile.session() as session: - await oob_record.save(session) + # # Save record + # # OOB_TODO: I think we can remove this save. Other paths will save the record + # async with self.profile.session() as session: + # await oob_record.save(session) # Try to reuse the connection. If not accepted sets the conn_rec to None if conn_rec and not invitation.requests_attach: oob_record = await self._handle_hanshake_reuse(oob_record, conn_rec) - conn_rec = None LOGGER.warning( f"Connection reuse request finished with state {oob_record.state}" ) - # If reuse is accepted we can return as the oob exchange is complete - # TODO: update the state to DONE - # TODO: Should we remove the oob record if the reuse has been accepted? - if oob_record.state == OobRecord.STATE_ACCEPTED: - return oob_record + if oob_record.state != OobRecord.STATE_ACCEPTED: + # Set connection record to None if not accepted + # Will make new connection + conn_rec = None # Try to create a connection. Either if the reuse failed or we didn't have a connection yet # Throws an error if connection could not be created @@ -505,12 +520,19 @@ async def receive_invitation( LOGGER.debug( f"Process attached messages for oob exchange {oob_record.oob_id} (connection_id {oob_record.connection_id})" ) - if oob_record.connection_id: - # Wait for connection to become active. - # FIXME: this should ideally be handled using an event handler. Once the connection is ready - # we start processing the attached messages. For now we use the timeout method - # TODO: what if not ready within the timeout? - await self._wait_for_conn_rec_active(oob_record.connection_id) + # FIXME: this should ideally be handled using an event handler. Once the connection is ready + # we start processing the attached messages. For now we use the timeout method + if ( + conn_rec + and not conn_rec.is_ready + and not await self._wait_for_conn_rec_active(conn_rec.connection_id) + ): + raise OutOfBandManagerError( + "Connection not ready to process attach message" + f"For connection_id: {oob_record.connection_id} and " + f"invitation_msg_id {invitation._id}", + ) + if not conn_rec: # Create and store new key for connectionless exchange async with self.profile.session() as session: @@ -521,8 +543,13 @@ async def receive_invitation( await self._respond_request_attach(oob_record) - # TODO: remove record? not possible with connectionless - oob_record.state = OobRecord.STATE_DONE + # If a connection record is associated with the oob record we can remove it + # Otherwise we need to keep it around for the connectionless exchange + if conn_rec: + oob_record.state = OobRecord.STATE_DONE + async with self.profile.session() as session: + await oob_record.save(session) + await oob_record.delete_record(session) return oob_record @@ -534,17 +561,56 @@ async def _respond_request_attach(self, oob_record: OobRecord): raise OutOfBandManagerError("requests~attach is not properly formatted") message_processor = self.profile.inject(OobMessageProcessor) + message = req_attach.content + + if not oob_record.connection_id: + service = oob_record.invitation.services[0] + service_decorator = await self._service_decorator_from_service(service) - LOGGER.warning("Handle inbound oob message") + oob_record.their_service = service_decorator.serialize() + + async with self.profile.session() as session: + oob_record.attach_thread_id = message_processor.get_thread_id(message) + await oob_record.save(session) - # TODO: should we add somethign to get the outcome of processing the message? - # Success will happen through protocol specific webhooks await message_processor.handle_message( self.profile, req_attach.content, oob_record=oob_record, ) + async def _service_decorator_from_service( + self, service: Union[Service, str] + ) -> ServiceDecorator: + if isinstance(service, str): + ( + endpoint, + recipient_keys, + routing_keys, + ) = await self.resolve_invitation(service) + + return ServiceDecorator( + endpoint=endpoint, + recipient_keys=recipient_keys, + routing_keys=routing_keys, + ) + else: + # Create ~service decorator from the oob service + recipient_keys = [ + DIDKey.from_did(did_key).public_key_b58 + for did_key in service.recipient_keys + ] + routing_keys = [ + DIDKey.from_did(did_key).public_key_b58 + for did_key in service.routing_keys + ] + + return ServiceDecorator( + endpoint=service.service_endpoint, + recipient_keys=recipient_keys, + routing_keys=routing_keys, + ) + async def _wait_for_reuse_response( self, oob_id: str, timeout: int = 15 ) -> OobRecord: @@ -656,7 +722,7 @@ async def _handle_hanshake_reuse( # Remove associated connection id as reuse has ben denied oob_record.connection_id = None - # TODO: replace webhook event with new oob webhook event + # OOB_TODO: replace webhook event with new oob webhook event # Emit webhook if the reuse was not accepted await self.profile.notify( REUSE_ACCEPTED_WEBHOOK_TOPIC, diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/models/oob_record.py b/aries_cloudagent/protocols/out_of_band/v1_0/models/oob_record.py index 8110a4dea9..03455e37fd 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/models/oob_record.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/models/oob_record.py @@ -56,6 +56,7 @@ def __init__( oob_id: Optional[str] = None, attach_thread_id: Optional[str] = None, our_recipient_key: Optional[str] = None, + our_service: Optional[ServiceDecorator] = None, trace: bool = False, **kwargs, ): @@ -69,6 +70,7 @@ def __init__( self.connection_id = connection_id self.reuse_msg_id = reuse_msg_id self.their_service = their_service + self.our_service = our_service self.attach_thread_id = attach_thread_id self.our_recipient_key = our_recipient_key self.trace = trace @@ -99,6 +101,7 @@ def record_value(self) -> dict: "their_service", "connection_id", "role", + "our_service", ) }, **{ From a8a9338187fced2e10957c844dd3b02d2fd9d321 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 21 Mar 2022 12:14:24 +0100 Subject: [PATCH 090/872] fix: clean finished oob records Signed-off-by: Timo Glastra --- aries_cloudagent/core/oob_processor.py | 61 ++++++++++++++++--- .../protocols/connections/v1_0/manager.py | 6 ++ .../protocols/didexchange/v1_0/manager.py | 11 ++-- .../protocols/out_of_band/v1_0/manager.py | 24 +++++--- .../out_of_band/v1_0/models/oob_record.py | 2 + 5 files changed, 83 insertions(+), 21 deletions(-) diff --git a/aries_cloudagent/core/oob_processor.py b/aries_cloudagent/core/oob_processor.py index 11c3494ad8..d512ed7209 100644 --- a/aries_cloudagent/core/oob_processor.py +++ b/aries_cloudagent/core/oob_processor.py @@ -2,16 +2,18 @@ import json import logging -from typing import Any, Callable, Dict, Optional, cast +from typing import Any, Callable, Dict, List, Optional, cast - -from ..ledger.base import BaseLedger +from ..messaging.agent_message import AgentMessage from ..connections.models.conn_record import ConnRecord from ..connections.models.connection_target import ConnectionTarget -from ..did.did_key import DIDKey from ..messaging.decorators.service_decorator import ServiceDecorator from ..messaging.request_context import RequestContext -from ..protocols.out_of_band.v1_0.messages.service import Service +from ..protocols.didcomm_prefix import DIDCommPrefix +from ..protocols.issue_credential.v1_0.message_types import CREDENTIAL_OFFER +from ..protocols.issue_credential.v2_0.message_types import CRED_20_OFFER +from ..protocols.present_proof.v1_0.message_types import PRESENTATION_REQUEST +from ..protocols.present_proof.v2_0.message_types import PRES_20_REQUEST from ..protocols.out_of_band.v1_0.models.oob_record import OobRecord from ..storage.error import StorageNotFoundError from ..transport.inbound.message import InboundMessage @@ -41,6 +43,23 @@ def __init__( self._inbound_message_router = inbound_message_router self.wire_format = JsonWireFormat() + async def clean_finished_oob_record(self, profile: Profile, message: AgentMessage): + try: + async with profile.session() as session: + oob_record = await OobRecord.retrieve_by_tag_filter( + session, + {"invi_msg_id": message._thread.pthid}, + {"role": OobRecord.ROLE_SENDER}, + ) + + # If the oob record is not multi use and it doesn't contain any attachments + # We can now safely remove the oob record + if not oob_record.multi_use and not oob_record.invitation.requests_attach: + await oob_record.delete_record(session) + except Exception: + # It is fine if no oob record is found, Only retrieved for cleanup + pass + async def find_oob_target_for_outbound_message( self, profile: Profile, outbound_message: OutboundMessage ) -> Optional[ConnectionTarget]: @@ -176,7 +195,7 @@ async def find_oob_record_for_inbound_message( if not oob_record.attach_thread_id and oob_record.invitation.requests_attach: # Check if the current message thread_id corresponds to one of the invitation ~thread.thid allowed_thread_ids = [ - self._get_thread_id(attachment.content) + self.get_thread_id(attachment.content) for attachment in oob_record.invitation.requests_attach ] @@ -230,15 +249,41 @@ async def find_oob_record_for_inbound_message( oob_record.their_service = context.message._service.serialize() async with context.profile.session() as session: - await oob_record.save(session, reason="Update their service in oob record") + # We can now remove the oob record as the connection should now be stored in the + # exchange record itself. + if oob_record.connection_id: + await oob_record.delete_record(session) + else: + await oob_record.save( + session, reason="Update their service in oob record" + ) return oob_record async def handle_message( - self, profile: Profile, message: Dict[str, Any], oob_record: OobRecord + self, profile: Profile, messages: List[Dict[str, Any]], oob_record: OobRecord ): """Message handler for inbound messages.""" + supported_types = [ + CREDENTIAL_OFFER, + CRED_20_OFFER, + PRESENTATION_REQUEST, + PRES_20_REQUEST, + ] + + supported_messages = [ + message + for message in messages + if DIDCommPrefix.unqualify(message["@type"]) in supported_types + ] + + if not supported_messages: + raise Exception( + f"None of the oob attached messages supported. Supported message types are {supported_types}" + ) + + message = supported_messages[0] message_str = json.dumps(message) async with profile.session() as session: diff --git a/aries_cloudagent/protocols/connections/v1_0/manager.py b/aries_cloudagent/protocols/connections/v1_0/manager.py index f9806e58fd..95bf25ed8a 100644 --- a/aries_cloudagent/protocols/connections/v1_0/manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/manager.py @@ -4,6 +4,8 @@ from typing import Coroutine, Optional, Sequence, Tuple + +from ....core.oob_processor import OobMessageProcessor from ....cache.base import BaseCache from ....config.base import InjectionError from ....connections.base_manager import BaseConnectionManager @@ -641,6 +643,10 @@ async def receive_request( keylist_updates, connection_id=mediation_record.connection_id ) + # Clean associated oob record if not needed anymore + oob_processor = self.profile.inject(OobMessageProcessor) + await oob_processor.clean_finished_oob_record(self.profile, request) + return connection async def create_response( diff --git a/aries_cloudagent/protocols/didexchange/v1_0/manager.py b/aries_cloudagent/protocols/didexchange/v1_0/manager.py index af88808037..52fa9c8230 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/manager.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/manager.py @@ -4,6 +4,8 @@ import logging from typing import Optional + +from ....core.oob_processor import OobMessageProcessor from ....connections.models.conn_record import ConnRecord from ....connections.models.diddoc import DIDDoc from ....connections.base_manager import BaseConnectionManager @@ -493,10 +495,7 @@ async def receive_request( conn_rec.their_did = request.did conn_rec.state = ConnRecord.State.REQUEST.rfc23 conn_rec.request_id = request._id - async with self.profile.session() as session: - await conn_rec.save( - session, reason="Received connection request from invitation" - ) + else: # request is against implicit invitation on public DID async with self.profile.session() as session: @@ -554,6 +553,10 @@ async def receive_request( keylist_updates, connection_id=mediation_record.connection_id ) + # Clean associated oob record if not needed anymore + oob_processor = self.profile.inject(OobMessageProcessor) + await oob_processor.clean_finished_oob_record(self.profile, request) + return conn_rec async def create_response( diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py index 1269d901b0..bbab56de7b 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py @@ -398,6 +398,7 @@ async def create_invitation( invitation=invi_msg, our_recipient_key=our_recipient_key, our_service=our_service, + multi_use=multi_use, ) async with self.profile.session() as session: @@ -520,6 +521,7 @@ async def receive_invitation( LOGGER.debug( f"Process attached messages for oob exchange {oob_record.oob_id} (connection_id {oob_record.connection_id})" ) + # FIXME: this should ideally be handled using an event handler. Once the connection is ready # we start processing the attached messages. For now we use the timeout method if ( @@ -555,13 +557,12 @@ async def receive_invitation( async def _respond_request_attach(self, oob_record: OobRecord): invitation = oob_record.invitation - req_attach = invitation.requests_attach[0] if not isinstance(req_attach, AttachDecorator): raise OutOfBandManagerError("requests~attach is not properly formatted") message_processor = self.profile.inject(OobMessageProcessor) - message = req_attach.content + messages = [attachment.content for attachment in invitation.requests_attach] if not oob_record.connection_id: service = oob_record.invitation.services[0] @@ -575,7 +576,7 @@ async def _respond_request_attach(self, oob_record: OobRecord): await message_processor.handle_message( self.profile, - req_attach.content, + messages, oob_record=oob_record, ) @@ -951,10 +952,14 @@ async def receive_reuse_message( {"state": OobRecord.STATE_AWAIT_RESPONSE}, ) - oob_record.state = OobRecord.STATE_ACCEPTED - oob_record.reuse_msg_id = reuse_msg_id - oob_record.connection_id = conn_rec.connection_id - await oob_record.save(session) + # If the oob_record is not multi-use we can now remove it + if not oob_record.multi_use: + await oob_record.delete_record(session) + # OOB_TODO + # oob_record.state = OobRecord.STATE_DONE + # oob_record.reuse_msg_id = reuse_msg_id + # oob_record.connection_id = conn_rec.connection_id + # await oob_record.save(session) conn_rec.invitation_msg_id = invi_msg_id await conn_rec.save(session, reason="Assigning new invitation_msg_id") @@ -1012,12 +1017,13 @@ async def receive_reuse_accepted_message( {"invi_msg_id": invi_msg_id, "reuse_msg_id": thread_reuse_msg_id}, ) - oob_record.state = OobRecord.STATE_ACCEPTED + # We can now remove the oob_record + await oob_record.delete_record(session) + conn_record.invitation_msg_id = invi_msg_id await conn_record.save( session, reason="Assigning new invitation_msg_id" ) - await oob_record.save(session, reason="Reuse accepted") # Emit webhook await self.profile.notify( REUSE_ACCEPTED_WEBHOOK_TOPIC, diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/models/oob_record.py b/aries_cloudagent/protocols/out_of_band/v1_0/models/oob_record.py index 03455e37fd..536f8b8ad7 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/models/oob_record.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/models/oob_record.py @@ -57,6 +57,7 @@ def __init__( attach_thread_id: Optional[str] = None, our_recipient_key: Optional[str] = None, our_service: Optional[ServiceDecorator] = None, + multi_use: bool = False, trace: bool = False, **kwargs, ): @@ -73,6 +74,7 @@ def __init__( self.our_service = our_service self.attach_thread_id = attach_thread_id self.our_recipient_key = our_recipient_key + self.multi_use = multi_use self.trace = trace @property From 865257c2460e8c78ce639ceaa0ac221074ab4c08 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 21 Mar 2022 12:24:02 +0100 Subject: [PATCH 091/872] fix: handle webhook states Signed-off-by: Timo Glastra --- aries_cloudagent/core/oob_processor.py | 4 ++++ .../protocols/out_of_band/v1_0/manager.py | 18 +++++++++++++----- .../out_of_band/v1_0/models/oob_record.py | 2 +- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/aries_cloudagent/core/oob_processor.py b/aries_cloudagent/core/oob_processor.py index d512ed7209..8a33c06dbf 100644 --- a/aries_cloudagent/core/oob_processor.py +++ b/aries_cloudagent/core/oob_processor.py @@ -55,6 +55,8 @@ async def clean_finished_oob_record(self, profile: Profile, message: AgentMessag # If the oob record is not multi use and it doesn't contain any attachments # We can now safely remove the oob record if not oob_record.multi_use and not oob_record.invitation.requests_attach: + oob_record.state = OobRecord.STATE_DONE + await oob_record.emit_event(session) await oob_record.delete_record(session) except Exception: # It is fine if no oob record is found, Only retrieved for cleanup @@ -252,6 +254,8 @@ async def find_oob_record_for_inbound_message( # We can now remove the oob record as the connection should now be stored in the # exchange record itself. if oob_record.connection_id: + oob_record.state = OobRecord.STATE_DONE + await oob_record.emit_event(session) await oob_record.delete_record(session) else: await oob_record.save( diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py index bbab56de7b..81ed530a12 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py @@ -722,6 +722,7 @@ async def _handle_hanshake_reuse( if oob_record.state != OobRecord.STATE_ACCEPTED: # Remove associated connection id as reuse has ben denied oob_record.connection_id = None + oob_record.state = OobRecord.STATE_NOT_ACCEPTED # OOB_TODO: replace webhook event with new oob webhook event # Emit webhook if the reuse was not accepted @@ -952,14 +953,17 @@ async def receive_reuse_message( {"state": OobRecord.STATE_AWAIT_RESPONSE}, ) + oob_record.state = OobRecord.STATE_DONE + oob_record.reuse_msg_id = reuse_msg_id + oob_record.connection_id = conn_rec.connection_id + + # We don't want to store this state. We either remove the record (no multi-use) + # or we can't update the record (multi-use) + await oob_record.emit_event(session) + # If the oob_record is not multi-use we can now remove it if not oob_record.multi_use: await oob_record.delete_record(session) - # OOB_TODO - # oob_record.state = OobRecord.STATE_DONE - # oob_record.reuse_msg_id = reuse_msg_id - # oob_record.connection_id = conn_rec.connection_id - # await oob_record.save(session) conn_rec.invitation_msg_id = invi_msg_id await conn_rec.save(session, reason="Assigning new invitation_msg_id") @@ -1017,7 +1021,11 @@ async def receive_reuse_accepted_message( {"invi_msg_id": invi_msg_id, "reuse_msg_id": thread_reuse_msg_id}, ) + oob_record.state = OobRecord.STATE_DONE + oob_record.connection_id = conn_record.connection_id + # We can now remove the oob_record + await oob_record.emit_event(session) await oob_record.delete_record(session) conn_record.invitation_msg_id = invi_msg_id diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/models/oob_record.py b/aries_cloudagent/protocols/out_of_band/v1_0/models/oob_record.py index 536f8b8ad7..8420288589 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/models/oob_record.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/models/oob_record.py @@ -4,7 +4,7 @@ from marshmallow import fields -from aries_cloudagent.messaging.decorators.service_decorator import ( +from .....messaging.decorators.service_decorator import ( ServiceDecorator, ServiceDecoratorSchema, ) From 2f2c3107816597ea057bba233462aad81073b03c Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Tue, 22 Mar 2022 07:03:01 -0700 Subject: [PATCH 092/872] updates Signed-off-by: Shaanjot Gill --- aries_cloudagent/config/logging.py | 31 ++++--- aries_cloudagent/config/tests/test_logging.py | 25 +----- aries_cloudagent/core/conductor.py | 66 +------------- aries_cloudagent/core/tests/test_conductor.py | 86 +++---------------- aries_cloudagent/transport/inbound/base.py | 2 + aries_cloudagent/transport/outbound/base.py | 8 +- .../transport/outbound/manager.py | 36 ++++++-- .../transport/outbound/queue/__init__.py | 0 .../transport/outbound/queue/base.py | 62 ------------- .../transport/outbound/queue/loader.py | 26 ------ .../outbound/queue/tests/__init__.py | 0 .../outbound/queue/tests/fixtures.py | 20 ----- .../outbound/queue/tests/test_loader.py | 48 ----------- .../transport/outbound/tests/test_manager.py | 5 +- 14 files changed, 77 insertions(+), 338 deletions(-) delete mode 100644 aries_cloudagent/transport/outbound/queue/__init__.py delete mode 100644 aries_cloudagent/transport/outbound/queue/base.py delete mode 100644 aries_cloudagent/transport/outbound/queue/loader.py delete mode 100644 aries_cloudagent/transport/outbound/queue/tests/__init__.py delete mode 100644 aries_cloudagent/transport/outbound/queue/tests/fixtures.py delete mode 100644 aries_cloudagent/transport/outbound/queue/tests/test_loader.py diff --git a/aries_cloudagent/config/logging.py b/aries_cloudagent/config/logging.py index 2ab1d52206..1511d049a5 100644 --- a/aries_cloudagent/config/logging.py +++ b/aries_cloudagent/config/logging.py @@ -83,7 +83,6 @@ def print_banner( agent_label, inbound_transports, outbound_transports, - outbound_queue, public_did, admin_server=None, banner_length=40, @@ -96,7 +95,6 @@ def print_banner( agent_label: Agent Label inbound_transports: Configured inbound transports outbound_transports: Configured outbound transports - outbound_queue: The outbound queue engine instance admin_server: Admin server info public_did: Public DID banner_length: (Default value = 40) Length of the banner @@ -124,6 +122,18 @@ def print_banner( ) banner.print_spacer() + external_in_transports = set().union( + *( + transport + for transport in outbound_transports.values() + if transport.is_external + ) + ) + if external_in_transports: + banner.print_spacer() + banner.print_list([f"{external_in_transports}"]) + banner.print_spacer() + # Outbound transports schemes = set().union( *(transport.schemes for transport in outbound_transports.values()) @@ -134,15 +144,16 @@ def print_banner( banner.print_list([f"{scheme}" for scheme in sorted(schemes)]) banner.print_spacer() - # Outbound queue - if outbound_queue: - banner.print_subtitle("Outbound Queue") - banner.print_spacer() - banner.print_list( - [ - f"{outbound_queue}", - ] + external_out_transports = set().union( + *( + transport + for transport in outbound_transports.values() + if transport.is_external ) + ) + if external_out_transports: + banner.print_spacer() + banner.print_list([f"{external_out_transports}"]) banner.print_spacer() # DID info diff --git a/aries_cloudagent/config/tests/test_logging.py b/aries_cloudagent/config/tests/test_logging.py index bd74b8471e..2a15cdfd21 100644 --- a/aries_cloudagent/config/tests/test_logging.py +++ b/aries_cloudagent/config/tests/test_logging.py @@ -66,38 +66,15 @@ def test_banner_did(self): test_label, {"in": mock_http}, {"out": mock_https}, - None, test_did, mock_admin_server, ) test_module.LoggingConfigurator.print_banner( - test_label, {"in": mock_http}, {"out": mock_https}, None, test_did + test_label, {"in": mock_http}, {"out": mock_https}, test_did ) output = stdout.getvalue() assert test_did in output - def test_banner_outbound_queue(self): - stdout = StringIO() - mock_http = async_mock.MagicMock(scheme="http", host="1.2.3.4", port=8081) - mock_queue = "mocked queue text" - mock_admin_server = async_mock.MagicMock(host="1.2.3.4", port=8091) - with contextlib.redirect_stdout(stdout): - test_label = "Aries Cloud Agent" - test_did = "55GkHamhTU1ZbTbV2ab9DE" - test_module.LoggingConfigurator.print_banner( - test_label, - {"in": mock_http}, - {}, - mock_queue, - test_did, - mock_admin_server, - ) - test_module.LoggingConfigurator.print_banner( - test_label, {"in": mock_http}, {}, mock_queue, test_did - ) - output = stdout.getvalue() - assert "mocked queue text" in output - def test_load_resource(self): with async_mock.patch("builtins.open", async_mock.MagicMock()) as mock_open: test_module.load_resource("abc", encoding="utf-8") diff --git a/aries_cloudagent/core/conductor.py b/aries_cloudagent/core/conductor.py index 31cd7c579b..e47a6fc135 100644 --- a/aries_cloudagent/core/conductor.py +++ b/aries_cloudagent/core/conductor.py @@ -56,8 +56,6 @@ from ..transport.outbound.base import OutboundDeliveryError from ..transport.outbound.manager import OutboundTransportManager, QueuedOutboundMessage from ..transport.outbound.message import OutboundMessage -from ..transport.outbound.queue.base import BaseOutboundQueue -from ..transport.outbound.queue.loader import get_outbound_queue from ..transport.outbound.status import OutboundSendStatus from ..transport.wire_format import BaseWireFormat from ..utils.stats import Collector @@ -97,7 +95,6 @@ def __init__(self, context_builder: ContextBuilder) -> None: self.outbound_transport_manager: OutboundTransportManager = None self.root_profile: Profile = None self.setup_public_did: DIDInfo = None - self.outbound_queue: BaseOutboundQueue = None @property def context(self) -> InjectionContext: @@ -211,24 +208,6 @@ async def setup(self): DocumentLoader, DocumentLoader(self.root_profile) ) - self.outbound_queue = get_outbound_queue(self.root_profile) - - if ( - not self.outbound_queue - and self.outbound_transport_manager.registered_transports == {} - ): - LOGGER.exception("outbound_transport or outbound_queue is required") - raise - - if ( - self.outbound_queue - and self.outbound_transport_manager.registered_transports != {} - ): - LOGGER.exception( - "outbound_transport and outbound-queue are not allowed together" - ) - raise - # Admin API if context.settings.get("admin.enabled"): try: @@ -289,13 +268,6 @@ async def start(self) -> None: LOGGER.exception("Unable to start outbound transports") raise - if self.outbound_queue: - try: - await self.outbound_queue.start() - except Exception: - LOGGER.exception("Unable to start outbound queue") - raise - # Start up Admin server if self.admin_server: try: @@ -319,7 +291,6 @@ async def start(self) -> None: default_label, self.inbound_transport_manager.registered_transports, self.outbound_transport_manager.registered_transports, - self.outbound_queue, self.setup_public_did and self.setup_public_did.did, self.admin_server, ) @@ -505,8 +476,6 @@ async def stop(self, timeout=1.0): shutdown.run(self.inbound_transport_manager.stop()) if self.outbound_transport_manager: shutdown.run(self.outbound_transport_manager.stop()) - if self.outbound_queue: - shutdown.run(self.outbound_queue.stop()) # close multitenant profiles multitenant_mgr = self.context.inject_or(BaseMultitenantManager) @@ -665,43 +634,14 @@ async def queue_outbound( self.admin_server.notify_fatal_error() raise del conn_mgr - # If ``self.outbound_queue`` is specified (redis_queue plugin), - # use that external queue. Else save the message to an internal - # queue. This internal queue usually results in the message to - # be sent over ACA-py `-ot` transport. - if self.outbound_queue: - return await self._queue_external(profile, outbound) - else: - return self._queue_internal(profile, outbound) - - async def _queue_external( - self, - profile: Profile, - outbound: OutboundMessage, - ) -> OutboundSendStatus: - """Save the message to an external outbound queue.""" - async with self.outbound_queue: - targets = ( - [outbound.target] if outbound.target else (outbound.target_list or []) - ) - for target in targets: - encoded_outbound_message = ( - await self.outbound_transport_manager.encode_outbound_message( - profile, outbound, target - ) - ) - await self.outbound_queue.enqueue_message( - encoded_outbound_message.payload, target.endpoint - ) - - return OutboundSendStatus.SENT_TO_EXTERNAL_QUEUE + return await self._queue_message(profile, outbound) - def _queue_internal( + async def _queue_message( self, profile: Profile, outbound: OutboundMessage ) -> OutboundSendStatus: """Save the message to an internal outbound queue.""" try: - self.outbound_transport_manager.enqueue_message(profile, outbound) + await self.outbound_transport_manager.enqueue_message(profile, outbound) return OutboundSendStatus.QUEUED_FOR_DELIVERY except OutboundDeliveryError: LOGGER.warning("Cannot queue message for delivery, no supported transport") diff --git a/aries_cloudagent/core/tests/test_conductor.py b/aries_cloudagent/core/tests/test_conductor.py index 52819d4563..0552e89147 100644 --- a/aries_cloudagent/core/tests/test_conductor.py +++ b/aries_cloudagent/core/tests/test_conductor.py @@ -187,15 +187,11 @@ async def test_startup_no_public_did(self): async_mock.CoroutineMock( return_value=async_mock.MagicMock(value=f"v{__version__}") ), - ), async_mock.patch.object( - test_module, "get_outbound_queue", async_mock.MagicMock() - ) as mock_get_outbound_queue: + ): mock_outbound_mgr.return_value.registered_transports = {} - mock_get_outbound_queue.return_value = async_mock.MagicMock( - enqueue_message=async_mock.CoroutineMock(), - start=async_mock.CoroutineMock(), - stop=async_mock.CoroutineMock(), - ) + mock_outbound_mgr.return_value.enqueue_message = async_mock.CoroutineMock() + mock_outbound_mgr.return_value.start = async_mock.CoroutineMock() + mock_outbound_mgr.return_value.stop = async_mock.CoroutineMock() await conductor.setup() mock_inbound_mgr.return_value.setup.assert_awaited_once() @@ -320,16 +316,7 @@ async def test_outbound_message_handler_return_route(self): conductor = test_module.Conductor(builder) test_to_verkey = "test-to-verkey" test_from_verkey = "test-from-verkey" - with async_mock.patch.object( - test_module, - "get_outbound_queue", - async_mock.MagicMock( - return_value=async_mock.MagicMock( - enqueue_message=async_mock.CoroutineMock() - ) - ), - ) as mock_get_outbound_queue: - await conductor.setup() + await conductor.setup() payload = "{}" message = OutboundMessage(payload=payload) @@ -340,13 +327,10 @@ async def test_outbound_message_handler_return_route(self): with async_mock.patch.object( conductor.inbound_transport_manager, "return_to_session" - ) as mock_return, async_mock.patch.object( - conductor, "queue_outbound", async_mock.CoroutineMock() - ) as mock_queue: + ) as mock_return: mock_return.return_value = True await conductor.outbound_message_router(conductor.context, message) mock_return.assert_called_once_with(message) - mock_queue.assert_not_awaited() async def test_outbound_message_handler_with_target(self): builder: ContextBuilder = StubContextBuilder(self.test_settings) @@ -442,7 +426,7 @@ async def test_handle_nots(self): ) as mock_outbound_mgr: mock_outbound_mgr.return_value = async_mock.MagicMock( setup=async_mock.CoroutineMock(), - enqueue_message=async_mock.MagicMock(), + enqueue_message=async_mock.CoroutineMock(), ) payload = "{}" @@ -487,47 +471,8 @@ async def test_handle_outbound_queue(self): target=async_mock.MagicMock(endpoint="endpoint"), reply_to_verkey=TestDIDs.test_verkey, ) - with async_mock.patch.object( - test_module, "get_outbound_queue", async_mock.MagicMock() - ) as mock_get_outbound_queue: - mock_get_outbound_queue.return_value = async_mock.MagicMock( - enqueue_message=async_mock.CoroutineMock() - ) - await conductor.setup() - await conductor.queue_outbound(conductor.root_profile, message) - conductor.outbound_queue.enqueue_message.assert_called_once() - - async def test_handle_outbound_queue_and_transport_missing(self): - builder: ContextBuilder = StubContextBuilder(self.test_settings) - conductor = test_module.Conductor(builder) - with async_mock.patch.object(test_module, "LOGGER") as mock_logger: - with self.assertRaises(RuntimeError): - await conductor.setup() - mock_logger.exception.assert_called_once_with( - "outbound_transport or outbound_queue is required" - ) - - async def test_handle_outbound_queue_and_transport_both(self): - builder: ContextBuilder = StubContextBuilder(self.test_settings) - conductor = test_module.Conductor(builder) - with async_mock.patch.object( - test_module, "LOGGER" - ) as mock_logger, async_mock.patch.object( - test_module, "OutboundTransportManager", autospec=True - ) as mock_outbound_mgr, async_mock.patch.object( - test_module, "get_outbound_queue", async_mock.MagicMock() - ) as mock_get_outbound_queue: - mock_outbound_mgr.return_value.registered_transports = { - "test": async_mock.MagicMock(schemes=["http"]) - } - mock_get_outbound_queue.return_value = async_mock.MagicMock( - enqueue_message=async_mock.CoroutineMock() - ) - with self.assertRaises(RuntimeError): - await conductor.setup() - mock_logger.exception.assert_called_once_with( - "outbound_transport and outbound-queue are not allowed together" - ) + await conductor.setup() + await conductor.queue_outbound(conductor.root_profile, message) async def test_handle_not_returned_ledger_x(self): builder: ContextBuilder = StubContextBuilder(self.test_settings_admin) @@ -795,18 +740,13 @@ async def test_start_x_out_b(self): test_module, "ConnectionManager" ) as mock_mgr, async_mock.patch.object( test_module, "OutboundTransportManager" - ) as mock_outx_mgr, async_mock.patch.object( - test_module, "get_outbound_queue", async_mock.MagicMock() - ) as mock_get_outbound_queue: - mock_get_outbound_queue.return_value = async_mock.MagicMock( - enqueue_message=async_mock.CoroutineMock(), - start=async_mock.CoroutineMock(side_effect=KeyError("trouble")), - stop=async_mock.CoroutineMock(), - ) + ) as mock_outx_mgr: mock_outx_mgr.return_value = async_mock.MagicMock( setup=async_mock.CoroutineMock(), - start=async_mock.CoroutineMock(), + start=async_mock.CoroutineMock(side_effect=KeyError("trouble")), + stop=async_mock.CoroutineMock(), registered_transports={}, + enqueue_message=async_mock.CoroutineMock(), ) await conductor.setup() mock_mgr.return_value.create_static_connection = async_mock.CoroutineMock() diff --git a/aries_cloudagent/transport/inbound/base.py b/aries_cloudagent/transport/inbound/base.py index 648514a6be..550f57bfe4 100644 --- a/aries_cloudagent/transport/inbound/base.py +++ b/aries_cloudagent/transport/inbound/base.py @@ -21,6 +21,7 @@ def __init__( max_message_size: int = 0, wire_format: BaseWireFormat = None, root_profile: Profile = None, + is_external: bool = False, ): """ Initialize the inbound transport instance. @@ -35,6 +36,7 @@ def __init__( self._scheme = scheme self.wire_format: BaseWireFormat = wire_format self.root_profile: Profile = root_profile + self.is_external = is_external @property def max_message_size(self): diff --git a/aries_cloudagent/transport/outbound/base.py b/aries_cloudagent/transport/outbound/base.py index 467ef69d55..2f731fedad 100644 --- a/aries_cloudagent/transport/outbound/base.py +++ b/aries_cloudagent/transport/outbound/base.py @@ -15,12 +15,16 @@ class BaseOutboundTransport(ABC): """Base outbound transport class.""" def __init__( - self, wire_format: BaseWireFormat = None, root_profile: Profile = None + self, + wire_format: BaseWireFormat = None, + root_profile: Profile = None, + is_external: bool = False, ) -> None: """Initialize a `BaseOutboundTransport` instance.""" self._collector = None self._wire_format = wire_format self.root_profile = root_profile + self.is_external = is_external @property def collector(self) -> Collector: @@ -69,7 +73,7 @@ async def handle_message( metadata: dict = None, ): """ - Handle message from queue. + Handle message. Args: profile: the profile that produced the message diff --git a/aries_cloudagent/transport/outbound/manager.py b/aries_cloudagent/transport/outbound/manager.py index 18fdf4b182..87d1fe0d94 100644 --- a/aries_cloudagent/transport/outbound/manager.py +++ b/aries_cloudagent/transport/outbound/manager.py @@ -214,6 +214,17 @@ def get_running_transport_for_scheme(self, scheme: str) -> str: except StopIteration: pass + def get_external_running_transport(self) -> str: + """Find the external running transport ID.""" + try: + return next( + transport_id + for transport_id, transport in self.running_transports.items() + if transport.is_external + ) + except StopIteration: + pass + def get_running_transport_for_endpoint(self, endpoint: str): """Find the running transport ID to use for a given endpoint.""" # Grab the scheme from the uri @@ -235,7 +246,7 @@ def get_transport_instance(self, transport_id: str) -> BaseOutboundTransport: """Get an instance of a running transport by ID.""" return self.running_transports[transport_id] - def enqueue_message(self, profile: Profile, outbound: OutboundMessage): + async def enqueue_message(self, profile: Profile, outbound: OutboundMessage): """ Add an outbound message to the queue. @@ -244,22 +255,31 @@ def enqueue_message(self, profile: Profile, outbound: OutboundMessage): outbound: The outbound message to deliver """ targets = [outbound.target] if outbound.target else (outbound.target_list or []) - transport_id = None for target in targets: endpoint = target.endpoint try: - transport_id = self.get_running_transport_for_endpoint(endpoint) + transport_id = self.get_external_running_transport() + if not transport_id: + transport_id = self.get_running_transport_for_endpoint(endpoint) except OutboundDeliveryError: pass if transport_id: break if not transport_id: raise OutboundDeliveryError("No supported transport for outbound message") - - queued = QueuedOutboundMessage(profile, outbound, target, transport_id) - queued.retries = self.MAX_RETRY_COUNT - self.outbound_new.append(queued) - self.process_queued() + transport = self.get_transport_instance(transport_id) + if transport.is_external: + encoded_outbound_message = await self.encode_outbound_message( + profile, outbound, target + ) + await transport.handle_message( + profile, encoded_outbound_message.payload, target.endpoint + ) + else: + queued = QueuedOutboundMessage(profile, outbound, target, transport_id) + queued.retries = self.MAX_RETRY_COUNT + self.outbound_new.append(queued) + self.process_queued() async def encode_outbound_message( self, profile: Profile, outbound: OutboundMessage, target: ConnectionTarget diff --git a/aries_cloudagent/transport/outbound/queue/__init__.py b/aries_cloudagent/transport/outbound/queue/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/aries_cloudagent/transport/outbound/queue/base.py b/aries_cloudagent/transport/outbound/queue/base.py deleted file mode 100644 index 65153abbe3..0000000000 --- a/aries_cloudagent/transport/outbound/queue/base.py +++ /dev/null @@ -1,62 +0,0 @@ -"""Base classes for the queue module.""" -from abc import ABC, abstractmethod -import asyncio -import logging -from typing import Union - -from ....core.profile import Profile -from ...error import BaseError, TransportError - - -class BaseOutboundQueue(ABC): - """Base class for the outbound queue generic type.""" - - def __init__(self, root_profile: Profile): - """Initialize base queue type.""" - self.logger = logging.getLogger(__name__) - - def __str__(self): - """Return string representation used in banner on startup.""" - return type(self).__name__ - - async def __aenter__(self): - """Async context manager enter.""" - await self.open() - - async def __aexit__(self, err_type, err_value, err_t): - """Async context manager exit.""" - if err_type and err_type != asyncio.CancelledError: - self.logger.exception("Exception in outbound queue") - await self.close() - - async def start(self): - """Start the queue.""" - - async def stop(self): - """Stop the queue.""" - - async def open(self): - """Start the queue.""" - - async def close(self): - """Stop the queue.""" - - @abstractmethod - async def enqueue_message( - self, - payload: Union[str, bytes], - endpoint: str, - ): - """Prepare and send message to external queue.""" - - -class OutboundQueueError(TransportError): - """Generic outbound transport error.""" - - -class OutboundQueueConfigurationError(BaseError): - """An error with the queue configuration.""" - - def __init__(self, message): - """Initialize the exception instance.""" - super().__init__(message) diff --git a/aries_cloudagent/transport/outbound/queue/loader.py b/aries_cloudagent/transport/outbound/queue/loader.py deleted file mode 100644 index e184ad23ad..0000000000 --- a/aries_cloudagent/transport/outbound/queue/loader.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Dynamic loading of pluggable outbound queue engine classes.""" -import logging -from typing import Optional, cast - -from ....core.profile import Profile -from ....utils.classloader import ClassLoader -from .base import BaseOutboundQueue, OutboundQueueConfigurationError - - -LOGGER = logging.getLogger(__name__) - - -def get_outbound_queue(root_profile: Profile) -> Optional[BaseOutboundQueue]: - """Given settings, return instantiated outbound queue class.""" - class_path = root_profile.settings.get("transport.outbound_queue") - if not class_path: - LOGGER.info("No outbound queue loaded") - return None - class_path = cast(str, class_path) - klass = ClassLoader.load_class(class_path) - instance = klass(root_profile) - if not isinstance(instance, BaseOutboundQueue): - raise OutboundQueueConfigurationError( - "Configured class is not a subclass of BaseOutboundQueue" - ) - return instance diff --git a/aries_cloudagent/transport/outbound/queue/tests/__init__.py b/aries_cloudagent/transport/outbound/queue/tests/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/aries_cloudagent/transport/outbound/queue/tests/fixtures.py b/aries_cloudagent/transport/outbound/queue/tests/fixtures.py deleted file mode 100644 index cc92f7e890..0000000000 --- a/aries_cloudagent/transport/outbound/queue/tests/fixtures.py +++ /dev/null @@ -1,20 +0,0 @@ -from ..base import BaseOutboundQueue - - -class QueueClassNoBaseClass: - def __init__(self, settings): - pass - - -class QueueClassValid(BaseOutboundQueue): - async def enqueue_message(self, payload, endpoint): - pass - - async def push(self, key, message): - pass - - async def start(self): - pass - - async def stop(self): - pass diff --git a/aries_cloudagent/transport/outbound/queue/tests/test_loader.py b/aries_cloudagent/transport/outbound/queue/tests/test_loader.py deleted file mode 100644 index 32b3de7b72..0000000000 --- a/aries_cloudagent/transport/outbound/queue/tests/test_loader.py +++ /dev/null @@ -1,48 +0,0 @@ -import pytest - -from .....core.in_memory import InMemoryProfile -from .....utils.classloader import ClassNotFoundError -from ..base import OutboundQueueConfigurationError -from ..loader import get_outbound_queue -from .fixtures import QueueClassValid - - -@pytest.fixture -def profile(): - yield InMemoryProfile.test_profile( - settings={ - "transport.outbound_queue": "aries_cloudagent.transport.outbound.queue.tests.fixtures.QueueClassValid" - } - ) - - -def test_get_outbound_queue_valid(profile): - queue = get_outbound_queue(profile) - assert isinstance(queue, QueueClassValid) - - -@pytest.mark.parametrize( - "queue", - [ - None, - "", - ], -) -def test_get_outbound_not_set(queue, profile): - profile.settings["transport.outbound_queue"] = queue - assert get_outbound_queue(profile) is None - - -def test_get_outbound_x_no_class(profile): - profile.settings["transport.outbound_queue"] = "invalid queue class path" - with pytest.raises(ClassNotFoundError): - get_outbound_queue(profile) - - -def test_get_outbound_x_bad_instance(profile): - profile.settings["transport.outbound_queue"] = ( - "aries_cloudagent.transport.outbound.queue.tests.fixtures." - "QueueClassNoBaseClass" - ) - with pytest.raises(OutboundQueueConfigurationError): - get_outbound_queue(profile) diff --git a/aries_cloudagent/transport/outbound/tests/test_manager.py b/aries_cloudagent/transport/outbound/tests/test_manager.py index 834184b208..a2e958f2cf 100644 --- a/aries_cloudagent/transport/outbound/tests/test_manager.py +++ b/aries_cloudagent/transport/outbound/tests/test_manager.py @@ -59,6 +59,7 @@ async def test_send_message(self): transport.start = async_mock.CoroutineMock() transport.stop = async_mock.CoroutineMock() transport.schemes = ["http"] + transport.is_external = False transport_cls = async_mock.MagicMock() transport_cls.schemes = ["http"] @@ -85,7 +86,7 @@ async def test_send_message(self): setattr( send_profile, "session", async_mock.MagicMock(return_value=send_session) ) - mgr.enqueue_message(send_profile, message) + await mgr.enqueue_message(send_profile, message) await mgr.flush() transport.wire_format.encode_message.assert_awaited_once_with( @@ -113,7 +114,7 @@ async def test_send_message(self): sender_key=4, ) with self.assertRaises(OutboundDeliveryError) as context: - mgr.enqueue_message(send_profile, message) + await mgr.enqueue_message(send_profile, message) assert "No supported transport" in str(context.exception) await mgr.stop() From 7d88b235e225cc158b54b8f8e4f438c5f55f751a Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Tue, 22 Mar 2022 23:02:54 -0400 Subject: [PATCH 093/872] feat: add caching for multi-tenant profiles Signed-off-by: Timo Glastra Signed-off-by: Daniel Bluhm --- aries_cloudagent/multitenant/cache.py | 94 ++++++++++++++++ .../multitenant/tests/test_cache.py | 100 ++++++++++++++++++ 2 files changed, 194 insertions(+) create mode 100644 aries_cloudagent/multitenant/cache.py create mode 100644 aries_cloudagent/multitenant/tests/test_cache.py diff --git a/aries_cloudagent/multitenant/cache.py b/aries_cloudagent/multitenant/cache.py new file mode 100644 index 0000000000..b228878160 --- /dev/null +++ b/aries_cloudagent/multitenant/cache.py @@ -0,0 +1,94 @@ +"""Cache for multitenancy profiles.""" + +import logging +import sys +from collections import OrderedDict +from typing import Optional + +from ..core.profile import Profile + +LOGGER = logging.getLogger(__name__) + + +class ProfileCache: + """Profile cache that caches based on LRU strategy.""" + + def __init__(self, capacity: int): + """Initialize ProfileCache. + + Args: + capacity: The capacity of the cache. If capacity is exceeded + profiles are closed. + """ + + self.profiles: OrderedDict[str, Profile] = OrderedDict() + self.capacity = capacity + + async def _cleanup(self): + for (key, profile) in self.profiles.items(): + # When ref count is 4 we can assume the profile is not referenced + # 1 = profiles dict + # 2 = self.profiles.items() + # 3 = profile above + # 4 = sys.getrefcount + if sys.getrefcount(profile) <= 4: + LOGGER.debug(f"closing profile with id {key}") + del self.profiles[key] + await profile.close() + + if len(self.profiles) <= self.capacity: + break + + def get(self, key: str) -> Optional[Profile]: + """Get profile with associated key from cache. + + Args: + key (str): the key to get the profile for. + + Returns: + Optional[Profile]: Profile if found in cache. + + """ + if key not in self.profiles: + return None + else: + self.profiles.move_to_end(key) + return self.profiles[key] + + def has(self, key: str) -> bool: + """Check whether there is a profile with associated key in the cache. + + Args: + key (str): the key to check for a profile + + Returns: + bool: Whether the key exists in the cache + + """ + return key in self.profiles + + async def put(self, key: str, value: Profile) -> None: + """Add profile with associated key to the cache. + + If new profile exceeds the cache capacity least recently used profiles + that are not used will be removed from the cache. + + Args: + key (str): the key to set + value (Profile): the profile to set + """ + self.profiles[key] = value + self.profiles.move_to_end(key) + LOGGER.debug(f"setting profile with id {key} in profile cache") + + if len(self.profiles) > self.capacity: + LOGGER.debug(f"profile limit of {self.capacity} reached. cleaning...") + await self._cleanup() + + def remove(self, key: str): + """Remove profile with associated key from the cache. + + Args: + key (str): The key to remove from the cache. + """ + del self.profiles[key] diff --git a/aries_cloudagent/multitenant/tests/test_cache.py b/aries_cloudagent/multitenant/tests/test_cache.py new file mode 100644 index 0000000000..9d2f1f8279 --- /dev/null +++ b/aries_cloudagent/multitenant/tests/test_cache.py @@ -0,0 +1,100 @@ +import sys +from asynctest import TestCase as AsyncTestCase +from asynctest import mock as async_mock + +from ..cache import ProfileCache + + +class TestProfileCache(AsyncTestCase): + async def setUp(self): + pass + + async def test_cache_cleanup_capacity_reached(self): + with async_mock.patch.object(ProfileCache, "_cleanup") as _cleanup: + cache = ProfileCache(1) + + await cache.put("1", async_mock.MagicMock()) + _cleanup.assert_not_called() + + await cache.put("2", async_mock.MagicMock()) + _cleanup.assert_called_once() + + async def test_get_not_in_cache(self): + cache = ProfileCache(1) + + assert cache.get("1") is None + + async def test_put_get_in_cache(self): + cache = ProfileCache(1) + + profile = async_mock.MagicMock() + await cache.put("1", profile) + + assert cache.get("1") is profile + + async def test_remove(self): + cache = ProfileCache(1) + + profile = async_mock.MagicMock() + await cache.put("1", profile) + + assert cache.get("1") is profile + + cache.remove("1") + + assert cache.get("1") is None + + async def test_has_true(self): + cache = ProfileCache(1) + + profile = async_mock.MagicMock() + + assert cache.has("1") is False + await cache.put("1", profile) + assert cache.has("1") is True + + async def test_cleanup(self): + cache = ProfileCache(1) + + with async_mock.patch.object(sys, "getrefcount") as getrefcount: + getrefcount.return_value = 4 + + profile1 = async_mock.MagicMock(close=async_mock.CoroutineMock()) + profile2 = async_mock.MagicMock(close=async_mock.CoroutineMock()) + + await cache.put("1", profile1) + + assert len(cache.profiles) == 1 + + await cache.put("2", profile2) + + assert len(cache.profiles) == 1 + assert cache.get("1") == None + profile1.close.assert_called_once() + + async def test_cleanup_reference(self): + cache = ProfileCache(3) + + with async_mock.patch.object(sys, "getrefcount") as getrefcount: + getrefcount.side_effect = [6, 4] + + profile1 = async_mock.MagicMock(close=async_mock.CoroutineMock()) + profile2 = async_mock.MagicMock(close=async_mock.CoroutineMock()) + profile3 = async_mock.MagicMock(close=async_mock.CoroutineMock()) + profile4 = async_mock.MagicMock(close=async_mock.CoroutineMock()) + + await cache.put("1", profile1) + await cache.put("2", profile2) + await cache.put("3", profile3) + + assert len(cache.profiles) == 3 + + await cache.put("4", profile4) + + assert len(cache.profiles) == 3 + assert cache.get("1") == profile1 + assert cache.get("2") == None + assert cache.get("3") == profile3 + assert cache.get("4") == profile4 + + profile2.close.assert_called_once() From 076935189fb7de603474a4b9426cd3f385b2b628 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Wed, 23 Mar 2022 09:41:31 -0400 Subject: [PATCH 094/872] feat: add finalizers to profile impls Signed-off-by: Daniel Bluhm --- aries_cloudagent/askar/profile.py | 16 ++++- aries_cloudagent/core/profile.py | 13 ++++ aries_cloudagent/indy/sdk/profile.py | 17 ++++- aries_cloudagent/multitenant/cache.py | 24 ++----- .../multitenant/tests/test_cache.py | 64 +++++++------------ 5 files changed, 71 insertions(+), 63 deletions(-) diff --git a/aries_cloudagent/askar/profile.py b/aries_cloudagent/askar/profile.py index 4b72d20a57..b36d151fca 100644 --- a/aries_cloudagent/askar/profile.py +++ b/aries_cloudagent/askar/profile.py @@ -6,8 +6,8 @@ # import traceback -from typing import Any, Mapping -from weakref import ref +from typing import Any, Mapping, Optional +from weakref import finalize, ref from aries_askar import AskarError, Session, Store @@ -146,6 +146,18 @@ async def close(self): await self.opened.close() self.opened = None + def finalizer(self) -> Optional[finalize]: + """Return a finalizer for this profile. + + See docs for weakref.finalize for more details on behavior of finalizers. + """ + + def _finalize(opened: Optional[AskarOpenStore]): + if opened: + asyncio.get_event_loop().run_until_complete(opened.close()) + + return finalize(self, _finalize, self.opened) + class AskarProfileSession(ProfileSession): """An active connection to the profile management backend.""" diff --git a/aries_cloudagent/core/profile.py b/aries_cloudagent/core/profile.py index e5de864d6e..c8682aa222 100644 --- a/aries_cloudagent/core/profile.py +++ b/aries_cloudagent/core/profile.py @@ -4,6 +4,7 @@ from abc import ABC, abstractmethod from typing import Any, Mapping, Optional, Type +from weakref import finalize from .event_bus import EventBus, Event from ..config.base import InjectionError @@ -115,6 +116,18 @@ def inject_or( async def close(self): """Close the profile instance.""" + def finalizer(self) -> Optional[finalize]: + """Create and return a finalizer for the profile or None. + + None is returned if no special handling is required to close the profile. + + Finalizers enable automatic clean up of wallet profiles when all references to + the profile expire. + + See docs for weakref.finalize for more details on the behavior of finalizers. + """ + return None + async def remove(self): """Remove the profile.""" diff --git a/aries_cloudagent/indy/sdk/profile.py b/aries_cloudagent/indy/sdk/profile.py index 24badc748a..b5fb130b3d 100644 --- a/aries_cloudagent/indy/sdk/profile.py +++ b/aries_cloudagent/indy/sdk/profile.py @@ -1,9 +1,10 @@ """Manage Indy-SDK profile interaction.""" +import asyncio import logging -from typing import Any, Mapping -from weakref import ref +from typing import Any, Mapping, Optional +from weakref import finalize, ref from ...config.injection_context import InjectionContext from ...config.provider import ClassProvider @@ -116,6 +117,18 @@ async def close(self): await self.opened.close() self.opened = None + def finalizer(self) -> Optional[finalize]: + """Return a finalizer for this profile. + + See docs for weakref.finalize for more details on behavior of finalizers. + """ + + def _finalize(opened: Optional[IndyOpenWallet]): + if opened: + asyncio.get_event_loop().run_until_complete(opened.close()) + + return finalize(self, _finalize, self.opened) + async def remove(self): """Remove the profile associated with this instance.""" if not self.opened: diff --git a/aries_cloudagent/multitenant/cache.py b/aries_cloudagent/multitenant/cache.py index b228878160..b2f0141085 100644 --- a/aries_cloudagent/multitenant/cache.py +++ b/aries_cloudagent/multitenant/cache.py @@ -1,7 +1,6 @@ """Cache for multitenancy profiles.""" import logging -import sys from collections import OrderedDict from typing import Optional @@ -24,21 +23,6 @@ def __init__(self, capacity: int): self.profiles: OrderedDict[str, Profile] = OrderedDict() self.capacity = capacity - async def _cleanup(self): - for (key, profile) in self.profiles.items(): - # When ref count is 4 we can assume the profile is not referenced - # 1 = profiles dict - # 2 = self.profiles.items() - # 3 = profile above - # 4 = sys.getrefcount - if sys.getrefcount(profile) <= 4: - LOGGER.debug(f"closing profile with id {key}") - del self.profiles[key] - await profile.close() - - if len(self.profiles) <= self.capacity: - break - def get(self, key: str) -> Optional[Profile]: """Get profile with associated key from cache. @@ -77,13 +61,17 @@ async def put(self, key: str, value: Profile) -> None: key (str): the key to set value (Profile): the profile to set """ + value.finalizer() self.profiles[key] = value self.profiles.move_to_end(key) LOGGER.debug(f"setting profile with id {key} in profile cache") if len(self.profiles) > self.capacity: - LOGGER.debug(f"profile limit of {self.capacity} reached. cleaning...") - await self._cleanup() + LOGGER.debug( + f"Profile limit of {self.capacity} reached." + " Evicting least recently used profile..." + ) + self.profiles.popitem(last=False) def remove(self, key: str): """Remove profile with associated key from the cache. diff --git a/aries_cloudagent/multitenant/tests/test_cache.py b/aries_cloudagent/multitenant/tests/test_cache.py index 9d2f1f8279..8a805122c5 100644 --- a/aries_cloudagent/multitenant/tests/test_cache.py +++ b/aries_cloudagent/multitenant/tests/test_cache.py @@ -1,4 +1,3 @@ -import sys from asynctest import TestCase as AsyncTestCase from asynctest import mock as async_mock @@ -9,16 +8,6 @@ class TestProfileCache(AsyncTestCase): async def setUp(self): pass - async def test_cache_cleanup_capacity_reached(self): - with async_mock.patch.object(ProfileCache, "_cleanup") as _cleanup: - cache = ProfileCache(1) - - await cache.put("1", async_mock.MagicMock()) - _cleanup.assert_not_called() - - await cache.put("2", async_mock.MagicMock()) - _cleanup.assert_called_once() - async def test_get_not_in_cache(self): cache = ProfileCache(1) @@ -56,45 +45,38 @@ async def test_has_true(self): async def test_cleanup(self): cache = ProfileCache(1) - with async_mock.patch.object(sys, "getrefcount") as getrefcount: - getrefcount.return_value = 4 + profile1 = async_mock.MagicMock() + profile2 = async_mock.MagicMock() - profile1 = async_mock.MagicMock(close=async_mock.CoroutineMock()) - profile2 = async_mock.MagicMock(close=async_mock.CoroutineMock()) + await cache.put("1", profile1) - await cache.put("1", profile1) + assert len(cache.profiles) == 1 - assert len(cache.profiles) == 1 + await cache.put("2", profile2) - await cache.put("2", profile2) + assert len(cache.profiles) == 1 + assert cache.get("1") == None - assert len(cache.profiles) == 1 - assert cache.get("1") == None - profile1.close.assert_called_once() - - async def test_cleanup_reference(self): + async def test_cleanup_lru(self): cache = ProfileCache(3) - with async_mock.patch.object(sys, "getrefcount") as getrefcount: - getrefcount.side_effect = [6, 4] - - profile1 = async_mock.MagicMock(close=async_mock.CoroutineMock()) - profile2 = async_mock.MagicMock(close=async_mock.CoroutineMock()) - profile3 = async_mock.MagicMock(close=async_mock.CoroutineMock()) - profile4 = async_mock.MagicMock(close=async_mock.CoroutineMock()) + profile1 = async_mock.MagicMock() + profile2 = async_mock.MagicMock() + profile3 = async_mock.MagicMock() + profile4 = async_mock.MagicMock() - await cache.put("1", profile1) - await cache.put("2", profile2) - await cache.put("3", profile3) + await cache.put("1", profile1) + await cache.put("2", profile2) + await cache.put("3", profile3) - assert len(cache.profiles) == 3 + assert len(cache.profiles) == 3 - await cache.put("4", profile4) + cache.get("1") - assert len(cache.profiles) == 3 - assert cache.get("1") == profile1 - assert cache.get("2") == None - assert cache.get("3") == profile3 - assert cache.get("4") == profile4 + await cache.put("4", profile4) - profile2.close.assert_called_once() + assert len(cache.profiles) == 3 + assert cache.get("1") == profile1 + assert cache.get("2") == None + assert cache.get("3") == profile3 + assert cache.get("4") == profile4 From 577f34a48daea5b3c87705ab84f4f7e722afeedf Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Wed, 23 Mar 2022 07:31:11 -0700 Subject: [PATCH 095/872] cleanup Signed-off-by: Shaanjot Gill --- aries_cloudagent/config/logging.py | 2 +- aries_cloudagent/transport/inbound/base.py | 2 -- aries_cloudagent/transport/inbound/http.py | 2 ++ aries_cloudagent/transport/inbound/ws.py | 2 ++ aries_cloudagent/transport/outbound/base.py | 22 ++++++++++----------- aries_cloudagent/transport/outbound/http.py | 1 + aries_cloudagent/transport/outbound/ws.py | 1 + 7 files changed, 17 insertions(+), 15 deletions(-) diff --git a/aries_cloudagent/config/logging.py b/aries_cloudagent/config/logging.py index 1511d049a5..e33d7cc550 100644 --- a/aries_cloudagent/config/logging.py +++ b/aries_cloudagent/config/logging.py @@ -125,7 +125,7 @@ def print_banner( external_in_transports = set().union( *( transport - for transport in outbound_transports.values() + for transport in inbound_transports.values() if transport.is_external ) ) diff --git a/aries_cloudagent/transport/inbound/base.py b/aries_cloudagent/transport/inbound/base.py index 550f57bfe4..648514a6be 100644 --- a/aries_cloudagent/transport/inbound/base.py +++ b/aries_cloudagent/transport/inbound/base.py @@ -21,7 +21,6 @@ def __init__( max_message_size: int = 0, wire_format: BaseWireFormat = None, root_profile: Profile = None, - is_external: bool = False, ): """ Initialize the inbound transport instance. @@ -36,7 +35,6 @@ def __init__( self._scheme = scheme self.wire_format: BaseWireFormat = wire_format self.root_profile: Profile = root_profile - self.is_external = is_external @property def max_message_size(self): diff --git a/aries_cloudagent/transport/inbound/http.py b/aries_cloudagent/transport/inbound/http.py index 8179023379..d5cbda3e8c 100644 --- a/aries_cloudagent/transport/inbound/http.py +++ b/aries_cloudagent/transport/inbound/http.py @@ -15,6 +15,8 @@ class HttpTransport(BaseInboundTransport): """Http Transport class.""" + is_external = False + def __init__(self, host: str, port: int, create_session, **kwargs) -> None: """ Initialize an inbound HTTP transport instance. diff --git a/aries_cloudagent/transport/inbound/ws.py b/aries_cloudagent/transport/inbound/ws.py index 0b74a46990..96739ad273 100644 --- a/aries_cloudagent/transport/inbound/ws.py +++ b/aries_cloudagent/transport/inbound/ws.py @@ -16,6 +16,8 @@ class WsTransport(BaseInboundTransport): """Websockets Transport class.""" + is_external = False + def __init__(self, host: str, port: int, create_session, **kwargs) -> None: """ Initialize an inbound WebSocket transport instance. diff --git a/aries_cloudagent/transport/outbound/base.py b/aries_cloudagent/transport/outbound/base.py index 2f731fedad..2186b8bfc0 100644 --- a/aries_cloudagent/transport/outbound/base.py +++ b/aries_cloudagent/transport/outbound/base.py @@ -18,13 +18,11 @@ def __init__( self, wire_format: BaseWireFormat = None, root_profile: Profile = None, - is_external: bool = False, ) -> None: """Initialize a `BaseOutboundTransport` instance.""" self._collector = None self._wire_format = wire_format self.root_profile = root_profile - self.is_external = is_external @property def collector(self) -> Collector: @@ -36,6 +34,16 @@ def collector(self, coll: Collector): """Assign a new stats collector instance.""" self._collector = coll + @property + def wire_format(self) -> BaseWireFormat: + """Accessor for a custom wire format for the transport.""" + return self._wire_format + + @wire_format.setter + def wire_format(self, format: BaseWireFormat): + """Setter for a custom wire format for the transport.""" + self._wire_format = format + async def __aenter__(self): """Async context manager enter.""" await self.start() @@ -54,16 +62,6 @@ async def start(self): async def stop(self): """Shut down the transport.""" - @property - def wire_format(self) -> BaseWireFormat: - """Accessor for a custom wire format for the transport.""" - return self._wire_format - - @wire_format.setter - def wire_format(self, format: BaseWireFormat): - """Setter for a custom wire format for the transport.""" - self._wire_format = format - @abstractmethod async def handle_message( self, diff --git a/aries_cloudagent/transport/outbound/http.py b/aries_cloudagent/transport/outbound/http.py index 363e59ee2a..63c1c52980 100644 --- a/aries_cloudagent/transport/outbound/http.py +++ b/aries_cloudagent/transport/outbound/http.py @@ -17,6 +17,7 @@ class HttpTransport(BaseOutboundTransport): """Http outbound transport class.""" schemes = ("http", "https") + is_external = False def __init__(self, **kwargs) -> None: """Initialize an `HttpTransport` instance.""" diff --git a/aries_cloudagent/transport/outbound/ws.py b/aries_cloudagent/transport/outbound/ws.py index 4ffcfdac08..6ec531c494 100644 --- a/aries_cloudagent/transport/outbound/ws.py +++ b/aries_cloudagent/transport/outbound/ws.py @@ -14,6 +14,7 @@ class WsTransport(BaseOutboundTransport): """Websockets outbound transport class.""" schemes = ("ws", "wss") + is_external = False def __init__(self, **kwargs) -> None: """Initialize an `WsTransport` instance.""" From 69b913d238c893931380fcf69e5e23b2e2b3874d Mon Sep 17 00:00:00 2001 From: Adam Burdett Date: Wed, 23 Mar 2022 08:35:33 -0600 Subject: [PATCH 096/872] multitenant routes with key der Signed-off-by: Adam Burdett --- aries_cloudagent/config/argparse.py | 14 ++++------ aries_cloudagent/multitenant/admin/routes.py | 29 ++++++++++++-------- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index 8c203a04e0..e9a3a82641 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -1656,16 +1656,14 @@ def get_settings(self, args: Namespace): settings["multitenant.wallet_name"] = multitenancyConfig.get( "wallet_name" ) - + if multitenancyConfig.get("key"): - settings["multitenant.key"] = multitenancyConfig.get( - "key" - ) - + settings["multitenant.key"] = multitenancyConfig.get("key") + if multitenancyConfig.get("key_derivation_method"): - settings["multitenant.key_derivation_method"] = multitenancyConfig.get( - "key_derivation_method" - ) + settings[ + "multitenant.key_derivation_method" + ] = multitenancyConfig.get("key_derivation_method") return settings diff --git a/aries_cloudagent/multitenant/admin/routes.py b/aries_cloudagent/multitenant/admin/routes.py index fd0a8a6b3e..959cc36b4e 100644 --- a/aries_cloudagent/multitenant/admin/routes.py +++ b/aries_cloudagent/multitenant/admin/routes.py @@ -3,25 +3,23 @@ from aiohttp import web from aiohttp_apispec import ( docs, - request_schema, match_info_schema, - response_schema, querystring_schema, + request_schema, + response_schema, ) -from marshmallow import fields, validate, validates_schema, ValidationError +from marshmallow import ValidationError, fields, validate, validates_schema from ...admin.request_context import AdminRequestContext -from ...messaging.valid import JSONWebToken, UUIDFour +from ...core.error import BaseError +from ...core.profile import ProfileManagerProvider from ...messaging.models.base import BaseModelError from ...messaging.models.openapi import OpenAPISchema +from ...messaging.valid import JSONWebToken, UUIDFour from ...multitenant.base import BaseMultitenantManager from ...storage.error import StorageError, StorageNotFoundError -from ...wallet.models.wallet_record import WalletRecord, WalletRecordSchema from ...wallet.error import WalletSettingsError - -from ...core.error import BaseError -from ...core.profile import ProfileManagerProvider - +from ...wallet.models.wallet_record import WalletRecord, WalletRecordSchema from ..error import WalletKeyMissingError @@ -58,6 +56,13 @@ class CreateWalletRequestSchema(OpenAPISchema): description="Master key used for key derivation.", example="MySecretKey123" ) + wallet_key_derivation = fields.Str( + description="Key derivation", + example="RAW", + default="ARGON2I_MOD", + validate=validate.OneOf(["ARGON2I_MOD", "ARGON2I_INT", "RAW"]), + ) + wallet_type = fields.Str( description="Type of the wallet to create", example="indy", @@ -225,8 +230,7 @@ async def wallets_list(request: web.BaseRequest): profile = context.profile query = {} - wallet_name = request.query.get("wallet_name") - if wallet_name: + if wallet_name := request.query.get("wallet_name"): query["wallet_name"] = wallet_name try: @@ -297,6 +301,9 @@ async def wallet_create(request: web.BaseRequest): "wallet.type": body.get("wallet_type") or "in_memory", "wallet.name": body.get("wallet_name"), "wallet.key": wallet_key, + "wallet.key_derivation_method": body.get( + "wallet_key_derivation", "ARGON2I_MOD" + ), "wallet.webhook_urls": wallet_webhook_urls, "wallet.dispatch_type": wallet_dispatch_type, } From 70f535417fbcf05ff2432469630882bd78eb02f9 Mon Sep 17 00:00:00 2001 From: Adam Burdett Date: Wed, 23 Mar 2022 08:51:38 -0600 Subject: [PATCH 097/872] multitenant wallet record key derivation prop Signed-off-by: Adam Burdett --- aries_cloudagent/wallet/models/wallet_record.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/wallet/models/wallet_record.py b/aries_cloudagent/wallet/models/wallet_record.py index 3421968d34..3620fb54f3 100644 --- a/aries_cloudagent/wallet/models/wallet_record.py +++ b/aries_cloudagent/wallet/models/wallet_record.py @@ -36,7 +36,7 @@ def __init__( wallet_id: str = None, key_management_mode: str = None, settings: dict = None, - # MTODO: how to make this a tag without making it + # TODO: how to make this a tag without making it # a constructor param wallet_name: str = None, **kwargs, @@ -81,6 +81,11 @@ def wallet_key(self) -> Optional[str]: """Accessor for the key of the wallet.""" return self.settings.get("wallet.key") + @property + def wallet_key_derivation_method(self): + """Accessor for the key derivation method of the wallet.""" + return self.settings.get("wallet.key_derivation_method") + @property def record_value(self) -> dict: """Accessor for the JSON record value generated for this record.""" From 7a68fcffcbcba4642bcc1087e96a5f5eb404cbf9 Mon Sep 17 00:00:00 2001 From: Micah Peltier Date: Wed, 23 Mar 2022 09:24:50 -0600 Subject: [PATCH 098/872] feat: emitting OutboundSendStatus events Signed-off-by: Micah Peltier --- aries_cloudagent/core/conductor.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/aries_cloudagent/core/conductor.py b/aries_cloudagent/core/conductor.py index 51e39d80bf..9aed9a9e77 100644 --- a/aries_cloudagent/core/conductor.py +++ b/aries_cloudagent/core/conductor.py @@ -70,7 +70,7 @@ from .util import STARTUP_EVENT_TOPIC, SHUTDOWN_EVENT_TOPIC LOGGER = logging.getLogger(__name__) -UNDELIVERABLE_EVENT_TOPIC = "acapy::outbound-message::undeliverable" +OUTBOUND_STATUS_PREFIX = "acapy::outbound-message::" class Conductor: @@ -603,6 +603,10 @@ async def outbound_message_router( outbound.reply_from_verkey = inbound.receipt.recipient_verkey # return message to an inbound session if self.inbound_transport_manager.return_to_session(outbound): + await profile.notify( + f"{OUTBOUND_STATUS_PREFIX}{OutboundSendStatus.SENT_TO_SESSION}", + outbound, + ) return OutboundSendStatus.SENT_TO_SESSION if not outbound.to_session_only: @@ -680,6 +684,10 @@ async def _queue_external( encoded_outbound_message.payload, target.endpoint ) + await profile.notify( + f"{OUTBOUND_STATUS_PREFIX}{OutboundSendStatus.SENT_TO_EXTERNAL_QUEUE}", + outbound, + ) return OutboundSendStatus.SENT_TO_EXTERNAL_QUEUE async def _queue_internal( @@ -688,6 +696,10 @@ async def _queue_internal( """Save the message to an internal outbound queue.""" try: self.outbound_transport_manager.enqueue_message(profile, outbound) + await profile.notify( + f"{OUTBOUND_STATUS_PREFIX}{OutboundSendStatus.QUEUED_FOR_DELIVERY}", + outbound, + ) return OutboundSendStatus.QUEUED_FOR_DELIVERY except OutboundDeliveryError: LOGGER.warning("Cannot queue message for delivery, no supported transport") @@ -698,12 +710,13 @@ async def handle_not_delivered( ) -> OutboundSendStatus: """Handle a message that failed delivery via outbound transports.""" queued_for_inbound = self.inbound_transport_manager.return_undelivered(outbound) - await profile.notify(UNDELIVERABLE_EVENT_TOPIC, outbound) - return ( + status = ( OutboundSendStatus.WAITING_FOR_PICKUP if queued_for_inbound else OutboundSendStatus.UNDELIVERABLE ) + await profile.notify(f"{OUTBOUND_STATUS_PREFIX}{status}", outbound) + return status def webhook_router( self, From 2e75d978b48efd08cb297de0d1af91a862d33be0 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Thu, 24 Mar 2022 10:05:14 -0700 Subject: [PATCH 099/872] Remove references to play with von Signed-off-by: Ian Costanzo --- demo/AliceGetsAPhone.md | 4 ++-- demo/README.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/demo/AliceGetsAPhone.md b/demo/AliceGetsAPhone.md index 4f0b63d710..fee1074b64 100644 --- a/demo/AliceGetsAPhone.md +++ b/demo/AliceGetsAPhone.md @@ -206,7 +206,7 @@ https://abfde260.ngrok.io?c_i=eyJAdHlwZSI6ICJkaWQ6c292OkJ6Q2JzTlloTXJqSGlxWkRUVU Or this: ```bash -http://ip10-0-121-4-bquqo816b480a4bfn3kg-8020.direct.play-with-von.vonx.io?c_i=eyJAdHlwZSI6ICJkaWQ6c292OkJ6Q2JzTlloTXJqSGlxWkRUVUFTSGc7c3BlYy9jb25uZWN0aW9ucy8xLjAvaW52aXRhdGlvbiIsICJAaWQiOiAiZWI2MTI4NDUtYmU1OC00YTNiLTk2MGUtZmE3NDUzMGEwNzkyIiwgInJlY2lwaWVudEtleXMiOiBbIkFacEdoMlpIOTJVNnRFRTlmYk13Z3BqQkp3TEUzRFJIY1dCbmg4Y2FqdzNiIl0sICJzZXJ2aWNlRW5kcG9pbnQiOiAiaHR0cDovL2lwMTAtMC0xMjEtNC1icXVxbzgxNmI0ODBhNGJmbjNrZy04MDIwLmRpcmVjdC5wbGF5LXdpdGgtdm9uLnZvbnguaW8iLCAibGFiZWwiOiAiRmFiZXIuQWdlbnQifQ== +http://ip10-0-121-4-bquqo816b480a4bfn3kg-8020.direct.play-with-docker.com?c_i=eyJAdHlwZSI6ICJkaWQ6c292OkJ6Q2JzTlloTXJqSGlxWkRUVUFTSGc7c3BlYy9jb25uZWN0aW9ucy8xLjAvaW52aXRhdGlvbiIsICJAaWQiOiAiZWI2MTI4NDUtYmU1OC00YTNiLTk2MGUtZmE3NDUzMGEwNzkyIiwgInJlY2lwaWVudEtleXMiOiBbIkFacEdoMlpIOTJVNnRFRTlmYk13Z3BqQkp3TEUzRFJIY1dCbmg4Y2FqdzNiIl0sICJzZXJ2aWNlRW5kcG9pbnQiOiAiaHR0cDovL2lwMTAtMC0xMjEtNC1icXVxbzgxNmI0ODBhNGJmbjNrZy04MDIwLmRpcmVjdC5wbGF5LXdpdGgtdm9uLnZvbnguaW8iLCAibGFiZWwiOiAiRmFiZXIuQWdlbnQifQ== ``` Note that this will use the ngrok endpoint if you are running locally, or your PWD endpoint if you are running on PWD. @@ -306,7 +306,7 @@ Once that is done, try sending another proof request and see what happens! Exper A connectionless proof request works the same way as a regular proof request, however it does not require a connection to be established between the Verifier and Holder/Prover. -This is supported in the Faber demo, however note that it will only work when running Faber on the Docker playground service [Play with Docker](https://labs.play-with-docker.com/) (or on [Play with VON](http://play-with-von.vonx.io)). (This is because both the Faber agent *and* controller both need to be exposed to the mobile agent.) +This is supported in the Faber demo, however note that it will only work when running Faber on the Docker playground service [Play with Docker](https://labs.play-with-docker.com/). (This is because both the Faber agent *and* controller both need to be exposed to the mobile agent.) If you have gone through the above steps, you can delete the Faber connection in your mobile agent (however *do not* delete the credential that Faber issued to you). diff --git a/demo/README.md b/demo/README.md index 83c39d02d4..9791504592 100644 --- a/demo/README.md +++ b/demo/README.md @@ -41,7 +41,7 @@ The Alice/Faber demo is the (in)famous first verifiable credentials demo. Alice, ### Running in a Browser -In your browser, go to the docker playground service [Play with VON](http://play-with-von.vonx.io) (from the BC Gov). On the title screen, click "Start". On the next screen, click (in the left menu) "+Add a new instance". That will start up a terminal in your browser. Run the following commands to start the Faber agent: +In your browser, go to the docker playground service [Play with Docker](https://labs.play-with-docker.com/). On the title screen, click "Start". On the next screen, click (in the left menu) "+Add a new instance". That will start up a terminal in your browser. Run the following commands to start the Faber agent: ```bash git clone https://github.com/hyperledger/aries-cloudagent-python From 286f23ba1d361f1c0669cb70ba876bd794ec1e94 Mon Sep 17 00:00:00 2001 From: Micah Peltier Date: Thu, 24 Mar 2022 13:03:50 -0600 Subject: [PATCH 100/872] feat: added testing paths for events added in conductor Signed-off-by: Micah Peltier --- aries_cloudagent/core/tests/test_conductor.py | 68 ++++++++++++++++--- 1 file changed, 60 insertions(+), 8 deletions(-) diff --git a/aries_cloudagent/core/tests/test_conductor.py b/aries_cloudagent/core/tests/test_conductor.py index d3f17d499b..10bc51ae8c 100644 --- a/aries_cloudagent/core/tests/test_conductor.py +++ b/aries_cloudagent/core/tests/test_conductor.py @@ -15,6 +15,7 @@ PublicKeyType, Service, ) +from ...core.event_bus import EventBus, MockEventBus from ...core.in_memory import InMemoryProfileManager from ...core.profile import ProfileManager from ...core.protocol_registry import ProtocolRegistry @@ -34,6 +35,7 @@ from ...transport.outbound.base import OutboundDeliveryError from ...transport.outbound.manager import QueuedOutboundMessage from ...transport.outbound.message import OutboundMessage +from ...transport.outbound.status import OutboundSendStatus from ...transport.wire_format import BaseWireFormat from ...transport.pack_format import PackWireFormat from ...utils.stats import Collector @@ -44,6 +46,8 @@ from .. import conductor as test_module +EVENT_PREFIX = test_module.OUTBOUND_STATUS_PREFIX + class Config: test_settings = {"admin.webhook_urls": ["http://sample.webhook.ca"]} @@ -92,6 +96,7 @@ async def build_context(self) -> InjectionContext: context.injector.bind_instance(ProtocolRegistry, ProtocolRegistry()) context.injector.bind_instance(BaseWireFormat, self.wire_format) context.injector.bind_instance(DIDResolver, DIDResolver(DIDResolverRegistry())) + context.injector.bind_instance(EventBus, MockEventBus()) return context @@ -297,6 +302,8 @@ async def test_outbound_message_handler_return_route(self): await conductor.setup() + bus = conductor.root_profile.inject(EventBus) + payload = "{}" message = OutboundMessage(payload=payload) message.reply_to_verkey = test_to_verkey @@ -310,7 +317,17 @@ async def test_outbound_message_handler_return_route(self): conductor, "queue_outbound", async_mock.CoroutineMock() ) as mock_queue: mock_return.return_value = True - await conductor.outbound_message_router(conductor.context, message) + + status = await conductor.outbound_message_router( + conductor.root_profile, message + ) + assert status == OutboundSendStatus.SENT_TO_SESSION + assert bus.events + assert ( + bus.events[0][1].topic + == f"{EVENT_PREFIX}{OutboundSendStatus.SENT_TO_SESSION}" + ) + assert bus.events[0][1].payload == message mock_return.assert_called_once_with(message) mock_queue.assert_not_awaited() @@ -324,16 +341,27 @@ async def test_outbound_message_handler_with_target(self): await conductor.setup() + bus = conductor.root_profile.inject(EventBus) + payload = "{}" target = ConnectionTarget( endpoint="endpoint", recipient_keys=(), routing_keys=(), sender_key="" ) message = OutboundMessage(payload=payload, target=target) - await conductor.outbound_message_router(conductor.context, message) - + status = await conductor.outbound_message_router( + conductor.root_profile, message + ) + assert status == OutboundSendStatus.QUEUED_FOR_DELIVERY + assert bus.events + print(bus.events) + assert ( + bus.events[0][1].topic + == f"{EVENT_PREFIX}{OutboundSendStatus.QUEUED_FOR_DELIVERY}" + ) + assert bus.events[0][1].payload == message mock_outbound_mgr.return_value.enqueue_message.assert_called_once_with( - conductor.context, message + conductor.root_profile, message ) async def test_outbound_message_handler_with_connection(self): @@ -348,11 +376,24 @@ async def test_outbound_message_handler_with_connection(self): await conductor.setup() + bus = conductor.root_profile.inject(EventBus) + payload = "{}" connection_id = "connection_id" message = OutboundMessage(payload=payload, connection_id=connection_id) - await conductor.outbound_message_router(conductor.root_profile, message) + status = await conductor.outbound_message_router( + conductor.root_profile, message + ) + + assert status == OutboundSendStatus.QUEUED_FOR_DELIVERY + assert bus.events + print(bus.events) + assert ( + bus.events[0][1].topic + == f"{EVENT_PREFIX}{OutboundSendStatus.QUEUED_FOR_DELIVERY}" + ) + assert bus.events[0][1].payload == message conn_mgr.return_value.get_connection_targets.assert_awaited_once_with( connection_id=connection_id @@ -376,21 +417,32 @@ async def test_outbound_message_handler_with_verkey_no_target(self): await conductor.setup() + bus = conductor.root_profile.inject(EventBus) + payload = "{}" message = OutboundMessage( payload=payload, reply_to_verkey=TestDIDs.test_verkey ) - await conductor.outbound_message_router( - conductor.context, + status = await conductor.outbound_message_router( + conductor.root_profile, message, inbound=async_mock.MagicMock( receipt=async_mock.MagicMock(recipient_verkey=TestDIDs.test_verkey) ), ) + assert status == OutboundSendStatus.QUEUED_FOR_DELIVERY + assert bus.events + print(bus.events) + assert ( + bus.events[0][1].topic + == f"{EVENT_PREFIX}{OutboundSendStatus.QUEUED_FOR_DELIVERY}" + ) + assert bus.events[0][1].payload == message + mock_outbound_mgr.return_value.enqueue_message.assert_called_once_with( - conductor.context, message + conductor.root_profile, message ) async def test_handle_nots(self): From 0b62eb35c23930e89313d191385305b8139e0b03 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Thu, 24 Mar 2022 13:14:40 -0700 Subject: [PATCH 101/872] verify signature from kid Signed-off-by: Shaanjot Gill --- .../messaging/decorators/attach_decorator.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/aries_cloudagent/messaging/decorators/attach_decorator.py b/aries_cloudagent/messaging/decorators/attach_decorator.py index b0fb504cf1..9f173e72ff 100644 --- a/aries_cloudagent/messaging/decorators/attach_decorator.py +++ b/aries_cloudagent/messaging/decorators/attach_decorator.py @@ -428,7 +428,7 @@ async def verify(self, wallet: BaseWallet, signer_verkey: str = None) -> bool: assert self.jws b64_payload = unpad(set_urlsafe_b64(self.base64, True)) - verkey_list = [] + verkey_to_check = None for sig in [self.jws] if self.signatures == 1 else self.jws.signatures: b64_protected = sig.protected b64_sig = sig.signature @@ -438,12 +438,17 @@ async def verify(self, wallet: BaseWallet, signer_verkey: str = None) -> bool: sign_input = (b64_protected + "." + b64_payload).encode("ascii") b_sig = b64_to_bytes(b64_sig, urlsafe=True) verkey = bytes_to_b58(b64_to_bytes(protected["jwk"]["x"], urlsafe=True)) - verkey_list.append(verkey) + encoded_pk = DIDKey.from_did(protected["jwk"]["kid"]).public_key_b58 + verkey_to_check = encoded_pk if not await wallet.verify_message( sign_input, b_sig, verkey, KeyType.ED25519 ): return False - if signer_verkey and signer_verkey not in verkey_list: + if not await wallet.verify_message( + sign_input, b_sig, encoded_pk, KeyType.ED25519 + ): + return False + if signer_verkey and signer_verkey != verkey_to_check: return False return True From 028fc060c09c658e784630cbef67b6e1a68815ae Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Thu, 24 Mar 2022 14:54:52 -0700 Subject: [PATCH 102/872] retrigger checks Signed-off-by: Shaanjot Gill From f4f6c1936897de2e1e1f72249fbe285e18fdf357 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Thu, 24 Mar 2022 15:38:51 -0700 Subject: [PATCH 103/872] Update integration tests for proof of non-revocation for a mixed proof Signed-off-by: Ian Costanzo --- demo/features/0454-present-proof.feature | 29 +++++++++++++--- ...uest_DL_age_over_19_v2_with_health_id.json | 3 ++ demo/runners/agent_container.py | 33 ++++++++++++++++++- 3 files changed, 60 insertions(+), 5 deletions(-) diff --git a/demo/features/0454-present-proof.feature b/demo/features/0454-present-proof.feature index ad8d16e3f9..a95686da00 100644 --- a/demo/features/0454-present-proof.feature +++ b/demo/features/0454-present-proof.feature @@ -97,14 +97,14 @@ Feature: RFC 0454 Aries agent present proof | Acme | --revocation --public-did --mediation | | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | | Acme | --revocation --public-did --multitenant | --multitenant | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | - @T003-RFC0454 + @T003-RFC0454.1 Scenario Outline: Present Proof for multiple credentials where the one is revocable and one isn't Given we have "4" agents | name | role | capabilities | | Acme1 | issuer1 | | | Acme2 | issuer2 | | | Faber | verifier | | - | Bob | prover | | + | Bob | prover | | And "" and "Bob" have an existing connection And "Bob" has an issued credential from "" And "" and "Bob" have an existing connection @@ -114,5 +114,26 @@ Feature: RFC 0454 Aries agent present proof Then "Faber" has the proof verified Examples: - | issuer1 | Acme1_capabilities | issuer2 | Acme2_capabilities | Bob_capabilities | Schema_name_1 | Credential_data_1 | Schema_name_2 | Credential_data_2 | Proof_request | - | Acme1 | --revocation --public-did | Acme2 | --public-did | | driverslicense_v2 | Data_DL_MaxValues | health_id | Data_DL_MaxValues | DL_age_over_19_v2_with_health_id | + | issuer1 | Acme1_capabilities | issuer2 | Acme2_capabilities | Bob_cap | Schema_name_1 | Credential_data_1 | Schema_name_2 | Credential_data_2 | Proof_request | + | Acme1 | --revocation --public-did | Acme2 | --public-did | | driverslicense_v2 | Data_DL_MaxValues | health_id | Data_DL_MaxValues | DL_age_over_19_v2_with_health_id | + + @T003-RFC0454.2 + Scenario Outline: Present Proof for multiple credentials where the one is revocable and one isn't, and the revocable credential is revoked + Given we have "4" agents + | name | role | capabilities | + | Acme1 | issuer1 | | + | Acme2 | issuer2 | | + | Faber | verifier | | + | Bob | prover | | + And "" and "Bob" have an existing connection + And "Bob" has an issued credential from "" + And "" revokes the credential + And "" and "Bob" have an existing connection + And "Bob" has an issued credential from "" + And "Faber" and "Bob" have an existing connection + When "Faber" sends a request for proof presentation to "Bob" + Then "Faber" has the proof verification fail + + Examples: + | issuer1 | Acme1_capabilities | issuer2 | Acme2_capabilities | Bob_cap | Schema_name_1 | Credential_data_1 | Schema_name_2 | Credential_data_2 | Proof_request | + | Acme1 | --revocation --public-did | Acme2 | --public-did | | driverslicense_v2 | Data_DL_MaxValues | health_id | Data_DL_MaxValues | DL_age_over_19_v2_with_health_id | diff --git a/demo/features/data/proof_request_DL_age_over_19_v2_with_health_id.json b/demo/features/data/proof_request_DL_age_over_19_v2_with_health_id.json index e52e1e245a..8130595ff8 100644 --- a/demo/features/data/proof_request_DL_age_over_19_v2_with_health_id.json +++ b/demo/features/data/proof_request_DL_age_over_19_v2_with_health_id.json @@ -3,6 +3,7 @@ "requested_attributes": { "address_attrs": { "name": "address", + "non_revoked": "change-me", "restrictions": [ { "schema_name": "Schema_DriversLicense_v2", @@ -12,6 +13,7 @@ }, "health_attrs": { "name": "health_id_num", + "non_revoked": "change-me", "restrictions": [ { "schema_name": "Schema_Health_ID", @@ -25,6 +27,7 @@ "name": "age", "p_type": ">", "p_value": 19, + "non_revoked": "change-me", "restrictions": [ { "schema_name": "Schema_DriversLicense_v2", diff --git a/demo/runners/agent_container.py b/demo/runners/agent_container.py index 33f245e91d..8a9daf0f5d 100644 --- a/demo/runners/agent_container.py +++ b/demo/runners/agent_container.py @@ -866,7 +866,38 @@ async def request_proof(self, proof_request): } if self.revocation: - indy_proof_request["non_revoked"] = {"to": int(time.time())} + non_revoked_supplied = False + # plug in revocation where requested in the supplied proof request + non_revoked = {"to": int(time.time())} + if "non_revoked" in proof_request: + indy_proof_request["non_revoked"] = non_revoked + non_revoked_supplied = True + for attr in proof_request["requested_attributes"]: + if "non_revoked" in proof_request["requested_attributes"][attr]: + indy_proof_request["requested_attributes"][attr]["non_revoked"] = non_revoked + non_revoked_supplied = True + for pred in proof_request["requested_predicates"]: + if "non_revoked" in proof_request["requested_predicates"][pred]: + indy_proof_request["requested_predicates"][pred]["non_revoked"] = non_revoked + non_revoked_supplied = True + + if not non_revoked_supplied: + # else just make it global + indy_proof_request["non_revoked"] = non_revoked + + else: + # make sure we are not leaking non-revoc requests + if "non_revoked" in proof_request: + del proof_request["non_revoked"] + for attr in proof_request["requested_attributes"]: + if "non_revoked" in proof_request["requested_attributes"][attr]: + del proof_request["requested_attributes"][attr]["non_revoked"] + for pred in proof_request["requested_predicates"]: + if "non_revoked" in proof_request["requested_predicates"][pred]: + del proof_request["requested_predicates"][pred]["non_revoked"] + + log_status(f" >>> asking for proof for request: {indy_proof_request}") + proof_request_web_request = { "connection_id": self.agent.connection_id, "presentation_request": { From d0ba818655ac66173bf2bf74b6e8c10abbab61a0 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Thu, 24 Mar 2022 15:42:56 -0700 Subject: [PATCH 104/872] Formatting fix Signed-off-by: Ian Costanzo --- demo/runners/agent_container.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/demo/runners/agent_container.py b/demo/runners/agent_container.py index 8a9daf0f5d..e683ca2850 100644 --- a/demo/runners/agent_container.py +++ b/demo/runners/agent_container.py @@ -874,11 +874,15 @@ async def request_proof(self, proof_request): non_revoked_supplied = True for attr in proof_request["requested_attributes"]: if "non_revoked" in proof_request["requested_attributes"][attr]: - indy_proof_request["requested_attributes"][attr]["non_revoked"] = non_revoked + indy_proof_request["requested_attributes"][attr][ + "non_revoked" + ] = non_revoked non_revoked_supplied = True for pred in proof_request["requested_predicates"]: if "non_revoked" in proof_request["requested_predicates"][pred]: - indy_proof_request["requested_predicates"][pred]["non_revoked"] = non_revoked + indy_proof_request["requested_predicates"][pred][ + "non_revoked" + ] = non_revoked non_revoked_supplied = True if not non_revoked_supplied: From 48fc1130deeda7d3aea569a707a3f5232638e5a0 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Fri, 25 Mar 2022 00:55:51 -0700 Subject: [PATCH 105/872] initial commit Signed-off-by: Shaanjot Gill --- .../present_proof/dif/pres_proposal_schema.py | 6 +- .../present_proof/v2_0/formats/dif/handler.py | 9 ++- .../v2_0/formats/dif/tests/test_handler.py | 60 ++++++++++++++++++- 3 files changed, 72 insertions(+), 3 deletions(-) diff --git a/aries_cloudagent/protocols/present_proof/dif/pres_proposal_schema.py b/aries_cloudagent/protocols/present_proof/dif/pres_proposal_schema.py index 4f48851001..f8c227e150 100644 --- a/aries_cloudagent/protocols/present_proof/dif/pres_proposal_schema.py +++ b/aries_cloudagent/protocols/present_proof/dif/pres_proposal_schema.py @@ -3,7 +3,7 @@ from ....messaging.models.openapi import OpenAPISchema -from .pres_exch import InputDescriptorsSchema +from .pres_exch import InputDescriptorsSchema, DIFOptionsSchema class DIFProofProposalSchema(OpenAPISchema): @@ -16,3 +16,7 @@ class DIFProofProposalSchema(OpenAPISchema): ), required=False, ) + options = fields.Nested( + DIFOptionsSchema(), + required=False, + ) diff --git a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/handler.py b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/handler.py index 33f1b653d0..831bc4ce33 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/handler.py @@ -151,9 +151,16 @@ async def create_bound_request( A tuple (updated presentation exchange record, presentation request message) """ - dif_proof_request = pres_ex_record.pres_proposal.attachment( + dif_proof_request = {} + pres_proposal_dict = pres_ex_record.pres_proposal.attachment( DIFPresFormatHandler.format ) + if "options" not in pres_proposal_dict: + dif_proof_request["options"] = {"challenge": str(uuid4())} + else: + dif_proof_request["options"] = pres_proposal_dict["options"] + del pres_proposal_dict["options"] + dif_proof_request["presentation_definition"] = pres_proposal_dict return self.get_format_data(PRES_20_REQUEST, dif_proof_request) diff --git a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py index 4ac318c506..ef12014838 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py @@ -284,7 +284,7 @@ async def test_get_all_suites(self): for suite in suites: assert type(suite) in types - async def test_create_bound_request(self): + async def test_create_bound_request_a(self): dif_proposal_dict = { "input_descriptors": [ { @@ -339,6 +339,64 @@ async def test_create_bound_request(self): output[1], AttachDecorator ) + async def test_create_bound_request_b(self): + dif_proposal_dict = { + "options": { + "challenge": "test123" + }, + "input_descriptors": [ + { + "id": "citizenship_input_1", + "name": "EU Driver's License", + "group": ["A"], + "schema": [ + { + "uri": "https://www.w3.org/2018/credentials#VerifiableCredential" + } + ], + "constraints": { + "limit_disclosure": "required", + "fields": [ + { + "path": ["$.credentialSubject.givenName"], + "purpose": "The claim must be from one of the specified issuers", + "filter": {"type": "string", "enum": ["JOHN", "CAI"]}, + } + ], + }, + } + ] + } + dif_pres_proposal = V20PresProposal( + formats=[ + V20PresFormat( + attach_id="dif", + format_=ATTACHMENT_FORMAT[PRES_20_PROPOSAL][ + V20PresFormat.Format.DIF.api + ], + ) + ], + proposals_attach=[ + AttachDecorator.data_json(dif_proposal_dict, ident="dif") + ], + ) + record = V20PresExRecord( + pres_ex_id="pxid", + thread_id="thid", + connection_id="conn_id", + initiator="init", + role="role", + state="state", + pres_proposal=dif_pres_proposal, + verified="false", + auto_present=True, + error_msg="error", + ) + output = await self.handler.create_bound_request(pres_ex_record=record) + assert isinstance(output[0], V20PresFormat) and isinstance( + output[1], AttachDecorator + ) + async def test_create_pres(self): dif_pres_request = V20PresRequest( formats=[ From 149e567c646e50a0c705663712339074d34b81bb Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Fri, 25 Mar 2022 01:26:07 -0700 Subject: [PATCH 106/872] format fix Signed-off-by: Shaanjot Gill --- .../present_proof/v2_0/formats/dif/tests/test_handler.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py index ef12014838..cc50421b61 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py @@ -341,9 +341,7 @@ async def test_create_bound_request_a(self): async def test_create_bound_request_b(self): dif_proposal_dict = { - "options": { - "challenge": "test123" - }, + "options": {"challenge": "test123"}, "input_descriptors": [ { "id": "citizenship_input_1", @@ -365,7 +363,7 @@ async def test_create_bound_request_b(self): ], }, } - ] + ], } dif_pres_proposal = V20PresProposal( formats=[ From fd057e25a766383b9b22f4677cb27874f5ec0e13 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Fri, 25 Mar 2022 11:42:57 -0400 Subject: [PATCH 107/872] feat: profile cache only on non askar-profile remove usage of self._instances in AskarProfileMultitenantManager add profile_id to AskarProfile init (to clearly differentiate between AskarProfiles where default profile is used and alternate profiles are used) base update_wallet only updates records; managers where profiles are held open are responsible for updating currently open profiles Signed-off-by: Daniel Bluhm --- aries_cloudagent/askar/profile.py | 23 ++++++--- .../multitenant/askar_profile_manager.py | 47 ++++++++++++++----- aries_cloudagent/multitenant/base.py | 25 ++++------ aries_cloudagent/multitenant/manager.py | 47 +++++++++++++++++-- 4 files changed, 102 insertions(+), 40 deletions(-) diff --git a/aries_cloudagent/askar/profile.py b/aries_cloudagent/askar/profile.py index b36d151fca..6277a936df 100644 --- a/aries_cloudagent/askar/profile.py +++ b/aries_cloudagent/askar/profile.py @@ -36,11 +36,18 @@ class AskarProfile(Profile): BACKEND_NAME = "askar" - def __init__(self, opened: AskarOpenStore, context: InjectionContext = None): + def __init__( + self, + opened: AskarOpenStore, + context: InjectionContext = None, + *, + profile_id: str = None + ): """Create a new AskarProfile instance.""" super().__init__(context=context, name=opened.name, created=opened.created) self.opened = opened self.ledger_pool: IndyVdrLedgerPool = None + self.profile_id = profile_id self.init_ledger_pool() self.bind_providers() @@ -56,8 +63,8 @@ def store(self) -> Store: async def remove(self): """Remove the profile.""" - if self.settings.get("multitenant.wallet_type") == "askar-profile": - await self.store.remove_profile(self.settings.get("wallet.askar_profile")) + if self.profile_id: + await self.store.remove_profile(self.profile_id) def init_ledger_pool(self): """Initialize the ledger pool.""" @@ -151,6 +158,11 @@ def finalizer(self) -> Optional[finalize]: See docs for weakref.finalize for more details on behavior of finalizers. """ + # Askar Profiles (not to be confused with AskarProfiles) don't need + # additional clean up + + if self.profile_id: + return None def _finalize(opened: Optional[AskarOpenStore]): if opened: @@ -172,11 +184,10 @@ def __init__( ): """Create a new IndySdkProfileSession instance.""" super().__init__(profile=profile, context=context, settings=settings) - profile_id = profile.context.settings.get("wallet.askar_profile") if is_txn: - self._opener = self.profile.store.transaction(profile_id) + self._opener = self.profile.store.transaction(profile.profile_id) else: - self._opener = self.profile.store.session(profile_id) + self._opener = self.profile.store.session(profile.profile_id) self._handle: Session = None self._acquire_start: float = None self._acquire_end: float = None diff --git a/aries_cloudagent/multitenant/askar_profile_manager.py b/aries_cloudagent/multitenant/askar_profile_manager.py index c692b04ea8..d7d13b964c 100644 --- a/aries_cloudagent/multitenant/askar_profile_manager.py +++ b/aries_cloudagent/multitenant/askar_profile_manager.py @@ -1,5 +1,6 @@ """Manager for askar profile multitenancy mode.""" +from typing import Iterable, Optional, cast from ..core.profile import ( Profile, ) @@ -15,13 +16,23 @@ class AskarProfileMultitenantManager(BaseMultitenantManager): DEFAULT_MULTIENANT_WALLET_NAME = "multitenant_sub_wallet" - def __init__(self, profile: Profile): + def __init__(self, profile: Profile, multitenant_profile: AskarProfile = None): """Initialize askar profile multitenant Manager. Args: profile: The base profile for this manager """ super().__init__(profile) + self._multitenant_profile: Optional[AskarProfile] = multitenant_profile + + @property + def open_profiles(self) -> Iterable[Profile]: + """Return iterator over open profiles. + + Only the core multitenant profile is considered open. + """ + if self._multitenant_profile: + yield self._multitenant_profile async def get_wallet_profile( self, @@ -33,6 +44,13 @@ async def get_wallet_profile( ) -> Profile: """Get Askar profile for a wallet record. + An object of type AskarProfile is returned but this should not be + confused with the underlying profile mechanism provided by Askar that + enables multiple "profiles" to share a wallet. Usage of this mechanism + is what causes this implementation of BaseMultitenantManager.get_wallet_profile + to look different from others, especially since no explicit clean up is + required for profiles that are no longer in use. + Args: base_context: Base context to extend from wallet_record: Wallet record to get the context for @@ -42,12 +60,10 @@ async def get_wallet_profile( Profile: Profile for the wallet record """ - multitenant_wallet_name = ( - base_context.settings.get("multitenant.wallet_name") - or self.DEFAULT_MULTIENANT_WALLET_NAME - ) - - if multitenant_wallet_name not in self._instances: + if not self._multitenant_profile: + multitenant_wallet_name = base_context.settings.get( + "multitenant.wallet_name", self.DEFAULT_MULTIENANT_WALLET_NAME + ) context = base_context.copy() sub_wallet_settings = { "wallet.recreate": False, @@ -65,13 +81,14 @@ async def get_wallet_profile( context.settings = context.settings.extend(sub_wallet_settings) profile, _ = await wallet_config(context, provision=False) - self._instances[multitenant_wallet_name] = profile + self._multitenant_profile = cast(AskarProfile, profile) - multitenant_wallet = self._instances[multitenant_wallet_name] - profile_context = multitenant_wallet.context.copy() + profile_context = self._multitenant_profile.context.copy() if provision: - await multitenant_wallet.store.create_profile(wallet_record.wallet_id) + await self._multitenant_profile.store.create_profile( + wallet_record.wallet_id + ) extra_settings = { "admin.webhook_urls": self.get_webhook_urls(base_context, wallet_record), @@ -82,7 +99,13 @@ async def get_wallet_profile( wallet_record.settings ).extend(extra_settings) - return AskarProfile(multitenant_wallet.opened, profile_context) + assert self._multitenant_profile.opened + + return AskarProfile( + self._multitenant_profile.opened, + profile_context, + profile_id=wallet_record.wallet_id, + ) async def remove_wallet_profile(self, profile: Profile): """Remove the wallet profile instance. diff --git a/aries_cloudagent/multitenant/base.py b/aries_cloudagent/multitenant/base.py index 5bdd9a9787..6788f325a5 100644 --- a/aries_cloudagent/multitenant/base.py +++ b/aries_cloudagent/multitenant/base.py @@ -1,10 +1,10 @@ """Manager for multitenancy.""" import logging -from abc import abstractmethod +from abc import abstractmethod, ABC, abstractproperty import jwt -from typing import List, Optional, cast +from typing import Iterable, List, Optional, cast from ..core.profile import ( Profile, @@ -34,7 +34,7 @@ class MultitenantManagerError(BaseError): """Generic multitenant error.""" -class BaseMultitenantManager: +class BaseMultitenantManager(ABC): """Base class for handling multitenancy.""" def __init__(self, profile: Profile): @@ -47,7 +47,10 @@ def __init__(self, profile: Profile): if not profile: raise MultitenantManagerError("Missing profile") - self._instances: dict[str, Profile] = {} + @abstractproperty + def open_profiles(self) -> Iterable[Profile]: + """Return iterator over open profiles.""" + ... async def get_default_mediator(self) -> Optional[MediationRecord]: """Retrieve the default mediator used for subwallet routing. @@ -211,7 +214,7 @@ async def update_wallet( wallet_id: str, new_settings: dict, ) -> WalletRecord: - """Update a existing wallet and wallet record. + """Update an existing wallet record. Args: wallet_id: The wallet id of the wallet record @@ -227,18 +230,6 @@ async def update_wallet( wallet_record.update_settings(new_settings) await wallet_record.save(session) - # update profile only if loaded - if wallet_id in self._instances: - profile = self._instances[wallet_id] - profile.settings.update(wallet_record.settings) - - extra_settings = { - "admin.webhook_urls": self.get_webhook_urls( - self._profile.context, wallet_record - ), - } - profile.settings.update(extra_settings) - return wallet_record async def remove_wallet(self, wallet_id: str, wallet_key: str = None): diff --git a/aries_cloudagent/multitenant/manager.py b/aries_cloudagent/multitenant/manager.py index 5bbbcc6632..e3e4418186 100644 --- a/aries_cloudagent/multitenant/manager.py +++ b/aries_cloudagent/multitenant/manager.py @@ -1,5 +1,6 @@ """Manager for multitenancy.""" +from typing import Iterable from ..core.profile import ( Profile, ) @@ -8,6 +9,8 @@ from ..wallet.models.wallet_record import WalletRecord from ..multitenant.base import BaseMultitenantManager +from .cache import ProfileCache + class MultitenantManager(BaseMultitenantManager): """Class for handling multitenancy.""" @@ -19,6 +22,11 @@ def __init__(self, profile: Profile): profile: The profile for this manager """ super().__init__(profile) + self._profiles = ProfileCache(100) + + def open_profiles(self) -> Iterable[Profile]: + """Return iterator over open profiles.""" + yield from self._profiles.profiles.values() async def get_wallet_profile( self, @@ -40,7 +48,8 @@ async def get_wallet_profile( """ wallet_id = wallet_record.wallet_id - if wallet_id not in self._instances: + profile = self._profiles.get(wallet_id) + if not profile: # Extend base context context = base_context.copy() @@ -68,9 +77,37 @@ async def get_wallet_profile( # MTODO: add ledger config profile, _ = await wallet_config(context, provision=provision) - self._instances[wallet_id] = profile + self._profiles.put(wallet_id, profile) + + return profile + + async def update_wallet(self, wallet_id: str, new_settings: dict) -> WalletRecord: + """Update an existing wallet and wallet record. + + Args: + wallet_id: The wallet id of the wallet record + new_settings: The context settings to be updated for this wallet + + Returns: + WalletRecord: The updated wallet record + + """ + wallet_record = await super().update_wallet(wallet_id, new_settings) + + # Wallet record has been updated but profile settings in memory must + # also be refreshed; update profile only if loaded + profile = self._profiles.get(wallet_id) + if profile: + profile.settings.update(wallet_record.settings) + + extra_settings = { + "admin.webhook_urls": self.get_webhook_urls( + self._profile.context, wallet_record + ), + } + profile.settings.update(extra_settings) - return self._instances[wallet_id] + return wallet_record async def remove_wallet_profile(self, profile: Profile): """Remove the wallet profile instance. @@ -79,6 +116,6 @@ async def remove_wallet_profile(self, profile: Profile): profile: The wallet profile instance """ - wallet_id = profile.settings.get("wallet.id") - del self._instances[wallet_id] + wallet_id = profile.settings.get_str("wallet.id") + self._profiles.remove(wallet_id) await profile.remove() From fdac67cc53923b4d3ce7840e91f701b7fbd5c7a6 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Fri, 25 Mar 2022 11:58:23 -0400 Subject: [PATCH 108/872] refactor: conductor iterates through open_profiles Signed-off-by: Daniel Bluhm --- aries_cloudagent/core/conductor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aries_cloudagent/core/conductor.py b/aries_cloudagent/core/conductor.py index 6ed174545f..1af7f32a76 100644 --- a/aries_cloudagent/core/conductor.py +++ b/aries_cloudagent/core/conductor.py @@ -495,7 +495,7 @@ async def stop(self, timeout=1.0): # close multitenant profiles multitenant_mgr = self.context.inject_or(BaseMultitenantManager) if multitenant_mgr: - for profile in multitenant_mgr._instances.values(): + for profile in multitenant_mgr.open_profiles: shutdown.run(profile.close()) if self.root_profile: From 22e05f6144e9315bf2d1c3238ea5e0570a1abbe3 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Fri, 25 Mar 2022 11:59:55 -0400 Subject: [PATCH 109/872] fix: multitenant manager tests Signed-off-by: Daniel Bluhm --- .../admin/tests/test_admin_server.py | 3 +- aries_cloudagent/core/tests/test_conductor.py | 16 ++- aries_cloudagent/multitenant/cache.py | 2 +- .../tests/test_askar_profile_manager.py | 126 ++++++++---------- .../multitenant/tests/test_base.py | 53 +++++--- .../multitenant/tests/test_cache.py | 102 +++++++------- .../multitenant/tests/test_manager.py | 45 ++++++- 7 files changed, 188 insertions(+), 159 deletions(-) diff --git a/aries_cloudagent/admin/tests/test_admin_server.py b/aries_cloudagent/admin/tests/test_admin_server.py index 7707315e12..8f5429dead 100644 --- a/aries_cloudagent/admin/tests/test_admin_server.py +++ b/aries_cloudagent/admin/tests/test_admin_server.py @@ -198,10 +198,9 @@ async def test_import_routes_multitenant_middleware(self): context = InjectionContext() context.injector.bind_instance(ProtocolRegistry, ProtocolRegistry()) context.injector.bind_instance(GoalCodeRegistry, GoalCodeRegistry()) - profile = InMemoryProfile.test_profile() context.injector.bind_instance( test_module.BaseMultitenantManager, - test_module.BaseMultitenantManager(profile), + async_mock.MagicMock(spec=test_module.BaseMultitenantManager), ) await DefaultContextBuilder().load_plugins(context) server = self.get_admin_server( diff --git a/aries_cloudagent/core/tests/test_conductor.py b/aries_cloudagent/core/tests/test_conductor.py index d3f17d499b..7eb87b12c9 100644 --- a/aries_cloudagent/core/tests/test_conductor.py +++ b/aries_cloudagent/core/tests/test_conductor.py @@ -918,15 +918,19 @@ async def test_shutdown_multitenant_profiles(self): await conductor.setup() multitenant_mgr = conductor.context.inject(BaseMultitenantManager) - multitenant_mgr._instances = { - "test1": async_mock.MagicMock(close=async_mock.CoroutineMock()), - "test2": async_mock.MagicMock(close=async_mock.CoroutineMock()), - } + multitenant_mgr._profiles.put( + "test1", + async_mock.MagicMock(close=async_mock.CoroutineMock()), + ) + multitenant_mgr._profiles.put( + "test2", + async_mock.MagicMock(close=async_mock.CoroutineMock()), + ) await conductor.stop() - multitenant_mgr._instances["test1"].close.assert_called_once_with() - multitenant_mgr._instances["test2"].close.assert_called_once_with() + multitenant_mgr._profiles.profiles["test1"].close.assert_called_once_with() + multitenant_mgr._profiles.profiles["test2"].close.assert_called_once_with() def get_invite_store_mock( diff --git a/aries_cloudagent/multitenant/cache.py b/aries_cloudagent/multitenant/cache.py index b2f0141085..edaed40b8b 100644 --- a/aries_cloudagent/multitenant/cache.py +++ b/aries_cloudagent/multitenant/cache.py @@ -51,7 +51,7 @@ def has(self, key: str) -> bool: """ return key in self.profiles - async def put(self, key: str, value: Profile) -> None: + def put(self, key: str, value: Profile) -> None: """Add profile with associated key to the cache. If new profile exceeds the cache capacity least recently used profiles diff --git a/aries_cloudagent/multitenant/tests/test_askar_profile_manager.py b/aries_cloudagent/multitenant/tests/test_askar_profile_manager.py index 6bad949cb4..b93930f2b0 100644 --- a/aries_cloudagent/multitenant/tests/test_askar_profile_manager.py +++ b/aries_cloudagent/multitenant/tests/test_askar_profile_manager.py @@ -43,79 +43,61 @@ async def test_get_wallet_profile_should_open_store_and_return_profile_with_wall with async_mock.patch( "aries_cloudagent.multitenant.askar_profile_manager.wallet_config" - ) as wallet_config: - with async_mock.patch( - "aries_cloudagent.multitenant.askar_profile_manager.AskarProfile" - ) as AskarProfile: - sub_wallet_profile_context = InjectionContext() - sub_wallet_profile = AskarProfile(None, None) - sub_wallet_profile.context.copy.return_value = ( - sub_wallet_profile_context - ) + ) as wallet_config, async_mock.patch( + "aries_cloudagent.multitenant.askar_profile_manager.AskarProfile", + ) as AskarProfile: + sub_wallet_profile_context = InjectionContext() + sub_wallet_profile = AskarProfile(None, None) + sub_wallet_profile.context.copy.return_value = sub_wallet_profile_context - def side_effect(context, provision): - sub_wallet_profile.name = askar_profile_mock_name - return sub_wallet_profile, None + def side_effect(context, provision): + sub_wallet_profile.name = askar_profile_mock_name + return sub_wallet_profile, None - wallet_config.side_effect = side_effect + wallet_config.side_effect = side_effect - profile = await self.manager.get_wallet_profile( - self.profile.context, wallet_record - ) + profile = await self.manager.get_wallet_profile( + self.profile.context, wallet_record + ) - assert profile.name == askar_profile_mock_name - wallet_config.assert_called_once() - wallet_config_settings_argument = wallet_config.call_args[0][0].settings - assert ( - wallet_config_settings_argument.get("wallet.name") - == self.DEFAULT_MULTIENANT_WALLET_NAME - ) - assert wallet_config_settings_argument.get("wallet.id") == None - assert wallet_config_settings_argument.get("auto_provision") == True - assert wallet_config_settings_argument.get("wallet.type") == "askar" - AskarProfile.assert_called_with( - sub_wallet_profile.opened, sub_wallet_profile_context - ) - assert ( - sub_wallet_profile_context.settings.get("wallet.seed") - == "test_seed" - ) - assert ( - sub_wallet_profile_context.settings.get("wallet.rekey") - == "test_rekey" - ) - assert ( - sub_wallet_profile_context.settings.get("wallet.name") - == "test_name" - ) - assert ( - sub_wallet_profile_context.settings.get("wallet.type") - == "test_type" - ) - assert sub_wallet_profile_context.settings.get("mediation.open") == True - assert ( - sub_wallet_profile_context.settings.get("mediation.invite") - == "http://invite.com" - ) - assert ( - sub_wallet_profile_context.settings.get("mediation.default_id") - == "24a96ef5" - ) - assert ( - sub_wallet_profile_context.settings.get("mediation.clear") == True - ) - assert ( - sub_wallet_profile_context.settings.get("wallet.id") - == wallet_record.wallet_id - ) - assert ( - sub_wallet_profile_context.settings.get("wallet.name") - == "test_name" - ) - assert ( - sub_wallet_profile_context.settings.get("wallet.askar_profile") - == wallet_record.wallet_id - ) + assert profile.name == askar_profile_mock_name + wallet_config.assert_called_once() + wallet_config_settings_argument = wallet_config.call_args[0][0].settings + assert ( + wallet_config_settings_argument.get("wallet.name") + == self.DEFAULT_MULTIENANT_WALLET_NAME + ) + assert wallet_config_settings_argument.get("wallet.id") == None + assert wallet_config_settings_argument.get("auto_provision") == True + assert wallet_config_settings_argument.get("wallet.type") == "askar" + AskarProfile.assert_called_with( + sub_wallet_profile.opened, sub_wallet_profile_context, profile_id="test" + ) + assert sub_wallet_profile_context.settings.get("wallet.seed") == "test_seed" + assert ( + sub_wallet_profile_context.settings.get("wallet.rekey") == "test_rekey" + ) + assert sub_wallet_profile_context.settings.get("wallet.name") == "test_name" + assert sub_wallet_profile_context.settings.get("wallet.type") == "test_type" + assert sub_wallet_profile_context.settings.get("mediation.open") == True + assert ( + sub_wallet_profile_context.settings.get("mediation.invite") + == "http://invite.com" + ) + assert ( + sub_wallet_profile_context.settings.get("mediation.default_id") + == "24a96ef5" + ) + assert sub_wallet_profile_context.settings.get("mediation.clear") == True + assert ( + sub_wallet_profile_context.settings.get("wallet.id") + == wallet_record.wallet_id + ) + assert sub_wallet_profile_context.settings.get("wallet.name") == "test_name" + assert ( + sub_wallet_profile_context.settings.get("wallet.askar_profile") + == wallet_record.wallet_id + ) async def test_get_wallet_profile_should_create_profile(self): wallet_record = WalletRecord(wallet_id="test", settings={}) @@ -128,9 +110,7 @@ async def test_get_wallet_profile_should_create_profile(self): sub_wallet_profile = AskarProfile(None, None) sub_wallet_profile.context.copy.return_value = InjectionContext() sub_wallet_profile.store.create_profile.return_value = create_profile_stub - self.manager._instances[ - self.DEFAULT_MULTIENANT_WALLET_NAME - ] = sub_wallet_profile + self.manager._multitenant_profile = sub_wallet_profile await self.manager.get_wallet_profile( self.profile.context, wallet_record, provision=True @@ -172,7 +152,7 @@ def side_effect(context, provision): ) async def test_remove_wallet_profile(self): - test_profile = InMemoryProfile.test_profile() + test_profile = InMemoryProfile.test_profile({"wallet.id": "test"}) with async_mock.patch.object(InMemoryProfile, "remove") as profile_remove: await self.manager.remove_wallet_profile(test_profile) diff --git a/aries_cloudagent/multitenant/tests/test_base.py b/aries_cloudagent/multitenant/tests/test_base.py index 4f6cb8dabf..b37392c014 100644 --- a/aries_cloudagent/multitenant/tests/test_base.py +++ b/aries_cloudagent/multitenant/tests/test_base.py @@ -23,6 +23,25 @@ from ..error import WalletKeyMissingError +class MockMultitenantManager(BaseMultitenantManager): + async def get_wallet_profile( + self, + base_context, + wallet_record: WalletRecord, + extra_settings: dict = ..., + *, + provision=False + ): + """Do nothing.""" + + async def remove_wallet_profile(self, profile): + """Do nothing.""" + + @property + def open_profiles(self): + """Do nothing.""" + + class TestBaseMultitenantManager(AsyncTestCase): async def setUp(self): self.profile = InMemoryProfile.test_profile() @@ -31,11 +50,11 @@ async def setUp(self): self.responder = async_mock.CoroutineMock(send=async_mock.CoroutineMock()) self.context.injector.bind_instance(BaseResponder, self.responder) - self.manager = BaseMultitenantManager(self.profile) + self.manager = MockMultitenantManager(self.profile) async def test_init_throws_no_profile(self): with self.assertRaises(MultitenantManagerError): - BaseMultitenantManager(None) + MockMultitenantManager(None) async def test_get_default_mediator(self): with async_mock.patch.object( @@ -158,7 +177,7 @@ async def test_get_wallet_by_key(self): async def test_create_wallet_removes_key_only_unmanaged_mode(self): with async_mock.patch.object( - BaseMultitenantManager, "get_wallet_profile" + self.manager, "get_wallet_profile" ) as get_wallet_profile: get_wallet_profile.return_value = InMemoryProfile.test_profile() @@ -174,7 +193,7 @@ async def test_create_wallet_removes_key_only_unmanaged_mode(self): async def test_create_wallet_fails_if_wallet_name_exists(self): with async_mock.patch.object( - BaseMultitenantManager, "_wallet_name_exists" + self.manager, "_wallet_name_exists" ) as _wallet_name_exists: _wallet_name_exists.return_value = True @@ -191,9 +210,9 @@ async def test_create_wallet_saves_wallet_record_creates_profile(self): with async_mock.patch.object( WalletRecord, "save" ) as wallet_record_save, async_mock.patch.object( - BaseMultitenantManager, "get_wallet_profile" + self.manager, "get_wallet_profile" ) as get_wallet_profile, async_mock.patch.object( - BaseMultitenantManager, "add_key" + self.manager, "add_key" ) as add_key: get_wallet_profile.return_value = InMemoryProfile.test_profile() @@ -227,9 +246,9 @@ async def test_create_wallet_adds_wallet_route(self): with async_mock.patch.object( WalletRecord, "save" ) as wallet_record_save, async_mock.patch.object( - BaseMultitenantManager, "get_wallet_profile" + self.manager, "get_wallet_profile" ) as get_wallet_profile, async_mock.patch.object( - BaseMultitenantManager, "add_key" + self.manager, "add_key" ) as add_key, async_mock.patch.object( InMemoryWallet, "get_public_did" ) as get_public_did: @@ -257,15 +276,13 @@ async def test_create_wallet_adds_wallet_route(self): assert wallet_record.key_management_mode == WalletRecord.MODE_MANAGED assert wallet_record.wallet_key == "test_key" - async def test_update_wallet_update_wallet_profile(self): + async def test_update_wallet(self): with async_mock.patch.object( WalletRecord, "retrieve_by_id" ) as retrieve_by_id, async_mock.patch.object( WalletRecord, "save" ) as wallet_record_save: wallet_id = "test-wallet-id" - wallet_profile = InMemoryProfile.test_profile() - self.manager._instances["test-wallet-id"] = wallet_profile retrieve_by_id.return_value = WalletRecord( wallet_id=wallet_id, settings={ @@ -285,10 +302,6 @@ async def test_update_wallet_update_wallet_profile(self): assert isinstance(wallet_record, WalletRecord) assert wallet_record.wallet_webhook_urls == ["new-webhook-url"] assert wallet_record.wallet_dispatch_type == "default" - assert wallet_profile.settings.get("wallet.webhook_urls") == [ - "new-webhook-url" - ] - assert wallet_profile.settings.get("wallet.dispatch_type") == "default" async def test_remove_wallet_fails_no_wallet_key_but_required(self): with async_mock.patch.object(WalletRecord, "retrieve_by_id") as retrieve_by_id: @@ -305,9 +318,9 @@ async def test_remove_wallet_removes_profile_wallet_storage_records(self): with async_mock.patch.object( WalletRecord, "retrieve_by_id" ) as retrieve_by_id, async_mock.patch.object( - BaseMultitenantManager, "get_wallet_profile" + self.manager, "get_wallet_profile" ) as get_wallet_profile, async_mock.patch.object( - BaseMultitenantManager, "remove_wallet_profile" + self.manager, "remove_wallet_profile" ) as remove_wallet_profile, async_mock.patch.object( WalletRecord, "delete_record" ) as wallet_delete_record, async_mock.patch.object( @@ -483,7 +496,7 @@ async def test_get_profile_for_token_managed_wallet(self): ).decode() with async_mock.patch.object( - BaseMultitenantManager, "get_wallet_profile" + self.manager, "get_wallet_profile" ) as get_wallet_profile: mock_profile = InMemoryProfile.test_profile() get_wallet_profile.return_value = mock_profile @@ -517,7 +530,7 @@ async def test_get_profile_for_token_unmanaged_wallet(self): ).decode() with async_mock.patch.object( - BaseMultitenantManager, "get_wallet_profile" + self.manager, "get_wallet_profile" ) as get_wallet_profile: mock_profile = InMemoryProfile.test_profile() get_wallet_profile.return_value = mock_profile @@ -557,7 +570,7 @@ async def test_get_wallets_by_message(self): ] with async_mock.patch.object( - BaseMultitenantManager, "_get_wallet_by_key" + self.manager, "_get_wallet_by_key" ) as get_wallet_by_key: get_wallet_by_key.side_effect = return_wallets diff --git a/aries_cloudagent/multitenant/tests/test_cache.py b/aries_cloudagent/multitenant/tests/test_cache.py index 8a805122c5..451e465051 100644 --- a/aries_cloudagent/multitenant/tests/test_cache.py +++ b/aries_cloudagent/multitenant/tests/test_cache.py @@ -1,82 +1,82 @@ -from asynctest import TestCase as AsyncTestCase from asynctest import mock as async_mock from ..cache import ProfileCache -class TestProfileCache(AsyncTestCase): - async def setUp(self): - pass +def test_get_not_in_cache(): + cache = ProfileCache(1) - async def test_get_not_in_cache(self): - cache = ProfileCache(1) + assert cache.get("1") is None - assert cache.get("1") is None - async def test_put_get_in_cache(self): - cache = ProfileCache(1) +def test_put_get_in_cache(): + cache = ProfileCache(1) - profile = async_mock.MagicMock() - await cache.put("1", profile) + profile = async_mock.MagicMock() + cache.put("1", profile) - assert cache.get("1") is profile + assert cache.get("1") is profile - async def test_remove(self): - cache = ProfileCache(1) - profile = async_mock.MagicMock() - await cache.put("1", profile) +def test_remove(): + cache = ProfileCache(1) - assert cache.get("1") is profile + profile = async_mock.MagicMock() + cache.put("1", profile) - cache.remove("1") + assert cache.get("1") is profile - assert cache.get("1") is None + cache.remove("1") - async def test_has_true(self): - cache = ProfileCache(1) + assert cache.get("1") is None - profile = async_mock.MagicMock() - assert cache.has("1") is False - await cache.put("1", profile) - assert cache.has("1") is True +def test_has_true(): + cache = ProfileCache(1) - async def test_cleanup(self): - cache = ProfileCache(1) + profile = async_mock.MagicMock() - profile1 = async_mock.MagicMock() - profile2 = async_mock.MagicMock() + assert cache.has("1") is False + cache.put("1", profile) + assert cache.has("1") is True - await cache.put("1", profile1) - assert len(cache.profiles) == 1 +def test_cleanup(): + cache = ProfileCache(1) - await cache.put("2", profile2) + profile1 = async_mock.MagicMock() + profile2 = async_mock.MagicMock() - assert len(cache.profiles) == 1 - assert cache.get("1") == None + cache.put("1", profile1) - async def test_cleanup_lru(self): - cache = ProfileCache(3) + assert len(cache.profiles) == 1 - profile1 = async_mock.MagicMock() - profile2 = async_mock.MagicMock() - profile3 = async_mock.MagicMock() - profile4 = async_mock.MagicMock() + cache.put("2", profile2) - await cache.put("1", profile1) - await cache.put("2", profile2) - await cache.put("3", profile3) + assert len(cache.profiles) == 1 + assert cache.get("1") == None - assert len(cache.profiles) == 3 - cache.get("1") +def test_cleanup_lru(): + cache = ProfileCache(3) - await cache.put("4", profile4) + profile1 = async_mock.MagicMock() + profile2 = async_mock.MagicMock() + profile3 = async_mock.MagicMock() + profile4 = async_mock.MagicMock() - assert len(cache.profiles) == 3 - assert cache.get("1") == profile1 - assert cache.get("2") == None - assert cache.get("3") == profile3 - assert cache.get("4") == profile4 + cache.put("1", profile1) + cache.put("2", profile2) + cache.put("3", profile3) + + assert len(cache.profiles) == 3 + + cache.get("1") + + cache.put("4", profile4) + + assert len(cache.profiles) == 3 + assert cache.get("1") == profile1 + assert cache.get("2") == None + assert cache.get("3") == profile3 + assert cache.get("4") == profile4 diff --git a/aries_cloudagent/multitenant/tests/test_manager.py b/aries_cloudagent/multitenant/tests/test_manager.py index 7e01959f95..c851369c11 100644 --- a/aries_cloudagent/multitenant/tests/test_manager.py +++ b/aries_cloudagent/multitenant/tests/test_manager.py @@ -19,7 +19,7 @@ async def setUp(self): async def test_get_wallet_profile_returns_from_cache(self): wallet_record = WalletRecord(wallet_id="test") - self.manager._instances["test"] = InMemoryProfile.test_profile() + self.manager._profiles.put("test", InMemoryProfile.test_profile()) with async_mock.patch( "aries_cloudagent.config.wallet.wallet_config" @@ -27,12 +27,12 @@ async def test_get_wallet_profile_returns_from_cache(self): profile = await self.manager.get_wallet_profile( self.profile.context, wallet_record ) - assert profile is self.manager._instances["test"] + assert profile is self.manager._profiles.get("test") wallet_config.assert_not_called() async def test_get_wallet_profile_not_in_cache(self): wallet_record = WalletRecord(wallet_id="test", settings={}) - self.manager._instances["test"] = InMemoryProfile.test_profile() + self.manager._profiles.put("test", InMemoryProfile.test_profile()) self.profile.context.update_settings( {"admin.webhook_urls": ["http://localhost:8020"]} ) @@ -43,7 +43,7 @@ async def test_get_wallet_profile_not_in_cache(self): profile = await self.manager.get_wallet_profile( self.profile.context, wallet_record ) - assert profile is self.manager._instances["test"] + assert profile is self.manager._profiles.get("test") wallet_config.assert_not_called() async def test_get_wallet_profile_settings(self): @@ -174,13 +174,46 @@ def side_effect(context, provision): assert profile.settings.get("mediation.default_id") == "24a96ef5" assert profile.settings.get("mediation.clear") == True + async def test_update_wallet_update_wallet_profile(self): + with async_mock.patch.object( + WalletRecord, "retrieve_by_id" + ) as retrieve_by_id, async_mock.patch.object( + WalletRecord, "save" + ) as wallet_record_save: + wallet_id = "test-wallet-id" + wallet_profile = InMemoryProfile.test_profile() + self.manager._profiles.put("test-wallet-id", wallet_profile) + retrieve_by_id.return_value = WalletRecord( + wallet_id=wallet_id, + settings={ + "wallet.webhook_urls": ["test-webhook-url"], + "wallet.dispatch_type": "both", + }, + ) + + new_settings = { + "wallet.webhook_urls": ["new-webhook-url"], + "wallet.dispatch_type": "default", + } + wallet_record = await self.manager.update_wallet(wallet_id, new_settings) + + wallet_record_save.assert_called_once() + + assert isinstance(wallet_record, WalletRecord) + assert wallet_record.wallet_webhook_urls == ["new-webhook-url"] + assert wallet_record.wallet_dispatch_type == "default" + assert wallet_profile.settings.get("wallet.webhook_urls") == [ + "new-webhook-url" + ] + assert wallet_profile.settings.get("wallet.dispatch_type") == "default" + async def test_remove_wallet_profile(self): test_profile = InMemoryProfile.test_profile( settings={"wallet.id": "test"}, ) - self.manager._instances["test"] = test_profile + self.manager._profiles.put("test", test_profile) with async_mock.patch.object(InMemoryProfile, "remove") as profile_remove: await self.manager.remove_wallet_profile(test_profile) - assert "test" not in self.manager._instances + assert not self.manager._profiles.has("test") profile_remove.assert_called_once_with() From ac585c98eca0bcd4330e1ef163eb4aa84192a591 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Fri, 25 Mar 2022 12:09:26 -0400 Subject: [PATCH 110/872] fix: askar profile tests Signed-off-by: Daniel Bluhm --- aries_cloudagent/askar/tests/test_profile.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/aries_cloudagent/askar/tests/test_profile.py b/aries_cloudagent/askar/tests/test_profile.py index f01da0d2fe..f943ca6a76 100644 --- a/aries_cloudagent/askar/tests/test_profile.py +++ b/aries_cloudagent/askar/tests/test_profile.py @@ -28,7 +28,7 @@ async def test_remove_success(self, AskarOpenStore): "wallet.askar_profile": profile_id, "ledger.genesis_transactions": mock.MagicMock(), } - askar_profile = AskarProfile(openStore, context) + askar_profile = AskarProfile(openStore, context, profile_id=profile_id) remove_profile_stub = asyncio.Future() remove_profile_stub.set_result(True) openStore.store.remove_profile.return_value = remove_profile_stub @@ -63,9 +63,6 @@ async def test_profile_manager_transaction(self): transactionProfile = test_module.AskarProfileSession(askar_profile, True) assert transactionProfile._opener == askar_profile_transaction - askar_profile.context.settings.get.assert_called_once_with( - "wallet.askar_profile" - ) askar_profile.store.transaction.assert_called_once_with(profile) @pytest.mark.asyncio @@ -81,7 +78,4 @@ async def test_profile_manager_store(self): sessionProfile = test_module.AskarProfileSession(askar_profile, False) assert sessionProfile._opener == askar_profile_session - askar_profile.context.settings.get.assert_called_once_with( - "wallet.askar_profile" - ) askar_profile.store.session.assert_called_once_with(profile) From 6f613ea9148f7980008dd2d6ad9c2664764b71b3 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Fri, 25 Mar 2022 12:09:43 -0400 Subject: [PATCH 111/872] fix: open_profiles should be property Signed-off-by: Daniel Bluhm --- aries_cloudagent/core/tests/test_conductor.py | 2 ++ aries_cloudagent/multitenant/manager.py | 1 + 2 files changed, 3 insertions(+) diff --git a/aries_cloudagent/core/tests/test_conductor.py b/aries_cloudagent/core/tests/test_conductor.py index 7eb87b12c9..a35744efdd 100644 --- a/aries_cloudagent/core/tests/test_conductor.py +++ b/aries_cloudagent/core/tests/test_conductor.py @@ -27,6 +27,7 @@ ) from ...resolver.did_resolver import DIDResolver, DIDResolverRegistry from ...multitenant.base import BaseMultitenantManager +from ...multitenant.manager import MultitenantManager from ...storage.base import BaseStorage from ...storage.error import StorageNotFoundError from ...transport.inbound.message import InboundMessage @@ -917,6 +918,7 @@ async def test_shutdown_multitenant_profiles(self): await conductor.setup() multitenant_mgr = conductor.context.inject(BaseMultitenantManager) + assert isinstance(multitenant_mgr, MultitenantManager) multitenant_mgr._profiles.put( "test1", diff --git a/aries_cloudagent/multitenant/manager.py b/aries_cloudagent/multitenant/manager.py index e3e4418186..210d799a06 100644 --- a/aries_cloudagent/multitenant/manager.py +++ b/aries_cloudagent/multitenant/manager.py @@ -24,6 +24,7 @@ def __init__(self, profile: Profile): super().__init__(profile) self._profiles = ProfileCache(100) + @property def open_profiles(self) -> Iterable[Profile]: """Return iterator over open profiles.""" yield from self._profiles.profiles.values() From ce7ccf17da91f2567a1e49729960d3a09417a11a Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Fri, 25 Mar 2022 12:14:13 -0400 Subject: [PATCH 112/872] fix: askar profile tests, profile passed to sesh, txn Signed-off-by: Daniel Bluhm --- aries_cloudagent/askar/tests/test_profile.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/aries_cloudagent/askar/tests/test_profile.py b/aries_cloudagent/askar/tests/test_profile.py index f943ca6a76..4c4c646a84 100644 --- a/aries_cloudagent/askar/tests/test_profile.py +++ b/aries_cloudagent/askar/tests/test_profile.py @@ -55,10 +55,10 @@ async def test_profile_manager_transaction(self): profile = "profileId" with mock.patch("aries_cloudagent.askar.profile.AskarProfile") as AskarProfile: - askar_profile = AskarProfile(None, True) + askar_profile = AskarProfile(None, True, profile_id=profile) + askar_profile.profile_id = profile askar_profile_transaction = mock.MagicMock() askar_profile.store.transaction.return_value = askar_profile_transaction - askar_profile.context.settings.get.return_value = profile transactionProfile = test_module.AskarProfileSession(askar_profile, True) @@ -70,10 +70,10 @@ async def test_profile_manager_store(self): profile = "profileId" with mock.patch("aries_cloudagent.askar.profile.AskarProfile") as AskarProfile: - askar_profile = AskarProfile(None, False) + askar_profile = AskarProfile(None, False, profile_id=profile) + askar_profile.profile_id = profile askar_profile_session = mock.MagicMock() askar_profile.store.session.return_value = askar_profile_session - askar_profile.context.settings.get.return_value = profile sessionProfile = test_module.AskarProfileSession(askar_profile, False) From 42477bc63d5d475d5c222f266298ed3bb4298dc2 Mon Sep 17 00:00:00 2001 From: Micah Peltier Date: Fri, 25 Mar 2022 10:48:13 -0600 Subject: [PATCH 113/872] feat: added event topic accessor Signed-off-by: Micah Peltier --- aries_cloudagent/transport/outbound/status.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/aries_cloudagent/transport/outbound/status.py b/aries_cloudagent/transport/outbound/status.py index 42576a52ec..34e76184b0 100644 --- a/aries_cloudagent/transport/outbound/status.py +++ b/aries_cloudagent/transport/outbound/status.py @@ -2,6 +2,8 @@ from enum import Enum +OUTBOUND_STATUS_PREFIX = "acapy::outbound-message::" + class OutboundSendStatus(Enum): """Send status of outbound messages.""" @@ -21,3 +23,7 @@ class OutboundSendStatus(Enum): # No endpoint available, and no internal queue for messages. UNDELIVERABLE = "undeliverable" + + @property + def topic(self): + return f"{OUTBOUND_STATUS_PREFIX}{self.value}" From a3fac5b0dcd7098040774110691787da0894b7a3 Mon Sep 17 00:00:00 2001 From: Micah Peltier Date: Fri, 25 Mar 2022 10:49:16 -0600 Subject: [PATCH 114/872] feat: cleanup event emission Signed-off-by: Micah Peltier --- aries_cloudagent/core/conductor.py | 34 ++++++++++++++++++------------ 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/aries_cloudagent/core/conductor.py b/aries_cloudagent/core/conductor.py index 9aed9a9e77..7589a12d3f 100644 --- a/aries_cloudagent/core/conductor.py +++ b/aries_cloudagent/core/conductor.py @@ -70,7 +70,6 @@ from .util import STARTUP_EVENT_TOPIC, SHUTDOWN_EVENT_TOPIC LOGGER = logging.getLogger(__name__) -OUTBOUND_STATUS_PREFIX = "acapy::outbound-message::" class Conductor: @@ -593,6 +592,26 @@ async def outbound_message_router( """ Route an outbound message. + Args: + profile: The active profile for the request + message: An outbound message to be sent + inbound: The inbound message that produced this response, if available + """ + status: OutboundSendStatus = await self._outbound_message_router( + profile=profile, outbound=outbound, inbound=inbound + ) + await profile.notify(status.topic, outbound) + return status + + async def _outbound_message_router( + self, + profile: Profile, + outbound: OutboundMessage, + inbound: InboundMessage = None, + ) -> OutboundSendStatus: + """ + Route an outbound message. + Args: profile: The active profile for the request message: An outbound message to be sent @@ -603,10 +622,6 @@ async def outbound_message_router( outbound.reply_from_verkey = inbound.receipt.recipient_verkey # return message to an inbound session if self.inbound_transport_manager.return_to_session(outbound): - await profile.notify( - f"{OUTBOUND_STATUS_PREFIX}{OutboundSendStatus.SENT_TO_SESSION}", - outbound, - ) return OutboundSendStatus.SENT_TO_SESSION if not outbound.to_session_only: @@ -684,10 +699,6 @@ async def _queue_external( encoded_outbound_message.payload, target.endpoint ) - await profile.notify( - f"{OUTBOUND_STATUS_PREFIX}{OutboundSendStatus.SENT_TO_EXTERNAL_QUEUE}", - outbound, - ) return OutboundSendStatus.SENT_TO_EXTERNAL_QUEUE async def _queue_internal( @@ -696,10 +707,6 @@ async def _queue_internal( """Save the message to an internal outbound queue.""" try: self.outbound_transport_manager.enqueue_message(profile, outbound) - await profile.notify( - f"{OUTBOUND_STATUS_PREFIX}{OutboundSendStatus.QUEUED_FOR_DELIVERY}", - outbound, - ) return OutboundSendStatus.QUEUED_FOR_DELIVERY except OutboundDeliveryError: LOGGER.warning("Cannot queue message for delivery, no supported transport") @@ -715,7 +722,6 @@ async def handle_not_delivered( if queued_for_inbound else OutboundSendStatus.UNDELIVERABLE ) - await profile.notify(f"{OUTBOUND_STATUS_PREFIX}{status}", outbound) return status def webhook_router( From 65433283e0a73bdd3d1f5df61a4b7c20246ec421 Mon Sep 17 00:00:00 2001 From: Micah Peltier Date: Fri, 25 Mar 2022 10:49:52 -0600 Subject: [PATCH 115/872] feat: update tests to reflect changes in conductor Signed-off-by: Micah Peltier --- aries_cloudagent/core/tests/test_conductor.py | 20 ++++--------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/aries_cloudagent/core/tests/test_conductor.py b/aries_cloudagent/core/tests/test_conductor.py index 10bc51ae8c..febef8e6ed 100644 --- a/aries_cloudagent/core/tests/test_conductor.py +++ b/aries_cloudagent/core/tests/test_conductor.py @@ -323,10 +323,7 @@ async def test_outbound_message_handler_return_route(self): ) assert status == OutboundSendStatus.SENT_TO_SESSION assert bus.events - assert ( - bus.events[0][1].topic - == f"{EVENT_PREFIX}{OutboundSendStatus.SENT_TO_SESSION}" - ) + assert bus.events[0][1].topic == status.topic assert bus.events[0][1].payload == message mock_return.assert_called_once_with(message) mock_queue.assert_not_awaited() @@ -355,10 +352,7 @@ async def test_outbound_message_handler_with_target(self): assert status == OutboundSendStatus.QUEUED_FOR_DELIVERY assert bus.events print(bus.events) - assert ( - bus.events[0][1].topic - == f"{EVENT_PREFIX}{OutboundSendStatus.QUEUED_FOR_DELIVERY}" - ) + assert bus.events[0][1].topic == status.topic assert bus.events[0][1].payload == message mock_outbound_mgr.return_value.enqueue_message.assert_called_once_with( conductor.root_profile, message @@ -389,10 +383,7 @@ async def test_outbound_message_handler_with_connection(self): assert status == OutboundSendStatus.QUEUED_FOR_DELIVERY assert bus.events print(bus.events) - assert ( - bus.events[0][1].topic - == f"{EVENT_PREFIX}{OutboundSendStatus.QUEUED_FOR_DELIVERY}" - ) + assert bus.events[0][1].topic == status.topic assert bus.events[0][1].payload == message conn_mgr.return_value.get_connection_targets.assert_awaited_once_with( @@ -435,10 +426,7 @@ async def test_outbound_message_handler_with_verkey_no_target(self): assert status == OutboundSendStatus.QUEUED_FOR_DELIVERY assert bus.events print(bus.events) - assert ( - bus.events[0][1].topic - == f"{EVENT_PREFIX}{OutboundSendStatus.QUEUED_FOR_DELIVERY}" - ) + assert bus.events[0][1].topic == status.topic assert bus.events[0][1].payload == message mock_outbound_mgr.return_value.enqueue_message.assert_called_once_with( From 651b0dae790c7094889fe1f216db544c67166e13 Mon Sep 17 00:00:00 2001 From: Harsh Multani Date: Mon, 31 Jan 2022 21:38:25 +0530 Subject: [PATCH 116/872] #1447 , Added support for endpoints of agent to get updated on ledger after endorsement from an endorser. Signed-off-by: Harsh Multani Signed-off-by: Colton Wolkins (Indicio work address) --- aries_cloudagent/ledger/base.py | 2 + aries_cloudagent/ledger/indy.py | 17 ++- aries_cloudagent/wallet/indy.py | 6 +- aries_cloudagent/wallet/routes.py | 218 ++++++++++++++++++++++++++++-- 4 files changed, 227 insertions(+), 16 deletions(-) diff --git a/aries_cloudagent/ledger/base.py b/aries_cloudagent/ledger/base.py index bf52118c70..5489d7d81d 100644 --- a/aries_cloudagent/ledger/base.py +++ b/aries_cloudagent/ledger/base.py @@ -75,6 +75,8 @@ async def update_endpoint_for_did( did: str, endpoint: str, endpoint_type: EndpointType = EndpointType.ENDPOINT, + write_ledger: bool = True, + endorser_did: str = None ) -> bool: """Check and update the endpoint on the ledger. diff --git a/aries_cloudagent/ledger/indy.py b/aries_cloudagent/ledger/indy.py index abf5cfe561..d9e55bc50a 100644 --- a/aries_cloudagent/ledger/indy.py +++ b/aries_cloudagent/ledger/indy.py @@ -888,7 +888,7 @@ async def get_endpoint_for_did( return address async def update_endpoint_for_did( - self, did: str, endpoint: str, endpoint_type: EndpointType = None + self, did: str, endpoint: str, endpoint_type: EndpointType = None, write_ledger: bool = True, endorser_did: str = None ) -> bool: """Check and update the endpoint on the ledger. @@ -897,6 +897,10 @@ async def update_endpoint_for_did( endpoint: The endpoint address endpoint_type: The type of the endpoint """ + public_info = await self.get_wallet_public_did() + if not public_info: + raise BadLedgerRequestError("Cannot update endpoint at ledger without a public DID") + if not endpoint_type: endpoint_type = EndpointType.ENDPOINT @@ -925,6 +929,17 @@ async def update_endpoint_for_did( request_json = await indy.ledger.build_attrib_request( nym, nym, None, attr_json, None ) + + if endorser_did and not write_ledger: + request_json = await indy.ledger.append_request_endorser( + request_json, endorser_did + ) + resp = await self._submit( + request_json, True, sign_did=public_info, write_ledger=write_ledger + ) + if not write_ledger: + return {"signed_txn": resp} + await self._submit(request_json, True, True) return True return False diff --git a/aries_cloudagent/wallet/indy.py b/aries_cloudagent/wallet/indy.py index a5d40732f6..864c5697b4 100644 --- a/aries_cloudagent/wallet/indy.py +++ b/aries_cloudagent/wallet/indy.py @@ -705,6 +705,8 @@ async def set_did_endpoint( endpoint: str, ledger: BaseLedger, endpoint_type: EndpointType = None, + write_ledger: bool = True, + endorser_did: str = None, ): """ Update the endpoint for a DID in the wallet, send to ledger if public or posted. @@ -738,7 +740,9 @@ async def set_did_endpoint( ) if not ledger.read_only: async with ledger: - await ledger.update_endpoint_for_did(did, endpoint, endpoint_type) + attrib_def = await ledger.update_endpoint_for_did(did, endpoint, endpoint_type, write_ledger=write_ledger, endorser_did=endorser_did) + if not write_ledger: + return attrib_def await self.replace_local_did_metadata(did, metadata) diff --git a/aries_cloudagent/wallet/routes.py b/aries_cloudagent/wallet/routes.py index 7cdd27be0e..b07edf9e33 100644 --- a/aries_cloudagent/wallet/routes.py +++ b/aries_cloudagent/wallet/routes.py @@ -1,5 +1,7 @@ """Wallet admin routes.""" +import json + from aiohttp import web from aiohttp_apispec import ( docs, @@ -13,9 +15,11 @@ from ..admin.request_context import AdminRequestContext from ..core.event_bus import Event, EventBus from ..core.profile import Profile +from ..connections.models.conn_record import ConnRecord from ..ledger.base import BaseLedger from ..ledger.endpoint_type import EndpointType from ..ledger.error import LedgerConfigError, LedgerError +from ..messaging.models.base import BaseModelError from ..messaging.models.openapi import OpenAPISchema from ..messaging.valid import ( DID_POSTURE, @@ -26,9 +30,12 @@ INDY_RAW_PUBLIC_KEY, ) from ..multitenant.base import BaseMultitenantManager +from ..protocols.endorse_transaction.v1_0.manager import (TransactionManager, TransactionManagerError) from ..protocols.endorse_transaction.v1_0.util import ( is_author_role, + get_endorser_connection_id ) +from ..storage.error import ( StorageNotFoundError, StorageError) from .base import BaseWallet from .did_info import DIDInfo @@ -178,6 +185,18 @@ class DIDCreateSchema(OpenAPISchema): description="To define a key type for a did:key", ) +class CreateAttribTxnForEndorserOptionSchema(OpenAPISchema): + """Class for user to input whether to create a transaction for endorser or not.""" + create_transaction_for_endorser = fields.Boolean( + description="Create Transaction For Endorser's signature", + required=False, + ) + +class AttribConnIdMatchInfoSchema(OpenAPISchema): + """Path parameters and validators for request taking connection id.""" + conn_id = fields.Str( + description="Connection identifier", required=False + ) def format_did_info(info: DIDInfo): """Serialize a DIDInfo object.""" @@ -373,6 +392,8 @@ async def wallet_get_public_did(request: web.BaseRequest): @docs(tags=["wallet"], summary="Assign the current public DID") @querystring_schema(DIDQueryStringSchema()) +@querystring_schema(CreateAttribTxnForEndorserOptionSchema()) +@querystring_schema(AttribConnIdMatchInfoSchema()) @response_schema(DIDResultSchema, 200, description="") async def wallet_set_public_did(request: web.BaseRequest): """ @@ -386,19 +407,30 @@ async def wallet_set_public_did(request: web.BaseRequest): """ context: AdminRequestContext = request["context"] - async with context.session() as session: - wallet = session.inject_or(BaseWallet) - if not wallet: - raise web.HTTPForbidden(reason="No wallet available") + profile = context.profile + session = await context.session() + outbound_handler = request["outbound_message_router"] + + create_transaction_for_endorser = json.loads( + request.query.get("create_transaction_for_endorser", "false") + ) + write_ledger = not create_transaction_for_endorser + endorser_did = None + connection_id = request.query.get("conn_id") + attrib_def = None + + wallet = session.inject_or(BaseWallet) + if not wallet: + raise web.HTTPForbidden(reason="No wallet available") did = request.query.get("did") if not did: raise web.HTTPBadRequest(reason="Request query must include DID") info: DIDInfo = None try: - info = await promote_wallet_public_did( - context.profile, context, context.session, did + info, attrib_def = await promote_wallet_public_did( + context.profile, context, context.session, did, write_ledger=write_ledger ) except LookupError as err: raise web.HTTPNotFound(reason=str(err)) from err @@ -409,11 +441,39 @@ async def wallet_set_public_did(request: web.BaseRequest): except (LedgerError, WalletError) as err: raise web.HTTPBadRequest(reason=err.roll_up) from err - return web.json_response({"result": format_did_info(info)}) + if not create_transaction_for_endorser: + return web.json_response({"result": format_did_info(info)}) + + else: + transaction_mgr = TransactionManager(context.profile) + try: + transaction = await transaction_mgr.create_record( + messages_attach=attrib_def["signed_txn"], + connection_id=connection_id + ) + except StorageError as err: + raise web.HTTPBadRequest(reason=err.roll_up) from err + + # if auto-request, send the request to the endorser + if context.settings.get_value("endorser.auto_request"): + try: + transaction, transaction_request = await transaction_mgr.create_request( + transaction=transaction, + # TODO see if we need to parameterize these params + # expires_time=expires_time, + # endorser_write_txn=endorser_write_txn, + ) + except (StorageError, TransactionManagerError) as err: + raise web.HTTPBadRequest(reason=err.roll_up) from err + + await outbound_handler(transaction_request, connection_id=connection_id) + + return web.json_response({"txn": transaction.serialize()}) + async def promote_wallet_public_did( - profile: Profile, context: AdminRequestContext, session_fn, did: str + profile: Profile, context: AdminRequestContext, session_fn, did: str, write_ledger: bool = False ) -> DIDInfo: """Promote supplied DID to the wallet public DID.""" @@ -421,6 +481,7 @@ async def promote_wallet_public_did( wallet_id = context.settings.get("wallet.id") info: DIDInfo = None + endorser_did = None ledger = profile.inject_or(BaseLedger) if not ledger: reason = "No ledger available" @@ -431,7 +492,48 @@ async def promote_wallet_public_did( async with ledger: if not await ledger.get_key_for_did(did): raise LookupError(f"DID {did} is not posted to the ledger") + + + # check if we need to endorse + if is_author_role(context.profile): + # authors cannot write to the ledger + write_ledger = False + create_transaction_for_endorser = True + if not connection_id: + # author has not provided a connection id, so determine which to use + connection_id = await get_endorser_connection_id(context.profile) + if not connection_id: + raise web.HTTPBadRequest(reason="No endorser connection found") + + if not write_ledger: + try: + async with profile.session() as session: + connection_record = await ConnRecord.retrieve_by_id( + session, connection_id + ) + except StorageNotFoundError as err: + raise web.HTTPNotFound(reason=err.roll_up) from err + except BaseModelError as err: + raise web.HTTPBadRequest(reason=err.roll_up) from err + + async with profile.session() as session: + endorser_info = await connection_record.metadata_get( + session, "endorser_info" + ) + if not endorser_info: + raise web.HTTPForbidden( + reason="Endorser Info is not set up in " + "connection metadata for this connection record" + ) + if "endorser_did" not in endorser_info.keys(): + raise web.HTTPForbidden( + reason=' "endorser_did" is not set in "endorser_info"' + " in connection metadata for this connection record" + ) + endorser_did = endorser_info["endorser_did"] + did_info: DIDInfo = None + attrib_def = None async with session_fn() as session: wallet = session.inject_or(BaseWallet) did_info = await wallet.get_local_did(did) @@ -444,10 +546,14 @@ async def promote_wallet_public_did( async with session_fn() as session: wallet = session.inject_or(BaseWallet) endpoint = context.settings.get("default_endpoint") - await wallet.set_did_endpoint(info.did, endpoint, ledger) + attrib_def = await wallet.set_did_endpoint(info.did, endpoint, ledger, + write_ledger=write_ledger, + endorser_did=endorser_did) - async with ledger: - await ledger.update_endpoint_for_did(info.did, endpoint) + # Commented the below lines as the function set_did_endpoint + # was calling update_endpoint_for_did of ledger + # async with ledger: + # await ledger.update_endpoint_for_did(info.did, endpoint) # Multitenancy setup multitenant_mgr = profile.inject_or(BaseMultitenantManager) @@ -455,13 +561,15 @@ async def promote_wallet_public_did( if multitenant_mgr and wallet_id: await multitenant_mgr.add_key(wallet_id, info.verkey, skip_if_exists=True) - return info + return info, attrib_def @docs( tags=["wallet"], summary="Update endpoint in wallet and on ledger if posted to it" ) @request_schema(DIDEndpointWithTypeSchema) +@querystring_schema(CreateAttribTxnForEndorserOptionSchema()) +@querystring_schema(AttribConnIdMatchInfoSchema()) @response_schema(WalletModuleResponseSchema(), description="") async def wallet_set_did_endpoint(request: web.BaseRequest): """ @@ -471,20 +579,76 @@ async def wallet_set_did_endpoint(request: web.BaseRequest): request: aiohttp request object """ context: AdminRequestContext = request["context"] + + profile = context.profile + session = await context.session() + + outbound_handler = request["outbound_message_router"] + body = await request.json() did = body["did"] endpoint = body.get("endpoint") endpoint_type = EndpointType.get( body.get("endpoint_type", EndpointType.ENDPOINT.w3c) ) + + + create_transaction_for_endorser = json.loads( + request.query.get("create_transaction_for_endorser", "false") + ) + write_ledger = not create_transaction_for_endorser + endorser_did = None + connection_id = request.query.get("conn_id") + attrib_def = None + async with context.session() as session: wallet = session.inject_or(BaseWallet) if not wallet: raise web.HTTPForbidden(reason="No wallet available") + + # check if we need to endorse + if is_author_role(context.profile): + # authors cannot write to the ledger + write_ledger = False + create_transaction_for_endorser = True + if not connection_id: + # author has not provided a connection id, so determine which to use + connection_id = await get_endorser_connection_id(context.profile) + if not connection_id: + raise web.HTTPBadRequest(reason="No endorser connection found") + + if not write_ledger: + try: + async with profile.session() as session: + connection_record = await ConnRecord.retrieve_by_id( + session, connection_id + ) + except StorageNotFoundError as err: + raise web.HTTPNotFound(reason=err.roll_up) from err + except BaseModelError as err: + raise web.HTTPBadRequest(reason=err.roll_up) from err + + async with profile.session() as session: + endorser_info = await connection_record.metadata_get( + session, "endorser_info" + ) + if not endorser_info: + raise web.HTTPForbidden( + reason="Endorser Info is not set up in " + "connection metadata for this connection record" + ) + if "endorser_did" not in endorser_info.keys(): + raise web.HTTPForbidden( + reason=' "endorser_did" is not set in "endorser_info"' + " in connection metadata for this connection record" + ) + endorser_did = endorser_info["endorser_did"] try: ledger = context.profile.inject_or(BaseLedger) - await wallet.set_did_endpoint(did, endpoint, ledger, endpoint_type) + attrib_def = await wallet.set_did_endpoint(did, endpoint, ledger, endpoint_type, + write_ledger=write_ledger, + endorser_did=endorser_did) except WalletNotFoundError as err: raise web.HTTPNotFound(reason=err.roll_up) from err except LedgerConfigError as err: @@ -492,7 +656,33 @@ async def wallet_set_did_endpoint(request: web.BaseRequest): except (LedgerError, WalletError) as err: raise web.HTTPBadRequest(reason=err.roll_up) from err - return web.json_response({}) + if not create_transaction_for_endorser: + return web.json_response({}) + else: + transaction_mgr = TransactionManager(context.profile) + try: + transaction = await transaction_mgr.create_record( + messages_attach=attrib_def["signed_txn"], + connection_id=connection_id + ) + except StorageError as err: + raise web.HTTPBadRequest(reason=err.roll_up) from err + + # if auto-request, send the request to the endorser + if context.settings.get_value("endorser.auto_request"): + try: + transaction, transaction_request = await transaction_mgr.create_request( + transaction=transaction, + # TODO see if we need to parameterize these params + # expires_time=expires_time, + # endorser_write_txn=endorser_write_txn, + ) + except (StorageError, TransactionManagerError) as err: + raise web.HTTPBadRequest(reason=err.roll_up) from err + + await outbound_handler(transaction_request, connection_id=connection_id) + + return web.json_response({"txn": transaction.serialize()}) @docs(tags=["wallet"], summary="Query DID endpoint in wallet") From ea80fe45c6215da845c87819bb55c7eb49dbc794 Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Fri, 25 Mar 2022 08:26:15 -0600 Subject: [PATCH 117/872] fix: implement suggested fixes from ianco Signed-off-by: Colton Wolkins (Indicio work address) --- aries_cloudagent/ledger/indy_vdr.py | 27 ++++++++++++++++++- aries_cloudagent/wallet/askar.py | 12 ++++++++- .../wallet/tests/test_askar_wallet.py | 6 ++++- .../wallet/tests/test_indy_wallet.py | 6 ++++- 4 files changed, 47 insertions(+), 4 deletions(-) diff --git a/aries_cloudagent/ledger/indy_vdr.py b/aries_cloudagent/ledger/indy_vdr.py index 4e519d8c20..0af73049a4 100644 --- a/aries_cloudagent/ledger/indy_vdr.py +++ b/aries_cloudagent/ledger/indy_vdr.py @@ -893,7 +893,12 @@ async def get_endpoint_for_did( return address async def update_endpoint_for_did( - self, did: str, endpoint: str, endpoint_type: EndpointType = None + self, + did: str, + endpoint: str, + endpoint_type: EndpointType = None, + write_ledger: bool = True, + endorser_did: str = None, ) -> bool: """Check and update the endpoint on the ledger. @@ -902,6 +907,12 @@ async def update_endpoint_for_did( endpoint: The endpoint address endpoint_type: The type of the endpoint """ + public_info = await self.get_wallet_public_did() + if not public_info: + raise BadLedgerRequestError( + "Cannot update endpoint at ledger without a public DID" + ) + if not endpoint_type: endpoint_type = EndpointType.ENDPOINT @@ -930,6 +941,20 @@ async def update_endpoint_for_did( attrib_req = ledger.build_attrib_request( nym, nym, None, attr_json, None ) + + if endorser_did and not write_ledger: + request_json = await indy.ledger.append_request_endorser( + request_json, endorser_did + ) + resp = await self._submit( + request_json, + True, + sign_did=public_info, + write_ledger=write_ledger, + ) + if not write_ledger: + return {"signed_txn": resp} + except VdrError as err: raise LedgerError("Exception when building attribute request") from err diff --git a/aries_cloudagent/wallet/askar.py b/aries_cloudagent/wallet/askar.py index 63dd4f4225..7982df01ed 100644 --- a/aries_cloudagent/wallet/askar.py +++ b/aries_cloudagent/wallet/askar.py @@ -446,6 +446,8 @@ async def set_did_endpoint( endpoint: str, ledger: BaseLedger, endpoint_type: EndpointType = None, + write_ledger: bool = True, + endorser_did: str = None, ): """ Update the endpoint for a DID in the wallet, send to ledger if public or posted. @@ -478,7 +480,15 @@ async def set_did_endpoint( ) if not ledger.read_only: async with ledger: - await ledger.update_endpoint_for_did(did, endpoint, endpoint_type) + attrib_def = await ledger.update_endpoint_for_did( + did, + endpoint, + endpoint_type, + write_ledger=write_ledger, + endorser_did=endorser_did, + ) + if not write_ledger: + return attrib_def await self.replace_local_did_metadata(did, metadata) diff --git a/aries_cloudagent/wallet/tests/test_askar_wallet.py b/aries_cloudagent/wallet/tests/test_askar_wallet.py index 26949b0e12..4d0c5d93bd 100644 --- a/aries_cloudagent/wallet/tests/test_askar_wallet.py +++ b/aries_cloudagent/wallet/tests/test_askar_wallet.py @@ -120,7 +120,11 @@ async def test_set_did_endpoint_ledger(self, wallet): ) await wallet.set_did_endpoint(info_pub.did, "http://1.2.3.4:8021", mock_ledger) mock_ledger.update_endpoint_for_did.assert_called_once_with( - info_pub.did, "http://1.2.3.4:8021", EndpointType.ENDPOINT + info_pub.did, + "http://1.2.3.4:8021", + EndpointType.ENDPOINT, + endorser_did=None, + write_ledger=True, ) info_pub2 = await wallet.get_public_did() assert info_pub2.metadata["endpoint"] == "http://1.2.3.4:8021" diff --git a/aries_cloudagent/wallet/tests/test_indy_wallet.py b/aries_cloudagent/wallet/tests/test_indy_wallet.py index fc6234256c..aa3f7ee88d 100644 --- a/aries_cloudagent/wallet/tests/test_indy_wallet.py +++ b/aries_cloudagent/wallet/tests/test_indy_wallet.py @@ -120,7 +120,11 @@ async def test_set_did_endpoint_ledger(self, wallet: IndySdkWallet): ) await wallet.set_did_endpoint(info_pub.did, "http://1.2.3.4:8021", mock_ledger) mock_ledger.update_endpoint_for_did.assert_called_once_with( - info_pub.did, "http://1.2.3.4:8021", EndpointType.ENDPOINT + info_pub.did, + "http://1.2.3.4:8021", + EndpointType.ENDPOINT, + endorser_did=None, + write_ledger=True, ) info_pub2 = await wallet.get_public_did() assert info_pub2.metadata["endpoint"] == "http://1.2.3.4:8021" From 78ed6cd65655e841d16c6f2ce5c6f8876fe076f0 Mon Sep 17 00:00:00 2001 From: Micah Peltier Date: Mon, 28 Mar 2022 11:13:10 -0600 Subject: [PATCH 118/872] feat: refactor, back to sync functions Signed-off-by: Micah Peltier --- aries_cloudagent/core/conductor.py | 9 ++++----- aries_cloudagent/core/tests/test_conductor.py | 2 -- aries_cloudagent/transport/outbound/manager.py | 4 +--- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/aries_cloudagent/core/conductor.py b/aries_cloudagent/core/conductor.py index 7589a12d3f..6303ab0b22 100644 --- a/aries_cloudagent/core/conductor.py +++ b/aries_cloudagent/core/conductor.py @@ -677,7 +677,7 @@ async def queue_outbound( if self.outbound_queue: return await self._queue_external(profile, outbound) else: - return await self._queue_internal(profile, outbound) + return self._queue_internal(profile, outbound) async def _queue_external( self, @@ -701,7 +701,7 @@ async def _queue_external( return OutboundSendStatus.SENT_TO_EXTERNAL_QUEUE - async def _queue_internal( + def _queue_internal( self, profile: Profile, outbound: OutboundMessage ) -> OutboundSendStatus: """Save the message to an internal outbound queue.""" @@ -710,19 +710,18 @@ async def _queue_internal( return OutboundSendStatus.QUEUED_FOR_DELIVERY except OutboundDeliveryError: LOGGER.warning("Cannot queue message for delivery, no supported transport") - return await self.handle_not_delivered(profile, outbound) + return self.handle_not_delivered(profile, outbound) async def handle_not_delivered( self, profile: Profile, outbound: OutboundMessage ) -> OutboundSendStatus: """Handle a message that failed delivery via outbound transports.""" queued_for_inbound = self.inbound_transport_manager.return_undelivered(outbound) - status = ( + return ( OutboundSendStatus.WAITING_FOR_PICKUP if queued_for_inbound else OutboundSendStatus.UNDELIVERABLE ) - return status def webhook_router( self, diff --git a/aries_cloudagent/core/tests/test_conductor.py b/aries_cloudagent/core/tests/test_conductor.py index febef8e6ed..bb42463f16 100644 --- a/aries_cloudagent/core/tests/test_conductor.py +++ b/aries_cloudagent/core/tests/test_conductor.py @@ -46,8 +46,6 @@ from .. import conductor as test_module -EVENT_PREFIX = test_module.OUTBOUND_STATUS_PREFIX - class Config: test_settings = {"admin.webhook_urls": ["http://sample.webhook.ca"]} diff --git a/aries_cloudagent/transport/outbound/manager.py b/aries_cloudagent/transport/outbound/manager.py index fe4e09745c..18fdf4b182 100644 --- a/aries_cloudagent/transport/outbound/manager.py +++ b/aries_cloudagent/transport/outbound/manager.py @@ -363,9 +363,7 @@ async def _process_loop(self): exc_info=queued.error, ) if self.handle_not_delivered and queued.message: - await self.handle_not_delivered( - queued.profile, queued.message - ) + self.handle_not_delivered(queued.profile, queued.message) continue # remove from buffer deliver = False From 17cfe720bf0a111e6ca50ac14e965c78e0615f28 Mon Sep 17 00:00:00 2001 From: Micah Peltier Date: Mon, 28 Mar 2022 11:51:35 -0600 Subject: [PATCH 119/872] chore: final un-asyncification Signed-off-by: Micah Peltier --- aries_cloudagent/core/conductor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aries_cloudagent/core/conductor.py b/aries_cloudagent/core/conductor.py index 6303ab0b22..e139a4f423 100644 --- a/aries_cloudagent/core/conductor.py +++ b/aries_cloudagent/core/conductor.py @@ -712,7 +712,7 @@ def _queue_internal( LOGGER.warning("Cannot queue message for delivery, no supported transport") return self.handle_not_delivered(profile, outbound) - async def handle_not_delivered( + def handle_not_delivered( self, profile: Profile, outbound: OutboundMessage ) -> OutboundSendStatus: """Handle a message that failed delivery via outbound transports.""" From 54dd0c7d23bb57b8fbc3181b0bb226c495d54375 Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Mon, 28 Mar 2022 12:59:48 -0600 Subject: [PATCH 120/872] fix: Fix unit tests and black formatting Signed-off-by: Colton Wolkins (Indicio work address) --- aries_cloudagent/ledger/base.py | 4 +- aries_cloudagent/ledger/indy.py | 18 +++-- aries_cloudagent/ledger/indy_vdr.py | 6 +- aries_cloudagent/wallet/base.py | 2 + aries_cloudagent/wallet/indy.py | 8 ++- aries_cloudagent/wallet/routes.py | 76 +++++++++++--------- aries_cloudagent/wallet/tests/test_routes.py | 6 +- 7 files changed, 77 insertions(+), 43 deletions(-) diff --git a/aries_cloudagent/ledger/base.py b/aries_cloudagent/ledger/base.py index 5489d7d81d..b916def364 100644 --- a/aries_cloudagent/ledger/base.py +++ b/aries_cloudagent/ledger/base.py @@ -75,8 +75,8 @@ async def update_endpoint_for_did( did: str, endpoint: str, endpoint_type: EndpointType = EndpointType.ENDPOINT, - write_ledger: bool = True, - endorser_did: str = None + write_ledger: bool = True, + endorser_did: str = None, ) -> bool: """Check and update the endpoint on the ledger. diff --git a/aries_cloudagent/ledger/indy.py b/aries_cloudagent/ledger/indy.py index d9e55bc50a..13f27abac2 100644 --- a/aries_cloudagent/ledger/indy.py +++ b/aries_cloudagent/ledger/indy.py @@ -888,7 +888,12 @@ async def get_endpoint_for_did( return address async def update_endpoint_for_did( - self, did: str, endpoint: str, endpoint_type: EndpointType = None, write_ledger: bool = True, endorser_did: str = None + self, + did: str, + endpoint: str, + endpoint_type: EndpointType = None, + write_ledger: bool = True, + endorser_did: str = None, ) -> bool: """Check and update the endpoint on the ledger. @@ -899,8 +904,10 @@ async def update_endpoint_for_did( """ public_info = await self.get_wallet_public_did() if not public_info: - raise BadLedgerRequestError("Cannot update endpoint at ledger without a public DID") - + raise BadLedgerRequestError( + "Cannot update endpoint at ledger without a public DID" + ) + if not endpoint_type: endpoint_type = EndpointType.ENDPOINT @@ -935,7 +942,10 @@ async def update_endpoint_for_did( request_json, endorser_did ) resp = await self._submit( - request_json, True, sign_did=public_info, write_ledger=write_ledger + request_json, + True, + sign_did=public_info, + write_ledger=write_ledger, ) if not write_ledger: return {"signed_txn": resp} diff --git a/aries_cloudagent/ledger/indy_vdr.py b/aries_cloudagent/ledger/indy_vdr.py index 0af73049a4..f71e64c742 100644 --- a/aries_cloudagent/ledger/indy_vdr.py +++ b/aries_cloudagent/ledger/indy_vdr.py @@ -943,11 +943,11 @@ async def update_endpoint_for_did( ) if endorser_did and not write_ledger: - request_json = await indy.ledger.append_request_endorser( - request_json, endorser_did + attrib_req = await ledger.append_request_endorser( + attrib_req, endorser_did ) resp = await self._submit( - request_json, + attrib_req, True, sign_did=public_info, write_ledger=write_ledger, diff --git a/aries_cloudagent/wallet/base.py b/aries_cloudagent/wallet/base.py index d15cfce3ab..a9bf5091e3 100644 --- a/aries_cloudagent/wallet/base.py +++ b/aries_cloudagent/wallet/base.py @@ -224,6 +224,8 @@ async def set_did_endpoint( endpoint: str, _ledger: BaseLedger, endpoint_type: EndpointType = None, + write_ledger: bool = True, + endorser_did: str = None, ): """ Update the endpoint for a DID in the wallet, send to ledger if public or posted. diff --git a/aries_cloudagent/wallet/indy.py b/aries_cloudagent/wallet/indy.py index 864c5697b4..888cce6b5f 100644 --- a/aries_cloudagent/wallet/indy.py +++ b/aries_cloudagent/wallet/indy.py @@ -740,7 +740,13 @@ async def set_did_endpoint( ) if not ledger.read_only: async with ledger: - attrib_def = await ledger.update_endpoint_for_did(did, endpoint, endpoint_type, write_ledger=write_ledger, endorser_did=endorser_did) + attrib_def = await ledger.update_endpoint_for_did( + did, + endpoint, + endpoint_type, + write_ledger=write_ledger, + endorser_did=endorser_did, + ) if not write_ledger: return attrib_def diff --git a/aries_cloudagent/wallet/routes.py b/aries_cloudagent/wallet/routes.py index b07edf9e33..9351679531 100644 --- a/aries_cloudagent/wallet/routes.py +++ b/aries_cloudagent/wallet/routes.py @@ -30,12 +30,15 @@ INDY_RAW_PUBLIC_KEY, ) from ..multitenant.base import BaseMultitenantManager -from ..protocols.endorse_transaction.v1_0.manager import (TransactionManager, TransactionManagerError) +from ..protocols.endorse_transaction.v1_0.manager import ( + TransactionManager, + TransactionManagerError, +) from ..protocols.endorse_transaction.v1_0.util import ( is_author_role, get_endorser_connection_id ) -from ..storage.error import ( StorageNotFoundError, StorageError) +from ..storage.error import (StorageNotFoundError, StorageError) from .base import BaseWallet from .did_info import DIDInfo @@ -185,19 +188,24 @@ class DIDCreateSchema(OpenAPISchema): description="To define a key type for a did:key", ) + class CreateAttribTxnForEndorserOptionSchema(OpenAPISchema): """Class for user to input whether to create a transaction for endorser or not.""" + create_transaction_for_endorser = fields.Boolean( description="Create Transaction For Endorser's signature", required=False, ) + class AttribConnIdMatchInfoSchema(OpenAPISchema): """Path parameters and validators for request taking connection id.""" + conn_id = fields.Str( description="Connection identifier", required=False ) + def format_did_info(info: DIDInfo): """Serialize a DIDInfo object.""" if info: @@ -407,7 +415,6 @@ async def wallet_set_public_did(request: web.BaseRequest): """ context: AdminRequestContext = request["context"] - profile = context.profile session = await context.session() outbound_handler = request["outbound_message_router"] @@ -416,7 +423,6 @@ async def wallet_set_public_did(request: web.BaseRequest): request.query.get("create_transaction_for_endorser", "false") ) write_ledger = not create_transaction_for_endorser - endorser_did = None connection_id = request.query.get("conn_id") attrib_def = None @@ -443,7 +449,7 @@ async def wallet_set_public_did(request: web.BaseRequest): if not create_transaction_for_endorser: return web.json_response({"result": format_did_info(info)}) - + else: transaction_mgr = TransactionManager(context.profile) try: @@ -471,9 +477,12 @@ async def wallet_set_public_did(request: web.BaseRequest): return web.json_response({"txn": transaction.serialize()}) - async def promote_wallet_public_did( - profile: Profile, context: AdminRequestContext, session_fn, did: str, write_ledger: bool = False + profile: Profile, + context: AdminRequestContext, + session_fn, + did: str, + write_ledger: bool = False, ) -> DIDInfo: """Promote supplied DID to the wallet public DID.""" @@ -493,17 +502,15 @@ async def promote_wallet_public_did( if not await ledger.get_key_for_did(did): raise LookupError(f"DID {did} is not posted to the ledger") - # check if we need to endorse if is_author_role(context.profile): # authors cannot write to the ledger write_ledger = False - create_transaction_for_endorser = True + + # author has not provided a connection id, so determine which to use + connection_id = await get_endorser_connection_id(context.profile) if not connection_id: - # author has not provided a connection id, so determine which to use - connection_id = await get_endorser_connection_id(context.profile) - if not connection_id: - raise web.HTTPBadRequest(reason="No endorser connection found") + raise web.HTTPBadRequest(reason="No endorser connection found") if not write_ledger: try: @@ -538,6 +545,7 @@ async def promote_wallet_public_did( wallet = session.inject_or(BaseWallet) did_info = await wallet.get_local_did(did) info = await wallet.set_public_did(did_info) + if info: # Publish endpoint if necessary endpoint = did_info.metadata.get("endpoint") @@ -546,9 +554,13 @@ async def promote_wallet_public_did( async with session_fn() as session: wallet = session.inject_or(BaseWallet) endpoint = context.settings.get("default_endpoint") - attrib_def = await wallet.set_did_endpoint(info.did, endpoint, ledger, - write_ledger=write_ledger, - endorser_did=endorser_did) + attrib_def = await wallet.set_did_endpoint( + info.did, + endpoint, + ledger, + write_ledger=write_ledger, + endorser_did=endorser_did, + ) # Commented the below lines as the function set_did_endpoint # was calling update_endpoint_for_did of ledger @@ -579,9 +591,6 @@ async def wallet_set_did_endpoint(request: web.BaseRequest): request: aiohttp request object """ context: AdminRequestContext = request["context"] - - profile = context.profile - session = await context.session() outbound_handler = request["outbound_message_router"] @@ -592,7 +601,6 @@ async def wallet_set_did_endpoint(request: web.BaseRequest): body.get("endpoint_type", EndpointType.ENDPOINT.w3c) ) - create_transaction_for_endorser = json.loads( request.query.get("create_transaction_for_endorser", "false") ) @@ -601,12 +609,7 @@ async def wallet_set_did_endpoint(request: web.BaseRequest): connection_id = request.query.get("conn_id") attrib_def = None - async with context.session() as session: - wallet = session.inject_or(BaseWallet) - if not wallet: - raise web.HTTPForbidden(reason="No wallet available") - - # check if we need to endorse + # check if we need to endorse if is_author_role(context.profile): # authors cannot write to the ledger write_ledger = False @@ -616,10 +619,10 @@ async def wallet_set_did_endpoint(request: web.BaseRequest): connection_id = await get_endorser_connection_id(context.profile) if not connection_id: raise web.HTTPBadRequest(reason="No endorser connection found") - + if not write_ledger: try: - async with profile.session() as session: + async with context.session() as session: connection_record = await ConnRecord.retrieve_by_id( session, connection_id ) @@ -628,7 +631,7 @@ async def wallet_set_did_endpoint(request: web.BaseRequest): except BaseModelError as err: raise web.HTTPBadRequest(reason=err.roll_up) from err - async with profile.session() as session: + async with context.session() as session: endorser_info = await connection_record.metadata_get( session, "endorser_info" ) @@ -644,11 +647,20 @@ async def wallet_set_did_endpoint(request: web.BaseRequest): ) endorser_did = endorser_info["endorser_did"] + async with context.session() as session: + wallet = session.inject_or(BaseWallet) + if not wallet: + raise web.HTTPForbidden(reason="No wallet available") try: ledger = context.profile.inject_or(BaseLedger) - attrib_def = await wallet.set_did_endpoint(did, endpoint, ledger, endpoint_type, - write_ledger=write_ledger, - endorser_did=endorser_did) + attrib_def = await wallet.set_did_endpoint( + did, + endpoint, + ledger, + endpoint_type, + write_ledger=write_ledger, + endorser_did=endorser_did, + ) except WalletNotFoundError as err: raise web.HTTPNotFound(reason=err.roll_up) from err except LedgerConfigError as err: diff --git a/aries_cloudagent/wallet/tests/test_routes.py b/aries_cloudagent/wallet/tests/test_routes.py index 33df4b553a..e7869576fb 100644 --- a/aries_cloudagent/wallet/tests/test_routes.py +++ b/aries_cloudagent/wallet/tests/test_routes.py @@ -585,7 +585,11 @@ async def test_set_public_did_update_endpoint_use_default_update_in_wallet(self) result = await test_module.wallet_set_public_did(self.request) self.wallet.set_public_did.assert_awaited_once() self.wallet.set_did_endpoint.assert_awaited_once_with( - did_info.did, "https://default_endpoint.com", ledger + did_info.did, + "https://default_endpoint.com", + ledger, + write_ledger=True, + endorser_did=None, ) json_response.assert_called_once_with( { From 4784814fb6daf45a4f841d590f8692a5887b2d58 Mon Sep 17 00:00:00 2001 From: Adam Burdett Date: Mon, 28 Mar 2022 16:30:05 -0600 Subject: [PATCH 121/872] platform target in run_tests, fix m1 failure Signed-off-by: Adam Burdett --- scripts/run_tests | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/run_tests b/scripts/run_tests index 66eda58598..898d2361dc 100755 --- a/scripts/run_tests +++ b/scripts/run_tests @@ -8,7 +8,7 @@ FAST=${FAST:-0} cd "$(dirname "$0")" || exit if [[ $FAST -eq 0 ]]; then - $CONTAINER_RUNTIME build -t aries-cloudagent-test -f ../docker/Dockerfile.test .. || exit 1 + $CONTAINER_RUNTIME build --platform linux/amd64 -t aries-cloudagent-test -f ../docker/Dockerfile.test .. || exit 1 fi DOCKER_ARGS="" @@ -40,5 +40,6 @@ if [ ! -z "$TEST_REDIS_CONFIG" ]; then fi $CONTAINER_RUNTIME run --rm -ti --name aries-cloudagent-runner \ + --platform linux/amd64 \ -v "$(pwd)/../test-reports:/usr/src/app/test-reports:z" \ $DOCKER_ARGS aries-cloudagent-test "$@" From 8674f8d7b2dcb5ac68c19bdc4f6e8c625fed3c92 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Mon, 28 Mar 2022 19:05:58 -0400 Subject: [PATCH 122/872] style: format with black Signed-off-by: Daniel Bluhm --- .pre-commit-config.yaml | 2 +- aries_cloudagent/wallet/routes.py | 14 +++++--------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1c72e986f7..b4a0f811b8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -8,7 +8,7 @@ repos: args: ["--config", ".commitlint.config.js"] additional_dependencies: ['@commitlint/config-conventional'] - repo: https://github.com/psf/black - rev: 20.8b1 + rev: 22.3.0 hooks: - id: black stages: [commit] diff --git a/aries_cloudagent/wallet/routes.py b/aries_cloudagent/wallet/routes.py index 9351679531..34811d784d 100644 --- a/aries_cloudagent/wallet/routes.py +++ b/aries_cloudagent/wallet/routes.py @@ -36,9 +36,9 @@ ) from ..protocols.endorse_transaction.v1_0.util import ( is_author_role, - get_endorser_connection_id + get_endorser_connection_id, ) -from ..storage.error import (StorageNotFoundError, StorageError) +from ..storage.error import StorageNotFoundError, StorageError from .base import BaseWallet from .did_info import DIDInfo @@ -201,9 +201,7 @@ class CreateAttribTxnForEndorserOptionSchema(OpenAPISchema): class AttribConnIdMatchInfoSchema(OpenAPISchema): """Path parameters and validators for request taking connection id.""" - conn_id = fields.Str( - description="Connection identifier", required=False - ) + conn_id = fields.Str(description="Connection identifier", required=False) def format_did_info(info: DIDInfo): @@ -454,8 +452,7 @@ async def wallet_set_public_did(request: web.BaseRequest): transaction_mgr = TransactionManager(context.profile) try: transaction = await transaction_mgr.create_record( - messages_attach=attrib_def["signed_txn"], - connection_id=connection_id + messages_attach=attrib_def["signed_txn"], connection_id=connection_id ) except StorageError as err: raise web.HTTPBadRequest(reason=err.roll_up) from err @@ -674,8 +671,7 @@ async def wallet_set_did_endpoint(request: web.BaseRequest): transaction_mgr = TransactionManager(context.profile) try: transaction = await transaction_mgr.create_record( - messages_attach=attrib_def["signed_txn"], - connection_id=connection_id + messages_attach=attrib_def["signed_txn"], connection_id=connection_id ) except StorageError as err: raise web.HTTPBadRequest(reason=err.roll_up) from err From a2e306e9e41e10e2d95422da5aeec043a25f9ece Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Mon, 28 Mar 2022 20:38:19 -0400 Subject: [PATCH 123/872] feat: profile cache track open profiles via weakref Signed-off-by: Daniel Bluhm --- aries_cloudagent/multitenant/cache.py | 40 ++++++++++++++++++++------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/aries_cloudagent/multitenant/cache.py b/aries_cloudagent/multitenant/cache.py index edaed40b8b..8a00120ca8 100644 --- a/aries_cloudagent/multitenant/cache.py +++ b/aries_cloudagent/multitenant/cache.py @@ -3,6 +3,7 @@ import logging from collections import OrderedDict from typing import Optional +from weakref import WeakValueDictionary from ..core.profile import Profile @@ -21,11 +22,27 @@ def __init__(self, capacity: int): """ self.profiles: OrderedDict[str, Profile] = OrderedDict() + self._open_profiles: WeakValueDictionary[str, Profile] = WeakValueDictionary() self.capacity = capacity + def _cleanup(self): + """Prune cache until size matches defined capacity.""" + if len(self.profiles) > self.capacity: + LOGGER.debug( + f"Profile limit of {self.capacity} reached." + " Evicting least recently used profiles..." + ) + while len(self.profiles) > self.capacity: + key, _ = self.profiles.popitem(last=False) + LOGGER.debug(f"Evicted profile with key {key}") + def get(self, key: str) -> Optional[Profile]: """Get profile with associated key from cache. + If a profile is open but has been evicted from the cache, this will + reinsert the profile back into the cache. This prevents attempting to + open a profile that is already open. Triggers clean up. + Args: key (str): the key to get the profile for. @@ -33,11 +50,19 @@ def get(self, key: str) -> Optional[Profile]: Optional[Profile]: Profile if found in cache. """ - if key not in self.profiles: + if key not in self._open_profiles: return None else: + value = self._open_profiles[key] + if key not in self.profiles: + LOGGER.debug( + f"Rescuing profile {key} from eviction from cache; profile " + "will be reinserted into cache" + ) + self.profiles[key] = value self.profiles.move_to_end(key) - return self.profiles[key] + self._cleanup() + return value def has(self, key: str) -> bool: """Check whether there is a profile with associated key in the cache. @@ -62,16 +87,11 @@ def put(self, key: str, value: Profile) -> None: value (Profile): the profile to set """ value.finalizer() + self._open_profiles[key] = value self.profiles[key] = value + LOGGER.debug(f"Setting profile with id {key} in profile cache") self.profiles.move_to_end(key) - LOGGER.debug(f"setting profile with id {key} in profile cache") - - if len(self.profiles) > self.capacity: - LOGGER.debug( - f"Profile limit of {self.capacity} reached." - " Evicting least recently used profile..." - ) - self.profiles.popitem(last=False) + self._cleanup() def remove(self, key: str): """Remove profile with associated key from the cache. From f87522cefcfd395977f6aa0aae3dd7e839571a58 Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Mon, 28 Mar 2022 19:44:51 -0600 Subject: [PATCH 124/872] ci: Fix order of operations for author-endorser tests Signed-off-by: Colton Wolkins (Indicio work address) --- demo/features/0586-sign-transaction.feature | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/demo/features/0586-sign-transaction.feature b/demo/features/0586-sign-transaction.feature index fad7aa1dd5..e8c85d1e0b 100644 --- a/demo/features/0586-sign-transaction.feature +++ b/demo/features/0586-sign-transaction.feature @@ -8,10 +8,10 @@ Feature: RFC 0586 Aries sign (endorse) transactions functions | Bob | author | | And "Acme" and "Bob" have an existing connection When "Acme" has a DID with role "ENDORSER" - And "Bob" has a DID with role "AUTHOR" And "Acme" connection has job role "TRANSACTION_ENDORSER" And "Bob" connection has job role "TRANSACTION_AUTHOR" And "Bob" connection sets endorser info + And "Bob" has a DID with role "AUTHOR" And "Bob" authors a schema transaction with And "Bob" requests endorsement for the transaction And "Acme" endorses the transaction @@ -34,10 +34,10 @@ Feature: RFC 0586 Aries sign (endorse) transactions functions | Bob | author | | And "Acme" and "Bob" have an existing connection When "Acme" has a DID with role "ENDORSER" - And "Bob" has a DID with role "AUTHOR" And "Acme" connection has job role "TRANSACTION_ENDORSER" And "Bob" connection has job role "TRANSACTION_AUTHOR" And "Bob" connection sets endorser info + And "Bob" has a DID with role "AUTHOR" And "Bob" authors a schema transaction with And "Bob" requests endorsement for the transaction And "Acme" endorses the transaction @@ -57,10 +57,10 @@ Feature: RFC 0586 Aries sign (endorse) transactions functions | Bob | author | | And "Acme" and "Bob" have an existing connection When "Acme" has a DID with role "ENDORSER" - And "Bob" has a DID with role "AUTHOR" And "Acme" connection has job role "TRANSACTION_ENDORSER" And "Bob" connection has job role "TRANSACTION_AUTHOR" And "Bob" connection sets endorser info + And "Bob" has a DID with role "AUTHOR" And "Bob" authors a schema transaction with And "Bob" requests endorsement for the transaction And "Acme" endorses the transaction @@ -101,10 +101,10 @@ Feature: RFC 0586 Aries sign (endorse) transactions functions | Bob | author | | And "Acme" and "Bob" have an existing connection When "Acme" has a DID with role "ENDORSER" - And "Bob" has a DID with role "AUTHOR" And "Acme" connection has job role "TRANSACTION_ENDORSER" And "Bob" connection has job role "TRANSACTION_AUTHOR" And "Bob" connection sets endorser info + And "Bob" has a DID with role "AUTHOR" And "Bob" authors a schema transaction with And "Bob" requests endorsement for the transaction And "Acme" endorses the transaction @@ -142,10 +142,10 @@ Feature: RFC 0586 Aries sign (endorse) transactions functions | Bob | author | | And "Acme" and "Bob" have an existing connection When "Acme" has a DID with role "ENDORSER" - And "Bob" has a DID with role "AUTHOR" And "Acme" connection has job role "TRANSACTION_ENDORSER" And "Bob" connection has job role "TRANSACTION_AUTHOR" And "Bob" connection sets endorser info + And "Bob" has a DID with role "AUTHOR" And "Bob" authors a schema transaction with And "Bob" has written the schema to the ledger And "Bob" authors a credential definition transaction with @@ -172,10 +172,10 @@ Feature: RFC 0586 Aries sign (endorse) transactions functions | Bob | author | | And "Acme" and "Bob" have an existing connection When "Acme" has a DID with role "ENDORSER" - And "Bob" has a DID with role "AUTHOR" And "Acme" connection has job role "TRANSACTION_ENDORSER" And "Bob" connection has job role "TRANSACTION_AUTHOR" And "Bob" connection sets endorser info + And "Bob" has a DID with role "AUTHOR" And "Bob" authors a schema transaction with And "Bob" has written the schema to the ledger And "Bob" authors a credential definition transaction with From c80ed59e6323659c2a4008903976de9d77da3cdc Mon Sep 17 00:00:00 2001 From: Micah Peltier Date: Tue, 29 Mar 2022 08:31:59 -0600 Subject: [PATCH 125/872] chore: add missing docstring Signed-off-by: Micah Peltier --- aries_cloudagent/transport/outbound/status.py | 1 + 1 file changed, 1 insertion(+) diff --git a/aries_cloudagent/transport/outbound/status.py b/aries_cloudagent/transport/outbound/status.py index 34e76184b0..e1fba403ec 100644 --- a/aries_cloudagent/transport/outbound/status.py +++ b/aries_cloudagent/transport/outbound/status.py @@ -26,4 +26,5 @@ class OutboundSendStatus(Enum): @property def topic(self): + """Return an event topic associated with a given status.""" return f"{OUTBOUND_STATUS_PREFIX}{self.value}" From 459b72f67733e5da9854e079edb17ab23e776891 Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Tue, 29 Mar 2022 11:17:20 -0600 Subject: [PATCH 126/872] fix: Properly attach endorser info for Askar Signed-off-by: Colton Wolkins (Indicio work address) --- aries_cloudagent/ledger/indy_vdr.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/aries_cloudagent/ledger/indy_vdr.py b/aries_cloudagent/ledger/indy_vdr.py index f71e64c742..5c5e3fe689 100644 --- a/aries_cloudagent/ledger/indy_vdr.py +++ b/aries_cloudagent/ledger/indy_vdr.py @@ -943,9 +943,7 @@ async def update_endpoint_for_did( ) if endorser_did and not write_ledger: - attrib_req = await ledger.append_request_endorser( - attrib_req, endorser_did - ) + attrib_req.set_endorser(endorser_did) resp = await self._submit( attrib_req, True, From 037882316c2e9e5685ca0a564f018122886711fd Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Tue, 29 Mar 2022 15:58:57 -0700 Subject: [PATCH 127/872] updated IndyLedgerRequestsExecutor logic wrt multitenancy and base wallet is basic Signed-off-by: Shaanjot Gill --- aries_cloudagent/indy/models/pres_preview.py | 13 +++- .../indy/models/tests/test_pres_preview.py | 6 ++ aries_cloudagent/indy/tests/test_verifier.py | 24 +++++++ aries_cloudagent/indy/verifier.py | 7 +- aries_cloudagent/ledger/routes.py | 19 ++++- aries_cloudagent/ledger/tests/test_routes.py | 69 ++++++++++++++++++ .../credential_definitions/routes.py | 13 +++- .../tests/test_routes.py | 24 +++++++ aries_cloudagent/messaging/schemas/routes.py | 13 +++- .../messaging/schemas/tests/test_routes.py | 22 ++++++ .../issue_credential/v1_0/manager.py | 25 +++++-- .../v1_0/tests/test_manager.py | 34 ++++++--- .../v2_0/formats/indy/handler.py | 25 +++++-- .../v2_0/formats/indy/tests/test_handler.py | 70 ++++++++++++++----- .../present_proof/indy/pres_exch_handler.py | 19 ++++- .../present_proof/v2_0/tests/test_manager.py | 21 +++++- aries_cloudagent/resolver/default/indy.py | 7 +- .../resolver/default/tests/test_indy.py | 18 +++++ aries_cloudagent/revocation/indy.py | 13 +++- .../revocation/tests/test_indy.py | 39 +++++++++++ 20 files changed, 427 insertions(+), 54 deletions(-) diff --git a/aries_cloudagent/indy/models/pres_preview.py b/aries_cloudagent/indy/models/pres_preview.py index 7ab771309f..5f73978f33 100644 --- a/aries_cloudagent/indy/models/pres_preview.py +++ b/aries_cloudagent/indy/models/pres_preview.py @@ -14,6 +14,7 @@ from ...messaging.models.base import BaseModel, BaseModelSchema from ...messaging.util import canon from ...messaging.valid import INDY_CRED_DEF_ID, INDY_PREDICATE +from ...multitenant.base import BaseMultitenantManager from ...protocols.didcomm_prefix import DIDCommPrefix from ...wallet.util import b64_to_str @@ -351,7 +352,11 @@ def non_revoc(cred_def_id: str) -> IndyNonRevocationInterval: revoc_support = False if cd_id: if profile: - ledger_exec_inst = profile.inject(IndyLedgerRequestsExecutor) + multitenant_mgr = profile.inject_or(BaseMultitenantManager) + if multitenant_mgr: + ledger_exec_inst = IndyLedgerRequestsExecutor(profile) + else: + ledger_exec_inst = profile.inject(IndyLedgerRequestsExecutor) ledger = ( await ledger_exec_inst.get_ledger_for_identifier( cd_id, @@ -410,7 +415,11 @@ def non_revoc(cred_def_id: str) -> IndyNonRevocationInterval: revoc_support = False if cd_id: if profile: - ledger_exec_inst = profile.inject(IndyLedgerRequestsExecutor) + multitenant_mgr = profile.inject_or(BaseMultitenantManager) + if multitenant_mgr: + ledger_exec_inst = IndyLedgerRequestsExecutor(profile) + else: + ledger_exec_inst = profile.inject(IndyLedgerRequestsExecutor) ledger = ( await ledger_exec_inst.get_ledger_for_identifier( cd_id, diff --git a/aries_cloudagent/indy/models/tests/test_pres_preview.py b/aries_cloudagent/indy/models/tests/test_pres_preview.py index eee3071e49..5f5809b889 100644 --- a/aries_cloudagent/indy/models/tests/test_pres_preview.py +++ b/aries_cloudagent/indy/models/tests/test_pres_preview.py @@ -13,6 +13,8 @@ IndyLedgerRequestsExecutor, ) from ....messaging.util import canon +from ....multitenant.base import BaseMultitenantManager +from ....multitenant.manager import MultitenantManager from ....protocols.didcomm_prefix import DIDCommPrefix @@ -443,6 +445,10 @@ async def test_to_indy_proof_request_revo(self): context.injector.bind_instance( IndyLedgerRequestsExecutor, IndyLedgerRequestsExecutor(mock_profile) ) + context.injector.bind_instance( + BaseMultitenantManager, + async_mock.MagicMock(MultitenantManager, autospec=True), + ) with async_mock.patch.object( IndyLedgerRequestsExecutor, "get_ledger_for_identifier" ) as mock_get_ledger: diff --git a/aries_cloudagent/indy/tests/test_verifier.py b/aries_cloudagent/indy/tests/test_verifier.py index 7a00c7276d..90b75b92a6 100644 --- a/aries_cloudagent/indy/tests/test_verifier.py +++ b/aries_cloudagent/indy/tests/test_verifier.py @@ -10,6 +10,8 @@ from ...ledger.multiple_ledger.ledger_requests_executor import ( IndyLedgerRequestsExecutor, ) +from ...multitenant.base import BaseMultitenantManager +from ...multitenant.manager import MultitenantManager from .. import verifier as test_module from ..verifier import IndyVerifier @@ -332,6 +334,28 @@ def setUp(self): self.verifier = MockVerifier() async def test_check_timestamps(self): + # multitenant + mock_profile = InMemoryProfile.test_profile() + context = mock_profile.context + context.injector.bind_instance( + IndyLedgerRequestsExecutor, + IndyLedgerRequestsExecutor(mock_profile), + ) + context.injector.bind_instance( + BaseMultitenantManager, + async_mock.MagicMock(MultitenantManager, autospec=True), + ) + with async_mock.patch.object( + IndyLedgerRequestsExecutor, "get_ledger_for_identifier" + ) as mock_get_ledger: + mock_get_ledger.return_value = (None, self.ledger) + await self.verifier.check_timestamps( + mock_profile, + INDY_PROOF_REQ_NAME, + INDY_PROOF_NAME, + REV_REG_DEFS, + ) + # all clear, with timestamps mock_profile = InMemoryProfile.test_profile() context = mock_profile.context diff --git a/aries_cloudagent/indy/verifier.py b/aries_cloudagent/indy/verifier.py index d8a2d61a10..f6bbe6d26b 100644 --- a/aries_cloudagent/indy/verifier.py +++ b/aries_cloudagent/indy/verifier.py @@ -12,6 +12,7 @@ IndyLedgerRequestsExecutor, ) from ..messaging.util import canon, encode +from ..multitenant.base import BaseMultitenantManager from .models.xform import indy_proof_req2non_revoc_intervals @@ -111,7 +112,11 @@ async def check_timestamps( for (index, ident) in enumerate(pres["identifiers"]): if ident.get("timestamp"): cred_def_id = ident["cred_def_id"] - ledger_exec_inst = profile.inject(IndyLedgerRequestsExecutor) + multitenant_mgr = profile.inject_or(BaseMultitenantManager) + if multitenant_mgr: + ledger_exec_inst = IndyLedgerRequestsExecutor(profile) + else: + ledger_exec_inst = profile.inject(IndyLedgerRequestsExecutor) ledger = ( await ledger_exec_inst.get_ledger_for_identifier( cred_def_id, diff --git a/aries_cloudagent/ledger/routes.py b/aries_cloudagent/ledger/routes.py index dfeffee73b..fc24386aaa 100644 --- a/aries_cloudagent/ledger/routes.py +++ b/aries_cloudagent/ledger/routes.py @@ -18,6 +18,7 @@ INT_EPOCH, UUIDFour, ) +from ..multitenant.base import BaseMultitenantManager from ..protocols.endorse_transaction.v1_0.manager import ( TransactionManager, @@ -369,7 +370,11 @@ async def get_nym_role(request: web.BaseRequest): raise web.HTTPBadRequest(reason="Request query must include DID") async with context.profile.session() as session: - ledger_exec_inst = session.inject(IndyLedgerRequestsExecutor) + multitenant_mgr = session.inject_or(BaseMultitenantManager) + if multitenant_mgr: + ledger_exec_inst = IndyLedgerRequestsExecutor(context.profile) + else: + ledger_exec_inst = session.inject(IndyLedgerRequestsExecutor) ledger_id, ledger = await ledger_exec_inst.get_ledger_for_identifier( did, txn_record_type=GET_NYM_ROLE, @@ -442,7 +447,11 @@ async def get_did_verkey(request: web.BaseRequest): raise web.HTTPBadRequest(reason="Request query must include DID") async with context.profile.session() as session: - ledger_exec_inst = session.inject(IndyLedgerRequestsExecutor) + multitenant_mgr = session.inject_or(BaseMultitenantManager) + if multitenant_mgr: + ledger_exec_inst = IndyLedgerRequestsExecutor(context.profile) + else: + ledger_exec_inst = session.inject(IndyLedgerRequestsExecutor) ledger_id, ledger = await ledger_exec_inst.get_ledger_for_identifier( did, txn_record_type=GET_KEY_FOR_DID, @@ -487,7 +496,11 @@ async def get_did_endpoint(request: web.BaseRequest): raise web.HTTPBadRequest(reason="Request query must include DID") async with context.profile.session() as session: - ledger_exec_inst = session.inject(IndyLedgerRequestsExecutor) + multitenant_mgr = session.inject_or(BaseMultitenantManager) + if multitenant_mgr: + ledger_exec_inst = IndyLedgerRequestsExecutor(context.profile) + else: + ledger_exec_inst = session.inject(IndyLedgerRequestsExecutor) ledger_id, ledger = await ledger_exec_inst.get_ledger_for_identifier( did, txn_record_type=GET_ENDPOINT_FOR_DID, diff --git a/aries_cloudagent/ledger/tests/test_routes.py b/aries_cloudagent/ledger/tests/test_routes.py index fb94d5c692..aa682e2249 100644 --- a/aries_cloudagent/ledger/tests/test_routes.py +++ b/aries_cloudagent/ledger/tests/test_routes.py @@ -12,6 +12,8 @@ BaseMultipleLedgerManager, MultipleLedgerManagerError, ) +from ...multitenant.base import BaseMultitenantManager +from ...multitenant.manager import MultitenantManager from .. import routes as test_module from ..indy import Role @@ -121,6 +123,29 @@ async def test_get_verkey_b(self): ) assert result is json_response.return_value + async def test_get_verkey_multitenant(self): + self.context.injector.bind_instance( + BaseMultitenantManager, + async_mock.MagicMock(MultitenantManager, autospec=True), + ) + self.request.query = {"did": self.test_did} + with async_mock.patch.object( + IndyLedgerRequestsExecutor, + "get_ledger_for_identifier", + async_mock.CoroutineMock(return_value=("test_ledger_id", self.ledger)), + ), async_mock.patch.object( + test_module.web, "json_response", async_mock.Mock() + ) as json_response: + self.ledger.get_key_for_did.return_value = self.test_verkey + result = await test_module.get_did_verkey(self.request) + json_response.assert_called_once_with( + { + "ledger_id": "test_ledger_id", + "verkey": self.ledger.get_key_for_did.return_value, + } + ) + assert result is json_response.return_value + async def test_get_verkey_no_did(self): self.request.query = {"no": "did"} with self.assertRaises(test_module.web.HTTPBadRequest): @@ -174,6 +199,29 @@ async def test_get_endpoint(self): ) assert result is json_response.return_value + async def test_get_endpoint_multitenant(self): + self.context.injector.bind_instance( + BaseMultitenantManager, + async_mock.MagicMock(MultitenantManager, autospec=True), + ) + self.request.query = {"did": self.test_did} + with async_mock.patch.object( + IndyLedgerRequestsExecutor, + "get_ledger_for_identifier", + async_mock.CoroutineMock(return_value=("test_ledger_id", self.ledger)), + ), async_mock.patch.object( + test_module.web, "json_response", async_mock.Mock() + ) as json_response: + self.ledger.get_endpoint_for_did.return_value = self.test_endpoint + result = await test_module.get_did_endpoint(self.request) + json_response.assert_called_once_with( + { + "ledger_id": "test_ledger_id", + "endpoint": self.ledger.get_endpoint_for_did.return_value, + } + ) + assert result is json_response.return_value + async def test_get_endpoint_of_type_profile(self): self.profile.context.injector.bind_instance( IndyLedgerRequestsExecutor, @@ -486,6 +534,27 @@ async def test_get_nym_role_b(self): ) assert result is json_response.return_value + async def test_get_nym_role_multitenant(self): + self.context.injector.bind_instance( + BaseMultitenantManager, + async_mock.MagicMock(MultitenantManager, autospec=True), + ) + self.request.query = {"did": self.test_did} + + with async_mock.patch.object( + IndyLedgerRequestsExecutor, + "get_ledger_for_identifier", + async_mock.CoroutineMock(return_value=("test_ledger_id", self.ledger)), + ), async_mock.patch.object( + test_module.web, "json_response", async_mock.Mock() + ) as json_response: + self.ledger.get_nym_role.return_value = Role.USER + result = await test_module.get_nym_role(self.request) + json_response.assert_called_once_with( + {"ledger_id": "test_ledger_id", "role": "USER"} + ) + assert result is json_response.return_value + async def test_get_nym_role_bad_request(self): self.request.query = {"no": "did"} with self.assertRaises(test_module.web.HTTPBadRequest): diff --git a/aries_cloudagent/messaging/credential_definitions/routes.py b/aries_cloudagent/messaging/credential_definitions/routes.py index 1603c76cd1..cd78c623f1 100644 --- a/aries_cloudagent/messaging/credential_definitions/routes.py +++ b/aries_cloudagent/messaging/credential_definitions/routes.py @@ -28,6 +28,7 @@ GET_CRED_DEF, IndyLedgerRequestsExecutor, ) +from ...multitenant.base import BaseMultitenantManager from ...protocols.endorse_transaction.v1_0.manager import ( TransactionManager, TransactionManagerError, @@ -371,7 +372,11 @@ async def credential_definitions_get_credential_definition(request: web.BaseRequ cred_def_id = request.match_info["cred_def_id"] async with context.profile.session() as session: - ledger_exec_inst = session.inject(IndyLedgerRequestsExecutor) + multitenant_mgr = session.inject_or(BaseMultitenantManager) + if multitenant_mgr: + ledger_exec_inst = IndyLedgerRequestsExecutor(context.profile) + else: + ledger_exec_inst = session.inject(IndyLedgerRequestsExecutor) ledger_id, ledger = await ledger_exec_inst.get_ledger_for_identifier( cred_def_id, txn_record_type=GET_CRED_DEF, @@ -416,7 +421,11 @@ async def credential_definitions_fix_cred_def_wallet_record(request: web.BaseReq async with context.profile.session() as session: storage = session.inject(BaseStorage) - ledger_exec_inst = session.inject(IndyLedgerRequestsExecutor) + multitenant_mgr = session.inject_or(BaseMultitenantManager) + if multitenant_mgr: + ledger_exec_inst = IndyLedgerRequestsExecutor(context.profile) + else: + ledger_exec_inst = session.inject(IndyLedgerRequestsExecutor) ledger_id, ledger = await ledger_exec_inst.get_ledger_for_identifier( cred_def_id, txn_record_type=GET_CRED_DEF, diff --git a/aries_cloudagent/messaging/credential_definitions/tests/test_routes.py b/aries_cloudagent/messaging/credential_definitions/tests/test_routes.py index 953f185d73..88164b437a 100644 --- a/aries_cloudagent/messaging/credential_definitions/tests/test_routes.py +++ b/aries_cloudagent/messaging/credential_definitions/tests/test_routes.py @@ -8,6 +8,8 @@ from ....ledger.multiple_ledger.ledger_requests_executor import ( IndyLedgerRequestsExecutor, ) +from ....multitenant.base import BaseMultitenantManager +from ....multitenant.manager import MultitenantManager from ....storage.base import BaseStorage from ....tails.base import BaseTailsServer @@ -354,6 +356,28 @@ async def test_get_credential_definition(self): } ) + async def test_get_credential_definition_multitenant(self): + self.profile_injector.bind_instance( + BaseMultitenantManager, + async_mock.MagicMock(MultitenantManager, autospec=True), + ) + self.request.match_info = {"cred_def_id": CRED_DEF_ID} + with async_mock.patch.object( + IndyLedgerRequestsExecutor, + "get_ledger_for_identifier", + async_mock.CoroutineMock(return_value=("test_ledger_id", self.ledger)), + ), async_mock.patch.object(test_module.web, "json_response") as mock_response: + result = await test_module.credential_definitions_get_credential_definition( + self.request + ) + assert result == mock_response.return_value + mock_response.assert_called_once_with( + { + "ledger_id": "test_ledger_id", + "credential_definition": {"cred": "def", "signed_txn": "..."}, + } + ) + async def test_get_credential_definition_no_ledger(self): self.profile_injector.bind_instance( IndyLedgerRequestsExecutor, diff --git a/aries_cloudagent/messaging/schemas/routes.py b/aries_cloudagent/messaging/schemas/routes.py index 9a44dddbcf..c494adda5b 100644 --- a/aries_cloudagent/messaging/schemas/routes.py +++ b/aries_cloudagent/messaging/schemas/routes.py @@ -28,6 +28,7 @@ GET_SCHEMA, IndyLedgerRequestsExecutor, ) +from ...multitenant.base import BaseMultitenantManager from ...protocols.endorse_transaction.v1_0.manager import ( TransactionManager, TransactionManagerError, @@ -355,7 +356,11 @@ async def schemas_get_schema(request: web.BaseRequest): schema_id = request.match_info["schema_id"] async with context.profile.session() as session: - ledger_exec_inst = session.inject(IndyLedgerRequestsExecutor) + multitenant_mgr = session.inject_or(BaseMultitenantManager) + if multitenant_mgr: + ledger_exec_inst = IndyLedgerRequestsExecutor(context.profile) + else: + ledger_exec_inst = session.inject(IndyLedgerRequestsExecutor) ledger_id, ledger = await ledger_exec_inst.get_ledger_for_identifier( schema_id, txn_record_type=GET_SCHEMA, @@ -400,7 +405,11 @@ async def schemas_fix_schema_wallet_record(request: web.BaseRequest): async with profile.session() as session: storage = session.inject(BaseStorage) - ledger_exec_inst = session.inject(IndyLedgerRequestsExecutor) + multitenant_mgr = session.inject_or(BaseMultitenantManager) + if multitenant_mgr: + ledger_exec_inst = IndyLedgerRequestsExecutor(context.profile) + else: + ledger_exec_inst = session.inject(IndyLedgerRequestsExecutor) ledger_id, ledger = await ledger_exec_inst.get_ledger_for_identifier( schema_id, txn_record_type=GET_SCHEMA, diff --git a/aries_cloudagent/messaging/schemas/tests/test_routes.py b/aries_cloudagent/messaging/schemas/tests/test_routes.py index 74b9f79e82..91a826d628 100644 --- a/aries_cloudagent/messaging/schemas/tests/test_routes.py +++ b/aries_cloudagent/messaging/schemas/tests/test_routes.py @@ -8,6 +8,8 @@ from ....ledger.multiple_ledger.ledger_requests_executor import ( IndyLedgerRequestsExecutor, ) +from ....multitenant.base import BaseMultitenantManager +from ....multitenant.manager import MultitenantManager from ....storage.base import BaseStorage from .. import routes as test_module @@ -324,6 +326,26 @@ async def test_get_schema(self): } ) + async def test_get_schema_multitenant(self): + self.profile_injector.bind_instance( + BaseMultitenantManager, + async_mock.MagicMock(MultitenantManager, autospec=True), + ) + self.request.match_info = {"schema_id": SCHEMA_ID} + with async_mock.patch.object( + IndyLedgerRequestsExecutor, + "get_ledger_for_identifier", + async_mock.CoroutineMock(return_value=("test_ledger_id", self.ledger)), + ), async_mock.patch.object(test_module.web, "json_response") as mock_response: + result = await test_module.schemas_get_schema(self.request) + assert result == mock_response.return_value + mock_response.assert_called_once_with( + { + "ledger_id": "test_ledger_id", + "schema": {"schema": "def", "signed_txn": "..."}, + } + ) + async def test_get_schema_on_seq_no(self): self.profile_injector.bind_instance( IndyLedgerRequestsExecutor, diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/manager.py b/aries_cloudagent/protocols/issue_credential/v1_0/manager.py index b4b9291be4..9b87e934c9 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/manager.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/manager.py @@ -21,6 +21,7 @@ CRED_DEF_SENT_RECORD_TYPE, ) from ....messaging.responder import BaseResponder +from ....multitenant.base import BaseMultitenantManager from ....revocation.indy import IndyRevocation from ....revocation.models.revocation_registry import RevocationRegistry from ....revocation.models.issuer_rev_reg_record import IssuerRevRegRecord @@ -266,7 +267,11 @@ async def _create(cred_def_id): credential_preview = credential_proposal_message.credential_proposal # vet attributes - ledger_exec_inst = self._profile.inject(IndyLedgerRequestsExecutor) + multitenant_mgr = self.profile.inject_or(BaseMultitenantManager) + if multitenant_mgr: + ledger_exec_inst = IndyLedgerRequestsExecutor(self.profile) + else: + ledger_exec_inst = self.profile.inject(IndyLedgerRequestsExecutor) ledger = ( await ledger_exec_inst.get_ledger_for_identifier( cred_def_id, @@ -406,7 +411,11 @@ async def create_request( cred_req_meta = None async def _create(): - ledger_exec_inst = self._profile.inject(IndyLedgerRequestsExecutor) + multitenant_mgr = self.profile.inject_or(BaseMultitenantManager) + if multitenant_mgr: + ledger_exec_inst = IndyLedgerRequestsExecutor(self.profile) + else: + ledger_exec_inst = self.profile.inject(IndyLedgerRequestsExecutor) ledger = ( await ledger_exec_inst.get_ledger_for_identifier( credential_definition_id, @@ -572,7 +581,11 @@ async def issue_credential( cred_offer_ser = cred_ex_record._credential_offer.ser cred_req_ser = cred_ex_record._credential_request.ser schema_id = cred_ex_record.schema_id - ledger_exec_inst = self._profile.inject(IndyLedgerRequestsExecutor) + multitenant_mgr = self.profile.inject_or(BaseMultitenantManager) + if multitenant_mgr: + ledger_exec_inst = IndyLedgerRequestsExecutor(self.profile) + else: + ledger_exec_inst = self.profile.inject(IndyLedgerRequestsExecutor) ledger = ( await ledger_exec_inst.get_ledger_for_identifier( schema_id, @@ -804,7 +817,11 @@ async def store_credential( raw_cred_serde = cred_ex_record._raw_credential revoc_reg_def = None - ledger_exec_inst = self._profile.inject(IndyLedgerRequestsExecutor) + multitenant_mgr = self.profile.inject_or(BaseMultitenantManager) + if multitenant_mgr: + ledger_exec_inst = IndyLedgerRequestsExecutor(self.profile) + else: + ledger_exec_inst = self.profile.inject(IndyLedgerRequestsExecutor) ledger = ( await ledger_exec_inst.get_ledger_for_identifier( raw_cred_serde.de.cred_def_id, diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py index 24287a6523..201eb8c494 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py @@ -17,6 +17,8 @@ from .....ledger.multiple_ledger.ledger_requests_executor import ( IndyLedgerRequestsExecutor, ) +from .....multitenant.base import BaseMultitenantManager +from .....multitenant.manager import MultitenantManager from .....storage.base import StorageRecord from .....storage.error import StorageNotFoundError @@ -366,6 +368,10 @@ async def test_create_free_offer_attr_mismatch(self): credential_proposal_dict=proposal.serialize(), new_with_id=True, ) + self.context.injector.bind_instance( + BaseMultitenantManager, + async_mock.MagicMock(MultitenantManager, autospec=True), + ) await stored_exchange.save(self.session) with async_mock.patch.object( @@ -685,6 +691,10 @@ async def test_create_request_no_cache(self): thread_id=thread_id, new_with_id=True, ) + self.context.injector.bind_instance( + BaseMultitenantManager, + async_mock.MagicMock(MultitenantManager, autospec=True), + ) await stored_exchange.save(self.session) with async_mock.patch.object( @@ -941,7 +951,10 @@ async def test_issue_credential_non_revocable(self): comment = "comment" cred_values = {"attr": "value"} thread_id = "thread-id" - + self.context.injector.bind_instance( + BaseMultitenantManager, + async_mock.MagicMock(MultitenantManager, autospec=True), + ) stored_exchange = V10CredentialExchange( credential_exchange_id="dummy-cxid", connection_id=connection_id, @@ -979,17 +992,13 @@ async def test_issue_credential_non_revocable(self): self.ledger.__aenter__ = async_mock.CoroutineMock(return_value=self.ledger) self.context.injector.clear_binding(BaseLedger) self.context.injector.bind_instance(BaseLedger, self.ledger) - self.context.injector.bind_instance( - IndyLedgerRequestsExecutor, - async_mock.MagicMock( - get_ledger_for_identifier=async_mock.CoroutineMock( - return_value=("test_ledger_id", self.ledger) - ) - ), - ) with async_mock.patch.object( V10CredentialExchange, "save", autospec=True - ) as save_ex: + ) as save_ex, async_mock.patch.object( + IndyLedgerRequestsExecutor, + "get_ledger_for_identifier", + async_mock.CoroutineMock(return_value=("test_ledger_id", self.ledger)), + ): (ret_exchange, ret_cred_issue) = await self.manager.issue_credential( stored_exchange, comment=comment, retries=0 ) @@ -1468,7 +1477,10 @@ async def test_store_credential_no_preview(self): connection_id = "test_conn_id" cred_req_meta = {"req": "meta"} thread_id = "thread-id" - + self.context.injector.bind_instance( + BaseMultitenantManager, + async_mock.MagicMock(MultitenantManager, autospec=True), + ) cred_no_rev = {**INDY_CRED} cred_no_rev["rev_reg_id"] = None cred_no_rev["rev_reg"] = None diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/formats/indy/handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/formats/indy/handler.py index b536e69300..3561687065 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/formats/indy/handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/formats/indy/handler.py @@ -24,6 +24,7 @@ CredDefQueryStringSchema, ) from ......messaging.decorators.attach_decorator import AttachDecorator +from ......multitenant.base import BaseMultitenantManager from ......revocation.models.issuer_rev_reg_record import IssuerRevRegRecord from ......revocation.models.revocation_registry import RevocationRegistry from ......revocation.indy import IndyRevocation @@ -202,7 +203,11 @@ async def _create(): offer_json = await issuer.create_credential_offer(cred_def_id) return json.loads(offer_json) - ledger_exec_inst = self._profile.inject(IndyLedgerRequestsExecutor) + multitenant_mgr = self.profile.inject_or(BaseMultitenantManager) + if multitenant_mgr: + ledger_exec_inst = IndyLedgerRequestsExecutor(self.profile) + else: + ledger_exec_inst = self.profile.inject(IndyLedgerRequestsExecutor) ledger = ( await ledger_exec_inst.get_ledger_for_identifier( cred_def_id, @@ -263,7 +268,11 @@ async def create_request( cred_def_id = cred_offer["cred_def_id"] async def _create(): - ledger_exec_inst = self._profile.inject(IndyLedgerRequestsExecutor) + multitenant_mgr = self.profile.inject_or(BaseMultitenantManager) + if multitenant_mgr: + ledger_exec_inst = IndyLedgerRequestsExecutor(self.profile) + else: + ledger_exec_inst = self.profile.inject(IndyLedgerRequestsExecutor) ledger = ( await ledger_exec_inst.get_ledger_for_identifier( cred_def_id, @@ -331,7 +340,11 @@ async def issue_credential( rev_reg_id = None rev_reg = None - ledger_exec_inst = self._profile.inject(IndyLedgerRequestsExecutor) + multitenant_mgr = self.profile.inject_or(BaseMultitenantManager) + if multitenant_mgr: + ledger_exec_inst = IndyLedgerRequestsExecutor(self.profile) + else: + ledger_exec_inst = self.profile.inject(IndyLedgerRequestsExecutor) ledger = ( await ledger_exec_inst.get_ledger_for_identifier( schema_id, @@ -475,7 +488,11 @@ async def store_credential( cred = cred_ex_record.cred_issue.attachment(IndyCredFormatHandler.format) rev_reg_def = None - ledger_exec_inst = self._profile.inject(IndyLedgerRequestsExecutor) + multitenant_mgr = self.profile.inject_or(BaseMultitenantManager) + if multitenant_mgr: + ledger_exec_inst = IndyLedgerRequestsExecutor(self.profile) + else: + ledger_exec_inst = self.profile.inject(IndyLedgerRequestsExecutor) ledger = ( await ledger_exec_inst.get_ledger_for_identifier( cred["cred_def_id"], diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/formats/indy/tests/test_handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/formats/indy/tests/test_handler.py index 9bffbad2a9..c759e37e17 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/formats/indy/tests/test_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/formats/indy/tests/test_handler.py @@ -13,6 +13,8 @@ from .......ledger.multiple_ledger.ledger_requests_executor import ( IndyLedgerRequestsExecutor, ) +from .......multitenant.base import BaseMultitenantManager +from .......multitenant.manager import MultitenantManager from .......indy.issuer import IndyIssuer from .......cache.in_memory import InMemoryCache from .......cache.base import BaseCache @@ -497,6 +499,10 @@ async def test_create_offer_attr_mismatch(self): AttachDecorator.data_base64({"cred_def_id": CRED_DEF_ID}, ident="0") ], ) + self.context.injector.bind_instance( + BaseMultitenantManager, + async_mock.MagicMock(MultitenantManager, autospec=True), + ) cred_def_record = StorageRecord( CRED_DEF_SENT_RECORD_TYPE, @@ -516,9 +522,13 @@ async def test_create_offer_attr_mismatch(self): self.issuer.create_credential_offer = async_mock.CoroutineMock( return_value=json.dumps(INDY_OFFER) ) - - with self.assertRaises(V20CredFormatError): - await self.handler.create_offer(cred_proposal) + with async_mock.patch.object( + IndyLedgerRequestsExecutor, + "get_ledger_for_identifier", + async_mock.CoroutineMock(return_value=(None, self.ledger)), + ): + with self.assertRaises(V20CredFormatError): + await self.handler.create_offer(cred_proposal) async def test_create_offer_no_matching_sent_cred_def(self): cred_proposal = V20CredProposal( @@ -602,7 +612,18 @@ async def test_create_request(self): # cover case with no cache in injection context self.context.injector.clear_binding(BaseCache) cred_ex_record._id = "dummy-id3" - await self.handler.create_request(cred_ex_record, {"holder_did": holder_did}) + self.context.injector.bind_instance( + BaseMultitenantManager, + async_mock.MagicMock(MultitenantManager, autospec=True), + ) + with async_mock.patch.object( + IndyLedgerRequestsExecutor, + "get_ledger_for_identifier", + async_mock.CoroutineMock(return_value=(None, self.ledger)), + ): + await self.handler.create_request( + cred_ex_record, {"holder_did": holder_did} + ) async def test_create_request_bad_state(self): cred_ex_record = V20CredExRecord(state=V20CredExRecord.STATE_OFFER_SENT) @@ -802,20 +823,28 @@ async def test_issue_credential_non_revocable(self): self.ledger.get_credential_definition = async_mock.CoroutineMock( return_value=CRED_DEF_NR ) - - (cred_format, attachment) = await self.handler.issue_credential( - cred_ex_record, retries=0 + self.context.injector.bind_instance( + BaseMultitenantManager, + async_mock.MagicMock(MultitenantManager, autospec=True), ) + with async_mock.patch.object( + IndyLedgerRequestsExecutor, + "get_ledger_for_identifier", + async_mock.CoroutineMock(return_value=("test_ledger_id", self.ledger)), + ): + (cred_format, attachment) = await self.handler.issue_credential( + cred_ex_record, retries=0 + ) - self.issuer.create_credential.assert_called_once_with( - SCHEMA, - INDY_OFFER, - INDY_CRED_REQ, - attr_values, - cred_ex_record.cred_ex_id, - None, - None, - ) + self.issuer.create_credential.assert_called_once_with( + SCHEMA, + INDY_OFFER, + INDY_CRED_REQ, + attr_values, + cred_ex_record.cred_ex_id, + None, + None, + ) # assert identifier match assert cred_format.attach_id == self.handler.format.api == attachment.ident @@ -1254,8 +1283,15 @@ async def test_store_credential(self): with self.assertRaises(V20CredFormatError) as context: await self.handler.store_credential(stored_cx_rec, cred_id=cred_id) assert "No credential exchange " in str(context.exception) - + self.context.injector.bind_instance( + BaseMultitenantManager, + async_mock.MagicMock(MultitenantManager, autospec=True), + ) with async_mock.patch.object( + IndyLedgerRequestsExecutor, + "get_ledger_for_identifier", + async_mock.CoroutineMock(return_value=("test_ledger_id", self.ledger)), + ), async_mock.patch.object( test_module, "RevocationRegistry", autospec=True ) as mock_rev_reg, async_mock.patch.object( test_module.IndyCredFormatHandler, "get_detail_record", autospec=True diff --git a/aries_cloudagent/protocols/present_proof/indy/pres_exch_handler.py b/aries_cloudagent/protocols/present_proof/indy/pres_exch_handler.py index 56cec44dfd..51c6c264a9 100644 --- a/aries_cloudagent/protocols/present_proof/indy/pres_exch_handler.py +++ b/aries_cloudagent/protocols/present_proof/indy/pres_exch_handler.py @@ -14,6 +14,7 @@ GET_REVOC_REG_DELTA, IndyLedgerRequestsExecutor, ) +from ....multitenant.base import BaseMultitenantManager from ....revocation.models.revocation_registry import RevocationRegistry from ..v1_0.models.presentation_exchange import V10PresentationExchange @@ -93,7 +94,11 @@ async def return_presentation( for credential in credentials.values(): schema_id = credential["schema_id"] - ledger_exec_inst = self._profile.inject(IndyLedgerRequestsExecutor) + multitenant_mgr = self._profile.inject_or(BaseMultitenantManager) + if multitenant_mgr: + ledger_exec_inst = IndyLedgerRequestsExecutor(self._profile) + else: + ledger_exec_inst = self._profile.inject(IndyLedgerRequestsExecutor) ledger = ( await ledger_exec_inst.get_ledger_for_identifier( schema_id, @@ -127,7 +132,11 @@ async def return_presentation( if "timestamp" in precis: continue rev_reg_id = credentials[credential_id]["rev_reg_id"] - ledger_exec_inst = self._profile.inject(IndyLedgerRequestsExecutor) + multitenant_mgr = self._profile.inject_or(BaseMultitenantManager) + if multitenant_mgr: + ledger_exec_inst = IndyLedgerRequestsExecutor(self._profile) + else: + ledger_exec_inst = self._profile.inject(IndyLedgerRequestsExecutor) ledger = ( await ledger_exec_inst.get_ledger_for_identifier( rev_reg_id, @@ -222,7 +231,11 @@ async def process_pres_identifiers( for identifier in identifiers: schema_ids.append(identifier["schema_id"]) cred_def_ids.append(identifier["cred_def_id"]) - ledger_exec_inst = self._profile.inject(IndyLedgerRequestsExecutor) + multitenant_mgr = self._profile.inject_or(BaseMultitenantManager) + if multitenant_mgr: + ledger_exec_inst = IndyLedgerRequestsExecutor(self._profile) + else: + ledger_exec_inst = self._profile.inject(IndyLedgerRequestsExecutor) ledger = ( await ledger_exec_inst.get_ledger_for_identifier( identifier["schema_id"], diff --git a/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py b/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py index bf293f3cb6..defd623572 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py @@ -20,6 +20,8 @@ ) from .....messaging.decorators.attach_decorator import AttachDecorator from .....messaging.responder import BaseResponder, MockResponder +from .....multitenant.base import BaseMultitenantManager +from .....multitenant.manager import MultitenantManager from .....storage.error import StorageNotFoundError from ...indy import pres_exch_handler as test_indy_util_module @@ -805,7 +807,15 @@ async def test_create_pres_proof_req_non_revoc_interval_none(self): return_value="/tmp/sample/tails/path" ) ) + self.profile.context.injector.bind_instance( + BaseMultitenantManager, + async_mock.MagicMock(MultitenantManager, autospec=True), + ) with async_mock.patch.object( + IndyLedgerRequestsExecutor, + "get_ledger_for_identifier", + async_mock.CoroutineMock(return_value=("test_ledger_id", self.ledger)), + ), async_mock.patch.object( V20PresExRecord, "save", autospec=True ) as save_ex, async_mock.patch.object( test_indy_handler, "AttachDecorator", autospec=True @@ -1858,8 +1868,15 @@ async def test_verify_pres(self): pres_request=pres_request, pres=pres, ) - - with async_mock.patch.object(V20PresExRecord, "save", autospec=True) as save_ex: + self.profile.context.injector.bind_instance( + BaseMultitenantManager, + async_mock.MagicMock(MultitenantManager, autospec=True), + ) + with async_mock.patch.object( + IndyLedgerRequestsExecutor, + "get_ledger_for_identifier", + async_mock.CoroutineMock(return_value=("test_ledger_id", self.ledger)), + ), async_mock.patch.object(V20PresExRecord, "save", autospec=True) as save_ex: px_rec_out = await self.manager.verify_pres(px_rec_in) save_ex.assert_called_once() diff --git a/aries_cloudagent/resolver/default/indy.py b/aries_cloudagent/resolver/default/indy.py index a7364306d9..d1088121ba 100644 --- a/aries_cloudagent/resolver/default/indy.py +++ b/aries_cloudagent/resolver/default/indy.py @@ -17,6 +17,7 @@ IndyLedgerRequestsExecutor, ) from ...messaging.valid import IndyDID +from ...multitenant.base import BaseMultitenantManager from ..base import BaseDIDResolver, DIDNotFound, ResolverError, ResolverType @@ -44,7 +45,11 @@ def supported_did_regex(self) -> Pattern: async def _resolve(self, profile: Profile, did: str) -> dict: """Resolve an indy DID.""" - ledger_exec_inst = profile.inject(IndyLedgerRequestsExecutor) + multitenant_mgr = profile.inject_or(BaseMultitenantManager) + if multitenant_mgr: + ledger_exec_inst = IndyLedgerRequestsExecutor(profile) + else: + ledger_exec_inst = profile.inject(IndyLedgerRequestsExecutor) ledger = ( await ledger_exec_inst.get_ledger_for_identifier( did, diff --git a/aries_cloudagent/resolver/default/tests/test_indy.py b/aries_cloudagent/resolver/default/tests/test_indy.py index f9d4ad944a..7781dac13f 100644 --- a/aries_cloudagent/resolver/default/tests/test_indy.py +++ b/aries_cloudagent/resolver/default/tests/test_indy.py @@ -12,6 +12,8 @@ IndyLedgerRequestsExecutor, ) from ....messaging.valid import IndyDID +from ....multitenant.base import BaseMultitenantManager +from ....multitenant.manager import MultitenantManager from ...base import DIDNotFound, ResolverError from .. import indy as test_module @@ -65,6 +67,22 @@ async def test_resolve(self, profile: Profile, resolver: IndyDIDResolver): """Test resolve method.""" assert await resolver.resolve(profile, TEST_DID0) + @pytest.mark.asyncio + async def test_resolve_multitenant( + self, profile: Profile, resolver: IndyDIDResolver, ledger: BaseLedger + ): + """Test resolve method.""" + profile.context.injector.bind_instance( + BaseMultitenantManager, + async_mock.MagicMock(MultitenantManager, autospec=True), + ) + with async_mock.patch.object( + IndyLedgerRequestsExecutor, + "get_ledger_for_identifier", + async_mock.CoroutineMock(return_value=("test_ledger_id", ledger)), + ): + assert await resolver.resolve(profile, TEST_DID0) + @pytest.mark.asyncio async def test_resolve_x_no_ledger( self, profile: Profile, resolver: IndyDIDResolver diff --git a/aries_cloudagent/revocation/indy.py b/aries_cloudagent/revocation/indy.py index 3dcab450f7..669db1f31a 100644 --- a/aries_cloudagent/revocation/indy.py +++ b/aries_cloudagent/revocation/indy.py @@ -8,6 +8,7 @@ GET_REVOC_REG_DEF, IndyLedgerRequestsExecutor, ) +from ..multitenant.base import BaseMultitenantManager from ..storage.base import StorageNotFoundError from .error import RevocationNotSupportedError, RevocationRegistryBadSizeError @@ -32,7 +33,11 @@ async def init_issuer_registry( tag: str = None, ) -> "IssuerRevRegRecord": """Create a new revocation registry record for a credential definition.""" - ledger_exec_inst = self._profile.inject(IndyLedgerRequestsExecutor) + multitenant_mgr = self._profile.inject_or(BaseMultitenantManager) + if multitenant_mgr: + ledger_exec_inst = IndyLedgerRequestsExecutor(self._profile) + else: + ledger_exec_inst = self._profile.inject(IndyLedgerRequestsExecutor) ledger = ( await ledger_exec_inst.get_ledger_for_identifier( cred_def_id, @@ -108,7 +113,11 @@ async def get_ledger_registry(self, revoc_reg_id: str) -> "RevocationRegistry": if revoc_reg_id in IndyRevocation.REV_REG_CACHE: return IndyRevocation.REV_REG_CACHE[revoc_reg_id] - ledger_exec_inst = self._profile.inject(IndyLedgerRequestsExecutor) + multitenant_mgr = self._profile.inject_or(BaseMultitenantManager) + if multitenant_mgr: + ledger_exec_inst = IndyLedgerRequestsExecutor(self._profile) + else: + ledger_exec_inst = self._profile.inject(IndyLedgerRequestsExecutor) ledger = ( await ledger_exec_inst.get_ledger_for_identifier( revoc_reg_id, diff --git a/aries_cloudagent/revocation/tests/test_indy.py b/aries_cloudagent/revocation/tests/test_indy.py index 10708c6d28..e1a79ab585 100644 --- a/aries_cloudagent/revocation/tests/test_indy.py +++ b/aries_cloudagent/revocation/tests/test_indy.py @@ -7,6 +7,8 @@ from ...ledger.multiple_ledger.ledger_requests_executor import ( IndyLedgerRequestsExecutor, ) +from ...multitenant.base import BaseMultitenantManager +from ...multitenant.manager import MultitenantManager from ...storage.error import StorageNotFoundError from ..error import ( @@ -54,6 +56,22 @@ async def test_init_issuer_registry(self): assert result.revoc_def_type == IssuerRevRegRecord.REVOC_DEF_TYPE_CL assert result.tag is None + self.context.injector.bind_instance( + BaseMultitenantManager, + async_mock.MagicMock(MultitenantManager, autospec=True), + ) + with async_mock.patch.object( + IndyLedgerRequestsExecutor, + "get_ledger_for_identifier", + async_mock.CoroutineMock(return_value=(None, self.ledger)), + ): + result = await self.revoc.init_issuer_registry(CRED_DEF_ID) + assert result.cred_def_id == CRED_DEF_ID + assert result.issuer_did == self.test_did + assert result.max_cred_num == DEFAULT_REGISTRY_SIZE + assert result.revoc_def_type == IssuerRevRegRecord.REVOC_DEF_TYPE_CL + assert result.tag is None + async def test_init_issuer_registry_no_cred_def(self): CRED_DEF_ID = f"{self.test_did}:3:CL:1234:default" @@ -152,3 +170,24 @@ async def test_get_ledger_registry(self): mock_from_def.assert_called_once_with( self.ledger.get_revoc_reg_def.return_value, True ) + + self.context.injector.bind_instance( + BaseMultitenantManager, + async_mock.MagicMock(MultitenantManager, autospec=True), + ) + with async_mock.patch.object( + IndyLedgerRequestsExecutor, + "get_ledger_for_identifier", + async_mock.CoroutineMock(return_value=(None, self.ledger)), + ), async_mock.patch.object( + RevocationRegistry, "from_definition", async_mock.MagicMock() + ) as mock_from_def: + result = await self.revoc.get_ledger_registry("dummy2") + assert result == mock_from_def.return_value + assert "dummy2" in IndyRevocation.REV_REG_CACHE + + await self.revoc.get_ledger_registry("dummy2") + + mock_from_def.assert_called_once_with( + self.ledger.get_revoc_reg_def.return_value, True + ) From f16f32722376330e738f30cb42ad45670d2fcb2d Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Tue, 29 Mar 2022 16:14:43 -0700 Subject: [PATCH 128/872] retrigger checks Signed-off-by: Shaanjot Gill From f5f9ab03a5ad5891c482af3f3f6f1735a4fddf31 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Tue, 29 Mar 2022 16:58:22 -0700 Subject: [PATCH 129/872] failing int test - PR#1697 changes Signed-off-by: Shaanjot Gill --- scripts/run_tests | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/run_tests b/scripts/run_tests index 898d2361dc..66eda58598 100755 --- a/scripts/run_tests +++ b/scripts/run_tests @@ -8,7 +8,7 @@ FAST=${FAST:-0} cd "$(dirname "$0")" || exit if [[ $FAST -eq 0 ]]; then - $CONTAINER_RUNTIME build --platform linux/amd64 -t aries-cloudagent-test -f ../docker/Dockerfile.test .. || exit 1 + $CONTAINER_RUNTIME build -t aries-cloudagent-test -f ../docker/Dockerfile.test .. || exit 1 fi DOCKER_ARGS="" @@ -40,6 +40,5 @@ if [ ! -z "$TEST_REDIS_CONFIG" ]; then fi $CONTAINER_RUNTIME run --rm -ti --name aries-cloudagent-runner \ - --platform linux/amd64 \ -v "$(pwd)/../test-reports:/usr/src/app/test-reports:z" \ $DOCKER_ARGS aries-cloudagent-test "$@" From 05c3e1822ead5ca4decace06d59589f112c84357 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Tue, 29 Mar 2022 17:21:10 -0700 Subject: [PATCH 130/872] revert commit f5f9ab0 Signed-off-by: Shaanjot Gill --- scripts/run_tests | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/run_tests b/scripts/run_tests index 66eda58598..898d2361dc 100755 --- a/scripts/run_tests +++ b/scripts/run_tests @@ -8,7 +8,7 @@ FAST=${FAST:-0} cd "$(dirname "$0")" || exit if [[ $FAST -eq 0 ]]; then - $CONTAINER_RUNTIME build -t aries-cloudagent-test -f ../docker/Dockerfile.test .. || exit 1 + $CONTAINER_RUNTIME build --platform linux/amd64 -t aries-cloudagent-test -f ../docker/Dockerfile.test .. || exit 1 fi DOCKER_ARGS="" @@ -40,5 +40,6 @@ if [ ! -z "$TEST_REDIS_CONFIG" ]; then fi $CONTAINER_RUNTIME run --rm -ti --name aries-cloudagent-runner \ + --platform linux/amd64 \ -v "$(pwd)/../test-reports:/usr/src/app/test-reports:z" \ $DOCKER_ARGS aries-cloudagent-test "$@" From 742a99159a7cd67c134d74c8d986ae6fecb2cd16 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Tue, 29 Mar 2022 18:15:13 -0700 Subject: [PATCH 131/872] handle no challenge in provided options Signed-off-by: Shaanjot Gill --- .../present_proof/v2_0/formats/dif/handler.py | 2 + .../v2_0/formats/dif/tests/test_handler.py | 56 +++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/handler.py b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/handler.py index 831bc4ce33..d3324ff03f 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/handler.py @@ -160,6 +160,8 @@ async def create_bound_request( else: dif_proof_request["options"] = pres_proposal_dict["options"] del pres_proposal_dict["options"] + if "challenge" not in dif_proof_request.get("options"): + dif_proof_request["options"]["challenge"] = str(uuid4()) dif_proof_request["presentation_definition"] = pres_proposal_dict return self.get_format_data(PRES_20_REQUEST, dif_proof_request) diff --git a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py index cc50421b61..a21edbe833 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py @@ -395,6 +395,62 @@ async def test_create_bound_request_b(self): output[1], AttachDecorator ) + async def test_create_bound_request_c(self): + dif_proposal_dict = { + "options": {"domain": "test123"}, + "input_descriptors": [ + { + "id": "citizenship_input_1", + "name": "EU Driver's License", + "group": ["A"], + "schema": [ + { + "uri": "https://www.w3.org/2018/credentials#VerifiableCredential" + } + ], + "constraints": { + "limit_disclosure": "required", + "fields": [ + { + "path": ["$.credentialSubject.givenName"], + "purpose": "The claim must be from one of the specified issuers", + "filter": {"type": "string", "enum": ["JOHN", "CAI"]}, + } + ], + }, + } + ], + } + dif_pres_proposal = V20PresProposal( + formats=[ + V20PresFormat( + attach_id="dif", + format_=ATTACHMENT_FORMAT[PRES_20_PROPOSAL][ + V20PresFormat.Format.DIF.api + ], + ) + ], + proposals_attach=[ + AttachDecorator.data_json(dif_proposal_dict, ident="dif") + ], + ) + record = V20PresExRecord( + pres_ex_id="pxid", + thread_id="thid", + connection_id="conn_id", + initiator="init", + role="role", + state="state", + pres_proposal=dif_pres_proposal, + verified="false", + auto_present=True, + error_msg="error", + ) + output = await self.handler.create_bound_request(pres_ex_record=record) + assert isinstance(output[0], V20PresFormat) and isinstance( + output[1], AttachDecorator + ) + async def test_create_pres(self): dif_pres_request = V20PresRequest( formats=[ From f22547f4d3c841c9defd86c9e4690b18a04b7c92 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Tue, 29 Mar 2022 18:29:04 -0700 Subject: [PATCH 132/872] retrigger checks Signed-off-by: Shaanjot Gill From ce58a8f9d30172a7931755d0f2870b25f6ea095c Mon Sep 17 00:00:00 2001 From: Adam Burdett Date: Wed, 30 Mar 2022 00:32:46 -0600 Subject: [PATCH 133/872] fixed tests assertions Signed-off-by: Adam Burdett --- aries_cloudagent/multitenant/admin/routes.py | 3 ++- aries_cloudagent/multitenant/admin/tests/test_routes.py | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/multitenant/admin/routes.py b/aries_cloudagent/multitenant/admin/routes.py index 959cc36b4e..890eb29cc1 100644 --- a/aries_cloudagent/multitenant/admin/routes.py +++ b/aries_cloudagent/multitenant/admin/routes.py @@ -230,7 +230,8 @@ async def wallets_list(request: web.BaseRequest): profile = context.profile query = {} - if wallet_name := request.query.get("wallet_name"): + wallet_name = request.query.get("wallet_name") + if wallet_name: query["wallet_name"] = wallet_name try: diff --git a/aries_cloudagent/multitenant/admin/tests/test_routes.py b/aries_cloudagent/multitenant/admin/tests/test_routes.py index c1f50f49c3..d2299b8ff5 100644 --- a/aries_cloudagent/multitenant/admin/tests/test_routes.py +++ b/aries_cloudagent/multitenant/admin/tests/test_routes.py @@ -144,6 +144,7 @@ async def test_wallet_create(self): "wallet_name": "test", "wallet_type": "indy", "wallet_key": "test", + "key_derivation_method": "ARGON2I_MOD", "key_management_mode": "managed", "wallet_webhook_urls": [], "wallet_dispatch_type": "base", @@ -175,6 +176,7 @@ async def test_wallet_create(self): "wallet.name": body["wallet_name"], "wallet.type": body["wallet_type"], "wallet.key": body["wallet_key"], + 'wallet.key_derivation_method': body["key_derivation_method"], "wallet.webhook_urls": body["wallet_webhook_urls"], "wallet.dispatch_type": body["wallet_dispatch_type"], }, @@ -206,6 +208,7 @@ async def test_wallet_create_optional_default_fields(self): body = { "wallet_name": "test", "wallet_key": "test", + "key_derivation_method": "ARGON2I_MOD", "wallet_webhook_urls": [], "wallet_dispatch_type": "base", "label": "my_test_label", @@ -223,6 +226,7 @@ async def test_wallet_create_optional_default_fields(self): "wallet.name": body["wallet_name"], "wallet.type": "in_memory", "wallet.key": body["wallet_key"], + 'wallet.key_derivation_method': body["key_derivation_method"], "default_label": body["label"], "image_url": body["image_url"], "wallet.webhook_urls": body["wallet_webhook_urls"], From be2412a7f50368f9279b5bbca75caf375fb1549d Mon Sep 17 00:00:00 2001 From: Matthias Binzer Date: Wed, 30 Mar 2022 13:14:11 +0200 Subject: [PATCH 134/872] Allow deleting a pres exchange item without reading it first Related to #1687 this allows deleting a presentation exchange record without reading it from storage. Signed-off-by: Matthias Binzer --- aries_cloudagent/protocols/present_proof/v2_0/routes.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/protocols/present_proof/v2_0/routes.py b/aries_cloudagent/protocols/present_proof/v2_0/routes.py index 8d24d1afe4..211fca5488 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/routes.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/routes.py @@ -34,6 +34,8 @@ from ....storage.error import StorageError, StorageNotFoundError from ....storage.vc_holder.base import VCHolder from ....storage.vc_holder.vc_record import VCRecord +from ....storage.record import StorageRecord +from ....storage.base import BaseStorage from ....utils.tracing import trace_event, get_timer, AdminAPIMessageTracingSchema from ....vc.ld_proofs import BbsBlsSignature2020, Ed25519Signature2018 from ....wallet.error import WalletNotFoundError @@ -1234,8 +1236,10 @@ async def present_proof_remove(request: web.BaseRequest): pres_ex_record = None try: async with context.profile.session() as session: - pres_ex_record = await V20PresExRecord.retrieve_by_id(session, pres_ex_id) - await pres_ex_record.delete_record(session) + storage = session.inject(BaseStorage) + storage_record = StorageRecord(type=V20PresExRecord.RECORD_TYPE, value=None, id=pres_ex_id) + await storage.delete_record(storage_record) + except StorageNotFoundError as err: raise web.HTTPNotFound(reason=err.roll_up) from err except StorageError as err: From e1122ee1f1f2cdf90b2768c3f5553e341010bfb3 Mon Sep 17 00:00:00 2001 From: DaevMithran Date: Wed, 30 Mar 2022 18:25:22 +0530 Subject: [PATCH 135/872] Add auto_verify flag present-proof-v1_0 Signed-off-by: DaevMithran --- .../v1_0/handlers/presentation_handler.py | 2 +- .../protocols/present_proof/v1_0/manager.py | 6 +++- .../v1_0/models/presentation_exchange.py | 7 +++++ .../protocols/present_proof/v1_0/routes.py | 30 ++++++++++++++++++- 4 files changed, 42 insertions(+), 3 deletions(-) diff --git a/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_handler.py b/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_handler.py index 40c4d73dc6..24507b610a 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_handler.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_handler.py @@ -49,7 +49,7 @@ async def handle(self, context: RequestContext, responder: BaseResponder): ) # Automatically move to next state if flag is set - if context.settings.get("debug.auto_verify_presentation"): + if presentation_exchange_record and presentation_exchange_record.auto_verify: try: await presentation_manager.verify_presentation( presentation_exchange_record diff --git a/aries_cloudagent/protocols/present_proof/v1_0/manager.py b/aries_cloudagent/protocols/present_proof/v1_0/manager.py index 10354d3d29..2a5245a417 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/manager.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/manager.py @@ -165,7 +165,10 @@ async def create_bound_request( return presentation_exchange_record, presentation_request_message async def create_exchange_for_request( - self, connection_id: str, presentation_request_message: PresentationRequest + self, + connection_id: str, + presentation_request_message: PresentationRequest, + auto_verify: bool = None, ): """ Create a presentation exchange record for input presentation request. @@ -187,6 +190,7 @@ async def create_exchange_for_request( state=V10PresentationExchange.STATE_REQUEST_SENT, presentation_request=presentation_request_message.indy_proof_request(), presentation_request_dict=presentation_request_message, + auto_verify=auto_verify, trace=(presentation_request_message._trace is not None), ) async with self._profile.session() as session: diff --git a/aries_cloudagent/protocols/present_proof/v1_0/models/presentation_exchange.py b/aries_cloudagent/protocols/present_proof/v1_0/models/presentation_exchange.py index bce40f896d..1a68f2d6fa 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/models/presentation_exchange.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/models/presentation_exchange.py @@ -75,6 +75,7 @@ def __init__( presentation: Union[IndyProof, Mapping] = None, # indy proof verified: str = None, auto_present: bool = False, + auto_verify: bool = False, error_msg: str = None, trace: bool = False, # backward compat: BaseRecord.from_storage() **kwargs, @@ -96,6 +97,7 @@ def __init__( self._presentation = IndyProof.serde(presentation) self.verified = verified self.auto_present = auto_present + self.auto_verify = auto_verify self.error_msg = error_msg @property @@ -203,6 +205,7 @@ def record_value(self) -> Mapping: "role", "state", "auto_present", + "auto_verify", "error_msg", "verified", "trace", @@ -297,6 +300,10 @@ class Meta: description="Prover choice to auto-present proof as verifier requests", example=False, ) + auto_verify = fields.Bool( + required=False, + description="Verifier choice to auto-verify proof presentation" + ) error_msg = fields.Str( required=False, description="Error message", example="Invalid structure" ) diff --git a/aries_cloudagent/protocols/present_proof/v1_0/routes.py b/aries_cloudagent/protocols/present_proof/v1_0/routes.py index ddc90d4ef3..a3121d4227 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/routes.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/routes.py @@ -130,6 +130,11 @@ class V10PresentationCreateRequestRequestSchema(AdminAPIMessageTracingSchema): proof_request = fields.Nested(IndyProofRequestSchema(), required=True) comment = fields.Str(required=False, allow_none=True) + auto_verify = fields.Bool( + description="Verifier choice to auto-verify proof presentation", + required=False, + example=False + ) trace = fields.Bool( description="Whether to trace event (default false)", required=False, @@ -146,6 +151,18 @@ class V10PresentationSendRequestRequestSchema( description="Connection identifier", required=True, example=UUIDFour.EXAMPLE ) +class V10PresentationSendRequestToProposalSchema(AdminAPIMessageTracingSchema): + """Request schema for sending a proof request bound to a proposal""" + auto_verify = fields.Bool( + description="Verifier choice to auto-verify proof presentation", + required=False, + example=False + ) + trace = fields.Bool( + description="Whether to trace event (default false)", + required=False, + example=False, + ) class CredentialsFetchQueryStringSchema(OpenAPISchema): """Parameters and validators for credentials fetch request query string.""" @@ -475,6 +492,9 @@ async def presentation_exchange_create_request(request: web.BaseRequest): ) ], ) + auto_verify = body.get( + "auto_verify", context.settings.get("debug.auto_verify_presentation") + ) trace_msg = body.get("trace") presentation_request_message.assign_trace_decorator( context.settings, @@ -487,6 +507,7 @@ async def presentation_exchange_create_request(request: web.BaseRequest): pres_ex_record = await presentation_manager.create_exchange_for_request( connection_id=None, presentation_request_message=presentation_request_message, + auto_verify=auto_verify ) result = pres_ex_record.serialize() except (BaseModelError, StorageError) as err: @@ -562,6 +583,9 @@ async def presentation_exchange_send_free_request(request: web.BaseRequest): context.settings, trace_msg, ) + auto_verify = body.get( + "auto_verify", context.settings.get("debug.auto_verify_presentation") + ) pres_ex_record = None try: @@ -569,6 +593,7 @@ async def presentation_exchange_send_free_request(request: web.BaseRequest): pres_ex_record = await presentation_manager.create_exchange_for_request( connection_id=connection_id, presentation_request_message=presentation_request_message, + auto_verify=auto_verify ) result = pres_ex_record.serialize() except (BaseModelError, StorageError) as err: @@ -595,7 +620,7 @@ async def presentation_exchange_send_free_request(request: web.BaseRequest): summary="Sends a presentation request in reference to a proposal", ) @match_info_schema(V10PresExIdMatchInfoSchema()) -@request_schema(AdminAPIMessageTracingSchema()) +@request_schema(V10PresentationSendRequestToProposalSchema()) @response_schema(V10PresentationExchangeSchema(), 200, description="") async def presentation_exchange_send_bound_request(request: web.BaseRequest): """ @@ -644,6 +669,9 @@ async def presentation_exchange_send_bound_request(request: web.BaseRequest): if not connection_record.is_ready: raise web.HTTPForbidden(reason=f"Connection {conn_id} not ready") + pres_ex_record.auto_verify = body.get( + "auto_verify", context.settings.get("debug.auto_verify_presentation") + ) try: presentation_manager = PresentationManager(profile) ( From a0b06a6bcabeba696b8d41e99c5fc7458f399fa7 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Wed, 30 Mar 2022 07:47:23 -0700 Subject: [PATCH 136/872] use ursa-bbs-signatures 1.0.1 Signed-off-by: Shaanjot Gill --- requirements.bbs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.bbs.txt b/requirements.bbs.txt index 972fa90c8a..8debc1f11e 100644 --- a/requirements.bbs.txt +++ b/requirements.bbs.txt @@ -1 +1 @@ -ursa-bbs-signatures~=1.0.1 \ No newline at end of file +ursa-bbs-signatures==1.0.1 \ No newline at end of file From 6febdf7c3d18fdbe8aba93aca20ae2adba39d7a6 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Wed, 30 Mar 2022 08:32:50 -0700 Subject: [PATCH 137/872] retrigger check Signed-off-by: Shaanjot Gill From ae39a1bbc1981809a10d1ecc670a580d0f79517c Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Wed, 30 Mar 2022 09:02:53 -0700 Subject: [PATCH 138/872] use ursa-bbs-signatures 1.0.1 Signed-off-by: Shaanjot Gill --- requirements.bbs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.bbs.txt b/requirements.bbs.txt index 972fa90c8a..8debc1f11e 100644 --- a/requirements.bbs.txt +++ b/requirements.bbs.txt @@ -1 +1 @@ -ursa-bbs-signatures~=1.0.1 \ No newline at end of file +ursa-bbs-signatures==1.0.1 \ No newline at end of file From 5b31d802d76c949cb96e20243f3964bd16f5132e Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Wed, 30 Mar 2022 09:06:25 -0700 Subject: [PATCH 139/872] use ursa-bbs-signatures 1.0.1 Signed-off-by: Shaanjot Gill --- requirements.bbs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.bbs.txt b/requirements.bbs.txt index 972fa90c8a..8debc1f11e 100644 --- a/requirements.bbs.txt +++ b/requirements.bbs.txt @@ -1 +1 @@ -ursa-bbs-signatures~=1.0.1 \ No newline at end of file +ursa-bbs-signatures==1.0.1 \ No newline at end of file From 7a9295ece166491f872b8104aaa461201aa45ebd Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Wed, 30 Mar 2022 09:12:53 -0700 Subject: [PATCH 140/872] use ursa-bbs-signatures 1.0.1 Signed-off-by: Shaanjot Gill --- requirements.bbs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.bbs.txt b/requirements.bbs.txt index 972fa90c8a..8debc1f11e 100644 --- a/requirements.bbs.txt +++ b/requirements.bbs.txt @@ -1 +1 @@ -ursa-bbs-signatures~=1.0.1 \ No newline at end of file +ursa-bbs-signatures==1.0.1 \ No newline at end of file From 490c948dc2aae26b918c5d4cdf5483bbf9da0b1f Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Wed, 30 Mar 2022 09:15:12 -0700 Subject: [PATCH 141/872] use ursa-bbs-signatures 1.0.1 Signed-off-by: Shaanjot Gill --- requirements.bbs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.bbs.txt b/requirements.bbs.txt index 972fa90c8a..8debc1f11e 100644 --- a/requirements.bbs.txt +++ b/requirements.bbs.txt @@ -1 +1 @@ -ursa-bbs-signatures~=1.0.1 \ No newline at end of file +ursa-bbs-signatures==1.0.1 \ No newline at end of file From be8bc6c1d4280ce4e9dda7187a36c7b0c1f1069e Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Wed, 30 Mar 2022 13:24:13 -0700 Subject: [PATCH 142/872] revert locking ursa-bbs-signature package - 1.0.2 pypi removed Signed-off-by: Shaanjot Gill --- requirements.bbs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.bbs.txt b/requirements.bbs.txt index 8debc1f11e..972fa90c8a 100644 --- a/requirements.bbs.txt +++ b/requirements.bbs.txt @@ -1 +1 @@ -ursa-bbs-signatures==1.0.1 \ No newline at end of file +ursa-bbs-signatures~=1.0.1 \ No newline at end of file From 386d04dac38a4ca6999314044cb5130dce4ad772 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Wed, 30 Mar 2022 13:27:41 -0700 Subject: [PATCH 143/872] revert locking ursa-bbs-signature package - 1.0.2 pypi removed Signed-off-by: Shaanjot Gill --- requirements.bbs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.bbs.txt b/requirements.bbs.txt index 8debc1f11e..972fa90c8a 100644 --- a/requirements.bbs.txt +++ b/requirements.bbs.txt @@ -1 +1 @@ -ursa-bbs-signatures==1.0.1 \ No newline at end of file +ursa-bbs-signatures~=1.0.1 \ No newline at end of file From 7982a0f21fab9f64b86e2f1059479e69c5870bd5 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Wed, 30 Mar 2022 13:29:49 -0700 Subject: [PATCH 144/872] revert locking ursa-bbs-signature package - 1.0.2 pypi removed Signed-off-by: Shaanjot Gill --- requirements.bbs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.bbs.txt b/requirements.bbs.txt index 8debc1f11e..972fa90c8a 100644 --- a/requirements.bbs.txt +++ b/requirements.bbs.txt @@ -1 +1 @@ -ursa-bbs-signatures==1.0.1 \ No newline at end of file +ursa-bbs-signatures~=1.0.1 \ No newline at end of file From ed19dcce0809e4704a223216820ad6621b01edf0 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Wed, 30 Mar 2022 13:33:25 -0700 Subject: [PATCH 145/872] revert locking ursa-bbs-signature package - 1.0.2 pypi removed Signed-off-by: Shaanjot Gill --- requirements.bbs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.bbs.txt b/requirements.bbs.txt index 8debc1f11e..972fa90c8a 100644 --- a/requirements.bbs.txt +++ b/requirements.bbs.txt @@ -1 +1 @@ -ursa-bbs-signatures==1.0.1 \ No newline at end of file +ursa-bbs-signatures~=1.0.1 \ No newline at end of file From 08e9fd20b43eb32679995f2a217ddc7b2d17d466 Mon Sep 17 00:00:00 2001 From: DaevMithran Date: Thu, 31 Mar 2022 11:10:07 +0530 Subject: [PATCH 146/872] Add auto_verify flag present-proof-v2_0 Signed-off-by: DaevMithran --- .../v1_0/models/presentation_exchange.py | 3 +- .../v1_0/models/tests/test_record.py | 1 + .../protocols/present_proof/v1_0/routes.py | 13 +++++--- .../v2_0/handlers/pres_handler.py | 2 +- .../protocols/present_proof/v2_0/manager.py | 6 +++- .../v2_0/models/pres_exchange.py | 5 +++ .../v2_0/models/tests/test_record.py | 1 + .../protocols/present_proof/v2_0/routes.py | 33 ++++++++++++++++++- 8 files changed, 54 insertions(+), 10 deletions(-) diff --git a/aries_cloudagent/protocols/present_proof/v1_0/models/presentation_exchange.py b/aries_cloudagent/protocols/present_proof/v1_0/models/presentation_exchange.py index 1a68f2d6fa..bb760a0a64 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/models/presentation_exchange.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/models/presentation_exchange.py @@ -301,8 +301,7 @@ class Meta: example=False, ) auto_verify = fields.Bool( - required=False, - description="Verifier choice to auto-verify proof presentation" + required=False, description="Verifier choice to auto-verify proof presentation" ) error_msg = fields.Str( required=False, description="Error message", example="Invalid structure" diff --git a/aries_cloudagent/protocols/present_proof/v1_0/models/tests/test_record.py b/aries_cloudagent/protocols/present_proof/v1_0/models/tests/test_record.py index 62d5c64884..a757dcae2b 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/models/tests/test_record.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/models/tests/test_record.py @@ -110,6 +110,7 @@ async def test_record(self): "role": None, "state": None, "auto_present": True, + "auto_verify": False, "error_msg": None, "verified": None, "trace": False, diff --git a/aries_cloudagent/protocols/present_proof/v1_0/routes.py b/aries_cloudagent/protocols/present_proof/v1_0/routes.py index a3121d4227..95d4906714 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/routes.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/routes.py @@ -133,7 +133,7 @@ class V10PresentationCreateRequestRequestSchema(AdminAPIMessageTracingSchema): auto_verify = fields.Bool( description="Verifier choice to auto-verify proof presentation", required=False, - example=False + example=False, ) trace = fields.Bool( description="Whether to trace event (default false)", @@ -151,12 +151,14 @@ class V10PresentationSendRequestRequestSchema( description="Connection identifier", required=True, example=UUIDFour.EXAMPLE ) + class V10PresentationSendRequestToProposalSchema(AdminAPIMessageTracingSchema): - """Request schema for sending a proof request bound to a proposal""" + """Request schema for sending a proof request bound to a proposal.""" + auto_verify = fields.Bool( description="Verifier choice to auto-verify proof presentation", required=False, - example=False + example=False, ) trace = fields.Bool( description="Whether to trace event (default false)", @@ -164,6 +166,7 @@ class V10PresentationSendRequestToProposalSchema(AdminAPIMessageTracingSchema): example=False, ) + class CredentialsFetchQueryStringSchema(OpenAPISchema): """Parameters and validators for credentials fetch request query string.""" @@ -507,7 +510,7 @@ async def presentation_exchange_create_request(request: web.BaseRequest): pres_ex_record = await presentation_manager.create_exchange_for_request( connection_id=None, presentation_request_message=presentation_request_message, - auto_verify=auto_verify + auto_verify=auto_verify, ) result = pres_ex_record.serialize() except (BaseModelError, StorageError) as err: @@ -593,7 +596,7 @@ async def presentation_exchange_send_free_request(request: web.BaseRequest): pres_ex_record = await presentation_manager.create_exchange_for_request( connection_id=connection_id, presentation_request_message=presentation_request_message, - auto_verify=auto_verify + auto_verify=auto_verify, ) result = pres_ex_record.serialize() except (BaseModelError, StorageError) as err: diff --git a/aries_cloudagent/protocols/present_proof/v2_0/handlers/pres_handler.py b/aries_cloudagent/protocols/present_proof/v2_0/handlers/pres_handler.py index 9736a66658..bc002533f9 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/handlers/pres_handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/handlers/pres_handler.py @@ -49,7 +49,7 @@ async def handle(self, context: RequestContext, responder: BaseResponder): ) # Automatically move to next state if flag is set - if context.settings.get("debug.auto_verify_presentation"): + if pres_ex_record and pres_ex_record.auto_verify: try: await pres_manager.verify_pres(pres_ex_record) except (BaseModelError, LedgerError, StorageError) as err: diff --git a/aries_cloudagent/protocols/present_proof/v2_0/manager.py b/aries_cloudagent/protocols/present_proof/v2_0/manager.py index 7693942921..94d5eaaed9 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/manager.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/manager.py @@ -161,7 +161,10 @@ async def create_bound_request( return pres_ex_record, pres_request_message async def create_exchange_for_request( - self, connection_id: str, pres_request_message: V20PresRequest + self, + connection_id: str, + pres_request_message: V20PresRequest, + auto_verify: bool = None, ): """ Create a presentation exchange record for input presentation request. @@ -182,6 +185,7 @@ async def create_exchange_for_request( role=V20PresExRecord.ROLE_VERIFIER, state=V20PresExRecord.STATE_REQUEST_SENT, pres_request=pres_request_message, + auto_verify=auto_verify, trace=(pres_request_message._trace is not None), ) async with self._profile.session() as session: diff --git a/aries_cloudagent/protocols/present_proof/v2_0/models/pres_exchange.py b/aries_cloudagent/protocols/present_proof/v2_0/models/pres_exchange.py index 298081b848..c790ee6ceb 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/models/pres_exchange.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/models/pres_exchange.py @@ -63,6 +63,7 @@ def __init__( pres: Union[V20Pres, Mapping] = None, # aries message verified: str = None, auto_present: bool = False, + auto_verify: bool = False, error_msg: str = None, trace: bool = False, # backward compat: BaseRecord.FromStorage() by_format: Mapping = None, # backward compat: BaseRecord.FromStorage() @@ -190,6 +191,7 @@ def record_value(self) -> Mapping: "state", "verified", "auto_present", + "auto_verify", "error_msg", "trace", ) @@ -309,6 +311,9 @@ class Meta: description="Prover choice to auto-present proof as verifier requests", example=False, ) + auto_verify = fields.Bool( + required=False, description="Verifier choice to auto-verify proof presentation" + ) error_msg = fields.Str( required=False, description="Error message", example="Invalid structure" ) diff --git a/aries_cloudagent/protocols/present_proof/v2_0/models/tests/test_record.py b/aries_cloudagent/protocols/present_proof/v2_0/models/tests/test_record.py index 3f1922cb8d..529b72bb6a 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/models/tests/test_record.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/models/tests/test_record.py @@ -111,6 +111,7 @@ async def test_record(self): "pres_proposal": pres_proposal.serialize(), "verified": "false", "auto_present": True, + "auto_verify": False, "error_msg": "error", "trace": False, } diff --git a/aries_cloudagent/protocols/present_proof/v2_0/routes.py b/aries_cloudagent/protocols/present_proof/v2_0/routes.py index 8d24d1afe4..a74b9203d4 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/routes.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/routes.py @@ -208,6 +208,11 @@ class V20PresCreateRequestRequestSchema(AdminAPIMessageTracingSchema): presentation_request = fields.Nested(V20PresRequestByFormatSchema(), required=True) comment = fields.Str(required=False, allow_none=True) + auto_verify = fields.Bool( + description="Verifier choice to auto-verify proof presentation", + required=False, + example=False, + ) trace = fields.Bool( description="Whether to trace event (default false)", required=False, @@ -223,6 +228,21 @@ class V20PresSendRequestRequestSchema(V20PresCreateRequestRequestSchema): ) +class V20PresentationSendRequestToProposalSchema(AdminAPIMessageTracingSchema): + """Request schema for sending a proof request bound to a proposal.""" + + auto_verify = fields.Bool( + description="Verifier choice to auto-verify proof presentation", + required=False, + example=False, + ) + trace = fields.Bool( + description="Whether to trace event (default false)", + required=False, + example=False, + ) + + class V20PresSpecByFormatRequestSchema(AdminAPIMessageTracingSchema): """Presentation specification schema by format, for send-presentation request.""" @@ -803,6 +823,9 @@ async def present_proof_create_request(request: web.BaseRequest): will_confirm=True, **_formats_attach(pres_request_spec, PRES_20_REQUEST, "request_presentations"), ) + auto_verify = body.get( + "auto_verify", context.settings.get("debug.auto_verify_presentation") + ) trace_msg = body.get("trace") pres_request_message.assign_trace_decorator( context.settings, @@ -815,6 +838,7 @@ async def present_proof_create_request(request: web.BaseRequest): pres_ex_record = await pres_manager.create_exchange_for_request( connection_id=None, pres_request_message=pres_request_message, + auto_verify=auto_verify, ) result = pres_ex_record.serialize() except (BaseModelError, StorageError) as err: @@ -880,6 +904,9 @@ async def present_proof_send_free_request(request: web.BaseRequest): will_confirm=True, **_formats_attach(pres_request_spec, PRES_20_REQUEST, "request_presentations"), ) + auto_verify = body.get( + "auto_verify", context.settings.get("debug.auto_verify_presentation") + ) trace_msg = body.get("trace") pres_request_message.assign_trace_decorator( context.settings, @@ -892,6 +919,7 @@ async def present_proof_send_free_request(request: web.BaseRequest): pres_ex_record = await pres_manager.create_exchange_for_request( connection_id=connection_id, pres_request_message=pres_request_message, + auto_verify=auto_verify, ) result = pres_ex_record.serialize() except (BaseModelError, StorageError) as err: @@ -918,7 +946,7 @@ async def present_proof_send_free_request(request: web.BaseRequest): summary="Sends a presentation request in reference to a proposal", ) @match_info_schema(V20PresExIdMatchInfoSchema()) -@request_schema(AdminAPIMessageTracingSchema()) +@request_schema(V20PresentationSendRequestToProposalSchema()) @response_schema(V20PresExRecordSchema(), 200, description="") async def present_proof_send_bound_request(request: web.BaseRequest): """ @@ -966,6 +994,9 @@ async def present_proof_send_bound_request(request: web.BaseRequest): if not conn_record.is_ready: raise web.HTTPForbidden(reason=f"Connection {connection_id} not ready") + pres_ex_record.auto_verify = body.get( + "auto_verify", context.settings.get("debug.auto_verify_presentation") + ) pres_manager = V20PresManager(profile) try: ( From d7b4c40f52bb19fd01919e40bec534760e9b9e6e Mon Sep 17 00:00:00 2001 From: DaevMithran Date: Thu, 31 Mar 2022 11:24:27 +0530 Subject: [PATCH 147/872] Fix present proof model tests Signed-off-by: DaevMithran --- .../protocols/present_proof/v2_0/models/pres_exchange.py | 1 + 1 file changed, 1 insertion(+) diff --git a/aries_cloudagent/protocols/present_proof/v2_0/models/pres_exchange.py b/aries_cloudagent/protocols/present_proof/v2_0/models/pres_exchange.py index c790ee6ceb..f77a53166b 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/models/pres_exchange.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/models/pres_exchange.py @@ -81,6 +81,7 @@ def __init__( self._pres = V20Pres.serde(pres) self.verified = verified self.auto_present = auto_present + self.auto_verify = auto_verify self.error_msg = error_msg @property From 17c499024df7817bc2f908c2b60a2c7121b68923 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Thu, 31 Mar 2022 15:53:14 -0700 Subject: [PATCH 148/872] minor updates Signed-off-by: Shaanjot Gill --- .../messaging/models/base_record.py | 7 +++++-- .../models/tests/test_base_record.py | 21 +++++++++++++++++++ .../protocols/present_proof/v2_0/routes.py | 14 +++++++++++-- 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/aries_cloudagent/messaging/models/base_record.py b/aries_cloudagent/messaging/models/base_record.py index 44ed51e4fe..35f77baf8e 100644 --- a/aries_cloudagent/messaging/models/base_record.py +++ b/aries_cloudagent/messaging/models/base_record.py @@ -19,7 +19,7 @@ from ..util import datetime_to_str, time_now from ..valid import INDY_ISO8601_DATETIME -from .base import BaseModel, BaseModelSchema +from .base import BaseModel, BaseModelSchema, BaseModelError LOGGER = logging.getLogger(__name__) @@ -329,7 +329,10 @@ async def query( positive=False, alt=alt, ): - result.append(cls.from_storage(record.id, vals)) + try: + result.append(cls.from_storage(record.id, vals)) + except BaseModelError as err: + raise BaseModelError(f"{err}, for record id {record.id}") return result async def save( diff --git a/aries_cloudagent/messaging/models/tests/test_base_record.py b/aries_cloudagent/messaging/models/tests/test_base_record.py index 4caf5dc3d6..87749e2df8 100644 --- a/aries_cloudagent/messaging/models/tests/test_base_record.py +++ b/aries_cloudagent/messaging/models/tests/test_base_record.py @@ -12,6 +12,7 @@ StorageError, StorageRecord, ) +from ....messaging.models.base import BaseModelError from ...util import time_now @@ -181,6 +182,26 @@ async def test_query(self): assert result[0]._id == record_id assert result[0].value == record_value + async def test_query_x(self): + session = InMemoryProfile.test_session() + mock_storage = async_mock.MagicMock(BaseStorage, autospec=True) + session.context.injector.bind_instance(BaseStorage, mock_storage) + record_id = "record_id" + record_value = {"created_at": time_now(), "updated_at": time_now()} + tag_filter = {"tag": "filter"} + stored = StorageRecord( + BaseRecordImpl.RECORD_TYPE, json.dumps(record_value), {}, record_id + ) + + mock_storage.find_all_records.return_value = [stored] + with async_mock.patch.object( + BaseRecordImpl, + "from_storage", + async_mock.MagicMock(side_effect=BaseModelError), + ): + with self.assertRaises(BaseModelError): + await BaseRecordImpl.query(session, tag_filter) + async def test_query_post_filter(self): session = InMemoryProfile.test_session() mock_storage = async_mock.MagicMock(BaseStorage, autospec=True) diff --git a/aries_cloudagent/protocols/present_proof/v2_0/routes.py b/aries_cloudagent/protocols/present_proof/v2_0/routes.py index a74b9203d4..115bf09d08 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/routes.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/routes.py @@ -32,6 +32,7 @@ UUID4, ) from ....storage.error import StorageError, StorageNotFoundError +from ....storage.base import BaseStorage from ....storage.vc_holder.base import VCHolder from ....storage.vc_holder.vc_record import VCRecord from ....utils.tracing import trace_event, get_timer, AdminAPIMessageTracingSchema @@ -1265,8 +1266,17 @@ async def present_proof_remove(request: web.BaseRequest): pres_ex_record = None try: async with context.profile.session() as session: - pres_ex_record = await V20PresExRecord.retrieve_by_id(session, pres_ex_id) - await pres_ex_record.delete_record(session) + try: + pres_ex_record = await V20PresExRecord.retrieve_by_id( + session, pres_ex_id + ) + await pres_ex_record.delete_record(session) + except (BaseModelError, ValidationError): + storage = session.inject(BaseStorage) + storage_record = await storage.get_record( + record_type=V20PresExRecord.RECORD_TYPE, record_id=pres_ex_id + ) + await storage.delete_record(storage_record) except StorageNotFoundError as err: raise web.HTTPNotFound(reason=err.roll_up) from err except StorageError as err: From 6f905093fb28b20568887596e82627d6f4fec543 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Thu, 31 Mar 2022 16:27:21 -0700 Subject: [PATCH 149/872] cleanup from conflict resolve Signed-off-by: Shaanjot Gill --- aries_cloudagent/protocols/present_proof/v2_0/routes.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/aries_cloudagent/protocols/present_proof/v2_0/routes.py b/aries_cloudagent/protocols/present_proof/v2_0/routes.py index 20aa183a47..115bf09d08 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/routes.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/routes.py @@ -35,8 +35,6 @@ from ....storage.base import BaseStorage from ....storage.vc_holder.base import VCHolder from ....storage.vc_holder.vc_record import VCRecord -from ....storage.record import StorageRecord -from ....storage.base import BaseStorage from ....utils.tracing import trace_event, get_timer, AdminAPIMessageTracingSchema from ....vc.ld_proofs import BbsBlsSignature2020, Ed25519Signature2018 from ....wallet.error import WalletNotFoundError From a1d36fd8da149608529b81891c98154c530ed7ac Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 1 Apr 2022 14:43:12 +0200 Subject: [PATCH 150/872] feat: oob working for v1 exchange Signed-off-by: Timo Glastra --- aries_cloudagent/core/oob_processor.py | 51 +++++++++++++------ .../protocols/out_of_band/v1_0/manager.py | 39 +++++++------- .../out_of_band/v1_0/models/oob_record.py | 2 +- .../v1_0/handlers/presentation_handler.py | 12 ++++- .../protocols/present_proof/v1_0/manager.py | 30 ++++++----- 5 files changed, 85 insertions(+), 49 deletions(-) diff --git a/aries_cloudagent/core/oob_processor.py b/aries_cloudagent/core/oob_processor.py index 8a33c06dbf..e6eab20d88 100644 --- a/aries_cloudagent/core/oob_processor.py +++ b/aries_cloudagent/core/oob_processor.py @@ -72,13 +72,21 @@ async def find_oob_target_for_outbound_message( session, {"attach_thread_id": outbound_message.reply_thread_id} ) - their_service = oob_record.their_service - their_service = ServiceDecorator.deserialize(their_service) + LOGGER.debug( + "extracting their service from oob record %s", + oob_record.their_service, + ) + + their_service = ServiceDecorator.deserialize(oob_record.their_service) # Attach ~service decorator so other message can respond message = json.loads(outbound_message.payload) if not message.get("~service"): - message["~service"] = oob_record.our_service.serialize() + LOGGER.debug( + "Setting our service on the message ~service %s", + oob_record.our_service, + ) + message["~service"] = oob_record.our_service # OOB_TODO: state is somewhat done, but we need it for connectionless exchange # if is_first_response: @@ -89,6 +97,8 @@ async def find_oob_target_for_outbound_message( outbound_message.payload = json.dumps(message) + LOGGER.debug("Sending oob message payload %s", outbound_message.payload) + return ConnectionTarget( endpoint=their_service.endpoint, recipient_keys=their_service.recipient_keys, @@ -148,8 +158,7 @@ async def find_oob_record_for_inbound_message( return None LOGGER.debug( - f"Found out of band record for inbound message with type {message_type}: %s", - oob_record, + f"Found out of band record for inbound message with type {message_type}: {oob_record.oob_id}" ) # If the connection does not match with the connection id associated with the @@ -229,16 +238,13 @@ async def find_oob_record_for_inbound_message( # Verify the sender key is present in their service in our record # If we don't have the sender verkey stored yet we can allow any key if their_service and ( - # We either want the sender key to be present and present in their_service ( - context.message_receipt.sender_verkey - and context.message_receipt.sender_verkey - not in their_service.recipient_keys - ) - # Or we don't want the sender or recipient key to be present (in case of oob message handler) - or ( - not context.message_receipt.sender_verkey - and not context.message_receipt.recipient_verkey + context.message_receipt.recipient_verkey + and ( + not context.message_receipt.sender_verkey + or context.message_receipt.sender_verkey + not in their_service.recipient_keys + ) ) ): LOGGER.debug( @@ -248,6 +254,10 @@ async def find_oob_record_for_inbound_message( # If the message has a ~service decorator we save it in the oob record so we can reply to this message if context._message._service: + LOGGER.debug( + "Storing service decorator in oob record %s", + context.message._service.serialize(), + ) oob_record.their_service = context.message._service.serialize() async with context.profile.session() as session: @@ -265,7 +275,11 @@ async def find_oob_record_for_inbound_message( return oob_record async def handle_message( - self, profile: Profile, messages: List[Dict[str, Any]], oob_record: OobRecord + self, + profile: Profile, + messages: List[Dict[str, Any]], + oob_record: OobRecord, + their_service: Optional[ServiceDecorator], ): """Message handler for inbound messages.""" @@ -301,6 +315,13 @@ async def handle_message( receipt=receipt, ) + oob_record.attach_thread_id = self.get_thread_id(message) + if their_service: + LOGGER.debug("Storing their service in oob record %s", their_service) + oob_record.their_service = their_service.serialize() + + await oob_record.save(session) + self._inbound_message_router(profile, inbound_message, False) def get_thread_id(self, message: Dict[str, Any]) -> str: diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py index 81ed530a12..efbc66b9cf 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py @@ -269,7 +269,7 @@ async def create_invitation( recipient_keys=[our_recipient_key], endpoint=endpoint, routing_keys=[], - ) + ).serialize() else: if not my_endpoint: my_endpoint = self.profile.settings.get("default_endpoint") @@ -352,7 +352,7 @@ async def create_invitation( recipient_keys=[our_recipient_key], endpoint=my_endpoint, routing_keys=routing_keys, - ) + ).serialize() routing_keys = [ key if len(key.split(":")) == 3 @@ -493,7 +493,9 @@ async def receive_invitation( f"Connection reuse request finished with state {oob_record.state}" ) - if oob_record.state != OobRecord.STATE_ACCEPTED: + if oob_record.state == OobRecord.STATE_ACCEPTED: + return oob_record + else: # Set connection record to None if not accepted # Will make new connection conn_rec = None @@ -541,12 +543,18 @@ async def receive_invitation( wallet = session.inject(BaseWallet) connection_key = await wallet.create_signing_key(KeyType.ED25519) oob_record.our_recipient_key = connection_key.verkey + oob_record.our_service = ServiceDecorator( + recipient_keys=[connection_key.verkey], + endpoint=self.profile.settings.get("default_endpoint"), + routing_keys=[], + ).serialize() await oob_record.save(session) await self._respond_request_attach(oob_record) - # If a connection record is associated with the oob record we can remove it - # Otherwise we need to keep it around for the connectionless exchange + # If a connection record is associated with the oob record we can remove it now as + # we can leverage the connection for all exchanges. Otherwise we need to keep it around + # for the connectionless exchange if conn_rec: oob_record.state = OobRecord.STATE_DONE async with self.profile.session() as session: @@ -558,26 +566,17 @@ async def receive_invitation( async def _respond_request_attach(self, oob_record: OobRecord): invitation = oob_record.invitation - if not isinstance(req_attach, AttachDecorator): - raise OutOfBandManagerError("requests~attach is not properly formatted") - message_processor = self.profile.inject(OobMessageProcessor) messages = [attachment.content for attachment in invitation.requests_attach] + their_service = None if not oob_record.connection_id: service = oob_record.invitation.services[0] - service_decorator = await self._service_decorator_from_service(service) - - oob_record.their_service = service_decorator.serialize() - - async with self.profile.session() as session: - oob_record.attach_thread_id = message_processor.get_thread_id(message) - await oob_record.save(session) + their_service = await self._service_decorator_from_service(service) + LOGGER.debug("Found service for oob record %s", their_service) await message_processor.handle_message( - self.profile, - messages, - oob_record=oob_record, + self.profile, messages, oob_record=oob_record, their_service=their_service ) async def _service_decorator_from_service( @@ -650,8 +649,10 @@ async def _wait_for_state() -> OobRecord: return oob_record LOGGER.debug(f"Wait for oob {oob_id} to receive reuse accepted mesage") + # FIXME: event is not being picked up by the event listener. Is it the event, the cond? # Wait for oob_record to have reuse_accepted state event = await await_event + LOGGER.debug("Received reuse response message") return OobRecord.deserialize(event.payload) try: @@ -1021,7 +1022,7 @@ async def receive_reuse_accepted_message( {"invi_msg_id": invi_msg_id, "reuse_msg_id": thread_reuse_msg_id}, ) - oob_record.state = OobRecord.STATE_DONE + oob_record.state = OobRecord.STATE_ACCEPTED oob_record.connection_id = conn_record.connection_id # We can now remove the oob_record diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/models/oob_record.py b/aries_cloudagent/protocols/out_of_band/v1_0/models/oob_record.py index 8420288589..f1d21d7631 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/models/oob_record.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/models/oob_record.py @@ -25,7 +25,7 @@ class Meta: RECORD_TYPE = "oob_record" RECORD_ID_NAME = "oob_id" - RECORD_TOPIC = "oob_record" + RECORD_TOPIC = "out_of_band" TAG_NAMES = { "invi_msg_id", "attach_thread_id", diff --git a/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_handler.py b/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_handler.py index d2082e3ee0..971430e1f6 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_handler.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_handler.py @@ -40,10 +40,20 @@ async def handle(self, context: RequestContext, responder: BaseResponder): if context.connection_record and not context.connection_ready: raise HandlerException("Connection used for presentation not ready") + # Find associated oob record. If the presentation request was created as an oob attachment + # the presentation exchange record won't have a connection id (yet) + oob_processor = context.inject(OobMessageProcessor) + oob_record = await oob_processor.find_oob_record_for_inbound_message(context) + + # Normally we would do a check here that there is either a connection or + # an associated oob record. However as present proof supported receiving + # presentation without oob record or connection record (aip-1 style connectionless) + # we can't perform this check here + presentation_manager = PresentationManager(profile) presentation_exchange_record = await presentation_manager.receive_presentation( - context.message, context.connection_record + context.message, context.connection_record, oob_record ) # mgr saves record state null if need be and possible r_time = trace_event( diff --git a/aries_cloudagent/protocols/present_proof/v1_0/manager.py b/aries_cloudagent/protocols/present_proof/v1_0/manager.py index 446e4faa76..39638ce68f 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/manager.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/manager.py @@ -298,7 +298,10 @@ async def create_presentation( return presentation_exchange_record, presentation_message async def receive_presentation( - self, message: Presentation, connection_record: Optional[ConnRecord] + self, + message: Presentation, + connection_record: Optional[ConnRecord], + oob_record: Optional[OobRecord], ): """ Receive a presentation, from message in context on manager creation. @@ -314,7 +317,13 @@ async def receive_presentation( # But present proof supports the old-style AIP-1 connectionless exchange that # bypasses the oob record. So we can't verify if an oob record is associated with the # exchange because it is possible that there is None - connection_id = connection_record.connection_id if connection_record else None + connection_id = ( + None + if oob_record + else connection_record.connection_id + if connection_record + else None + ) async with self._profile.session() as session: # Find by thread_id and role. Verify connection id later @@ -442,24 +451,19 @@ async def send_presentation_ack( # Find associated oob record. If this presentation exchange is created # without oob (aip1 style connectionless) we can't send a presentation ack # because we don't have their service - try: - pthid = ( - presentation_exchange_record.presentation_request_dict._thread.pthid - ) - except AttributeError: - raise PresentationManagerError( - "Unable to send connectionless presentation ack without associated oob record" - ) try: async with self._profile.session() as session: await OobRecord.retrieve_by_tag_filter( session, - {"invi_msg_id": pthid}, + {"attach_thread_id": presentation_exchange_record.thread_id}, ) except StorageNotFoundError: - raise PresentationManagerError( - "Unable to send connectionless presentation ack without associated oob record" + # This can happen in AIP1 style connectionless exchange. ACA-PY only supported this for receiving a presentation + LOGGER.error( + "Unable to send connectionless presentation ack without associated oob record. " + "This can happen if proof request was sent without wrapping it in an out of band invitation (AIP1-style)." ) + return if responder: presentation_ack_message = PresentationAck() From 91d49be5cce2b16fbce86a2719790f05ce1b246c Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Fri, 1 Apr 2022 11:05:31 -0700 Subject: [PATCH 151/872] retrigger check Signed-off-by: Shaanjot Gill From cbf50858d967806d87de685b6450d2855858a8f9 Mon Sep 17 00:00:00 2001 From: Yuki I Date: Fri, 1 Apr 2022 10:50:00 -0500 Subject: [PATCH 152/872] Updated getting a token sign: Yuki I Signed-off-by: Yuki I --- Multitenancy.md | 75 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/Multitenancy.md b/Multitenancy.md index fbec89481c..c3cbe87ee9 100644 --- a/Multitenancy.md +++ b/Multitenancy.md @@ -203,6 +203,79 @@ The `Authorization` header is in addition to the Admin API key. So if the `admin A token can be obtained in two ways. The first method is the `token` parameter from the response of the create wallet (`POST /multitenancy/wallet`) endpoint. The second option is using the get wallet token endpoint (`POST /multitenancy/wallet/{wallet_id}/token`) endpoint. +#### Method 1: Register new tenant + +This is the method you use to obtain a token when you haven't already registered a tenant. In this process you will first register a tenant then an object containing your tenant `token` as well as other useful information like your `wallet id` will be returned to you. + +Example + +```jsonc +new_tenant='{ + "image_url": "https://aries.ca/images/sample.png", + "key_management_mode": "managed", + "label": "example-label-02", + "wallet_dispatch_type": "default", + "wallet_key": "example-encryption-key-02", + "wallet_name": "example-name-02", + "wallet_type": "askar", + "wallet_webhook_urls": [ + "https://example.com/webhook" + ] +}' +``` + +``` +echo $new_tenant | curl -X POST "${ACAPY_ADMIN_URL}/multitenancy/wallet" \ + -H "Content-Type: application/json" \ + -H "X-Api-Key: $ACAPY_ADMIN_URL_API_KEY" \ + -d @- +``` + +**`Response`** + +```jsonc +{ + "settings": { + "wallet.type": "askar", + "wallet.name": "example-name-02", + "wallet.webhook_urls": [ + "https://example.com/webhook" + ], + "wallet.dispatch_type": "default", + "default_label": "example-label-02", + "image_url": "https://aries.ca/images/sample.png", + "wallet.id": "3b64ad0d-f556-4c04-92bc-cd95bfde58cd" + }, + "key_management_mode": "managed", + "updated_at": "2022-04-01T15:12:35.474975Z", + "wallet_id": "3b64ad0d-f556-4c04-92bc-cd95bfde58cd", + "created_at": "2022-04-01T15:12:35.474975Z", + "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3YWxsZXRfaWQiOiIzYjY0YWQwZC1mNTU2LTRjMDQtOTJiYy1jZDk1YmZkZTU4Y2QifQ.A4eWbSR2M1Z6mbjcSLOlciBuUejehLyytCVyeUlxI0E" +} +``` + + +#### Method 2: Get tenant token + +This method allows you to retrieve a tenant `token` for an already registered tenant. To retrieve a token you will need an Admin API key (if your admin is protected with one), `wallet_key` and the `wallet_id` of the tenant. + +Example + +``` +curl -X POST "${ACAPY_ADMIN_URL}/multitenancy/wallet/{wallet_id}/token" \ + -H "Content-Type: application/json" \ + -H "X-Api-Key: $ACAPY_ADMIN_URL_API_KEY" \ + -d { "wallet_key": "example-encryption-key-02" } +``` + +**`Response`** + +```jsonc +{ + "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3YWxsZXRfaWQiOiIzYjY0YWQwZC1mNTU2LTRjMDQtOTJiYy1jZDk1YmZkZTU4Y2QifQ.A4eWbSR2M1Z6mbjcSLOlciBuUejehLyytCVyeUlxI0E" +} +``` + In unmanaged mode, the get token endpoint also requires the `wallet_key` parameter to be included in the request body. The wallet key will be included in the JWT so the wallet can be unlocked when making requests to the admin API. ```jsonc @@ -224,3 +297,5 @@ For deterministic JWT creation and verification between restarts and multiple in When using the SwaggerUI you can click the :lock: icon next to each of the endpoints or the `Authorize` button at the top to set the correct authentication headers. Make sure to also include the `Bearer ` part in the input field. This won't be automatically added. ![](/docs/assets/adminApiAuthentication.png) + + From 9f4d77483b8447199ba531fc3db2e9ed212b4ea9 Mon Sep 17 00:00:00 2001 From: Yuki I Date: Fri, 1 Apr 2022 11:58:01 -0500 Subject: [PATCH 153/872] Added section: Tenant Management sign: Yuki I Signed-off-by: Yuki I --- Multitenancy.md | 73 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/Multitenancy.md b/Multitenancy.md index c3cbe87ee9..ce61bcfd73 100644 --- a/Multitenancy.md +++ b/Multitenancy.md @@ -24,6 +24,9 @@ This allows ACA-Py to be used for a wider range of use cases. One use case could - [Getting a token](#getting-a-token) - [JWT Secret](#jwt-secret) - [SwaggerUI](#swaggerui) +- [Tenant Management](#tenant-management) + - [Update a tenant](#update-a-tenant) + - [Remove a tenant](#remove-a-tenant) ## General Concept @@ -298,4 +301,74 @@ When using the SwaggerUI you can click the :lock: icon next to each of the endpo ![](/docs/assets/adminApiAuthentication.png) +## Tenant Management +After registering a tenant which effectively creates a subwallet, you may need to update the tenant information or delete it. The following describes how to accomplish both goals. + +### Update a tenant + +The following properties can be updated: `image_url`, `label`, `wallet_dispatch_type`, and `wallet_webhook_urls` for tenants of a multitenancy wallet. To update these properties you will `PUT` a request json containing the properties you wish to update along with the updated values to the `/multitenancy/wallet/${TENANT_WALLET_ID}` admin endpoint. If the Admin API endoint is protected, you will also include the Admin API Key in the request header. + +Example + +```jsonc +update_tenant='{ + "image_url": "https://aries.ca/images/sample-updated.png", + "label": "example-label-02-updated", + "wallet_webhook_urls": [ + "https://example.com/webhook/updated" + ] +}' +``` + +``` +echo $update_tenant | curl -X PUT "${ACAPY_ADMIN_URL}/multitenancy/wallet/${TENANT_WALLET_ID}" \ + -H "Content-Type: application/json" \ + -H "x-api-key: $ACAPY_ADMIN_URL_API_KEY" \ + -d @- +``` + +**`Response`** + +```jsonc +{ + "settings": { + "wallet.type": "askar", + "wallet.name": "example-name-02", + "wallet.webhook_urls": [ + "https://example.com/webhook/updated" + ], + "wallet.dispatch_type": "default", + "default_label": "example-label-02-updated", + "image_url": "https://aries.ca/images/sample-updated.png", + "wallet.id": "3b64ad0d-f556-4c04-92bc-cd95bfde58cd" + }, + "key_management_mode": "managed", + "updated_at": "2022-04-01T16:23:58.642004Z", + "wallet_id": "3b64ad0d-f556-4c04-92bc-cd95bfde58cd", + "created_at": "2022-04-01T15:12:35.474975Z" +} +``` +> An Admin API Key is all that is ALLOWED to be included in a request header during an update. Inluding the Bearer token header will result in a 404: Unauthorized error + +## Remove a tenant + +The following information is required to delete a tenant: +- wallet_id +- wallet_key +- {Admin_Api_Key} if admin is protected + +Example + +``` +curl -X POST "${ACAPY_ADMIN_URL}/multitenancy/wallet/{wallet_id}/remove" \ + -H "Content-Type: application/json" \ + -H "x-api-key: $ACAPY_ADMIN_URL_API_KEY" \ + -d '{ "wallet_key": "example-encryption-key-02" }' +``` + +**`Response`** + +```jsonc +{} +``` \ No newline at end of file From 697c6f52a9c75f2e89020ed972327890eda7023d Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sun, 3 Apr 2022 11:52:36 +0200 Subject: [PATCH 154/872] feat: oob working for v2 exchange Signed-off-by: Timo Glastra --- .../v1_0/handlers/credential_issue_handler.py | 3 - .../v2_0/handlers/cred_ack_handler.py | 21 +++++- .../v2_0/handlers/cred_issue_handler.py | 21 +++++- .../v2_0/handlers/cred_offer_handler.py | 33 ++++++++- .../handlers/cred_problem_report_handler.py | 12 ++- .../v2_0/handlers/cred_proposal_handler.py | 9 ++- .../v2_0/handlers/cred_request_handler.py | 19 ++++- .../issue_credential/v2_0/manager.py | 74 +++++++++++-------- .../v2_0/models/cred_ex_record.py | 15 +++- .../protocols/issue_credential/v2_0/routes.py | 66 +++++++++++++---- .../v2_0/handlers/pres_ack_handler.py | 16 +++- .../v2_0/handlers/pres_handler.py | 19 ++++- .../v2_0/handlers/pres_proposal_handler.py | 9 ++- .../v2_0/handlers/pres_request_handler.py | 29 +++++++- .../protocols/present_proof/v2_0/manager.py | 56 +++++++++----- .../protocols/present_proof/v2_0/routes.py | 23 +++--- 16 files changed, 322 insertions(+), 103 deletions(-) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_issue_handler.py b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_issue_handler.py index f34560c624..7fe0bd7bf6 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_issue_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_issue_handler.py @@ -1,8 +1,5 @@ """Credential issue message handler.""" -from aries_cloudagent.protocols.connections.v1_0.handlers.tests.test_request_handler import ( - connection_record, -) from .....core.oob_processor import OobMessageProcessor from .....indy.holder import IndyHolderError from .....messaging.base_handler import BaseHandler, HandlerException diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_ack_handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_ack_handler.py index bd7a4404d7..818a3ec3a9 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_ack_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_ack_handler.py @@ -1,5 +1,6 @@ """Credential ack message handler.""" +from .....core.oob_processor import OobMessageProcessor from .....messaging.base_handler import BaseHandler, HandlerException from .....messaging.request_context import RequestContext from .....messaging.responder import BaseResponder @@ -29,12 +30,26 @@ async def handle(self, context: RequestContext, responder: BaseResponder): context.message.serialize(as_string=True), ) - if not context.connection_ready: - raise HandlerException("No connection established for credential ack") + # If connection is present it must be ready for use + if context.connection_record and not context.connection_ready: + raise HandlerException("Connection used for credential ack not ready") + + # Find associated oob record + oob_processor = context.inject(OobMessageProcessor) + oob_record = await oob_processor.find_oob_record_for_inbound_message(context) + + # Either connection or oob context must be present + if not context.connection_record and not oob_record: + raise HandlerException( + "No connection or associated connectionless exchange found for credential ack" + ) cred_manager = V20CredManager(context.profile) await cred_manager.receive_credential_ack( - context.message, context.connection_record.connection_id + context.message, + context.connection_record.connection_id + if context.connection_record + else None, ) trace_event( diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_issue_handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_issue_handler.py index e14f09ff19..ee148857f1 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_issue_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_issue_handler.py @@ -1,5 +1,6 @@ """Credential issue message handler.""" +from .....core.oob_processor import OobMessageProcessor from .....indy.holder import IndyHolderError from .....messaging.base_handler import BaseHandler, HandlerException from .....messaging.models.base import BaseModelError @@ -35,12 +36,26 @@ async def handle(self, context: RequestContext, responder: BaseResponder): context.message.serialize(as_string=True), ) - if not context.connection_ready: - raise HandlerException("No connection established for credential issue") + # If connection is present it must be ready for use + if context.connection_record and not context.connection_ready: + raise HandlerException("Connection used for credential not ready") + + # Find associated oob record + oob_processor = context.inject(OobMessageProcessor) + oob_record = await oob_processor.find_oob_record_for_inbound_message(context) + + # Either connection or oob context must be present + if not context.connection_record and not oob_record: + raise HandlerException( + "No connection or associated connectionless exchange found for credential" + ) cred_manager = V20CredManager(context.profile) cred_ex_record = await cred_manager.receive_credential( - context.message, context.connection_record.connection_id + context.message, + context.connection_record.connection_id + if context.connection_record + else None, ) # mgr only finds, saves record: on exception, saving null state is hopeless r_time = trace_event( diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_offer_handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_offer_handler.py index cebaa68b8d..cf849ce393 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_offer_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_offer_handler.py @@ -1,5 +1,7 @@ """Credential offer message handler.""" +from .....wallet.util import default_did_from_verkey +from .....core.oob_processor import OobMessageProcessor from .....indy.holder import IndyHolderError from .....ledger.error import LedgerError from .....messaging.base_handler import BaseHandler, HandlerException @@ -36,13 +38,30 @@ async def handle(self, context: RequestContext, responder: BaseResponder): context.message.serialize(as_string=True), ) - if not context.connection_ready: - raise HandlerException("No connection established for credential offer") + # If connection is present it must be ready for use + if context.connection_record and not context.connection_ready: + raise HandlerException("Connection used for credential offer not ready") + + # Find associated oob record + oob_processor = context.inject(OobMessageProcessor) + oob_record = await oob_processor.find_oob_record_for_inbound_message(context) + + # Either connection or oob context must be present + if not context.connection_record and not oob_record: + raise HandlerException( + "No connection or associated connectionless exchange found for credential offer" + ) + + connection_id = ( + context.connection_record.connection_id + if context.connection_record + else None + ) profile = context.profile cred_manager = V20CredManager(profile) cred_ex_record = await cred_manager.receive_offer( - context.message, context.connection_record.connection_id + context.message, connection_id ) # mgr only finds, saves record: on exception, saving state null is hopeless r_time = trace_event( @@ -52,13 +71,19 @@ async def handle(self, context: RequestContext, responder: BaseResponder): perf_counter=r_time, ) + if context.connection_record: + holder_did = context.connection_record.my_did + else: + # Transform recipient key into did + holder_did = default_did_from_verkey(oob_record.our_recipient_key) + # If auto respond is turned on, automatically reply with credential request if context.settings.get("debug.auto_respond_credential_offer"): cred_request_message = None try: (_, cred_request_message) = await cred_manager.create_request( cred_ex_record=cred_ex_record, - holder_did=context.connection_record.my_did, + holder_did=holder_did, ) await responder.send_reply(cred_request_message) except ( diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_problem_report_handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_problem_report_handler.py index 18b564dace..32515d1365 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_problem_report_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_problem_report_handler.py @@ -1,6 +1,6 @@ """Credential problem report message handler.""" -from .....messaging.base_handler import BaseHandler +from .....messaging.base_handler import BaseHandler, HandlerException from .....messaging.request_context import RequestContext from .....messaging.responder import BaseResponder from .....storage.error import StorageError, StorageNotFoundError @@ -26,6 +26,16 @@ async def handle(self, context: RequestContext, responder: BaseResponder): ) assert isinstance(context.message, V20CredProblemReport) + # If connection is present it must be ready for use + if context.connection_record and not context.connection_ready: + raise HandlerException( + "Connection used for credential problem report not ready" + ) + elif not context.connection_record: + raise HandlerException( + "Connectionless not supported for credential problem report" + ) + cred_manager = V20CredManager(context.profile) try: await cred_manager.receive_problem_report( diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_proposal_handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_proposal_handler.py index 81315bf4f4..e59e9a36d2 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_proposal_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_proposal_handler.py @@ -36,8 +36,13 @@ async def handle(self, context: RequestContext, responder: BaseResponder): context.message.serialize(as_string=True), ) - if not context.connection_ready: - raise HandlerException("No connection established for credential proposal") + # If connection is present it must be ready for use + if context.connection_record and not context.connection_ready: + raise HandlerException("Connection used for credential proposal not ready") + elif not context.connection_record: + raise HandlerException( + "Connectionless not supported for credential proposal" + ) profile = context.profile cred_manager = V20CredManager(profile) diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_request_handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_request_handler.py index 789bb00f20..6ea02cbddb 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_request_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_request_handler.py @@ -1,5 +1,6 @@ """Credential request message handler.""" +from .....core.oob_processor import OobMessageProcessor from .....indy.issuer import IndyIssuerError from .....ledger.error import LedgerError from .....messaging.base_handler import BaseHandler, HandlerException @@ -36,13 +37,25 @@ async def handle(self, context: RequestContext, responder: BaseResponder): context.message.serialize(as_string=True), ) - if not context.connection_ready: - raise HandlerException("No connection established for credential request") + # If connection is present it must be ready for use + if context.connection_record and not context.connection_ready: + raise HandlerException("Connection used for credential request not ready") + + # Find associated oob record. If the credential offer was created as an oob attachment + # the presentation exchange record won't have a connection id (yet) + oob_processor = context.inject(OobMessageProcessor) + oob_record = await oob_processor.find_oob_record_for_inbound_message(context) + + # Either connection or oob context must be present + if not context.connection_record and not oob_record: + raise HandlerException( + "No connection or associated connectionless exchange found for credential request" + ) profile = context.profile cred_manager = V20CredManager(profile) cred_ex_record = await cred_manager.receive_request( - context.message, context.connection_record.connection_id + context.message, context.connection_record, oob_record ) # mgr only finds, saves record: on exception, saving state null is hopeless r_time = trace_event( diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/manager.py b/aries_cloudagent/protocols/issue_credential/v2_0/manager.py index 5dd4a7e347..f1d3f541c3 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/manager.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/manager.py @@ -2,8 +2,10 @@ import logging -from typing import Mapping, Tuple +from typing import Mapping, Optional, Tuple +from ....connections.models.conn_record import ConnRecord +from ....core.oob_processor import OobRecord from ....core.error import BaseError from ....core.profile import Profile from ....messaging.responder import BaseResponder @@ -264,7 +266,7 @@ async def create_offer( async def receive_offer( self, cred_offer_message: V20CredOffer, - connection_id: str, + connection_id: Optional[str], ) -> V20CredExRecord: """ Receive a credential offer. @@ -284,7 +286,10 @@ async def receive_offer( async with self._profile.session() as session: cred_ex_record = await ( V20CredExRecord.retrieve_by_conn_and_thread( - session, connection_id, cred_offer_message._thread_id + session, + connection_id, + cred_offer_message._thread_id, + role=V20CredExRecord.ROLE_HOLDER, ) ) except StorageNotFoundError: # issuer sent this offer free of any proposal @@ -376,7 +381,8 @@ async def create_request( requests_attach=[attach for (_, attach) in request_formats], ) - cred_request_message._thread = {"thid": cred_ex_record.thread_id} + # Assign thid (and optionally pthid) to message + cred_request_message.assign_thread_from(cred_ex_record.cred_offer) cred_request_message.assign_trace_decorator( self._profile.settings, cred_ex_record.trace ) @@ -391,7 +397,10 @@ async def create_request( return (cred_ex_record, cred_request_message) async def receive_request( - self, cred_request_message: V20CredRequest, connection_id: str + self, + cred_request_message: V20CredRequest, + connection_record: Optional[ConnRecord], + oob_record: Optional[OobRecord], ) -> V20CredExRecord: """ Receive a credential request. @@ -404,36 +413,39 @@ async def receive_request( credential exchange record, updated """ + # connection_id is None in the record if this is in response to + # an request~attach from an OOB message. If so, we do not want to filter + # the record by connection_id. + connection_id = None if oob_record else connection_record.connection_id + async with self._profile.session() as session: try: cred_ex_record = await ( V20CredExRecord.retrieve_by_conn_and_thread( - session, connection_id, cred_request_message._thread_id - ) - ) - except StorageNotFoundError: - try: - cred_ex_record = await V20CredExRecord.retrieve_by_tag_filter( session, - {"thread_id": cred_request_message._thread_id}, - {"connection_id": None}, - ) - cred_ex_record.connection_id = connection_id - except StorageNotFoundError: - # holder sent this request free of any offer - cred_ex_record = V20CredExRecord( - connection_id=connection_id, - thread_id=cred_request_message._thread_id, - initiator=V20CredExRecord.INITIATOR_EXTERNAL, + connection_id, + cred_request_message._thread_id, role=V20CredExRecord.ROLE_ISSUER, - auto_remove=not self._profile.settings.get( - "preserve_exchange_records" - ), - trace=(cred_request_message._trace is not None), - auto_issue=self._profile.settings.get( - "debug.auto_respond_credential_request" - ), ) + ) + except StorageNotFoundError: + # holder sent this request free of any offer + cred_ex_record = V20CredExRecord( + connection_id=connection_id, + thread_id=cred_request_message._thread_id, + initiator=V20CredExRecord.INITIATOR_EXTERNAL, + role=V20CredExRecord.ROLE_ISSUER, + auto_remove=not self._profile.settings.get( + "preserve_exchange_records" + ), + trace=(cred_request_message._trace is not None), + auto_issue=self._profile.settings.get( + "debug.auto_respond_credential_request" + ), + ) + + if connection_record: + cred_ex_record.connection_id = connection_record.connection_id for format in cred_request_message.formats: cred_format = V20CredFormat.Format.get(format.format) @@ -526,7 +538,7 @@ async def issue_credential( return (cred_ex_record, cred_issue_message) async def receive_credential( - self, cred_issue_message: V20CredIssue, connection_id: str + self, cred_issue_message: V20CredIssue, connection_id: Optional[str] ) -> V20CredExRecord: """ Receive a credential issue message from an issuer. @@ -546,6 +558,7 @@ async def receive_credential( session, connection_id, cred_issue_message._thread_id, + role=V20CredExRecord.ROLE_HOLDER, ) ) @@ -669,7 +682,7 @@ async def send_cred_ack( return cred_ex_record, cred_ack_message async def receive_credential_ack( - self, cred_ack_message: V20CredAck, connection_id: str + self, cred_ack_message: V20CredAck, connection_id: Optional[str] ) -> V20CredExRecord: """ Receive credential ack from holder. @@ -689,6 +702,7 @@ async def receive_credential_ack( session, connection_id, cred_ack_message._thread_id, + role=V20CredExRecord.ROLE_ISSUER, ) ) diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/models/cred_ex_record.py b/aries_cloudagent/protocols/issue_credential/v2_0/models/cred_ex_record.py index 3ba98c7e98..fbbc0adaf5 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/models/cred_ex_record.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/models/cred_ex_record.py @@ -2,7 +2,7 @@ import logging -from typing import Any, Mapping, Union +from typing import Any, Mapping, Optional, Union from marshmallow import fields, Schema, validate @@ -214,7 +214,11 @@ def record_value(self) -> Mapping: @classmethod async def retrieve_by_conn_and_thread( - cls, session: ProfileSession, connection_id: str, thread_id: str + cls, + session: ProfileSession, + connection_id: Optional[str], + thread_id: str, + role: Optional[str] = None, ) -> "V20CredExRecord": """Retrieve a credential exchange record by connection and thread ID.""" cache_key = f"credential_exchange_ctidx::{connection_id}::{thread_id}" @@ -222,10 +226,15 @@ async def retrieve_by_conn_and_thread( if record_id: record = await cls.retrieve_by_id(session, record_id) else: + post_filter = {} + if role: + post_filter["role"] = role + if connection_id: + post_filter["connection_id"] = connection_id record = await cls.retrieve_by_tag_filter( session, {"thread_id": thread_id}, - {"connection_id": connection_id} if connection_id else None, + post_filter, ) await cls.set_cached_key(session, cache_key, record.cred_ex_id) return record diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/routes.py b/aries_cloudagent/protocols/issue_credential/v2_0/routes.py index b7d98758b0..29fa2fd2bd 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/routes.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/routes.py @@ -15,6 +15,8 @@ ) from marshmallow import fields, validate, validates_schema, ValidationError +from ...out_of_band.v1_0.models.oob_record import OobRecord +from ....wallet.util import default_did_from_verkey from ....admin.request_context import AdminRequestContext from ....connections.models.conn_record import ConnRecord from ....core.profile import Profile @@ -1232,16 +1234,36 @@ async def credential_exchange_send_bound_request(request: web.BaseRequest): except StorageNotFoundError as err: raise web.HTTPNotFound(reason=err.roll_up) from err - connection_id = cred_ex_record.connection_id - conn_record = await ConnRecord.retrieve_by_id(session, connection_id) + conn_record = None + if cred_ex_record.connection_id: + try: + conn_record = await ConnRecord.retrieve_by_id( + session, cred_ex_record.connection_id + ) + except StorageNotFoundError as err: + raise web.HTTPBadRequest(reason=err.roll_up) from err + + if conn_record and not conn_record.is_ready: + raise web.HTTPForbidden( + reason=f"Connection {cred_ex_record.connection_id} not ready" + ) - if not conn_record.is_ready: - raise web.HTTPForbidden(reason=f"Connection {connection_id} not ready") + if conn_record or holder_did: + holder_did = holder_did or conn_record.my_did + else: + # Need to get the holder DID from the out of band record + async with profile.session() as session: + oob_record = await OobRecord.retrieve_by_tag_filter( + session, + {"invi_msg_id": cred_ex_record.cred_offer._thread.pthid}, + ) + # Transform recipient key into did + holder_did = default_did_from_verkey(oob_record.our_recipient_key) cred_manager = V20CredManager(profile) cred_ex_record, cred_request_message = await cred_manager.create_request( cred_ex_record, - holder_did if holder_did else conn_record.my_did, + holder_did, ) result = cred_ex_record.serialize() @@ -1266,7 +1288,9 @@ async def credential_exchange_send_bound_request(request: web.BaseRequest): outbound_handler, ) - await outbound_handler(cred_request_message, connection_id=connection_id) + await outbound_handler( + cred_request_message, connection_id=cred_ex_record.connection_id + ) trace_event( context.settings, @@ -1318,11 +1342,16 @@ async def credential_exchange_issue(request: web.BaseRequest): ) except StorageNotFoundError as err: raise web.HTTPNotFound(reason=err.roll_up) from err - connection_id = cred_ex_record.connection_id - conn_record = await ConnRecord.retrieve_by_id(session, connection_id) - if not conn_record.is_ready: - raise web.HTTPForbidden(reason=f"Connection {connection_id} not ready") + conn_record = None + if cred_ex_record.connection_id: + conn_record = await ConnRecord.retrieve_by_id( + session, cred_ex_record.connection_id + ) + if conn_record and not conn_record.is_ready: + raise web.HTTPForbidden( + reason=f"Connection {cred_ex_record.connection_id} not ready" + ) cred_manager = V20CredManager(profile) (cred_ex_record, cred_issue_message) = await cred_manager.issue_credential( @@ -1352,7 +1381,9 @@ async def credential_exchange_issue(request: web.BaseRequest): outbound_handler, ) - await outbound_handler(cred_issue_message, connection_id=connection_id) + await outbound_handler( + cred_issue_message, connection_id=cred_ex_record.connection_id + ) trace_event( context.settings, @@ -1407,10 +1438,15 @@ async def credential_exchange_store(request: web.BaseRequest): except StorageNotFoundError as err: raise web.HTTPNotFound(reason=err.roll_up) from err - connection_id = cred_ex_record.connection_id - conn_record = await ConnRecord.retrieve_by_id(session, connection_id) - if not conn_record.is_ready: - raise web.HTTPForbidden(reason=f"Connection {connection_id} not ready") + conn_record = None + if cred_ex_record.connection_id: + conn_record = await ConnRecord.retrieve_by_id( + session, cred_ex_record.connection_id + ) + if conn_record and not conn_record.is_ready: + raise web.HTTPForbidden( + reason=f"Connection {cred_ex_record.connection_id} not ready" + ) cred_manager = V20CredManager(profile) cred_ex_record = await cred_manager.store_credential(cred_ex_record, cred_id) diff --git a/aries_cloudagent/protocols/present_proof/v2_0/handlers/pres_ack_handler.py b/aries_cloudagent/protocols/present_proof/v2_0/handlers/pres_ack_handler.py index 0a0d2a75c5..4ba4c3b06c 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/handlers/pres_ack_handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/handlers/pres_ack_handler.py @@ -1,5 +1,6 @@ """Presentation ack message handler.""" +from .....core.oob_processor import OobMessageProcessor from .....messaging.base_handler import BaseHandler, HandlerException from .....messaging.request_context import RequestContext from .....messaging.responder import BaseResponder @@ -29,8 +30,19 @@ async def handle(self, context: RequestContext, responder: BaseResponder): context.message.serialize(as_string=True), ) - if not context.connection_ready: - raise HandlerException("No connection established for presentation ack") + # If connection is present it must be ready for use + if context.connection_record and not context.connection_ready: + raise HandlerException("Connection used for presentation ack not ready") + + # Find associated oob record + oob_processor = context.inject(OobMessageProcessor) + oob_record = await oob_processor.find_oob_record_for_inbound_message(context) + + # Either connection or oob context must be present + if not context.connection_record and not oob_record: + raise HandlerException( + "No connection or associated connectionless exchange found for presentation ack" + ) pres_manager = V20PresManager(context.profile) await pres_manager.receive_pres_ack(context.message, context.connection_record) diff --git a/aries_cloudagent/protocols/present_proof/v2_0/handlers/pres_handler.py b/aries_cloudagent/protocols/present_proof/v2_0/handlers/pres_handler.py index 9736a66658..654476e8eb 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/handlers/pres_handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/handlers/pres_handler.py @@ -1,7 +1,8 @@ """Presentation message handler.""" +from .....core.oob_processor import OobMessageProcessor from .....ledger.error import LedgerError -from .....messaging.base_handler import BaseHandler +from .....messaging.base_handler import BaseHandler, HandlerException from .....messaging.models.base import BaseModelError from .....messaging.request_context import RequestContext from .....messaging.responder import BaseResponder @@ -35,10 +36,24 @@ async def handle(self, context: RequestContext, responder: BaseResponder): context.message.serialize(as_string=True), ) + # If connection is present it must be ready for use + if context.connection_record and not context.connection_ready: + raise HandlerException("Connection used for presentation not ready") + + # Find associated oob record. If the presentation request was created as an oob attachment + # the presentation exchange record won't have a connection id (yet) + oob_processor = context.inject(OobMessageProcessor) + oob_record = await oob_processor.find_oob_record_for_inbound_message(context) + + # Normally we would do a check here that there is either a connection or + # an associated oob record. However as present proof supported receiving + # presentation without oob record or connection record (aip-1 style connectionless) + # we can't perform this check here + pres_manager = V20PresManager(context.profile) pres_ex_record = await pres_manager.receive_pres( - context.message, context.connection_record + context.message, context.connection_record, oob_record ) r_time = trace_event( diff --git a/aries_cloudagent/protocols/present_proof/v2_0/handlers/pres_proposal_handler.py b/aries_cloudagent/protocols/present_proof/v2_0/handlers/pres_proposal_handler.py index 773852b185..e20c1d620c 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/handlers/pres_proposal_handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/handlers/pres_proposal_handler.py @@ -35,9 +35,14 @@ async def handle(self, context: RequestContext, responder: BaseResponder): context.message.serialize(as_string=True), ) - if not context.connection_ready: + if not context.connection_record: raise HandlerException( - "No connection established for presentation proposal" + "Connectionless not supported for presentation proposal" + ) + # If connection is present it must be ready for use + elif not context.connection_ready: + raise HandlerException( + "Connection used for presentation proposal not ready" ) profile = context.profile diff --git a/aries_cloudagent/protocols/present_proof/v2_0/handlers/pres_request_handler.py b/aries_cloudagent/protocols/present_proof/v2_0/handlers/pres_request_handler.py index 1cbca5838e..5b6156c7ec 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/handlers/pres_request_handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/handlers/pres_request_handler.py @@ -1,5 +1,6 @@ """Presentation request message handler.""" +from .....core.oob_processor import OobMessageProcessor from .....indy.holder import IndyHolderError from .....ledger.error import LedgerError from .....messaging.base_handler import BaseHandler, HandlerException @@ -39,8 +40,25 @@ async def handle(self, context: RequestContext, responder: BaseResponder): context.message.serialize(as_string=True), ) - if not context.connection_ready: - raise HandlerException("No connection established for presentation request") + # If connection is present it must be ready for use + if context.connection_record and not context.connection_ready: + raise HandlerException("Connection used for presentation request not ready") + + # Find associated oob record + oob_processor = context.inject(OobMessageProcessor) + oob_record = await oob_processor.find_oob_record_for_inbound_message(context) + + # Either connection or oob context must be present + if not context.connection_record and not oob_record: + raise HandlerException( + "No connection or associated connectionless exchange found for presentation request" + ) + + connection_id = ( + context.connection_record.connection_id + if context.connection_record + else None + ) profile = context.profile pres_manager = V20PresManager(profile) @@ -52,13 +70,16 @@ async def handle(self, context: RequestContext, responder: BaseResponder): pres_ex_record = await V20PresExRecord.retrieve_by_tag_filter( session, {"thread_id": context.message._thread_id}, - {"connection_id": context.connection_record.connection_id}, + { + "connection_id": connection_id, + "role": V20PresExRecord.ROLE_PROVER, + }, ) # holder initiated via proposal pres_ex_record.pres_request = context.message except StorageNotFoundError: # verifier sent this request free of any proposal pres_ex_record = V20PresExRecord( - connection_id=context.connection_record.connection_id, + connection_id=connection_id, thread_id=context.message._thread_id, initiator=V20PresExRecord.INITIATOR_EXTERNAL, role=V20PresExRecord.ROLE_PROVER, diff --git a/aries_cloudagent/protocols/present_proof/v2_0/manager.py b/aries_cloudagent/protocols/present_proof/v2_0/manager.py index 688f4ca35f..b1c5c1f3c9 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/manager.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/manager.py @@ -2,13 +2,13 @@ import logging -from typing import Tuple +from typing import Optional, Tuple +from ...out_of_band.v1_0.models.oob_record import OobRecord from ....connections.models.conn_record import ConnRecord from ....core.error import BaseError from ....core.profile import Profile from ....messaging.responder import BaseResponder -from ....storage.error import StorageNotFoundError from .messages.pres import V20Pres from .messages.pres_ack import V20PresAck @@ -279,7 +279,8 @@ async def create_pres( presentations_attach=[attach for (_, attach) in pres_formats], ) - pres_message._thread = {"thid": pres_ex_record.thread_id} + # Assign thid (and optionally pthid) to message + pres_message.assign_thread_from(pres_ex_record.pres_request) pres_message.assign_trace_decorator( self._profile.settings, pres_ex_record.trace ) @@ -294,7 +295,12 @@ async def create_pres( await pres_ex_record.save(session, reason="create v2.0 presentation") return pres_ex_record, pres_message - async def receive_pres(self, message: V20Pres, conn_record: ConnRecord): + async def receive_pres( + self, + message: V20Pres, + connection_record: Optional[ConnRecord], + oob_record: Optional[OobRecord], + ): """ Receive a presentation, from message in context on manager creation. @@ -304,21 +310,31 @@ async def receive_pres(self, message: V20Pres, conn_record: ConnRecord): """ thread_id = message._thread_id - conn_id_filter = ( + # Normally we only set the connection_id to None if an oob record is present + # But present proof supports the old-style AIP-1 connectionless exchange that + # bypasses the oob record. So we can't verify if an oob record is associated with the + # exchange because it is possible that there is None + connection_id = ( None - if conn_record is None - else {"connection_id": conn_record.connection_id} + if oob_record + else connection_record.connection_id + if connection_record + else None ) + async with self._profile.session() as session: - try: - pres_ex_record = await V20PresExRecord.retrieve_by_tag_filter( - session, {"thread_id": thread_id}, conn_id_filter - ) - except StorageNotFoundError: - # Proof req not bound to any connection: requests_attach in OOB msg - pres_ex_record = await V20PresExRecord.retrieve_by_tag_filter( - session, {"thread_id": thread_id}, None - ) + pres_ex_record = await V20PresExRecord.retrieve_by_tag_filter( + session, + {"thread_id": thread_id}, + { + "role": V20PresExRecord.ROLE_VERIFIER, + "connection_id": connection_id, + }, + ) + + # Save connection id (if it wasn't already present) + if connection_record: + pres_ex_record.connection_id = connection_record.connection_id input_formats = message.formats @@ -398,6 +414,7 @@ async def send_pres_ack(self, pres_ex_record: V20PresExRecord): await responder.send_reply( pres_ack_message, + # connection_id can be none in case of connectionless connection_id=pres_ex_record.connection_id, ) else: @@ -414,11 +431,16 @@ async def receive_pres_ack(self, message: V20PresAck, conn_record: ConnRecord): presentation exchange record, retrieved and updated """ + connection_id = conn_record.connection_id if conn_record else None async with self._profile.session() as session: pres_ex_record = await V20PresExRecord.retrieve_by_tag_filter( session, {"thread_id": message._thread_id}, - {"connection_id": conn_record.connection_id}, + { + # connection_id can be null in connectionless + "connection_id": connection_id, + "role": V20PresExRecord.ROLE_PROVER, + }, ) pres_ex_record.state = V20PresExRecord.STATE_DONE diff --git a/aries_cloudagent/protocols/present_proof/v2_0/routes.py b/aries_cloudagent/protocols/present_proof/v2_0/routes.py index 8d24d1afe4..09b2170624 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/routes.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/routes.py @@ -1055,15 +1055,20 @@ async def present_proof_send_presentation(request: web.BaseRequest): ) ) - connection_id = pres_ex_record.connection_id - try: - async with profile.session() as session: - conn_record = await ConnRecord.retrieve_by_id(session, connection_id) - except StorageNotFoundError as err: - raise web.HTTPBadRequest(reason=err.roll_up) from err + # Fetch connection if exchange has record + conn_record = None + if pres_ex_record.connection_id: + try: + conn_record = await ConnRecord.retrieve_by_id( + session, pres_ex_record.connection_id + ) + except StorageNotFoundError as err: + raise web.HTTPBadRequest(reason=err.roll_up) from err - if not conn_record.is_ready: - raise web.HTTPForbidden(reason=f"Connection {connection_id} not ready") + if conn_record and not conn_record.is_ready: + raise web.HTTPForbidden( + reason=f"Connection {pres_ex_record.connection_id} not ready" + ) pres_manager = V20PresManager(profile) try: @@ -1097,7 +1102,7 @@ async def present_proof_send_presentation(request: web.BaseRequest): context.settings, trace_msg, ) - await outbound_handler(pres_message, connection_id=connection_id) + await outbound_handler(pres_message, connection_id=pres_ex_record.connection_id) trace_event( context.settings, From 034f5b174f4e6fb30a20fbb0f5037f12a8fd16f7 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sun, 3 Apr 2022 12:23:26 +0200 Subject: [PATCH 155/872] fix: no session when retrieving from storage Signed-off-by: Timo Glastra --- .../protocols/present_proof/v2_0/routes.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/aries_cloudagent/protocols/present_proof/v2_0/routes.py b/aries_cloudagent/protocols/present_proof/v2_0/routes.py index 09b2170624..af9b14dd14 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/routes.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/routes.py @@ -1043,6 +1043,13 @@ async def present_proof_send_presentation(request: web.BaseRequest): try: async with profile.session() as session: pres_ex_record = await V20PresExRecord.retrieve_by_id(session, pres_ex_id) + + # Fetch connection if exchange has record + conn_record = None + if pres_ex_record.connection_id: + conn_record = await ConnRecord.retrieve_by_id( + session, pres_ex_record.connection_id + ) except StorageNotFoundError as err: raise web.HTTPNotFound(reason=err.roll_up) from err @@ -1055,16 +1062,6 @@ async def present_proof_send_presentation(request: web.BaseRequest): ) ) - # Fetch connection if exchange has record - conn_record = None - if pres_ex_record.connection_id: - try: - conn_record = await ConnRecord.retrieve_by_id( - session, pres_ex_record.connection_id - ) - except StorageNotFoundError as err: - raise web.HTTPBadRequest(reason=err.roll_up) from err - if conn_record and not conn_record.is_ready: raise web.HTTPForbidden( reason=f"Connection {pres_ex_record.connection_id} not ready" From 90cd4749540c55c26f86cdcb39d72bd112159d59 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sun, 3 Apr 2022 13:48:40 +0200 Subject: [PATCH 156/872] test: fix issue credential v1 tests Signed-off-by: Timo Glastra --- .../v1_0/handlers/credential_ack_handler.py | 3 +- .../v1_0/handlers/credential_offer_handler.py | 3 +- .../handlers/credential_request_handler.py | 7 +- .../tests/test_credential_ack_handler.py | 47 +++++++++- .../tests/test_credential_issue_handler.py | 60 ++++++++++++- .../tests/test_credential_offer_handler.py | 63 +++++++++++++- .../test_credential_problem_report_handler.py | 47 ++++++++++ .../tests/test_credential_proposal_handler.py | 22 ++++- .../tests/test_credential_request_handler.py | 82 +++++++++++++++++- .../v1_0/tests/test_manager.py | 85 +++++++++++++------ .../v1_0/tests/test_routes.py | 47 +++++++++- 11 files changed, 422 insertions(+), 44 deletions(-) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_ack_handler.py b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_ack_handler.py index da5712e7ab..5229bfef50 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_ack_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_ack_handler.py @@ -41,7 +41,8 @@ async def handle(self, context: RequestContext, responder: BaseResponder): # Either connection or oob context must be present if not context.connection_record and not oob_record: raise HandlerException( - "No connection or associated connectionless exchange found for credential ack" + "No connection or associated connectionless exchange found for credential" + " ack" ) credential_manager = CredentialManager(context.profile) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_offer_handler.py b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_offer_handler.py index 987a3feead..a9e114ee40 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_offer_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_offer_handler.py @@ -50,7 +50,8 @@ async def handle(self, context: RequestContext, responder: BaseResponder): # Either connection or oob context must be present if not context.connection_record and not oob_record: raise HandlerException( - "No connection or associated connectionless exchange found for credential offer" + "No connection or associated connectionless exchange found for credential" + " offer" ) connection_id = ( diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_request_handler.py b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_request_handler.py index eecbf2e348..f9bc43e70e 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_request_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_request_handler.py @@ -41,15 +41,16 @@ async def handle(self, context: RequestContext, responder: BaseResponder): if context.connection_record and not context.connection_ready: raise HandlerException("Connection used for credential request not ready") - # Find associated oob record. If the credential offer was created as an oob attachment - # the presentation exchange record won't have a connection id (yet) + # Find associated oob record. If the credential offer was created as an oob + # attachment the presentation exchange record won't have a connection id (yet) oob_processor = context.inject(OobMessageProcessor) oob_record = await oob_processor.find_oob_record_for_inbound_message(context) # Either connection or oob context must be present if not context.connection_record and not oob_record: raise HandlerException( - "No connection or associated connectionless exchange found for credential request" + "No connection or associated connectionless exchange found for credential" + " request" ) credential_manager = CredentialManager(profile) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_ack_handler.py b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_ack_handler.py index fa0fad4c10..ae0c31f0e6 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_ack_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_ack_handler.py @@ -1,5 +1,7 @@ from asynctest import mock as async_mock, TestCase as AsyncTestCase + +from ......core.oob_processor import OobMessageProcessor from ......messaging.request_context import RequestContext from ......messaging.responder import MockResponder from ......transport.inbound.receipt import MessageReceipt @@ -12,6 +14,14 @@ class TestCredentialAckHandler(AsyncTestCase): async def test_called(self): request_context = RequestContext.test_context() + + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=async_mock.MagicMock() + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + request_context.message_receipt = MessageReceipt() request_context.connection_record = async_mock.MagicMock() @@ -31,6 +41,9 @@ async def test_called(self): mock_cred_mgr.return_value.receive_credential_ack.assert_called_once_with( request_context.message, request_context.connection_record.connection_id ) + mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( + request_context + ) assert not responder.messages async def test_called_not_ready(self): @@ -48,7 +61,39 @@ async def test_called_not_ready(self): request_context.connection_ready = False handler = test_module.CredentialAckHandler() responder = MockResponder() - with self.assertRaises(test_module.HandlerException): + with self.assertRaises(test_module.HandlerException) as err: + await handler.handle(request_context, responder) + assert ( + err.exception.message == "Connection used for credential ack not ready" + ) + + async def test_called_no_connection_no_oob(self): + request_context = RequestContext.test_context() + request_context.message_receipt = MessageReceipt() + + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + # No oob record found + return_value=None + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + + with async_mock.patch.object( + test_module, "CredentialManager", autospec=True + ) as mock_cred_mgr: + mock_cred_mgr.return_value.receive_credential_ack = ( + async_mock.CoroutineMock() + ) + request_context.message = CredentialAck() + request_context.connection_ready = False + handler = test_module.CredentialAckHandler() + responder = MockResponder() + with self.assertRaises(test_module.HandlerException) as err: await handler.handle(request_context, responder) + assert ( + err.exception.message + == "No connection or associated connectionless exchange found for credential ack" + ) assert not responder.messages diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_issue_handler.py b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_issue_handler.py index f7c8d490c4..0f831927a1 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_issue_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_issue_handler.py @@ -1,5 +1,6 @@ from asynctest import mock as async_mock, TestCase as AsyncTestCase +from ......core.oob_processor import OobMessageProcessor from ......messaging.request_context import RequestContext from ......messaging.responder import MockResponder from ......transport.inbound.receipt import MessageReceipt @@ -16,6 +17,13 @@ async def test_called(self): request_context.settings["debug.auto_store_credential"] = False request_context.connection_record = async_mock.MagicMock() + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=async_mock.MagicMock() + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + with async_mock.patch.object( test_module, "CredentialManager", autospec=True ) as mock_cred_mgr: @@ -30,6 +38,9 @@ async def test_called(self): mock_cred_mgr.return_value.receive_credential.assert_called_once_with( request_context.message, request_context.connection_record.connection_id ) + mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( + request_context + ) assert not responder.messages async def test_called_auto_store(self): @@ -38,6 +49,13 @@ async def test_called_auto_store(self): request_context.settings["debug.auto_store_credential"] = True request_context.connection_record = async_mock.MagicMock() + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=async_mock.MagicMock() + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + with async_mock.patch.object( test_module, "CredentialManager", autospec=True ) as mock_cred_mgr: @@ -61,6 +79,9 @@ async def test_called_auto_store(self): mock_cred_mgr.return_value.receive_credential.assert_called_once_with( request_context.message, request_context.connection_record.connection_id ) + mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( + request_context + ) assert mock_cred_mgr.return_value.send_credential_ack.call_count == 1 async def test_called_auto_store_x(self): @@ -69,6 +90,13 @@ async def test_called_auto_store_x(self): request_context.settings["debug.auto_store_credential"] = True request_context.connection_record = async_mock.MagicMock() + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=async_mock.MagicMock() + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + with async_mock.patch.object( test_module, "CredentialManager", autospec=True ) as mock_cred_mgr: @@ -105,6 +133,7 @@ async def test_called_auto_store_x(self): async def test_called_not_ready(self): request_context = RequestContext.test_context() request_context.message_receipt = MessageReceipt() + request_context.connection_record = async_mock.MagicMock() with async_mock.patch.object( test_module, "CredentialManager", autospec=True @@ -114,7 +143,36 @@ async def test_called_not_ready(self): request_context.connection_ready = False handler = test_module.CredentialIssueHandler() responder = MockResponder() - with self.assertRaises(test_module.HandlerException): + with self.assertRaises(test_module.HandlerException) as err: + await handler.handle(request_context, responder) + assert err.exception.message == "Connection used for credential not ready" + + assert not responder.messages + + async def test_called_no_connection_no_oob(self): + request_context = RequestContext.test_context() + request_context.message_receipt = MessageReceipt() + + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + # No oob record found + return_value=None + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + + with async_mock.patch.object( + test_module, "CredentialManager", autospec=True + ) as mock_cred_mgr: + mock_cred_mgr.return_value.receive_credential = async_mock.CoroutineMock() + request_context.message = CredentialIssue() + handler = test_module.CredentialIssueHandler() + responder = MockResponder() + with self.assertRaises(test_module.HandlerException) as err: await handler.handle(request_context, responder) + assert ( + err.exception.message + == "No connection or associated connectionless exchange found for credential" + ) assert not responder.messages diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_offer_handler.py b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_offer_handler.py index 1716b67210..c2503d91e9 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_offer_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_offer_handler.py @@ -1,5 +1,6 @@ from asynctest import mock as async_mock, TestCase as AsyncTestCase +from ......core.oob_processor import OobMessageProcessor from ......messaging.request_context import RequestContext from ......messaging.responder import MockResponder from ......transport.inbound.receipt import MessageReceipt @@ -16,6 +17,13 @@ async def test_called(self): request_context.settings["debug.auto_respond_credential_offer"] = False request_context.connection_record = async_mock.MagicMock() + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=async_mock.MagicMock() + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + with async_mock.patch.object( test_module, "CredentialManager", autospec=True ) as mock_cred_mgr: @@ -30,6 +38,9 @@ async def test_called(self): mock_cred_mgr.return_value.receive_offer.assert_called_once_with( request_context.message, request_context.connection_record.connection_id ) + mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( + request_context + ) assert not responder.messages async def test_called_auto_request(self): @@ -39,6 +50,13 @@ async def test_called_auto_request(self): request_context.connection_record = async_mock.MagicMock() request_context.connection_record.my_did = "dummy" + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=async_mock.MagicMock() + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + with async_mock.patch.object( test_module, "CredentialManager", autospec=True ) as mock_cred_mgr: @@ -56,6 +74,9 @@ async def test_called_auto_request(self): mock_cred_mgr.return_value.receive_offer.assert_called_once_with( request_context.message, request_context.connection_record.connection_id ) + mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( + request_context + ) messages = responder.messages assert len(messages) == 1 (result, target) = messages[0] @@ -69,6 +90,13 @@ async def test_called_auto_request_x(self): request_context.connection_record = async_mock.MagicMock() request_context.connection_record.my_did = "dummy" + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=async_mock.MagicMock() + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + with async_mock.patch.object( test_module, "CredentialManager", autospec=True ) as mock_cred_mgr: @@ -107,7 +135,40 @@ async def test_called_not_ready(self): request_context.connection_ready = False handler = test_module.CredentialOfferHandler() responder = MockResponder() - with self.assertRaises(test_module.HandlerException): + with self.assertRaises(test_module.HandlerException) as err: + await handler.handle(request_context, responder) + assert ( + err.exception.message + == "Connection used for credential offer not ready" + ) + + assert not responder.messages + + async def test_called_not_ready(self): + request_context = RequestContext.test_context() + request_context.message_receipt = MessageReceipt() + + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + # No oob record found + return_value=None + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + + with async_mock.patch.object( + test_module, "CredentialManager", autospec=True + ) as mock_cred_mgr: + mock_cred_mgr.return_value.receive_offer = async_mock.CoroutineMock() + request_context.message = CredentialOffer() + request_context.connection_ready = False + handler = test_module.CredentialOfferHandler() + responder = MockResponder() + with self.assertRaises(test_module.HandlerException) as err: await handler.handle(request_context, responder) + assert ( + err.exception.message + == "No connection or associated connectionless exchange found for credential offer" + ) assert not responder.messages diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_problem_report_handler.py b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_problem_report_handler.py index ebac8cc496..2187e6258c 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_problem_report_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_problem_report_handler.py @@ -21,6 +21,7 @@ async def test_called(self): with async_mock.patch.object( test_module, "CredentialManager", autospec=True ) as mock_cred_mgr: + request_context.connection_ready = True mock_cred_mgr.return_value.receive_problem_report = ( async_mock.CoroutineMock() ) @@ -48,6 +49,7 @@ async def test_called_x(self): with async_mock.patch.object( test_module, "CredentialManager", autospec=True ) as mock_cred_mgr: + request_context.connection_ready = True mock_cred_mgr.return_value.receive_problem_report = ( async_mock.CoroutineMock( side_effect=test_module.StorageError("Disk full") @@ -68,3 +70,48 @@ async def test_called_x(self): request_context.message, request_context.connection_record.connection_id ) assert not responder.messages + + async def test_called_not_ready(self): + request_context = RequestContext.test_context() + request_context.message_receipt = MessageReceipt() + request_context.connection_record = async_mock.MagicMock() + request_context.connection_ready = False + + request_context.message = CredentialProblemReport( + description={ + "en": "Change of plans", + "code": ProblemReportReason.ISSUANCE_ABANDONED.value, + } + ) + handler = test_module.CredentialProblemReportHandler() + responder = MockResponder() + + with self.assertRaises(test_module.HandlerException) as err: + await handler.handle(request_context, responder) + assert ( + err.exception.message + == "Connection used for credential problem report not ready" + ) + + async def test_called_no_connection(self): + request_context = RequestContext.test_context() + request_context.message_receipt = MessageReceipt() + request_context.connection_record = None + + request_context.message = CredentialProblemReport( + description={ + "en": "Change of plans", + "code": ProblemReportReason.ISSUANCE_ABANDONED.value, + } + ) + handler = test_module.CredentialProblemReportHandler() + responder = MockResponder() + + with self.assertRaises(test_module.HandlerException) as err: + await handler.handle(request_context, responder) + assert ( + err.exception.message + == "Connectionless not supported for credential problem report" + ) + + assert not responder.messages diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_proposal_handler.py b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_proposal_handler.py index 57949df47f..911f056d43 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_proposal_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_proposal_handler.py @@ -109,7 +109,27 @@ async def test_called_not_ready(self): request_context.connection_ready = False handler = test_module.CredentialProposalHandler() responder = MockResponder() - with self.assertRaises(test_module.HandlerException): + with self.assertRaises(test_module.HandlerException) as err: await handler.handle(request_context, responder) + assert ( + err.exception.message + == "Connection used for credential proposal not ready" + ) + + assert not responder.messages + + async def test_called_no_connection(self): + request_context = RequestContext.test_context() + request_context.message_receipt = MessageReceipt() + + request_context.message = CredentialProposal() + handler = test_module.CredentialProposalHandler() + responder = MockResponder() + with self.assertRaises(test_module.HandlerException) as err: + await handler.handle(request_context, responder) + assert ( + err.exception.message + == "Connectionless not supported for credential proposal" + ) assert not responder.messages diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_request_handler.py b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_request_handler.py index bc7b2b8e43..b74bf445ef 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_request_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_request_handler.py @@ -1,5 +1,6 @@ from asynctest import mock as async_mock, TestCase as AsyncTestCase +from ......core.oob_processor import OobMessageProcessor from ......messaging.request_context import RequestContext from ......messaging.responder import MockResponder from ......transport.inbound.receipt import MessageReceipt @@ -19,6 +20,14 @@ async def test_called(self): request_context.message_receipt = MessageReceipt() request_context.connection_record = async_mock.MagicMock() + oob_record = async_mock.MagicMock() + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=oob_record + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + with async_mock.patch.object( test_module, "CredentialManager", autospec=True ) as mock_cred_mgr: @@ -34,7 +43,10 @@ async def test_called(self): mock_cred_mgr.assert_called_once_with(request_context.profile) mock_cred_mgr.return_value.receive_request.assert_called_once_with( - request_context.message, request_context.connection_record.connection_id + request_context.message, request_context.connection_record, oob_record + ) + mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( + request_context ) assert not responder.messages @@ -43,6 +55,14 @@ async def test_called_auto_issue(self): request_context.message_receipt = MessageReceipt() request_context.connection_record = async_mock.MagicMock() + oob_record = async_mock.MagicMock() + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=oob_record + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + ATTR_DICT = {"test": "123", "hello": "world"} cred_ex_rec = V10CredentialExchange( credential_proposal_dict={ @@ -74,7 +94,10 @@ async def test_called_auto_issue(self): mock_cred_mgr.assert_called_once_with(request_context.profile) mock_cred_mgr.return_value.receive_request.assert_called_once_with( - request_context.message, request_context.connection_record.connection_id + request_context.message, request_context.connection_record, oob_record + ) + mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( + request_context ) messages = responder.messages assert len(messages) == 1 @@ -87,6 +110,14 @@ async def test_called_auto_issue_x(self): request_context.message_receipt = MessageReceipt() request_context.connection_record = async_mock.MagicMock() + oob_record = async_mock.MagicMock() + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=oob_record + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + ATTR_DICT = {"test": "123", "hello": "world"} cred_ex_rec = V10CredentialExchange( credential_proposal_dict={ @@ -128,6 +159,14 @@ async def test_called_auto_issue_no_preview(self): request_context.message_receipt = MessageReceipt() request_context.connection_record = async_mock.MagicMock() + oob_record = async_mock.MagicMock() + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=oob_record + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + cred_ex_rec = V10CredentialExchange( credential_proposal_dict={"cred_def_id": CD_ID} ) @@ -152,7 +191,10 @@ async def test_called_auto_issue_no_preview(self): mock_cred_mgr.assert_called_once_with(request_context.profile) mock_cred_mgr.return_value.receive_request.assert_called_once_with( - request_context.message, request_context.connection_record.connection_id + request_context.message, request_context.connection_record, oob_record + ) + mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( + request_context ) assert not responder.messages @@ -169,7 +211,39 @@ async def test_called_not_ready(self): request_context.connection_ready = False handler = test_module.CredentialRequestHandler() responder = MockResponder() - with self.assertRaises(test_module.HandlerException): + with self.assertRaises(test_module.HandlerException) as err: + await handler.handle(request_context, responder) + assert ( + err.exception.message + == "Connection used for credential request not ready" + ) + + assert not responder.messages + + async def test_called_no_connection_no_oob(self): + request_context = RequestContext.test_context() + request_context.message_receipt = MessageReceipt() + + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + # No oob record found + return_value=None + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + + with async_mock.patch.object( + test_module, "CredentialManager", autospec=True + ) as mock_cred_mgr: + mock_cred_mgr.return_value.receive_request = async_mock.CoroutineMock() + request_context.message = CredentialRequest() + handler = test_module.CredentialRequestHandler() + responder = MockResponder() + with self.assertRaises(test_module.HandlerException) as err: await handler.handle(request_context, responder) + assert ( + err.exception.message + == "No connection or associated connectionless exchange found for credential request" + ) assert not responder.messages diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py index 6e3df6387e..ddee6d3a1d 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py @@ -6,6 +6,8 @@ from asynctest import mock as async_mock, TestCase as AsyncTestCase +from aries_cloudagent.messaging.decorators.thread_decorator import ThreadDecorator + from .....core.in_memory import InMemoryProfile from .....cache.base import BaseCache from .....cache.in_memory import InMemoryCache @@ -565,6 +567,7 @@ async def test_receive_offer_proposed(self): assert exchange.role == V10CredentialExchange.ROLE_HOLDER assert exchange.state == V10CredentialExchange.STATE_OFFER_RECEIVED assert exchange._credential_offer.ser == INDY_OFFER + assert exchange.credential_offer_dict == offer proposal = exchange.credential_proposal_dict assert proposal.credential_proposal.attributes == preview.attributes @@ -604,12 +607,18 @@ async def test_receive_free_offer(self): assert exchange.state == V10CredentialExchange.STATE_OFFER_RECEIVED assert exchange._credential_offer.ser == INDY_OFFER assert exchange.credential_proposal_dict + assert exchange.credential_offer_dict == offer async def test_create_request(self): connection_id = "test_conn_id" thread_id = "thread-id" holder_did = "did" + credential_offer_dict = CredentialOffer( + "thread-id", + ) + credential_offer_dict._thread = ThreadDecorator(pthid="some-pthid") + stored_exchange = V10CredentialExchange( credential_exchange_id="dummy-cxid", connection_id=connection_id, @@ -618,6 +627,7 @@ async def test_create_request(self): initiator=V10CredentialExchange.INITIATOR_SELF, role=V10CredentialExchange.ROLE_HOLDER, state=V10CredentialExchange.STATE_OFFER_RECEIVED, + credential_offer_dict=credential_offer_dict, schema_id=SCHEMA_ID, thread_id=thread_id, new_with_id=True, @@ -668,17 +678,24 @@ async def test_create_request(self): ret_existing_request, ) = await self.manager.create_request(stored_exchange, holder_did) assert ret_existing_request._thread_id == thread_id + assert ret_existing_request._thread.pthid == "some-pthid" async def test_create_request_no_cache(self): connection_id = "test_conn_id" thread_id = "thread-id" holder_did = "did" + credential_offer_dict = CredentialOffer( + "thread-id", + ) + credential_offer_dict._thread = ThreadDecorator(pthid="some-pthid") + stored_exchange = V10CredentialExchange( credential_exchange_id="dummy-cxid", connection_id=connection_id, credential_definition_id=CRED_DEF_ID, credential_offer=INDY_OFFER, + credential_offer_dict=credential_offer_dict, initiator=V10CredentialExchange.INITIATOR_SELF, role=V10CredentialExchange.ROLE_HOLDER, state=V10CredentialExchange.STATE_OFFER_RECEIVED, @@ -713,6 +730,7 @@ async def test_create_request_no_cache(self): assert ret_request.indy_cred_req() == INDY_CRED_REQ assert ret_request._thread_id == thread_id + assert ret_request._thread.pthid == "some-pthid" assert ret_exchange.state == V10CredentialExchange.STATE_REQUEST_SENT @@ -739,11 +757,11 @@ async def test_create_request_bad_state(self): await self.manager.create_request(stored_exchange, holder_did) async def test_receive_request(self): - connection_id = "test_conn_id" + mock_conn = async_mock.MagicMock(connection_id="test_conn_id") stored_exchange = V10CredentialExchange( credential_exchange_id="dummy-cxid", - connection_id=connection_id, + connection_id=mock_conn.connection_id, initiator=V10CredentialExchange.INITIATOR_EXTERNAL, role=V10CredentialExchange.ROLE_ISSUER, state=V10CredentialExchange.STATE_OFFER_SENT, @@ -762,10 +780,14 @@ async def test_receive_request(self): "retrieve_by_connection_and_thread", async_mock.CoroutineMock(return_value=stored_exchange), ) as retrieve_ex: - exchange = await self.manager.receive_request(request, connection_id) + exchange = await self.manager.receive_request(request, mock_conn, None) retrieve_ex.assert_called_once_with( - self.session, connection_id, request._thread_id, for_update=True + self.session, + "test_conn_id", + request._thread_id, + role=V10CredentialExchange.ROLE_ISSUER, + for_update=True, ) save_ex.assert_called_once() @@ -786,26 +808,26 @@ async def test_receive_request_no_connection_cred_request(self): requests_attach=[CredentialRequest.wrap_indy_cred_req(INDY_CRED_REQ)] ) + mock_conn = async_mock.MagicMock( + connection_id="test_conn_id", + ) + mock_oob = async_mock.MagicMock() + with async_mock.patch.object( V10CredentialExchange, "save", autospec=True ) as mock_save, async_mock.patch.object( V10CredentialExchange, "retrieve_by_connection_and_thread", async_mock.CoroutineMock(), - ) as mock_retrieve, async_mock.patch.object( - V10CredentialExchange, "retrieve_by_tag_filter", async_mock.CoroutineMock() - ) as mock_retrieve_tag_filter: - mock_retrieve.side_effect = (StorageNotFoundError(),) - mock_retrieve_tag_filter.return_value = stored_exchange - cx_rec = await self.manager.receive_request(request, "test_conn_id") + ) as mock_retrieve: + mock_retrieve.return_value = stored_exchange + cx_rec = await self.manager.receive_request(request, mock_conn, mock_oob) mock_retrieve.assert_called_once_with( - self.session, "test_conn_id", request._thread_id, for_update=True - ) - mock_retrieve_tag_filter.assert_called_once_with( self.session, - {"thread_id": request._thread_id}, - {"connection_id": None}, + None, + request._thread_id, + role=V10CredentialExchange.ROLE_ISSUER, for_update=True, ) mock_save.assert_called_once() @@ -827,27 +849,26 @@ async def test_receive_request_no_cred_ex_with_offer_found(self): requests_attach=[CredentialRequest.wrap_indy_cred_req(INDY_CRED_REQ)] ) + mock_conn = async_mock.MagicMock( + connection_id="test_conn_id", + ) + with async_mock.patch.object( V10CredentialExchange, "save", autospec=True ) as mock_save, async_mock.patch.object( V10CredentialExchange, "retrieve_by_connection_and_thread", async_mock.CoroutineMock(), - ) as mock_retrieve, async_mock.patch.object( - V10CredentialExchange, "retrieve_by_tag_filter", async_mock.CoroutineMock() - ) as mock_retrieve_tag_filter: + ) as mock_retrieve: mock_retrieve.side_effect = (StorageNotFoundError(),) - mock_retrieve_tag_filter.side_effect = (StorageNotFoundError(),) with self.assertRaises(CredentialManagerError): - cx_rec = await self.manager.receive_request(request, "test_conn_id") + cx_rec = await self.manager.receive_request(request, mock_conn, None) mock_retrieve.assert_called_once_with( - self.session, "test_conn_id", request._thread_id, for_update=True - ) - mock_retrieve_tag_filter.assert_called_once_with( self.session, - {"thread_id": request._thread_id}, - {"connection_id": None}, + "test_conn_id", + request._thread_id, + role=V10CredentialExchange.ROLE_ISSUER, for_update=True, ) @@ -1332,7 +1353,7 @@ async def test_receive_credential(self): credential_exchange_id="dummy-cxid", connection_id=connection_id, initiator=V10CredentialExchange.INITIATOR_EXTERNAL, - role=V10CredentialExchange.ROLE_ISSUER, + role=V10CredentialExchange.ROLE_HOLDER, state=V10CredentialExchange.STATE_REQUEST_SENT, new_with_id=True, ) @@ -1352,7 +1373,11 @@ async def test_receive_credential(self): exchange = await self.manager.receive_credential(issue, connection_id) retrieve_ex.assert_called_once_with( - self.session, connection_id, issue._thread_id, for_update=True + self.session, + connection_id, + issue._thread_id, + role=V10CredentialExchange.ROLE_HOLDER, + for_update=True, ) save_ex.assert_called_once() @@ -1653,7 +1678,11 @@ async def test_receive_credential_ack(self): ret_exchange = await self.manager.receive_credential_ack(ack, connection_id) retrieve_ex.assert_called_once_with( - self.session, connection_id, ack._thread_id, for_update=True + self.session, + connection_id, + ack._thread_id, + role=V10CredentialExchange.ROLE_ISSUER, + for_update=True, ) save_ex.assert_called_once() diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_routes.py index 280f19ce05..1afc367d20 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_routes.py @@ -1,10 +1,7 @@ from asynctest import mock as async_mock, TestCase as AsyncTestCase from .....admin.request_context import AdminRequestContext -from .....wallet.key_type import KeyType -from .....wallet.did_method import DIDMethod from .....wallet.base import BaseWallet -from .....wallet.did_info import DIDInfo from .. import routes as test_module @@ -900,6 +897,50 @@ async def test_credential_exchange_send_request(self): mock_cred_ex_record.serialize.return_value ) + async def test_credential_exchange_send_request_no_conn(self): + self.request.json = async_mock.CoroutineMock() + self.request.match_info = {"cred_ex_id": "dummy"} + + with async_mock.patch.object( + test_module, "OobRecord", autospec=True + ) as mock_oob_rec, async_mock.patch.object( + test_module, "default_did_from_verkey", autospec=True + ) as mock_default_did_from_verkey, async_mock.patch.object( + test_module, "CredentialManager", autospec=True + ) as mock_credential_manager, async_mock.patch.object( + test_module, "V10CredentialExchange", autospec=True + ) as mock_cred_ex, async_mock.patch.object( + test_module.web, "json_response" + ) as mock_response: + + mock_oob_rec.retrieve_by_tag_filter = async_mock.CoroutineMock( + return_value=async_mock.MagicMock(our_recipient_key="our-recipient_key") + ) + mock_default_did_from_verkey.return_value = "holder-did" + + mock_cred_ex.retrieve_by_id = async_mock.CoroutineMock() + mock_cred_ex.retrieve_by_id.return_value.state = ( + mock_cred_ex.STATE_OFFER_RECEIVED + ) + mock_cred_ex.retrieve_by_id.return_value.connection_id = None + + mock_cred_ex_record = async_mock.MagicMock() + + mock_credential_manager.return_value.create_request.return_value = ( + mock_cred_ex_record, + async_mock.MagicMock(), + ) + + await test_module.credential_exchange_send_request(self.request) + + mock_credential_manager.return_value.create_request.assert_called_once_with( + mock_cred_ex.retrieve_by_id.return_value, "holder-did" + ) + mock_response.assert_called_once_with( + mock_cred_ex_record.serialize.return_value + ) + mock_default_did_from_verkey.assert_called_once_with("our-recipient_key") + async def test_credential_exchange_send_request_bad_cred_ex_id(self): self.request.json = async_mock.CoroutineMock() self.request.match_info = {"cred_ex_id": "dummy"} From daadb2065390e56984f8f58cf67391dc7ffa65c2 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sun, 3 Apr 2022 15:28:27 +0200 Subject: [PATCH 157/872] test: fix issue credential v2 tests Signed-off-by: Timo Glastra --- .../tests/test_credential_offer_handler.py | 2 +- .../v1_0/tests/test_manager.py | 3 +- .../v2_0/handlers/cred_ack_handler.py | 3 +- .../v2_0/handlers/cred_offer_handler.py | 3 +- .../v2_0/handlers/cred_request_handler.py | 7 ++- .../handlers/tests/test_cred_ack_handler.py | 40 ++++++++++++- .../handlers/tests/test_cred_issue_handler.py | 56 +++++++++++++++++- .../handlers/tests/test_cred_offer_handler.py | 59 ++++++++++++++++++- .../tests/test_cred_problem_report_handler.py | 47 +++++++++++++++ .../tests/test_cred_proposal_handler.py | 22 ++++++- .../tests/test_cred_request_handler.py | 59 ++++++++++++++++++- .../v2_0/tests/test_manager.py | 57 ++++++++++-------- .../v2_0/tests/test_routes.py | 44 ++++++++++++++ 13 files changed, 363 insertions(+), 39 deletions(-) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_offer_handler.py b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_offer_handler.py index c2503d91e9..a9aedbb0a9 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_offer_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_offer_handler.py @@ -144,7 +144,7 @@ async def test_called_not_ready(self): assert not responder.messages - async def test_called_not_ready(self): + async def test_no_conn_no_oob(self): request_context = RequestContext.test_context() request_context.message_receipt = MessageReceipt() diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py index ddee6d3a1d..270692eb55 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py @@ -6,13 +6,12 @@ from asynctest import mock as async_mock, TestCase as AsyncTestCase -from aries_cloudagent.messaging.decorators.thread_decorator import ThreadDecorator - from .....core.in_memory import InMemoryProfile from .....cache.base import BaseCache from .....cache.in_memory import InMemoryCache from .....indy.holder import IndyHolder from .....indy.issuer import IndyIssuer +from .....messaging.decorators.thread_decorator import ThreadDecorator from .....messaging.credential_definitions.util import CRED_DEF_SENT_RECORD_TYPE from .....messaging.responder import BaseResponder, MockResponder from .....ledger.base import BaseLedger diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_ack_handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_ack_handler.py index 818a3ec3a9..9c580fd01d 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_ack_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_ack_handler.py @@ -41,7 +41,8 @@ async def handle(self, context: RequestContext, responder: BaseResponder): # Either connection or oob context must be present if not context.connection_record and not oob_record: raise HandlerException( - "No connection or associated connectionless exchange found for credential ack" + "No connection or associated connectionless exchange found for credential" + " ack" ) cred_manager = V20CredManager(context.profile) diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_offer_handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_offer_handler.py index cf849ce393..9fc223c4c0 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_offer_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_offer_handler.py @@ -49,7 +49,8 @@ async def handle(self, context: RequestContext, responder: BaseResponder): # Either connection or oob context must be present if not context.connection_record and not oob_record: raise HandlerException( - "No connection or associated connectionless exchange found for credential offer" + "No connection or associated connectionless exchange found for credential" + " offer" ) connection_id = ( diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_request_handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_request_handler.py index 6ea02cbddb..1bdf7671e9 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_request_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/handlers/cred_request_handler.py @@ -41,15 +41,16 @@ async def handle(self, context: RequestContext, responder: BaseResponder): if context.connection_record and not context.connection_ready: raise HandlerException("Connection used for credential request not ready") - # Find associated oob record. If the credential offer was created as an oob attachment - # the presentation exchange record won't have a connection id (yet) + # Find associated oob record. If the credential offer was created as an oob + # attachment the presentation exchange record won't have a connection id (yet) oob_processor = context.inject(OobMessageProcessor) oob_record = await oob_processor.find_oob_record_for_inbound_message(context) # Either connection or oob context must be present if not context.connection_record and not oob_record: raise HandlerException( - "No connection or associated connectionless exchange found for credential request" + "No connection or associated connectionless exchange found for credential" + " request" ) profile = context.profile diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/handlers/tests/test_cred_ack_handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/handlers/tests/test_cred_ack_handler.py index 8d047435b4..f4283168fa 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/handlers/tests/test_cred_ack_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/handlers/tests/test_cred_ack_handler.py @@ -1,5 +1,6 @@ from asynctest import mock as async_mock, TestCase as AsyncTestCase +from ......core.oob_processor import OobMessageProcessor from ......messaging.request_context import RequestContext from ......messaging.responder import MockResponder from ......transport.inbound.receipt import MessageReceipt @@ -15,6 +16,13 @@ async def test_called(self): request_context.message_receipt = MessageReceipt() request_context.connection_record = async_mock.MagicMock() + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=async_mock.MagicMock() + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + with async_mock.patch.object( test_module, "V20CredManager", autospec=True ) as mock_cred_mgr: @@ -32,6 +40,9 @@ async def test_called(self): request_context.message, request_context.connection_record.connection_id, ) + mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( + request_context + ) assert not responder.messages async def test_called_not_ready(self): @@ -47,7 +58,34 @@ async def test_called_not_ready(self): request_context.connection_ready = False handler = test_module.V20CredAckHandler() responder = MockResponder() - with self.assertRaises(test_module.HandlerException): + with self.assertRaises(test_module.HandlerException) as err: await handler.handle(request_context, responder) + assert ( + err.exception.message == "Connection used for credential ack not ready" + ) + + assert not responder.messages + + async def test_called_no_connection_no_oob(self): + request_context = RequestContext.test_context() + request_context.message_receipt = MessageReceipt() + + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + # No oob record found + return_value=None + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + + request_context.message = V20CredAck() + handler = test_module.V20CredAckHandler() + responder = MockResponder() + with self.assertRaises(test_module.HandlerException) as err: + await handler.handle(request_context, responder) + assert ( + err.exception.message + == "No connection or associated connectionless exchange found for credential ack" + ) assert not responder.messages diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/handlers/tests/test_cred_issue_handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/handlers/tests/test_cred_issue_handler.py index 26a848ba5e..2a47af1d5d 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/handlers/tests/test_cred_issue_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/handlers/tests/test_cred_issue_handler.py @@ -1,5 +1,6 @@ from asynctest import mock as async_mock, TestCase as AsyncTestCase +from ......core.oob_processor import OobMessageProcessor from ......messaging.request_context import RequestContext from ......messaging.responder import MockResponder from ......transport.inbound.receipt import MessageReceipt @@ -16,6 +17,13 @@ async def test_called(self): request_context.settings["debug.auto_store_credential"] = False request_context.connection_record = async_mock.MagicMock() + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=async_mock.MagicMock() + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + with async_mock.patch.object( test_module, "V20CredManager", autospec=True ) as mock_cred_mgr: @@ -30,6 +38,9 @@ async def test_called(self): mock_cred_mgr.return_value.receive_credential.assert_called_once_with( request_context.message, request_context.connection_record.connection_id ) + mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( + request_context + ) assert not responder.messages async def test_called_auto_store(self): @@ -38,6 +49,13 @@ async def test_called_auto_store(self): request_context.settings["debug.auto_store_credential"] = True request_context.connection_record = async_mock.MagicMock() + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=async_mock.MagicMock() + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + with async_mock.patch.object( test_module, "V20CredManager", autospec=True ) as mock_cred_mgr: @@ -56,6 +74,9 @@ async def test_called_auto_store(self): mock_cred_mgr.return_value.receive_credential.assert_called_once_with( request_context.message, request_context.connection_record.connection_id ) + mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( + request_context + ) assert mock_cred_mgr.return_value.send_cred_ack.call_count == 1 async def test_called_auto_store_x(self): @@ -64,6 +85,13 @@ async def test_called_auto_store_x(self): request_context.settings["debug.auto_store_credential"] = True request_context.connection_record = async_mock.MagicMock() + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=async_mock.MagicMock() + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + with async_mock.patch.object( test_module, "V20CredManager", autospec=True ) as mock_cred_mgr: @@ -94,6 +122,7 @@ async def test_called_auto_store_x(self): async def test_called_not_ready(self): request_context = RequestContext.test_context() request_context.message_receipt = MessageReceipt() + request_context.connection_record = async_mock.MagicMock() with async_mock.patch.object( test_module, "V20CredManager", autospec=True @@ -103,7 +132,32 @@ async def test_called_not_ready(self): request_context.connection_ready = False handler_inst = test_module.V20CredIssueHandler() responder = MockResponder() - with self.assertRaises(test_module.HandlerException): + with self.assertRaises(test_module.HandlerException) as err: await handler_inst.handle(request_context, responder) + assert err.exception.message == "Connection used for credential not ready" + + assert not responder.messages + + async def test_called_no_connection_no_oob(self): + request_context = RequestContext.test_context() + request_context.message_receipt = MessageReceipt() + + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + # No oob record found + return_value=None + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + + request_context.message = V20CredIssue() + handler_inst = test_module.V20CredIssueHandler() + responder = MockResponder() + with self.assertRaises(test_module.HandlerException) as err: + await handler_inst.handle(request_context, responder) + assert ( + err.exception.message + == "No connection or associated connectionless exchange found for credential" + ) assert not responder.messages diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/handlers/tests/test_cred_offer_handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/handlers/tests/test_cred_offer_handler.py index 9bd47ff2e5..66e295ce34 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/handlers/tests/test_cred_offer_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/handlers/tests/test_cred_offer_handler.py @@ -1,5 +1,6 @@ from asynctest import mock as async_mock, TestCase as AsyncTestCase +from ......core.oob_processor import OobMessageProcessor from ......messaging.request_context import RequestContext from ......messaging.responder import MockResponder from ......transport.inbound.receipt import MessageReceipt @@ -16,6 +17,13 @@ async def test_called(self): request_context.settings["debug.auto_respond_credential_offer"] = False request_context.connection_record = async_mock.MagicMock() + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=async_mock.MagicMock() + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + with async_mock.patch.object( test_module, "V20CredManager", autospec=True ) as mock_cred_mgr: @@ -30,6 +38,9 @@ async def test_called(self): mock_cred_mgr.return_value.receive_offer.assert_called_once_with( request_context.message, request_context.connection_record.connection_id ) + mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( + request_context + ) assert not responder.messages async def test_called_auto_request(self): @@ -39,6 +50,13 @@ async def test_called_auto_request(self): request_context.connection_record = async_mock.MagicMock() request_context.connection_record.my_did = "dummy" + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=async_mock.MagicMock() + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + with async_mock.patch.object( test_module, "V20CredManager", autospec=True ) as mock_cred_mgr: @@ -56,6 +74,9 @@ async def test_called_auto_request(self): mock_cred_mgr.return_value.receive_offer.assert_called_once_with( request_context.message, request_context.connection_record.connection_id ) + mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( + request_context + ) messages = responder.messages assert len(messages) == 1 (result, target) = messages[0] @@ -69,6 +90,13 @@ async def test_called_auto_request_x(self): request_context.connection_record = async_mock.MagicMock() request_context.connection_record.my_did = "dummy" + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=async_mock.MagicMock() + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + with async_mock.patch.object( test_module, "V20CredManager", autospec=True ) as mock_cred_mgr: @@ -107,7 +135,36 @@ async def test_called_not_ready(self): request_context.connection_ready = False handler_inst = test_module.V20CredOfferHandler() responder = MockResponder() - with self.assertRaises(test_module.HandlerException): + with self.assertRaises(test_module.HandlerException) as err: await handler_inst.handle(request_context, responder) + assert ( + err.exception.message + == "Connection used for credential offer not ready" + ) + + assert not responder.messages + + async def test_no_conn_no_oob(self): + request_context = RequestContext.test_context() + request_context.message_receipt = MessageReceipt() + + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + # No oob record found + return_value=None + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + + request_context.message = V20CredOffer() + request_context.connection_ready = False + handler_inst = test_module.V20CredOfferHandler() + responder = MockResponder() + with self.assertRaises(test_module.HandlerException) as err: + await handler_inst.handle(request_context, responder) + assert ( + err.exception.message + == "No connection or associated connectionless exchange found for credential offer" + ) assert not responder.messages diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/handlers/tests/test_cred_problem_report_handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/handlers/tests/test_cred_problem_report_handler.py index 54891e72ac..fa5195fce9 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/handlers/tests/test_cred_problem_report_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/handlers/tests/test_cred_problem_report_handler.py @@ -21,6 +21,7 @@ async def test_called(self): mock_cred_mgr.return_value.receive_problem_report = ( async_mock.CoroutineMock() ) + request_context.connection_ready = True request_context.message = V20CredProblemReport( description={ "en": "oh no", @@ -45,6 +46,7 @@ async def test_called_x(self): with async_mock.patch.object( test_module, "V20CredManager", autospec=True ) as mock_cred_mgr: + request_context.connection_ready = True mock_cred_mgr.return_value.receive_problem_report = ( async_mock.CoroutineMock( side_effect=test_module.StorageError("Disk full") @@ -65,3 +67,48 @@ async def test_called_x(self): request_context.message, request_context.connection_record.connection_id ) assert not responder.messages + + async def test_called_not_ready(self): + request_context = RequestContext.test_context() + request_context.message_receipt = MessageReceipt() + request_context.connection_record = async_mock.MagicMock() + request_context.connection_ready = False + + request_context.message = V20CredProblemReport( + description={ + "en": "Change of plans", + "code": ProblemReportReason.ISSUANCE_ABANDONED.value, + } + ) + handler = test_module.CredProblemReportHandler() + responder = MockResponder() + + with self.assertRaises(test_module.HandlerException) as err: + await handler.handle(request_context, responder) + assert ( + err.exception.message + == "Connection used for credential problem report not ready" + ) + + async def test_called_no_connection(self): + request_context = RequestContext.test_context() + request_context.message_receipt = MessageReceipt() + request_context.connection_record = None + + request_context.message = V20CredProblemReport( + description={ + "en": "Change of plans", + "code": ProblemReportReason.ISSUANCE_ABANDONED.value, + } + ) + handler = test_module.CredProblemReportHandler() + responder = MockResponder() + + with self.assertRaises(test_module.HandlerException) as err: + await handler.handle(request_context, responder) + assert ( + err.exception.message + == "Connectionless not supported for credential problem report" + ) + + assert not responder.messages diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/handlers/tests/test_cred_proposal_handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/handlers/tests/test_cred_proposal_handler.py index bcbe9373d3..daaf4c0d79 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/handlers/tests/test_cred_proposal_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/handlers/tests/test_cred_proposal_handler.py @@ -109,7 +109,27 @@ async def test_called_not_ready(self): request_context.connection_ready = False handler_inst = test_module.V20CredProposalHandler() responder = MockResponder() - with self.assertRaises(test_module.HandlerException): + with self.assertRaises(test_module.HandlerException) as err: await handler_inst.handle(request_context, responder) + assert ( + err.exception.message + == "Connection used for credential proposal not ready" + ) + + assert not responder.messages + + async def test_called_no_connection(self): + request_context = RequestContext.test_context() + request_context.message_receipt = MessageReceipt() + + request_context.message = V20CredProposal() + handler_inst = test_module.V20CredProposalHandler() + responder = MockResponder() + with self.assertRaises(test_module.HandlerException) as err: + await handler_inst.handle(request_context, responder) + assert ( + err.exception.message + == "Connectionless not supported for credential proposal" + ) assert not responder.messages diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/handlers/tests/test_cred_request_handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/handlers/tests/test_cred_request_handler.py index 9df3a5288a..25edba8b36 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/handlers/tests/test_cred_request_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/handlers/tests/test_cred_request_handler.py @@ -1,5 +1,6 @@ from asynctest import mock as async_mock, TestCase as AsyncTestCase +from ......core.oob_processor import OobMessageProcessor from ......messaging.request_context import RequestContext from ......messaging.responder import MockResponder from ......transport.inbound.receipt import MessageReceipt @@ -18,6 +19,14 @@ async def test_called(self): request_context.message_receipt = MessageReceipt() request_context.connection_record = async_mock.MagicMock() + oob_record = async_mock.MagicMock() + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=oob_record + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + with async_mock.patch.object( test_module, "V20CredManager", autospec=True ) as mock_cred_mgr: @@ -33,7 +42,7 @@ async def test_called(self): mock_cred_mgr.assert_called_once_with(request_context.profile) mock_cred_mgr.return_value.receive_request.assert_called_once_with( - request_context.message, request_context.connection_record.connection_id + request_context.message, request_context.connection_record, oob_record ) assert not responder.messages @@ -42,6 +51,14 @@ async def test_called_auto_issue(self): request_context.message_receipt = MessageReceipt() request_context.connection_record = async_mock.MagicMock() + oob_record = async_mock.MagicMock() + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=oob_record + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + cred_ex_rec = V20CredExRecord() with async_mock.patch.object( @@ -65,7 +82,7 @@ async def test_called_auto_issue(self): mock_cred_mgr.assert_called_once_with(request_context.profile) mock_cred_mgr.return_value.receive_request.assert_called_once_with( - request_context.message, request_context.connection_record.connection_id + request_context.message, request_context.connection_record, oob_record ) messages = responder.messages assert len(messages) == 1 @@ -80,6 +97,14 @@ async def test_called_auto_issue_x(self): cred_ex_rec = V20CredExRecord() + oob_record = async_mock.MagicMock() + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=oob_record + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + with async_mock.patch.object( test_module, "V20CredManager", autospec=True ) as mock_cred_mgr, async_mock.patch.object( @@ -119,7 +144,35 @@ async def test_called_not_ready(self): request_context.connection_ready = False handler = test_module.V20CredRequestHandler() responder = MockResponder() - with self.assertRaises(test_module.HandlerException): + with self.assertRaises(test_module.HandlerException) as err: await handler.handle(request_context, responder) + assert ( + err.exception.message + == "Connection used for credential request not ready" + ) + + assert not responder.messages + + async def test_called_no_connection_no_oob(self): + request_context = RequestContext.test_context() + request_context.message_receipt = MessageReceipt() + + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + # No oob record found + return_value=None + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + + request_context.message = V20CredRequest() + handler = test_module.V20CredRequestHandler() + responder = MockResponder() + with self.assertRaises(test_module.HandlerException) as err: + await handler.handle(request_context, responder) + assert ( + err.exception.message + == "No connection or associated connectionless exchange found for credential request" + ) assert not responder.messages diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/tests/test_manager.py b/aries_cloudagent/protocols/issue_credential/v2_0/tests/test_manager.py index e941710107..583f52e9c5 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/tests/test_manager.py @@ -10,6 +10,7 @@ from .....cache.in_memory import InMemoryCache from .....core.in_memory import InMemoryProfile from .....indy.issuer import IndyIssuer +from .....messaging.decorators.thread_decorator import ThreadDecorator from .....messaging.decorators.attach_decorator import AttachDecorator from .....messaging.responder import BaseResponder, MockResponder from .....ledger.base import BaseLedger @@ -693,9 +694,13 @@ async def test_create_free_request(self): filters_attach=[AttachDecorator.data_base64(LD_PROOF_VC_DETAIL, ident="0")], ) + cred_offer = V20CredOffer(thread_id) + cred_offer._thread = ThreadDecorator(pthid="some-pthid") + stored_cx_rec = V20CredExRecord( cred_ex_id="dummy-cxid", connection_id=connection_id, + cred_offer=cred_offer, initiator=V20CredExRecord.INITIATOR_SELF, role=V20CredExRecord.ROLE_HOLDER, thread_id=thread_id, @@ -735,6 +740,7 @@ async def test_create_free_request(self): assert ret_cred_req.attachment() == LD_PROOF_VC_DETAIL assert ret_cred_req._thread_id == thread_id + assert ret_cred_req._thread.pthid == "some-pthid" assert ret_cx_rec.state == V20CredExRecord.STATE_REQUEST_SENT @@ -757,10 +763,10 @@ async def test_create_request_bad_state(self): assert " state " in str(context.exception) async def test_receive_request(self): - connection_id = "test_conn_id" + mock_conn = async_mock.MagicMock(connection_id="test_conn_id") stored_cx_rec = V20CredExRecord( cred_ex_id="dummy-cxid", - connection_id=connection_id, + connection_id=mock_conn.connection_id, initiator=V20CredExRecord.INITIATOR_EXTERNAL, role=V20CredExRecord.ROLE_ISSUER, state=V20CredExRecord.STATE_OFFER_SENT, @@ -788,10 +794,13 @@ async def test_receive_request(self): mock_handler.return_value.receive_request = async_mock.CoroutineMock() # mock_retrieve.return_value = stored_cx_rec - cx_rec = await self.manager.receive_request(cred_request, connection_id) + cx_rec = await self.manager.receive_request(cred_request, mock_conn, None) mock_retrieve.assert_called_once_with( - self.session, connection_id, cred_request._thread_id + self.session, + "test_conn_id", + cred_request._thread_id, + role=V20CredExRecord.ROLE_ISSUER, ) mock_handler.return_value.receive_request.assert_called_once_with( cx_rec, cred_request @@ -821,28 +830,28 @@ async def test_receive_request_no_connection_cred_request(self): requests_attach=[AttachDecorator.data_base64(INDY_CRED_REQ, ident="0")], ) + mock_conn = async_mock.MagicMock(connection_id="test_conn_id") + mock_oob = async_mock.MagicMock() + with async_mock.patch.object( V20CredExRecord, "save", autospec=True ) as mock_save, async_mock.patch.object( V20CredExRecord, "retrieve_by_conn_and_thread", async_mock.CoroutineMock() ) as mock_retrieve, async_mock.patch.object( - V20CredExRecord, "retrieve_by_tag_filter", async_mock.CoroutineMock() - ) as mock_retrieve_tag_filter, async_mock.patch.object( V20CredFormat.Format, "handler" ) as mock_handler: - mock_retrieve.side_effect = (StorageNotFoundError(),) - mock_retrieve_tag_filter.return_value = stored_cx_rec + mock_retrieve.return_value = stored_cx_rec mock_handler.return_value.receive_request = async_mock.CoroutineMock() - cx_rec = await self.manager.receive_request(cred_request, "test_conn_id") + cx_rec = await self.manager.receive_request( + cred_request, mock_conn, mock_oob + ) mock_retrieve.assert_called_once_with( - self.session, "test_conn_id", cred_request._thread_id - ) - mock_retrieve_tag_filter.assert_called_once_with( self.session, - {"thread_id": cred_request._thread_id}, - {"connection_id": None}, + None, + cred_request._thread_id, + role=V20CredExRecord.ROLE_ISSUER, ) mock_handler.return_value.receive_request.assert_called_once_with( cx_rec, cred_request @@ -853,6 +862,7 @@ async def test_receive_request_no_connection_cred_request(self): assert cx_rec.connection_id == "test_conn_id" async def test_receive_request_no_cred_ex_with_offer_found(self): + mock_conn = async_mock.MagicMock(connection_id="test_conn_id") stored_cx_rec = V20CredExRecord( cred_ex_id="dummy-cxid", initiator=V20CredExRecord.INITIATOR_EXTERNAL, @@ -877,23 +887,18 @@ async def test_receive_request_no_cred_ex_with_offer_found(self): ) as mock_save, async_mock.patch.object( V20CredExRecord, "retrieve_by_conn_and_thread", async_mock.CoroutineMock() ) as mock_retrieve, async_mock.patch.object( - V20CredExRecord, "retrieve_by_tag_filter", async_mock.CoroutineMock() - ) as mock_retrieve_tag_filter, async_mock.patch.object( V20CredFormat.Format, "handler" ) as mock_handler: mock_retrieve.side_effect = (StorageNotFoundError(),) - mock_retrieve_tag_filter.side_effect = (StorageNotFoundError(),) mock_handler.return_value.receive_request = async_mock.CoroutineMock() - cx_rec = await self.manager.receive_request(cred_request, "test_conn_id") + cx_rec = await self.manager.receive_request(cred_request, mock_conn, None) mock_retrieve.assert_called_once_with( - self.session, "test_conn_id", cred_request._thread_id - ) - mock_retrieve_tag_filter.assert_called_once_with( self.session, - {"thread_id": cred_request._thread_id}, - {"connection_id": None}, + "test_conn_id", + cred_request._thread_id, + role=V20CredExRecord.ROLE_ISSUER, ) mock_handler.return_value.receive_request.assert_called_once_with( cx_rec, cred_request @@ -1103,7 +1108,10 @@ async def test_receive_cred(self): ) mock_retrieve.assert_called_once_with( - self.session, connection_id, cred_issue._thread_id + self.session, + connection_id, + cred_issue._thread_id, + role=V20CredExRecord.ROLE_HOLDER, ) mock_save.assert_called_once() mock_handler.return_value.receive_credential.assert_called_once_with( @@ -1331,6 +1339,7 @@ async def test_receive_cred_ack(self): self.session, connection_id, ack._thread_id, + role=V20CredExRecord.ROLE_ISSUER, ) mock_save.assert_called_once() diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/tests/test_routes.py b/aries_cloudagent/protocols/issue_credential/v2_0/tests/test_routes.py index 23c00f4b3f..3c72c33e75 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/tests/test_routes.py @@ -351,6 +351,50 @@ async def test_credential_exchange_send(self): mock_response.assert_called_once_with(mock_cx_rec.serialize.return_value) + async def test_credential_exchange_send_request_no_conn_no_holder_did(self): + self.request.json = async_mock.CoroutineMock(return_value={}) + self.request.match_info = {"cred_ex_id": "dummy"} + + with async_mock.patch.object( + test_module, "OobRecord", autospec=True + ) as mock_oob_rec, async_mock.patch.object( + test_module, "default_did_from_verkey", autospec=True + ) as mock_default_did_from_verkey, async_mock.patch.object( + test_module, "V20CredManager", autospec=True + ) as mock_cred_mgr, async_mock.patch.object( + test_module, "V20CredExRecord", autospec=True + ) as mock_cred_ex, async_mock.patch.object( + test_module.web, "json_response" + ) as mock_response: + + mock_oob_rec.retrieve_by_tag_filter = async_mock.CoroutineMock( + return_value=async_mock.MagicMock(our_recipient_key="our-recipient_key") + ) + mock_default_did_from_verkey.return_value = "holder-did" + + mock_cred_ex.retrieve_by_id = async_mock.CoroutineMock() + mock_cred_ex.retrieve_by_id.return_value.state = ( + mock_cred_ex.STATE_OFFER_RECEIVED + ) + mock_cred_ex.retrieve_by_id.return_value.connection_id = None + + mock_cred_ex_record = async_mock.MagicMock() + + mock_cred_mgr.return_value.create_request.return_value = ( + mock_cred_ex_record, + async_mock.MagicMock(), + ) + + await test_module.credential_exchange_send_bound_request(self.request) + + mock_cred_mgr.return_value.create_request.assert_called_once_with( + mock_cred_ex.retrieve_by_id.return_value, "holder-did" + ) + mock_response.assert_called_once_with( + mock_cred_ex_record.serialize.return_value + ) + mock_default_did_from_verkey.assert_called_once_with("our-recipient_key") + async def test_credential_exchange_send_no_conn_record(self): connection_id = "connection-id" preview_spec = {"attributes": [{"name": "attr", "value": "value"}]} From 8b05bcebbd21fd806f2eeb0b0b1211dab2bfca75 Mon Sep 17 00:00:00 2001 From: DaevMithran Date: Sun, 3 Apr 2022 20:13:50 +0530 Subject: [PATCH 158/872] Add support seed in wallet_create_did with startup parameter Signed-off-by: DaevMithran --- aries_cloudagent/config/argparse.py | 11 +++++++++++ aries_cloudagent/wallet/routes.py | 9 ++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index a8b6ae5110..17a572dd1e 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -1423,6 +1423,15 @@ def add_arguments(self, parser: ArgumentParser): "to use with a Hyperledger Indy ledger." ), ) + parser.add_argument( + "--wallet-allow-insecure-seed", + action="store_true", + env_var="ACAPY_WALLET_ALLOW_INSECURE_SEED", + help=( + "If this parameter is set, allows to use a custom seed " + "to create a local DID" + ), + ) parser.add_argument( "--wallet-key", type=str, @@ -1543,6 +1552,8 @@ def get_settings(self, args: Namespace) -> dict: settings["wallet.seed"] = args.seed if args.wallet_local_did: settings["wallet.local_did"] = True + if args.wallet_allow_insecure_seed: + settings["wallet.allow_insecure_seed"] = True if args.wallet_key: settings["wallet.key"] = args.wallet_key if args.wallet_rekey: diff --git a/aries_cloudagent/wallet/routes.py b/aries_cloudagent/wallet/routes.py index 34811d784d..a7772be595 100644 --- a/aries_cloudagent/wallet/routes.py +++ b/aries_cloudagent/wallet/routes.py @@ -355,13 +355,20 @@ async def wallet_create_did(request: web.BaseRequest): f" support key type {key_type.key_type}" ) ) + seed = None + if context.settings.get("wallet.allow_insecure_seed"): + seed = body.get("seed") or None info = None async with context.session() as session: wallet = session.inject_or(BaseWallet) if not wallet: raise web.HTTPForbidden(reason="No wallet available") try: - info = await wallet.create_local_did(method=method, key_type=key_type) + info = await wallet.create_local_did( + method=method, + key_type=key_type, + seed=seed, + ) except WalletError as err: raise web.HTTPBadRequest(reason=err.roll_up) from err From 01dcadf76f50b0510c1f1c3ebff4a4f1cbf6ee10 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sun, 3 Apr 2022 17:14:24 +0200 Subject: [PATCH 159/872] test: fix present proof tests Signed-off-by: Timo Glastra --- .../dif/tests/test_pres_exch_handler.py | 8 + .../v1_0/handlers/presentation_ack_handler.py | 3 +- .../v1_0/handlers/presentation_handler.py | 8 +- .../handlers/presentation_request_handler.py | 3 +- .../tests/test_presentation_ack_handler.py | 36 +- .../tests/test_presentation_handler.py | 29 +- ...est_presentation_problem_report_handler.py | 2 + .../test_presentation_proposal_handler.py | 26 +- .../test_presentation_request_handler.py | 328 ++++++++++++------ .../protocols/present_proof/v1_0/manager.py | 12 +- .../protocols/present_proof/v1_0/routes.py | 1 - .../present_proof/v1_0/tests/test_manager.py | 63 +++- .../present_proof/v1_0/tests/test_routes.py | 1 + .../v2_0/handlers/pres_ack_handler.py | 3 +- .../v2_0/handlers/pres_handler.py | 8 +- .../v2_0/handlers/pres_request_handler.py | 3 +- .../handlers/tests/test_pres_ack_handler.py | 36 +- .../v2_0/handlers/tests/test_pres_handler.py | 29 +- .../tests/test_pres_proposal_handler.py | 28 +- .../tests/test_pres_request_handler.py | 265 ++++++++++---- .../protocols/present_proof/v2_0/manager.py | 4 +- .../protocols/present_proof/v2_0/routes.py | 19 +- .../present_proof/v2_0/tests/test_manager.py | 58 ++-- .../present_proof/v2_0/tests/test_routes.py | 2 +- 24 files changed, 718 insertions(+), 257 deletions(-) diff --git a/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py b/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py index 67a56badb0..993466ac4a 100644 --- a/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py +++ b/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py @@ -1562,6 +1562,7 @@ async def test_filter_string(self, setup_tuple, profile): assert len(tmp_vp.get("verifiableCredential")) == 1 @pytest.mark.asyncio + @pytest.mark.ursa_bbs_signatures async def test_filter_schema(self, setup_tuple, profile): cred_list, pd_list = setup_tuple dif_pres_exch_handler = DIFPresExchHandler(profile) @@ -1583,6 +1584,7 @@ async def test_filter_schema(self, setup_tuple, profile): == 0 ) + @pytest.mark.ursa_bbs_signatures def test_cred_schema_match_a(self, setup_tuple, profile): cred_list, pd_list = setup_tuple dif_pres_exch_handler = DIFPresExchHandler(profile) @@ -1594,6 +1596,7 @@ def test_cred_schema_match_a(self, setup_tuple, profile): is True ) + @pytest.mark.ursa_bbs_signatures @pytest.mark.asyncio async def test_merge_nested(self, setup_tuple, profile): cred_list, pd_list = setup_tuple @@ -1655,6 +1658,7 @@ async def test_merge_nested_cred_no_id(self, profile): test_nested_result, {} ) + @pytest.mark.ursa_bbs_signatures def test_subject_is_issuer(self, setup_tuple, profile): cred_list, pd_list = setup_tuple dif_pres_exch_handler = DIFPresExchHandler(profile) @@ -1837,6 +1841,7 @@ def test_invalid_string_filter(self, profile): val="test", _filter=Filter() ) + @pytest.mark.ursa_bbs_signatures def test_cred_schema_match_b(self, profile, setup_tuple): dif_pres_exch_handler = DIFPresExchHandler(profile) cred_list, pd_list = setup_tuple @@ -2181,6 +2186,7 @@ async def test_get_sign_key_credential_subject_id_bbsbls(self, profile): ) assert len(filtered_creds) == 2 + @pytest.mark.ursa_bbs_signatures @pytest.mark.asyncio async def test_create_vp_no_issuer(self, profile, setup_tuple): dif_pres_exch_handler = DIFPresExchHandler(profile) @@ -2272,6 +2278,7 @@ async def test_create_vp_no_issuer(self, profile, setup_tuple): ) @pytest.mark.asyncio + @pytest.mark.ursa_bbs_signatures async def test_create_vp_with_bbs_suite(self, profile, setup_tuple): dif_pres_exch_handler = DIFPresExchHandler( profile, proof_type=BbsBlsSignature2020.signature_type @@ -2328,6 +2335,7 @@ async def test_create_vp_with_bbs_suite(self, profile, setup_tuple): assert SECURITY_CONTEXT_BBS_URL in vp["@context"] @pytest.mark.asyncio + @pytest.mark.ursa_bbs_signatures async def test_create_vp_no_issuer_with_bbs_suite(self, profile, setup_tuple): dif_pres_exch_handler = DIFPresExchHandler( profile, proof_type=BbsBlsSignature2020.signature_type diff --git a/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_ack_handler.py b/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_ack_handler.py index bf00d19580..d66c500fc0 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_ack_handler.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_ack_handler.py @@ -41,7 +41,8 @@ async def handle(self, context: RequestContext, responder: BaseResponder): # Either connection or oob context must be present if not context.connection_record and not oob_record: raise HandlerException( - "No connection or associated connectionless exchange found for presentation ack" + "No connection or associated connectionless exchange found for" + " presentation ack" ) presentation_manager = PresentationManager(context.profile) diff --git a/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_handler.py b/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_handler.py index 971430e1f6..e69a2b4766 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_handler.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_handler.py @@ -40,15 +40,15 @@ async def handle(self, context: RequestContext, responder: BaseResponder): if context.connection_record and not context.connection_ready: raise HandlerException("Connection used for presentation not ready") - # Find associated oob record. If the presentation request was created as an oob attachment - # the presentation exchange record won't have a connection id (yet) + # Find associated oob record. If the presentation request was created as an oob + # attachment the presentation exchange record won't have a connection id (yet) oob_processor = context.inject(OobMessageProcessor) oob_record = await oob_processor.find_oob_record_for_inbound_message(context) # Normally we would do a check here that there is either a connection or # an associated oob record. However as present proof supported receiving - # presentation without oob record or connection record (aip-1 style connectionless) - # we can't perform this check here + # presentation without oob record or connection record + # (aip-1 style connectionless) we can't perform this check here presentation_manager = PresentationManager(profile) diff --git a/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_request_handler.py b/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_request_handler.py index 581de65553..cbd01a8cec 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_request_handler.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_request_handler.py @@ -52,7 +52,8 @@ async def handle(self, context: RequestContext, responder: BaseResponder): # Either connection or oob context must be present if not context.connection_record and not oob_record: raise HandlerException( - "No connection or associated connectionless exchange found for presentation request" + "No connection or associated connectionless exchange found for" + " presentation request" ) connection_id = ( diff --git a/aries_cloudagent/protocols/present_proof/v1_0/handlers/tests/test_presentation_ack_handler.py b/aries_cloudagent/protocols/present_proof/v1_0/handlers/tests/test_presentation_ack_handler.py index 34f1578f03..db233adc17 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/handlers/tests/test_presentation_ack_handler.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/handlers/tests/test_presentation_ack_handler.py @@ -1,5 +1,6 @@ from asynctest import mock as async_mock, TestCase as AsyncTestCase +from ......core.oob_processor import OobMessageProcessor from ......messaging.request_context import RequestContext from ......messaging.responder import MockResponder from ......transport.inbound.receipt import MessageReceipt @@ -15,6 +16,13 @@ async def test_called(self): request_context.message_receipt = MessageReceipt() session = request_context.session() + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=async_mock.MagicMock() + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + with async_mock.patch.object( test_module, "PresentationManager", autospec=True ) as mock_pres_mgr: @@ -37,6 +45,7 @@ async def test_called(self): async def test_called_not_ready(self): request_context = RequestContext.test_context() request_context.message_receipt = MessageReceipt() + request_context.connection_record = async_mock.MagicMock() with async_mock.patch.object( test_module, "PresentationManager", autospec=True @@ -48,7 +57,32 @@ async def test_called_not_ready(self): request_context.connection_ready = False handler = test_module.PresentationAckHandler() responder = MockResponder() - with self.assertRaises(test_module.HandlerException): + with self.assertRaises(test_module.HandlerException) as err: await handler.handle(request_context, responder) + assert err.exception.message == "Connection used for presentation ack not ready" + + assert not responder.messages + + async def test_called_no_connection_no_oob(self): + request_context = RequestContext.test_context() + request_context.message_receipt = MessageReceipt() + + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + # No oob record found + return_value=None + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + + request_context.message = PresentationAck() + handler = test_module.PresentationAckHandler() + responder = MockResponder() + with self.assertRaises(test_module.HandlerException) as err: + await handler.handle(request_context, responder) + assert ( + err.exception.message + == "No connection or associated connectionless exchange found for presentation ack" + ) assert not responder.messages diff --git a/aries_cloudagent/protocols/present_proof/v1_0/handlers/tests/test_presentation_handler.py b/aries_cloudagent/protocols/present_proof/v1_0/handlers/tests/test_presentation_handler.py index 75fca2e9f7..775bdb068a 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/handlers/tests/test_presentation_handler.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/handlers/tests/test_presentation_handler.py @@ -2,6 +2,7 @@ from asynctest import mock as async_mock, TestCase as AsyncTestCase +from ......core.oob_processor import OobMessageProcessor from ......messaging.request_context import RequestContext from ......messaging.responder import MockResponder from ......transport.inbound.receipt import MessageReceipt @@ -17,6 +18,14 @@ async def test_called(self): request_context.message_receipt = MessageReceipt() request_context.settings["debug.auto_verify_presentation"] = False + oob_record = async_mock.MagicMock() + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=oob_record + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + with async_mock.patch.object( test_module, "PresentationManager", autospec=True ) as mock_pres_mgr: @@ -30,7 +39,7 @@ async def test_called(self): mock_pres_mgr.assert_called_once_with(request_context.profile) mock_pres_mgr.return_value.receive_presentation.assert_called_once_with( - request_context.message, request_context.connection_record + request_context.message, request_context.connection_record, oob_record ) assert not responder.messages @@ -39,6 +48,14 @@ async def test_called_auto_verify(self): request_context.message_receipt = MessageReceipt() request_context.settings["debug.auto_verify_presentation"] = True + oob_record = async_mock.MagicMock() + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=oob_record + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + with async_mock.patch.object( test_module, "PresentationManager", autospec=True ) as mock_pres_mgr: @@ -53,7 +70,7 @@ async def test_called_auto_verify(self): mock_pres_mgr.assert_called_once_with(request_context.profile) mock_pres_mgr.return_value.receive_presentation.assert_called_once_with( - request_context.message, request_context.connection_record + request_context.message, request_context.connection_record, oob_record ) assert not responder.messages @@ -62,6 +79,14 @@ async def test_called_auto_verify_x(self): request_context.message_receipt = MessageReceipt() request_context.settings["debug.auto_verify_presentation"] = True + oob_record = async_mock.MagicMock() + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=oob_record + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + with async_mock.patch.object( test_module, "PresentationManager", autospec=True ) as mock_pres_mgr: diff --git a/aries_cloudagent/protocols/present_proof/v1_0/handlers/tests/test_presentation_problem_report_handler.py b/aries_cloudagent/protocols/present_proof/v1_0/handlers/tests/test_presentation_problem_report_handler.py index 9d7b7cc925..a726dc349c 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/handlers/tests/test_presentation_problem_report_handler.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/handlers/tests/test_presentation_problem_report_handler.py @@ -21,6 +21,7 @@ async def test_called(self): with async_mock.patch.object( test_module, "PresentationManager", autospec=True ) as mock_pres_mgr: + request_context.connection_ready = True mock_pres_mgr.return_value.receive_problem_report = ( async_mock.CoroutineMock() ) @@ -48,6 +49,7 @@ async def test_called_x(self): with async_mock.patch.object( test_module, "PresentationManager", autospec=True ) as mock_pres_mgr: + request_context.connection_ready = True mock_pres_mgr.return_value.receive_problem_report = ( async_mock.CoroutineMock( side_effect=test_module.StorageError("Disk full") diff --git a/aries_cloudagent/protocols/present_proof/v1_0/handlers/tests/test_presentation_proposal_handler.py b/aries_cloudagent/protocols/present_proof/v1_0/handlers/tests/test_presentation_proposal_handler.py index 96e7c96e50..a60154533c 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/handlers/tests/test_presentation_proposal_handler.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/handlers/tests/test_presentation_proposal_handler.py @@ -16,6 +16,7 @@ async def test_called(self): request_context = RequestContext.test_context() request_context.message_receipt = MessageReceipt() request_context.settings["debug.auto_respond_presentation_proposal"] = False + request_context.connection_record = async_mock.MagicMock() with async_mock.patch.object( test_module, "PresentationManager", autospec=True @@ -42,6 +43,7 @@ async def test_called_auto_request(self): request_context.message.comment = "hello world" request_context.message_receipt = MessageReceipt() request_context.settings["debug.auto_respond_presentation_proposal"] = True + request_context.connection_record = async_mock.MagicMock() with async_mock.patch.object( test_module, "PresentationManager", autospec=True @@ -80,6 +82,7 @@ async def test_called_auto_request_x(self): request_context.message.comment = "hello world" request_context.message_receipt = MessageReceipt() request_context.settings["debug.auto_respond_presentation_proposal"] = True + request_context.connection_record = async_mock.MagicMock() with async_mock.patch.object( test_module, "PresentationManager", autospec=True @@ -107,6 +110,7 @@ async def test_called_auto_request_x(self): async def test_called_not_ready(self): request_context = RequestContext.test_context() request_context.message_receipt = MessageReceipt() + request_context.connection_record = async_mock.MagicMock() with async_mock.patch.object( test_module, "PresentationManager", autospec=True @@ -116,7 +120,27 @@ async def test_called_not_ready(self): request_context.connection_ready = False handler = test_module.PresentationProposalHandler() responder = MockResponder() - with self.assertRaises(test_module.HandlerException): + with self.assertRaises(test_module.HandlerException) as err: await handler.handle(request_context, responder) + assert ( + err.exception.message + == "Connection used for presentation proposal not ready" + ) + + assert not responder.messages + + async def test_called_no_connection(self): + request_context = RequestContext.test_context() + request_context.message_receipt = MessageReceipt() + + request_context.message = PresentationProposal() + handler = test_module.PresentationProposalHandler() + responder = MockResponder() + with self.assertRaises(test_module.HandlerException) as err: + await handler.handle(request_context, responder) + assert ( + err.exception.message + == "Connectionless not supported for presentation proposal" + ) assert not responder.messages diff --git a/aries_cloudagent/protocols/present_proof/v1_0/handlers/tests/test_presentation_request_handler.py b/aries_cloudagent/protocols/present_proof/v1_0/handlers/tests/test_presentation_request_handler.py index e8cefb0371..add1efa8e9 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/handlers/tests/test_presentation_request_handler.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/handlers/tests/test_presentation_request_handler.py @@ -1,5 +1,8 @@ from asynctest import mock as async_mock, TestCase as AsyncTestCase + +from ......core.oob_processor import OobMessageProcessor +from ......indy.holder import IndyHolder from ......indy.models.pres_preview import ( IndyPresAttrSpec, IndyPresPredSpec, @@ -71,6 +74,13 @@ async def test_called(self): return_value=INDY_PROOF_REQ ) + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=async_mock.MagicMock() + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + px_rec_instance = test_module.V10PresentationExchange( presentation_proposal_dict={ "presentation_proposal": { @@ -110,6 +120,9 @@ async def test_called(self): mock_pres_mgr.return_value.receive_request.assert_called_once_with( px_rec_instance ) + mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( + request_context + ) assert not responder.messages async def test_called_not_found(self): @@ -122,6 +135,13 @@ async def test_called_not_found(self): return_value=INDY_PROOF_REQ ) + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=async_mock.MagicMock() + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + px_rec_instance = test_module.V10PresentationExchange( presentation_proposal_dict={ "presentation_proposal": { @@ -162,6 +182,9 @@ async def test_called_not_found(self): mock_pres_mgr.return_value.receive_request.assert_called_once_with( px_rec_instance ) + mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( + request_context + ) assert not responder.messages async def test_called_auto_present(self): @@ -203,21 +226,25 @@ async def test_called_auto_present(self): presentation_proposal_dict=presentation_proposal, auto_present=True, ) + + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=async_mock.MagicMock() + ) + ) + mock_holder = async_mock.MagicMock( + get_credentials_for_presentation_request_by_referent=async_mock.CoroutineMock( + return_value=[{"cred_info": {"referent": "dummy"}}] + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + request_context.injector.bind_instance(IndyHolder, mock_holder) + with async_mock.patch.object( test_module, "PresentationManager", autospec=True ) as mock_pres_mgr, async_mock.patch.object( test_module, "V10PresentationExchange", autospec=True - ) as mock_pres_ex_cls, async_mock.patch.object( - test_module, "IndyHolder", autospec=True - ) as mock_holder: - - mock_holder.get_credentials_for_presentation_request_by_referent = ( - async_mock.CoroutineMock( - return_value=[{"cred_info": {"referent": "dummy"}}] - ) - ) - request_context.inject = async_mock.MagicMock(return_value=mock_holder) - + ) as mock_pres_ex_cls: mock_pres_ex_cls.return_value = px_rec_instance mock_pres_ex_cls.retrieve_by_tag_filter = async_mock.CoroutineMock( return_value=px_rec_instance @@ -238,6 +265,9 @@ async def test_called_auto_present(self): mock_pres_mgr.return_value.receive_request.assert_called_once_with( px_rec_instance ) + mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( + request_context + ) messages = responder.messages assert len(messages) == 1 (result, target) = messages[0] @@ -285,20 +315,26 @@ async def test_called_auto_present_x(self): save_error_state=async_mock.CoroutineMock(), ) - with async_mock.patch.object( - test_module, "PresentationManager", autospec=True - ) as mock_pres_mgr, async_mock.patch.object( - test_module, "V10PresentationExchange", autospec=True - ) as mock_pres_ex_cls, async_mock.patch.object( - test_module, "IndyHolder", autospec=True - ) as mock_holder: - - mock_holder.get_credentials_for_presentation_request_by_referent = ( + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=async_mock.MagicMock() + ) + ) + mock_holder = async_mock.MagicMock( + get_credentials_for_presentation_request_by_referent=( async_mock.CoroutineMock( return_value=[{"cred_info": {"referent": "dummy"}}] ) ) - request_context.inject = async_mock.MagicMock(return_value=mock_holder) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + request_context.injector.bind_instance(IndyHolder, mock_holder) + + with async_mock.patch.object( + test_module, "PresentationManager", autospec=True + ) as mock_pres_mgr, async_mock.patch.object( + test_module, "V10PresentationExchange", autospec=True + ) as mock_pres_ex_cls: mock_pres_ex_cls.return_value = mock_px_rec mock_pres_ex_cls.retrieve_by_tag_filter = async_mock.CoroutineMock( @@ -356,15 +392,13 @@ async def test_called_auto_present_no_preview(self): request_context.message_receipt = MessageReceipt() px_rec_instance = test_module.V10PresentationExchange(auto_present=True) - with async_mock.patch.object( - test_module, "PresentationManager", autospec=True - ) as mock_pres_mgr, async_mock.patch.object( - test_module, "V10PresentationExchange", autospec=True - ) as mock_pres_ex_cls, async_mock.patch.object( - test_module, "IndyHolder", autospec=True - ) as mock_holder: - - mock_holder.get_credentials_for_presentation_request_by_referent = ( + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=async_mock.MagicMock() + ) + ) + mock_holder = async_mock.MagicMock( + get_credentials_for_presentation_request_by_referent=( async_mock.CoroutineMock( return_value=[ {"cred_info": {"referent": "dummy-0"}}, @@ -372,7 +406,15 @@ async def test_called_auto_present_no_preview(self): ] ) ) - request_context.inject = async_mock.MagicMock(return_value=mock_holder) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + request_context.injector.bind_instance(IndyHolder, mock_holder) + + with async_mock.patch.object( + test_module, "PresentationManager", autospec=True + ) as mock_pres_mgr, async_mock.patch.object( + test_module, "V10PresentationExchange", autospec=True + ) as mock_pres_ex_cls: mock_pres_ex_cls.return_value = px_rec_instance mock_pres_ex_cls.retrieve_by_tag_filter = async_mock.CoroutineMock( @@ -394,6 +436,9 @@ async def test_called_auto_present_no_preview(self): mock_pres_mgr.return_value.receive_request.assert_called_once_with( px_rec_instance ) + mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( + request_context + ) messages = responder.messages assert len(messages) == 1 (result, target) = messages[0] @@ -428,18 +473,24 @@ async def test_called_auto_present_pred_no_match(self): request_context.message_receipt = MessageReceipt() px_rec_instance = test_module.V10PresentationExchange(auto_present=True) + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=async_mock.MagicMock() + ) + ) + mock_holder = async_mock.MagicMock( + get_credentials_for_presentation_request_by_referent=( + async_mock.CoroutineMock(return_value=[]) + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + request_context.injector.bind_instance(IndyHolder, mock_holder) + with async_mock.patch.object( test_module, "PresentationManager", autospec=True ) as mock_pres_mgr, async_mock.patch.object( test_module, "V10PresentationExchange", autospec=True - ) as mock_pres_ex_cls, async_mock.patch.object( - test_module, "IndyHolder", autospec=True - ) as mock_holder: - - mock_holder.get_credentials_for_presentation_request_by_referent = ( - async_mock.CoroutineMock(return_value=[]) - ) - request_context.inject = async_mock.MagicMock(return_value=mock_holder) + ) as mock_pres_ex_cls: mock_pres_ex_cls.return_value = px_rec_instance mock_pres_ex_cls.retrieve_by_tag_filter = async_mock.CoroutineMock( @@ -461,6 +512,9 @@ async def test_called_auto_present_pred_no_match(self): mock_pres_mgr.return_value.receive_request.assert_called_once_with( px_rec_instance ) + mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( + request_context + ) assert not responder.messages async def test_called_auto_present_pred_single_match(self): @@ -491,20 +545,26 @@ async def test_called_auto_present_pred_single_match(self): request_context.message_receipt = MessageReceipt() px_rec_instance = test_module.V10PresentationExchange(auto_present=True) - with async_mock.patch.object( - test_module, "PresentationManager", autospec=True - ) as mock_pres_mgr, async_mock.patch.object( - test_module, "V10PresentationExchange", autospec=True - ) as mock_pres_ex_cls, async_mock.patch.object( - test_module, "IndyHolder", autospec=True - ) as mock_holder: - - mock_holder.get_credentials_for_presentation_request_by_referent = ( + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=async_mock.MagicMock() + ) + ) + mock_holder = async_mock.MagicMock( + get_credentials_for_presentation_request_by_referent=( async_mock.CoroutineMock( return_value=[{"cred_info": {"referent": "dummy-0"}}] ) ) - request_context.inject = async_mock.MagicMock(return_value=mock_holder) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + request_context.injector.bind_instance(IndyHolder, mock_holder) + + with async_mock.patch.object( + test_module, "PresentationManager", autospec=True + ) as mock_pres_mgr, async_mock.patch.object( + test_module, "V10PresentationExchange", autospec=True + ) as mock_pres_ex_cls: mock_pres_ex_cls.return_value = px_rec_instance mock_pres_ex_cls.retrieve_by_tag_filter = async_mock.CoroutineMock( @@ -526,6 +586,9 @@ async def test_called_auto_present_pred_single_match(self): mock_pres_mgr.return_value.receive_request.assert_called_once_with( px_rec_instance ) + mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( + request_context + ) messages = responder.messages assert len(messages) == 1 (result, target) = messages[0] @@ -560,15 +623,13 @@ async def test_called_auto_present_pred_multi_match(self): request_context.message_receipt = MessageReceipt() px_rec_instance = test_module.V10PresentationExchange(auto_present=True) - with async_mock.patch.object( - test_module, "PresentationManager", autospec=True - ) as mock_pres_mgr, async_mock.patch.object( - test_module, "V10PresentationExchange", autospec=True - ) as mock_pres_ex_cls, async_mock.patch.object( - test_module, "IndyHolder", autospec=True - ) as mock_holder: - - mock_holder.get_credentials_for_presentation_request_by_referent = ( + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=async_mock.MagicMock() + ) + ) + mock_holder = async_mock.MagicMock( + get_credentials_for_presentation_request_by_referent=( async_mock.CoroutineMock( return_value=[ {"cred_info": {"referent": "dummy-0"}}, @@ -576,7 +637,15 @@ async def test_called_auto_present_pred_multi_match(self): ] ) ) - request_context.inject = async_mock.MagicMock(return_value=mock_holder) + ) + request_context.injector.bind_instance(IndyHolder, mock_holder) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + + with async_mock.patch.object( + test_module, "PresentationManager", autospec=True + ) as mock_pres_mgr, async_mock.patch.object( + test_module, "V10PresentationExchange", autospec=True + ) as mock_pres_ex_cls: mock_pres_ex_cls.return_value = px_rec_instance mock_pres_ex_cls.retrieve_by_tag_filter = async_mock.CoroutineMock( @@ -598,6 +667,9 @@ async def test_called_auto_present_pred_multi_match(self): mock_pres_mgr.return_value.receive_request.assert_called_once_with( px_rec_instance ) + mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( + request_context + ) messages = responder.messages assert len(messages) == 1 (result, target) = messages[0] @@ -652,15 +724,13 @@ async def test_called_auto_present_multi_cred_match_reft(self): auto_present=True, ) - with async_mock.patch.object( - test_module, "PresentationManager", autospec=True - ) as mock_pres_mgr, async_mock.patch.object( - test_module, "V10PresentationExchange", autospec=True - ) as mock_pres_ex_cls, async_mock.patch.object( - test_module, "IndyHolder", autospec=True - ) as mock_holder: - - mock_holder.get_credentials_for_presentation_request_by_referent = ( + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=async_mock.MagicMock() + ) + ) + mock_holder = async_mock.MagicMock( + get_credentials_for_presentation_request_by_referent=( async_mock.CoroutineMock( return_value=[ { @@ -699,7 +769,15 @@ async def test_called_auto_present_multi_cred_match_reft(self): ] ) ) - request_context.inject = async_mock.MagicMock(return_value=mock_holder) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + request_context.injector.bind_instance(IndyHolder, mock_holder) + + with async_mock.patch.object( + test_module, "PresentationManager", autospec=True + ) as mock_pres_mgr, async_mock.patch.object( + test_module, "V10PresentationExchange", autospec=True + ) as mock_pres_ex_cls: mock_pres_ex_cls.return_value = px_rec_instance mock_pres_ex_cls.retrieve_by_tag_filter = async_mock.CoroutineMock( @@ -721,6 +799,9 @@ async def test_called_auto_present_multi_cred_match_reft(self): mock_pres_mgr.return_value.receive_request.assert_called_once_with( px_rec_instance ) + mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( + request_context + ) messages = responder.messages assert len(messages) == 1 (result, target) = messages[0] @@ -766,44 +847,51 @@ async def test_called_auto_present_bait_and_switch(self): auto_present=True, ) + by_reft = async_mock.CoroutineMock( + return_value=[ + { + "cred_info": { + "referent": "dummy-0", + "cred_def_id": CD_ID, + "attrs": {"ident": "zero", "favourite": "yam"}, + } + }, + { + "cred_info": { + "referent": "dummy-1", + "cred_def_id": CD_ID, + "attrs": {"ident": "one", "favourite": "turnip"}, + } + }, + { + "cred_info": { + "referent": "dummy-2", + "cred_def_id": CD_ID, + "attrs": { + "ident": "two", + "favourite": "the idea of a potato but not a potato", + }, + } + }, + ] + ) + mock_holder = async_mock.MagicMock( + get_credentials_for_presentation_request_by_referent=by_reft + ) + request_context.injector.bind_instance(IndyHolder, mock_holder) + + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=async_mock.MagicMock() + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + with async_mock.patch.object( test_module, "PresentationManager", autospec=True ) as mock_pres_mgr, async_mock.patch.object( test_module, "V10PresentationExchange", autospec=True - ) as mock_pres_ex_cls, async_mock.patch.object( - test_module, "IndyHolder", autospec=True - ) as mock_holder: - - by_reft = async_mock.CoroutineMock( - return_value=[ - { - "cred_info": { - "referent": "dummy-0", - "cred_def_id": CD_ID, - "attrs": {"ident": "zero", "favourite": "yam"}, - } - }, - { - "cred_info": { - "referent": "dummy-1", - "cred_def_id": CD_ID, - "attrs": {"ident": "one", "favourite": "turnip"}, - } - }, - { - "cred_info": { - "referent": "dummy-2", - "cred_def_id": CD_ID, - "attrs": { - "ident": "two", - "favourite": "the idea of a potato but not a potato", - }, - } - }, - ] - ) - mock_holder.get_credentials_for_presentation_request_by_referent = by_reft - request_context.inject = async_mock.MagicMock(return_value=mock_holder) + ) as mock_pres_ex_cls: mock_pres_ex_cls.return_value = px_rec_instance mock_pres_ex_cls.retrieve_by_tag_filter = async_mock.CoroutineMock( @@ -826,11 +914,15 @@ async def test_called_auto_present_bait_and_switch(self): mock_pres_mgr.return_value.receive_request.assert_called_once_with( px_rec_instance ) + mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( + request_context + ) assert not responder.messages async def test_called_not_ready(self): request_context = RequestContext.test_context() request_context.message_receipt = MessageReceipt() + request_context.connection_record = async_mock.MagicMock() with async_mock.patch.object( test_module, "PresentationManager", autospec=True @@ -840,7 +932,35 @@ async def test_called_not_ready(self): request_context.connection_ready = False handler = test_module.PresentationRequestHandler() responder = MockResponder() - with self.assertRaises(test_module.HandlerException): + with self.assertRaises(test_module.HandlerException) as err: await handler.handle(request_context, responder) + assert ( + err.exception.message + == "Connection used for presentation request not ready" + ) + + assert not responder.messages + + async def test_no_conn_no_oob(self): + request_context = RequestContext.test_context() + request_context.message_receipt = MessageReceipt() + + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + # No oob record found + return_value=None + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + + request_context.message = PresentationRequest() + handler = test_module.PresentationRequestHandler() + responder = MockResponder() + with self.assertRaises(test_module.HandlerException) as err: + await handler.handle(request_context, responder) + assert ( + err.exception.message + == "No connection or associated connectionless exchange found for presentation request" + ) assert not responder.messages diff --git a/aries_cloudagent/protocols/present_proof/v1_0/manager.py b/aries_cloudagent/protocols/present_proof/v1_0/manager.py index 39638ce68f..52942fee15 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/manager.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/manager.py @@ -315,8 +315,8 @@ async def receive_presentation( thread_id = message._thread_id # Normally we only set the connection_id to None if an oob record is present # But present proof supports the old-style AIP-1 connectionless exchange that - # bypasses the oob record. So we can't verify if an oob record is associated with the - # exchange because it is possible that there is None + # bypasses the oob record. So we can't verify if an oob record is associated with + # the exchange because it is possible that there is None connection_id = ( None if oob_record @@ -458,10 +458,12 @@ async def send_presentation_ack( {"attach_thread_id": presentation_exchange_record.thread_id}, ) except StorageNotFoundError: - # This can happen in AIP1 style connectionless exchange. ACA-PY only supported this for receiving a presentation + # This can happen in AIP1 style connectionless exchange. ACA-PY only + # supported this for receiving a presentation LOGGER.error( - "Unable to send connectionless presentation ack without associated oob record. " - "This can happen if proof request was sent without wrapping it in an out of band invitation (AIP1-style)." + "Unable to send connectionless presentation ack without associated " + "oob record. This can happen if proof request was sent without " + "wrapping it in an out of band invitation (AIP1-style)." ) return diff --git a/aries_cloudagent/protocols/present_proof/v1_0/routes.py b/aries_cloudagent/protocols/present_proof/v1_0/routes.py index 5103a27ea6..9cdf8a5bf7 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/routes.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/routes.py @@ -457,7 +457,6 @@ async def presentation_exchange_create_request(request: web.BaseRequest): context: AdminRequestContext = request["context"] profile = context.profile - outbound_handler = request["outbound_message_router"] body = await request.json() diff --git a/aries_cloudagent/protocols/present_proof/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/present_proof/v1_0/tests/test_manager.py index 548a97c382..81bf9acc58 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/tests/test_manager.py @@ -955,22 +955,24 @@ async def test_receive_presentation(self): "session", async_mock.MagicMock(return_value=self.profile.session()), ) as session: - retrieve_ex.side_effect = [ - StorageNotFoundError("no such record"), - exchange_dummy, - ] + retrieve_ex.side_effect = [exchange_dummy] exchange_out = await self.manager.receive_presentation( - PRES, connection_record + PRES, connection_record, None + ) + retrieve_ex.assert_called_once_with( + session.return_value, + {"thread_id": "dummy"}, + { + "role": V10PresentationExchange.ROLE_VERIFIER, + "connection_id": CONN_ID, + }, ) - assert retrieve_ex.call_count == 2 save_ex.assert_called_once() assert exchange_out.state == ( V10PresentationExchange.STATE_PRESENTATION_RECEIVED ) async def test_receive_presentation_oob(self): - connection_record = async_mock.MagicMock(connection_id=CONN_ID) - exchange_dummy = V10PresentationExchange( presentation_proposal_dict={ "presentation_proposal": { @@ -1062,10 +1064,17 @@ async def test_receive_presentation_oob(self): V10PresentationExchange, "save", autospec=True ) as save_ex, async_mock.patch.object( V10PresentationExchange, "retrieve_by_tag_filter", autospec=True - ) as retrieve_ex: - retrieve_ex.side_effect = [StorageNotFoundError(), exchange_dummy] - exchange_out = await self.manager.receive_presentation( - PRES, connection_record + ) as retrieve_ex, async_mock.patch.object( + self.profile, + "session", + async_mock.MagicMock(return_value=self.profile.session()), + ) as session: + retrieve_ex.side_effect = [exchange_dummy] + exchange_out = await self.manager.receive_presentation(PRES, None, None) + retrieve_ex.assert_called_once_with( + session.return_value, + {"thread_id": "dummy"}, + {"role": V10PresentationExchange.ROLE_VERIFIER, "connection_id": None}, ) assert exchange_out.state == ( V10PresentationExchange.STATE_PRESENTATION_RECEIVED @@ -1168,7 +1177,7 @@ async def test_receive_presentation_bait_and_switch(self): ) as retrieve_ex: retrieve_ex.return_value = exchange_dummy with self.assertRaises(PresentationManagerError): - await self.manager.receive_presentation(PRES, connection_record) + await self.manager.receive_presentation(PRES, connection_record, None) async def test_receive_presentation_connectionless(self): exchange_dummy = V10PresentationExchange() @@ -1183,9 +1192,11 @@ async def test_receive_presentation_connectionless(self): async_mock.MagicMock(return_value=self.profile.session()), ) as session: retrieve_ex.return_value = exchange_dummy - exchange_out = await self.manager.receive_presentation(PRES, None) + exchange_out = await self.manager.receive_presentation(PRES, None, None) retrieve_ex.assert_called_once_with( - session.return_value, {"thread_id": PRES._thread_id}, None + session.return_value, + {"thread_id": PRES._thread_id}, + {"role": V10PresentationExchange.ROLE_VERIFIER, "connection_id": None}, ) save_ex.assert_called_once() @@ -1255,7 +1266,7 @@ async def test_verify_presentation_with_revocation(self): """ async def test_send_presentation_ack(self): - exchange = V10PresentationExchange() + exchange = V10PresentationExchange(connection_id="dummy") responder = MockResponder() self.profile.context.injector.bind_instance(BaseResponder, responder) @@ -1264,6 +1275,26 @@ async def test_send_presentation_ack(self): messages = responder.messages assert len(messages) == 1 + async def test_send_presentation_ack_oob(self): + exchange = V10PresentationExchange(thread_id="some-thread-id") + + responder = MockResponder() + self.profile.context.injector.bind_instance(BaseResponder, responder) + + with async_mock.patch.object( + test_module.OobRecord, "retrieve_by_tag_filter" + ) as mock_retrieve_oob, async_mock.patch.object( + self.profile, + "session", + async_mock.MagicMock(return_value=self.profile.session()), + ) as session: + await self.manager.send_presentation_ack(exchange) + messages = responder.messages + mock_retrieve_oob.assert_called_once_with( + session.return_value, {"attach_thread_id": "some-thread-id"} + ) + assert len(messages) == 1 + async def test_send_presentation_ack_no_responder(self): exchange = V10PresentationExchange() diff --git a/aries_cloudagent/protocols/present_proof/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/present_proof/v1_0/tests/test_routes.py index 41424eb20e..555c079b58 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/tests/test_routes.py @@ -1005,6 +1005,7 @@ async def test_presentation_exchange_send_presentation(self): mock_presentation_exchange.state = ( test_module.V10PresentationExchange.STATE_REQUEST_RECEIVED ) + mock_presentation_exchange.connection_id = "dummy" mock_presentation_exchange.retrieve_by_id = async_mock.CoroutineMock( return_value=async_mock.MagicMock( state=mock_presentation_exchange.STATE_REQUEST_RECEIVED, diff --git a/aries_cloudagent/protocols/present_proof/v2_0/handlers/pres_ack_handler.py b/aries_cloudagent/protocols/present_proof/v2_0/handlers/pres_ack_handler.py index 4ba4c3b06c..d7d20508f1 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/handlers/pres_ack_handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/handlers/pres_ack_handler.py @@ -41,7 +41,8 @@ async def handle(self, context: RequestContext, responder: BaseResponder): # Either connection or oob context must be present if not context.connection_record and not oob_record: raise HandlerException( - "No connection or associated connectionless exchange found for presentation ack" + "No connection or associated connectionless exchange found for" + " presentation ack" ) pres_manager = V20PresManager(context.profile) diff --git a/aries_cloudagent/protocols/present_proof/v2_0/handlers/pres_handler.py b/aries_cloudagent/protocols/present_proof/v2_0/handlers/pres_handler.py index 654476e8eb..7e3d60f6f6 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/handlers/pres_handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/handlers/pres_handler.py @@ -40,15 +40,15 @@ async def handle(self, context: RequestContext, responder: BaseResponder): if context.connection_record and not context.connection_ready: raise HandlerException("Connection used for presentation not ready") - # Find associated oob record. If the presentation request was created as an oob attachment - # the presentation exchange record won't have a connection id (yet) + # Find associated oob record. If the presentation request was created as an oob + # attachment the presentation exchange record won't have a connection id (yet) oob_processor = context.inject(OobMessageProcessor) oob_record = await oob_processor.find_oob_record_for_inbound_message(context) # Normally we would do a check here that there is either a connection or # an associated oob record. However as present proof supported receiving - # presentation without oob record or connection record (aip-1 style connectionless) - # we can't perform this check here + # presentation without oob record or connection record + # (aip-1 style connectionless) we can't perform this check here pres_manager = V20PresManager(context.profile) diff --git a/aries_cloudagent/protocols/present_proof/v2_0/handlers/pres_request_handler.py b/aries_cloudagent/protocols/present_proof/v2_0/handlers/pres_request_handler.py index 5b6156c7ec..1d8abe88b6 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/handlers/pres_request_handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/handlers/pres_request_handler.py @@ -51,7 +51,8 @@ async def handle(self, context: RequestContext, responder: BaseResponder): # Either connection or oob context must be present if not context.connection_record and not oob_record: raise HandlerException( - "No connection or associated connectionless exchange found for presentation request" + "No connection or associated connectionless exchange found for" + " presentation request" ) connection_id = ( diff --git a/aries_cloudagent/protocols/present_proof/v2_0/handlers/tests/test_pres_ack_handler.py b/aries_cloudagent/protocols/present_proof/v2_0/handlers/tests/test_pres_ack_handler.py index 90ea997150..928a2df6be 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/handlers/tests/test_pres_ack_handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/handlers/tests/test_pres_ack_handler.py @@ -1,5 +1,6 @@ from asynctest import mock as async_mock, TestCase as AsyncTestCase +from ......core.oob_processor import OobMessageProcessor from ......messaging.request_context import RequestContext from ......messaging.responder import MockResponder from ......transport.inbound.receipt import MessageReceipt @@ -15,6 +16,13 @@ async def test_called(self): request_context.message_receipt = MessageReceipt() session = request_context.session() + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=async_mock.MagicMock() + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + with async_mock.patch.object( test_module, "V20PresManager", autospec=True ) as mock_pres_mgr: @@ -35,6 +43,7 @@ async def test_called(self): async def test_called_not_ready(self): request_context = RequestContext.test_context() request_context.message_receipt = MessageReceipt() + request_context.connection_record = async_mock.MagicMock() with async_mock.patch.object( test_module, "V20PresManager", autospec=True @@ -44,7 +53,32 @@ async def test_called_not_ready(self): request_context.connection_ready = False handler = test_module.V20PresAckHandler() responder = MockResponder() - with self.assertRaises(test_module.HandlerException): + with self.assertRaises(test_module.HandlerException) as err: await handler.handle(request_context, responder) + assert err.exception.message == "Connection used for presentation ack not ready" + + assert not responder.messages + + async def test_called_no_connection_no_oob(self): + request_context = RequestContext.test_context() + request_context.message_receipt = MessageReceipt() + + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + # No oob record found + return_value=None + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + + request_context.message = V20PresAck() + handler = test_module.V20PresAckHandler() + responder = MockResponder() + with self.assertRaises(test_module.HandlerException) as err: + await handler.handle(request_context, responder) + assert ( + err.exception.message + == "No connection or associated connectionless exchange found for presentation ack" + ) assert not responder.messages diff --git a/aries_cloudagent/protocols/present_proof/v2_0/handlers/tests/test_pres_handler.py b/aries_cloudagent/protocols/present_proof/v2_0/handlers/tests/test_pres_handler.py index 9eb3ba1ae6..0603e77626 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/handlers/tests/test_pres_handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/handlers/tests/test_pres_handler.py @@ -2,6 +2,7 @@ from asynctest import mock as async_mock, TestCase as AsyncTestCase +from ......core.oob_processor import OobMessageProcessor from ......messaging.request_context import RequestContext from ......messaging.responder import MockResponder from ......transport.inbound.receipt import MessageReceipt @@ -17,6 +18,14 @@ async def test_called(self): request_context.message_receipt = MessageReceipt() request_context.settings["debug.auto_verify_presentation"] = False + oob_record = async_mock.MagicMock() + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=oob_record + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + with async_mock.patch.object( test_module, "V20PresManager", autospec=True ) as mock_pres_mgr: @@ -30,7 +39,7 @@ async def test_called(self): mock_pres_mgr.assert_called_once_with(request_context.profile) mock_pres_mgr.return_value.receive_pres.assert_called_once_with( - request_context.message, request_context.connection_record + request_context.message, request_context.connection_record, oob_record ) assert not responder.messages @@ -39,6 +48,14 @@ async def test_called_auto_verify(self): request_context.message_receipt = MessageReceipt() request_context.settings["debug.auto_verify_presentation"] = True + oob_record = async_mock.MagicMock() + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=oob_record + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + with async_mock.patch.object( test_module, "V20PresManager", autospec=True ) as mock_pres_mgr: @@ -53,7 +70,7 @@ async def test_called_auto_verify(self): mock_pres_mgr.assert_called_once_with(request_context.profile) mock_pres_mgr.return_value.receive_pres.assert_called_once_with( - request_context.message, request_context.connection_record + request_context.message, request_context.connection_record, oob_record ) assert not responder.messages @@ -62,6 +79,14 @@ async def test_called_auto_verify_x(self): request_context.message_receipt = MessageReceipt() request_context.settings["debug.auto_verify_presentation"] = True + oob_record = async_mock.MagicMock() + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=oob_record + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + with async_mock.patch.object( test_module, "V20PresManager", autospec=True ) as mock_pres_mgr: diff --git a/aries_cloudagent/protocols/present_proof/v2_0/handlers/tests/test_pres_proposal_handler.py b/aries_cloudagent/protocols/present_proof/v2_0/handlers/tests/test_pres_proposal_handler.py index 3f8a857b87..033742133e 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/handlers/tests/test_pres_proposal_handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/handlers/tests/test_pres_proposal_handler.py @@ -1,5 +1,3 @@ -import pytest - from asynctest import mock as async_mock, TestCase as AsyncTestCase from ......messaging.request_context import RequestContext @@ -14,6 +12,7 @@ class TestV20PresProposalHandler(AsyncTestCase): async def test_called(self): request_context = RequestContext.test_context() + request_context.connection_record = async_mock.MagicMock() request_context.message_receipt = MessageReceipt() request_context.settings["debug.auto_respond_presentation_proposal"] = False @@ -39,6 +38,7 @@ async def test_called(self): async def test_called_auto_request(self): request_context = RequestContext.test_context() request_context.message = async_mock.MagicMock() + request_context.connection_record = async_mock.MagicMock() request_context.message.comment = "hello world" request_context.message_receipt = MessageReceipt() request_context.settings["debug.auto_respond_presentation_proposal"] = True @@ -76,6 +76,7 @@ async def test_called_auto_request(self): async def test_called_auto_request_x(self): request_context = RequestContext.test_context() + request_context.connection_record = async_mock.MagicMock() request_context.message = async_mock.MagicMock() request_context.message.comment = "hello world" request_context.message_receipt = MessageReceipt() @@ -107,6 +108,7 @@ async def test_called_auto_request_x(self): async def test_called_not_ready(self): request_context = RequestContext.test_context() request_context.message_receipt = MessageReceipt() + request_context.connection_record = async_mock.MagicMock() with async_mock.patch.object( test_module, "V20PresManager", autospec=True @@ -118,7 +120,27 @@ async def test_called_not_ready(self): request_context.connection_ready = False handler = test_module.V20PresProposalHandler() responder = MockResponder() - with self.assertRaises(test_module.HandlerException): + with self.assertRaises(test_module.HandlerException) as err: await handler.handle(request_context, responder) + assert ( + err.exception.message + == "Connection used for presentation proposal not ready" + ) + + assert not responder.messages + + async def test_called_no_connection(self): + request_context = RequestContext.test_context() + request_context.message_receipt = MessageReceipt() + + request_context.message = V20PresProposal() + handler = test_module.V20PresProposalHandler() + responder = MockResponder() + with self.assertRaises(test_module.HandlerException) as err: + await handler.handle(request_context, responder) + assert ( + err.exception.message + == "Connectionless not supported for presentation proposal" + ) assert not responder.messages diff --git a/aries_cloudagent/protocols/present_proof/v2_0/handlers/tests/test_pres_request_handler.py b/aries_cloudagent/protocols/present_proof/v2_0/handlers/tests/test_pres_request_handler.py index 12fac3c9bc..bd1e3d2174 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/handlers/tests/test_pres_request_handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/handlers/tests/test_pres_request_handler.py @@ -1,6 +1,8 @@ from asynctest import mock as async_mock, TestCase as AsyncTestCase from copy import deepcopy +from ......core.oob_processor import OobMessageProcessor +from ......indy.holder import IndyHolder from ......messaging.decorators.attach_decorator import AttachDecorator from ......messaging.request_context import RequestContext from ......messaging.responder import MockResponder @@ -189,6 +191,13 @@ async def test_called(self): return_value=async_mock.MagicMock() ) + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=async_mock.MagicMock() + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + pres_proposal = V20PresProposal( formats=[ V20PresFormat( @@ -227,6 +236,9 @@ async def test_called(self): mock_pres_mgr.return_value.receive_pres_request.assert_called_once_with( px_rec_instance ) + mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( + request_context + ) assert not responder.messages async def test_called_not_found(self): @@ -239,6 +251,13 @@ async def test_called_not_found(self): return_value=async_mock.MagicMock() ) + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=async_mock.MagicMock() + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + pres_proposal = V20PresProposal( formats=[ V20PresFormat( @@ -278,6 +297,9 @@ async def test_called_not_found(self): mock_pres_mgr.return_value.receive_pres_request.assert_called_once_with( px_rec_instance ) + mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( + request_context + ) assert not responder.messages async def test_called_auto_present_x(self): @@ -307,20 +329,26 @@ async def test_called_auto_present_x(self): save_error_state=async_mock.CoroutineMock(), ) - with async_mock.patch.object( - test_module, "V20PresManager", autospec=True - ) as mock_pres_mgr, async_mock.patch.object( - test_module, "V20PresExRecord", autospec=True - ) as mock_pres_ex_rec_cls, async_mock.patch.object( - test_indy_handler, "IndyHolder", autospec=True - ) as mock_holder: - - mock_holder.get_credentials_for_presentation_request_by_referent = ( + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=async_mock.MagicMock() + ) + ) + mock_holder = async_mock.MagicMock( + get_credentials_for_presentation_request_by_referent=( async_mock.CoroutineMock( return_value=[{"cred_info": {"referent": "dummy"}}] ) ) - request_context.inject = async_mock.MagicMock(return_value=mock_holder) + ) + request_context.injector.bind_instance(IndyHolder, mock_holder) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + + with async_mock.patch.object( + test_module, "V20PresManager", autospec=True + ) as mock_pres_mgr, async_mock.patch.object( + test_module, "V20PresExRecord", autospec=True + ) as mock_pres_ex_rec_cls: mock_pres_ex_rec_cls.return_value = mock_px_rec mock_pres_ex_rec_cls.retrieve_by_tag_filter = async_mock.CoroutineMock( @@ -354,6 +382,13 @@ async def test_called_auto_present_indy(self): ) request_context.message_receipt = MessageReceipt() + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=async_mock.MagicMock() + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + pres_proposal = V20PresProposal( formats=[ V20PresFormat( @@ -370,20 +405,20 @@ async def test_called_auto_present_indy(self): auto_present=True, ) - with async_mock.patch.object( - test_module, "V20PresManager", autospec=True - ) as mock_pres_mgr, async_mock.patch.object( - test_module, "V20PresExRecord", autospec=True - ) as mock_pres_ex_rec_cls, async_mock.patch.object( - test_indy_handler, "IndyHolder", autospec=True - ) as mock_holder: - - mock_holder.get_credentials_for_presentation_request_by_referent = ( + mock_holder = async_mock.MagicMock( + get_credentials_for_presentation_request_by_referent=( async_mock.CoroutineMock( return_value=[{"cred_info": {"referent": "dummy"}}] ) ) - request_context.inject = async_mock.MagicMock(return_value=mock_holder) + ) + request_context.injector.bind_instance(IndyHolder, mock_holder) + + with async_mock.patch.object( + test_module, "V20PresManager", autospec=True + ) as mock_pres_mgr, async_mock.patch.object( + test_module, "V20PresExRecord", autospec=True + ) as mock_pres_ex_rec_cls: mock_pres_ex_rec_cls.return_value = mock_px_rec mock_pres_ex_rec_cls.retrieve_by_tag_filter = async_mock.CoroutineMock( @@ -407,6 +442,9 @@ async def test_called_auto_present_indy(self): mock_pres_mgr.return_value.receive_pres_request.assert_called_once_with( mock_px_rec ) + mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( + request_context + ) messages = responder.messages assert len(messages) == 1 (result, target) = messages[0] @@ -429,6 +467,14 @@ async def test_called_auto_present_dif(self): return_value=DIF_PROOF_REQ ) request_context.message_receipt = MessageReceipt() + + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=async_mock.MagicMock() + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + pres_proposal = V20PresProposal( formats=[ V20PresFormat( @@ -469,6 +515,9 @@ async def test_called_auto_present_dif(self): mock_pres_mgr.return_value.receive_pres_request.assert_called_once_with( px_rec_instance ) + mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( + request_context + ) messages = responder.messages assert len(messages) == 1 (result, target) = messages[0] @@ -493,15 +542,14 @@ async def test_called_auto_present_no_preview(self): request_context.message_receipt = MessageReceipt() px_rec_instance = test_module.V20PresExRecord(auto_present=True) - with async_mock.patch.object( - test_module, "V20PresManager", autospec=True - ) as mock_pres_mgr, async_mock.patch.object( - test_module, "V20PresExRecord", autospec=True - ) as mock_pres_ex_rec_cls, async_mock.patch.object( - test_indy_handler, "IndyHolder", autospec=True - ) as mock_holder: - - mock_holder.get_credentials_for_presentation_request_by_referent = ( + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=async_mock.MagicMock() + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + mock_holder = async_mock.MagicMock( + get_credentials_for_presentation_request_by_referent=( async_mock.CoroutineMock( return_value=[ {"cred_info": {"referent": "dummy-0"}}, @@ -509,7 +557,14 @@ async def test_called_auto_present_no_preview(self): ] ) ) - request_context.inject = async_mock.MagicMock(return_value=mock_holder) + ) + request_context.injector.bind_instance(IndyHolder, mock_holder) + + with async_mock.patch.object( + test_module, "V20PresManager", autospec=True + ) as mock_pres_mgr, async_mock.patch.object( + test_module, "V20PresExRecord", autospec=True + ) as mock_pres_ex_rec_cls: mock_pres_ex_rec_cls.return_value = px_rec_instance mock_pres_ex_rec_cls.retrieve_by_tag_filter = async_mock.CoroutineMock( @@ -531,6 +586,9 @@ async def test_called_auto_present_no_preview(self): mock_pres_mgr.return_value.receive_pres_request.assert_called_once_with( px_rec_instance ) + mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( + request_context + ) messages = responder.messages assert len(messages) == 1 (result, target) = messages[0] @@ -563,18 +621,25 @@ async def test_called_auto_present_pred_no_match(self): save_error_state=async_mock.CoroutineMock(), ) + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=async_mock.MagicMock() + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + + mock_holder = async_mock.MagicMock( + get_credentials_for_presentation_request_by_referent=( + async_mock.CoroutineMock(return_value=[]) + ) + ) + request_context.injector.bind_instance(IndyHolder, mock_holder) + with async_mock.patch.object( test_module, "V20PresManager", autospec=True ) as mock_pres_mgr, async_mock.patch.object( test_module, "V20PresExRecord", autospec=True - ) as mock_pres_ex_rec_cls, async_mock.patch.object( - test_indy_handler, "IndyHolder", autospec=True - ) as mock_holder: - - mock_holder.get_credentials_for_presentation_request_by_referent = ( - async_mock.CoroutineMock(return_value=[]) - ) - request_context.inject = async_mock.MagicMock(return_value=mock_holder) + ) as mock_pres_ex_rec_cls: mock_pres_ex_rec_cls.return_value = mock_px_rec mock_pres_ex_rec_cls.retrieve_by_tag_filter = async_mock.CoroutineMock( @@ -597,6 +662,9 @@ async def test_called_auto_present_pred_no_match(self): mock_pres_mgr.return_value.receive_pres_request.assert_called_once_with( mock_px_rec ) + mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( + request_context + ) async def test_called_auto_present_pred_single_match(self): request_context = RequestContext.test_context() @@ -616,20 +684,27 @@ async def test_called_auto_present_pred_single_match(self): request_context.message_receipt = MessageReceipt() px_rec_instance = test_module.V20PresExRecord(auto_present=True) - with async_mock.patch.object( - test_module, "V20PresManager", autospec=True - ) as mock_pres_mgr, async_mock.patch.object( - test_module, "V20PresExRecord", autospec=True - ) as mock_pres_ex_rec_cls, async_mock.patch.object( - test_indy_handler, "IndyHolder", autospec=True - ) as mock_holder: + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=async_mock.MagicMock() + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) - mock_holder.get_credentials_for_presentation_request_by_referent = ( + mock_holder = async_mock.MagicMock( + get_credentials_for_presentation_request_by_referent=( async_mock.CoroutineMock( return_value=[{"cred_info": {"referent": "dummy-0"}}] ) ) - request_context.inject = async_mock.MagicMock(return_value=mock_holder) + ) + request_context.injector.bind_instance(IndyHolder, mock_holder) + + with async_mock.patch.object( + test_module, "V20PresManager", autospec=True + ) as mock_pres_mgr, async_mock.patch.object( + test_module, "V20PresExRecord", autospec=True + ) as mock_pres_ex_rec_cls: mock_pres_ex_rec_cls.return_value = px_rec_instance mock_pres_ex_rec_cls.retrieve_by_tag_filter = async_mock.CoroutineMock( @@ -651,6 +726,9 @@ async def test_called_auto_present_pred_single_match(self): mock_pres_mgr.return_value.receive_pres_request.assert_called_once_with( px_rec_instance ) + mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( + request_context + ) messages = responder.messages assert len(messages) == 1 (result, target) = messages[0] @@ -675,15 +753,15 @@ async def test_called_auto_present_pred_multi_match(self): request_context.message_receipt = MessageReceipt() px_rec_instance = test_module.V20PresExRecord(auto_present=True) - with async_mock.patch.object( - test_module, "V20PresManager", autospec=True - ) as mock_pres_mgr, async_mock.patch.object( - test_module, "V20PresExRecord", autospec=True - ) as mock_pres_ex_rec_cls, async_mock.patch.object( - test_indy_handler, "IndyHolder", autospec=True - ) as mock_holder: + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=async_mock.MagicMock() + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) - mock_holder.get_credentials_for_presentation_request_by_referent = ( + mock_holder = async_mock.MagicMock( + get_credentials_for_presentation_request_by_referent=( async_mock.CoroutineMock( return_value=[ {"cred_info": {"referent": "dummy-0"}}, @@ -691,7 +769,14 @@ async def test_called_auto_present_pred_multi_match(self): ] ) ) - request_context.inject = async_mock.MagicMock(return_value=mock_holder) + ) + request_context.injector.bind_instance(IndyHolder, mock_holder) + + with async_mock.patch.object( + test_module, "V20PresManager", autospec=True + ) as mock_pres_mgr, async_mock.patch.object( + test_module, "V20PresExRecord", autospec=True + ) as mock_pres_ex_rec_cls: mock_pres_ex_rec_cls.return_value = px_rec_instance mock_pres_ex_rec_cls.retrieve_by_tag_filter = async_mock.CoroutineMock( @@ -713,6 +798,9 @@ async def test_called_auto_present_pred_multi_match(self): mock_pres_mgr.return_value.receive_pres_request.assert_called_once_with( px_rec_instance ) + mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( + request_context + ) messages = responder.messages assert len(messages) == 1 (result, target) = messages[0] @@ -747,19 +835,15 @@ async def test_called_auto_present_multi_cred_match_reft(self): ], ) - px_rec_instance = test_module.V20PresExRecord( - pres_proposal=pres_proposal.serialize(), - auto_present=True, + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + return_value=async_mock.MagicMock() + ) ) - with async_mock.patch.object( - test_module, "V20PresManager", autospec=True - ) as mock_pres_mgr, async_mock.patch.object( - test_module, "V20PresExRecord", autospec=True - ) as mock_pres_ex_rec_cls, async_mock.patch.object( - test_indy_handler, "IndyHolder", autospec=True - ) as mock_holder: + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) - mock_holder.get_credentials_for_presentation_request_by_referent = ( + mock_holder = async_mock.MagicMock( + get_credentials_for_presentation_request_by_referent=( async_mock.CoroutineMock( return_value=[ { @@ -798,7 +882,18 @@ async def test_called_auto_present_multi_cred_match_reft(self): ] ) ) - request_context.inject = async_mock.MagicMock(return_value=mock_holder) + ) + request_context.injector.bind_instance(IndyHolder, mock_holder) + + px_rec_instance = test_module.V20PresExRecord( + pres_proposal=pres_proposal.serialize(), + auto_present=True, + ) + with async_mock.patch.object( + test_module, "V20PresManager", autospec=True + ) as mock_pres_mgr, async_mock.patch.object( + test_module, "V20PresExRecord", autospec=True + ) as mock_pres_ex_rec_cls: mock_pres_ex_rec_cls.return_value = px_rec_instance mock_pres_ex_rec_cls.retrieve_by_tag_filter = async_mock.CoroutineMock( @@ -820,6 +915,9 @@ async def test_called_auto_present_multi_cred_match_reft(self): mock_pres_mgr.return_value.receive_pres_request.assert_called_once_with( px_rec_instance ) + mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( + request_context + ) messages = responder.messages assert len(messages) == 1 (result, target) = messages[0] @@ -829,6 +927,7 @@ async def test_called_auto_present_multi_cred_match_reft(self): async def test_called_not_ready(self): request_context = RequestContext.test_context() request_context.message_receipt = MessageReceipt() + request_context.connection_record = async_mock.MagicMock() with async_mock.patch.object( test_module, "V20PresManager", autospec=True @@ -838,7 +937,35 @@ async def test_called_not_ready(self): request_context.connection_ready = False handler = test_module.V20PresRequestHandler() responder = MockResponder() - with self.assertRaises(test_module.HandlerException): + with self.assertRaises(test_module.HandlerException) as err: await handler.handle(request_context, responder) + assert ( + err.exception.message + == "Connection used for presentation request not ready" + ) + + assert not responder.messages + + async def test_no_conn_no_oob(self): + request_context = RequestContext.test_context() + request_context.message_receipt = MessageReceipt() + + mock_oob_processor = async_mock.MagicMock( + find_oob_record_for_inbound_message=async_mock.CoroutineMock( + # No oob record found + return_value=None + ) + ) + request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) + + request_context.message = V20PresRequest() + handler = test_module.V20PresRequestHandler() + responder = MockResponder() + with self.assertRaises(test_module.HandlerException) as err: + await handler.handle(request_context, responder) + assert ( + err.exception.message + == "No connection or associated connectionless exchange found for presentation request" + ) assert not responder.messages diff --git a/aries_cloudagent/protocols/present_proof/v2_0/manager.py b/aries_cloudagent/protocols/present_proof/v2_0/manager.py index b1c5c1f3c9..797facb310 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/manager.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/manager.py @@ -312,8 +312,8 @@ async def receive_pres( thread_id = message._thread_id # Normally we only set the connection_id to None if an oob record is present # But present proof supports the old-style AIP-1 connectionless exchange that - # bypasses the oob record. So we can't verify if an oob record is associated with the - # exchange because it is possible that there is None + # bypasses the oob record. So we can't verify if an oob record is associated with + # the exchange because it is possible that there is None connection_id = ( None if oob_record diff --git a/aries_cloudagent/protocols/present_proof/v2_0/routes.py b/aries_cloudagent/protocols/present_proof/v2_0/routes.py index af9b14dd14..7226ca97a0 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/routes.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/routes.py @@ -1043,13 +1043,6 @@ async def present_proof_send_presentation(request: web.BaseRequest): try: async with profile.session() as session: pres_ex_record = await V20PresExRecord.retrieve_by_id(session, pres_ex_id) - - # Fetch connection if exchange has record - conn_record = None - if pres_ex_record.connection_id: - conn_record = await ConnRecord.retrieve_by_id( - session, pres_ex_record.connection_id - ) except StorageNotFoundError as err: raise web.HTTPNotFound(reason=err.roll_up) from err @@ -1062,6 +1055,18 @@ async def present_proof_send_presentation(request: web.BaseRequest): ) ) + # Fetch connection if exchange has record + conn_record = None + if pres_ex_record.connection_id: + try: + async with profile.session() as session: + + conn_record = await ConnRecord.retrieve_by_id( + session, pres_ex_record.connection_id + ) + except StorageNotFoundError as err: + raise web.HTTPBadRequest(reason=err.roll_up) from err + if conn_record and not conn_record.is_ready: raise web.HTTPForbidden( reason=f"Connection {pres_ex_record.connection_id} not ready" diff --git a/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py b/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py index bf293f3cb6..375bb2426f 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py @@ -688,15 +688,9 @@ async def test_receive_pres_catch_diferror(self): V20PresExRecord, "retrieve_by_tag_filter", autospec=True ) as retrieve_ex: mock_receive_pres.return_value = False - retrieve_ex.side_effect = [ - StorageNotFoundError("no such record"), # cover out-of-band - px_rec, - ] + retrieve_ex.side_effect = [px_rec] with self.assertRaises(V20PresManagerError) as context: - await self.manager.receive_pres( - pres_x, - connection_record, - ) + await self.manager.receive_pres(pres_x, connection_record, None) assert "Unable to verify received presentation." in str(context.exception) async def test_create_exchange_for_request(self): @@ -1261,6 +1255,7 @@ async def test_receive_pres(self): AttachDecorator.data_base64(INDY_PROOF, ident="indy") ], ) + pres.assign_thread_id("thread-id") px_rec_dummy = V20PresExRecord( pres_proposal=pres_proposal.serialize(), @@ -1282,12 +1277,13 @@ async def test_receive_pres(self): "session", async_mock.MagicMock(return_value=self.profile.session()), ) as session: - retrieve_ex.side_effect = [ - StorageNotFoundError("no such record"), # cover out-of-band - px_rec_dummy, - ] - px_rec_out = await self.manager.receive_pres(pres, connection_record) - assert retrieve_ex.call_count == 2 + retrieve_ex.side_effect = [px_rec_dummy] + px_rec_out = await self.manager.receive_pres(pres, connection_record, None) + retrieve_ex.assert_called_once_with( + session.return_value, + {"thread_id": "thread-id"}, + {"role": V20PresExRecord.ROLE_VERIFIER, "connection_id": CONN_ID}, + ) save_ex.assert_called_once() assert px_rec_out.state == (V20PresExRecord.STATE_PRESENTATION_RECEIVED) @@ -1334,6 +1330,7 @@ async def test_receive_pres_receive_pred_value_mismatch_punt_to_indy(self): AttachDecorator.data_base64(INDY_PROOF, ident="indy") ], ) + pres.assign_thread_id("thread-id") px_rec_dummy = V20PresExRecord( pres_proposal=pres_proposal.serialize(), @@ -1355,12 +1352,13 @@ async def test_receive_pres_receive_pred_value_mismatch_punt_to_indy(self): "session", async_mock.MagicMock(return_value=self.profile.session()), ) as session: - retrieve_ex.side_effect = [ - StorageNotFoundError("no such record"), # cover out-of-band - px_rec_dummy, - ] - px_rec_out = await self.manager.receive_pres(pres, connection_record) - assert retrieve_ex.call_count == 2 + retrieve_ex.side_effect = [px_rec_dummy] + px_rec_out = await self.manager.receive_pres(pres, connection_record, None) + retrieve_ex.assert_called_once_with( + session.return_value, + {"thread_id": "thread-id"}, + {"role": V20PresExRecord.ROLE_VERIFIER, "connection_id": CONN_ID}, + ) save_ex.assert_called_once() assert px_rec_out.state == (V20PresExRecord.STATE_PRESENTATION_RECEIVED) @@ -1421,7 +1419,7 @@ async def test_receive_pres_bait_and_switch_attr_name(self): ) as retrieve_ex: retrieve_ex.return_value = px_rec_dummy with self.assertRaises(V20PresFormatHandlerError) as context: - await self.manager.receive_pres(pres_x, connection_record) + await self.manager.receive_pres(pres_x, connection_record, None) assert "does not satisfy proof request restrictions" in str( context.exception ) @@ -1477,7 +1475,7 @@ async def test_receive_pres_bait_and_switch_attr_name(self): ) as retrieve_ex: retrieve_ex.return_value = px_rec_dummy with self.assertRaises(V20PresFormatHandlerError) as context: - await self.manager.receive_pres(pres_x, connection_record) + await self.manager.receive_pres(pres_x, connection_record, None) assert "Presentation referent" in str(context.exception) async def test_receive_pres_bait_and_switch_attr_names(self): @@ -1536,7 +1534,7 @@ async def test_receive_pres_bait_and_switch_attr_names(self): ) as retrieve_ex: retrieve_ex.return_value = px_rec_dummy with self.assertRaises(V20PresFormatHandlerError) as context: - await self.manager.receive_pres(pres_x, connection_record) + await self.manager.receive_pres(pres_x, connection_record, None) assert "does not satisfy proof request restrictions " in str( context.exception ) @@ -1592,7 +1590,7 @@ async def test_receive_pres_bait_and_switch_attr_names(self): ) as retrieve_ex: retrieve_ex.return_value = px_rec_dummy with self.assertRaises(V20PresFormatHandlerError) as context: - await self.manager.receive_pres(pres_x, connection_record) + await self.manager.receive_pres(pres_x, connection_record, None) assert "Presentation referent" in str(context.exception) async def test_receive_pres_bait_and_switch_pred(self): @@ -1649,7 +1647,7 @@ async def test_receive_pres_bait_and_switch_pred(self): ) as retrieve_ex: retrieve_ex.return_value = px_rec_dummy with self.assertRaises(V20PresFormatHandlerError) as context: - await self.manager.receive_pres(pres_x, connection_record) + await self.manager.receive_pres(pres_x, connection_record, None) assert "not in proposal request" in str(context.exception) indy_proof_req["requested_predicates"]["0_highscore_GE_uuid"] = { @@ -1707,7 +1705,7 @@ async def test_receive_pres_bait_and_switch_pred(self): ) as retrieve_ex: retrieve_ex.return_value = px_rec_dummy with self.assertRaises(V20PresFormatHandlerError) as context: - await self.manager.receive_pres(pres_x, connection_record) + await self.manager.receive_pres(pres_x, connection_record, None) assert "shenanigans not in presentation" in str(context.exception) indy_proof_req["requested_predicates"]["0_highscore_GE_uuid"] = { @@ -1765,7 +1763,7 @@ async def test_receive_pres_bait_and_switch_pred(self): ) as retrieve_ex: retrieve_ex.return_value = px_rec_dummy with self.assertRaises(V20PresFormatHandlerError) as context: - await self.manager.receive_pres(pres_x, connection_record) + await self.manager.receive_pres(pres_x, connection_record, None) assert "highScore mismatches proposal request" in str(context.exception) indy_proof_req["requested_predicates"]["0_highscore_GE_uuid"] = { @@ -1823,7 +1821,7 @@ async def test_receive_pres_bait_and_switch_pred(self): ) as retrieve_ex: retrieve_ex.return_value = px_rec_dummy with self.assertRaises(V20PresFormatHandlerError) as context: - await self.manager.receive_pres(pres_x, connection_record) + await self.manager.receive_pres(pres_x, connection_record, None) assert "does not satisfy proof request restrictions " in str( context.exception ) @@ -1962,7 +1960,7 @@ async def test_receive_problem_report_x(self): "retrieve_by_tag_filter", async_mock.CoroutineMock(), ) as retrieve_ex: - retrieve_ex.side_effect = test_module.StorageNotFoundError("No such record") + retrieve_ex.side_effect = StorageNotFoundError("No such record") - with self.assertRaises(test_module.StorageNotFoundError): + with self.assertRaises(StorageNotFoundError): await self.manager.receive_problem_report(problem, connection_id) diff --git a/aries_cloudagent/protocols/present_proof/v2_0/tests/test_routes.py b/aries_cloudagent/protocols/present_proof/v2_0/tests/test_routes.py index 7678bed49d..934404203f 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/tests/test_routes.py @@ -2048,7 +2048,7 @@ async def test_present_proof_send_presentation_bad_state(self): test_module, "V20PresExRecord", autospec=True ) as mock_px_rec_cls: mock_px_rec_inst = async_mock.MagicMock( - connection_id="dummy", + connection_id=None, state=test_module.V20PresExRecord.STATE_DONE, serialize=async_mock.MagicMock( return_value={"thread_id": "sample-thread-id"} From ea52a0a3d2d2eade4fdcd95f3bc73c0687049aab Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sun, 3 Apr 2022 17:24:19 +0200 Subject: [PATCH 160/872] style: flake errors Signed-off-by: Timo Glastra --- aries_cloudagent/core/conductor.py | 3 +- aries_cloudagent/core/oob_processor.py | 76 ++++++++++++------- .../protocols/out_of_band/v1_0/manager.py | 44 ++++++----- 3 files changed, 76 insertions(+), 47 deletions(-) diff --git a/aries_cloudagent/core/conductor.py b/aries_cloudagent/core/conductor.py index 82ef06bea3..5afef97277 100644 --- a/aries_cloudagent/core/conductor.py +++ b/aries_cloudagent/core/conductor.py @@ -207,7 +207,8 @@ async def setup(self): BaseMultitenantManager, MultitenantManagerProvider(self.root_profile) ) - # Bind oob message processor to be able to receive and process un-encrypted messages + # Bind oob message processor to be able to receive and process un-encrypted + # messages context.injector.bind_instance( OobMessageProcessor, OobMessageProcessor(inbound_message_router=self.inbound_message_router), diff --git a/aries_cloudagent/core/oob_processor.py b/aries_cloudagent/core/oob_processor.py index e6eab20d88..28836be151 100644 --- a/aries_cloudagent/core/oob_processor.py +++ b/aries_cloudagent/core/oob_processor.py @@ -33,8 +33,7 @@ def __init__( [Profile, InboundMessage, Optional[bool]], None ], ) -> None: - """ - Initialize an inbound OOB message processor + """Initialize an inbound OOB message processor. Args: inbound_message_router: Method to create a new inbound session @@ -44,6 +43,7 @@ def __init__( self.wire_format = JsonWireFormat() async def clean_finished_oob_record(self, profile: Profile, message: AgentMessage): + """Clean up oob record associated with agent message, if applicable.""" try: async with profile.session() as session: oob_record = await OobRecord.retrieve_by_tag_filter( @@ -65,6 +65,7 @@ async def clean_finished_oob_record(self, profile: Profile, message: AgentMessag async def find_oob_target_for_outbound_message( self, profile: Profile, outbound_message: OutboundMessage ) -> Optional[ConnectionTarget]: + """Find connection target for the outbound message.""" try: async with profile.session() as session: # Try to find the oob record for the outbound message: @@ -88,8 +89,6 @@ async def find_oob_target_for_outbound_message( ) message["~service"] = oob_record.our_service - # OOB_TODO: state is somewhat done, but we need it for connectionless exchange - # if is_first_response: message["~thread"] = { **message.get("~thread", {}), "pthid": oob_record.invi_msg_id, @@ -111,6 +110,7 @@ async def find_oob_target_for_outbound_message( async def find_oob_record_for_inbound_message( self, context: RequestContext ) -> Optional[OobRecord]: + """Find oob record for inbound message.""" message_type = context.message._type oob_record = None @@ -119,7 +119,9 @@ async def find_oob_record_for_inbound_message( if context.message_receipt.parent_thread_id: try: LOGGER.debug( - f"Retrieving OOB record using pthid {context.message_receipt.parent_thread_id} for message type {message_type}" + "Retrieving OOB record using pthid " + f"{context.message_receipt.parent_thread_id} " + f"for message type {message_type}" ) oob_record = await OobRecord.retrieve_by_tag_filter( session, @@ -129,9 +131,10 @@ async def find_oob_record_for_inbound_message( # Fine if record is not found pass # Otherwise try to find it using the attach thread id. This is only needed - # for connectionless exchanges where every handlers needs the context of the oob - # record for verification. We could attach the oob_record to all messages, even if - # we have a connection, but it would add another query to all inbound messages. + # for connectionless exchanges where every handlers needs the context of the + # oob record for verification. We could attach the oob_record to all messages, + # even if we have a connection, but it would add another query to all inbound + # messages. if ( not oob_record and not context.connection_record @@ -140,7 +143,10 @@ async def find_oob_record_for_inbound_message( ): try: LOGGER.debug( - f"Retrieving OOB record using thid {context.message_receipt.thread_id} and recipient verkey {context.message_receipt.recipient_verkey} for message type {message_type}" + "Retrieving OOB record using thid " + f"{context.message_receipt.thread_id} and recipient verkey" + f" {context.message_receipt.recipient_verkey} for " + f"message type {message_type}" ) oob_record = await OobRecord.retrieve_by_tag_filter( session, @@ -158,7 +164,8 @@ async def find_oob_record_for_inbound_message( return None LOGGER.debug( - f"Found out of band record for inbound message with type {message_type}: {oob_record.oob_id}" + f"Found out of band record for inbound message with type {message_type}" + f": {oob_record.oob_id}" ) # If the connection does not match with the connection id associated with the @@ -173,23 +180,30 @@ async def find_oob_record_for_inbound_message( and context.connection_record.connection_id != oob_record.connection_id ): LOGGER.debug( - f"Oob record connection id {oob_record.connection_id} is different from inbound message connection {context.connection_record.connection_id}", + f"Oob record connection id {oob_record.connection_id} is different from" + f" inbound message connection {context.connection_record.connection_id}", ) - # Mismatch in connection id's in only allowed in state await response (connection id can change bc of reuse) + # Mismatch in connection id's in only allowed in state await response + # (connection id can change bc of reuse) if oob_record.state != OobRecord.STATE_AWAIT_RESPONSE: LOGGER.debug( - f"Inbound message has incorrect connection_id {context.connection_record.connection_id}. Oob record {oob_record.oob_id} associated with connection id {oob_record.connection_id}" + "Inbound message has incorrect connection_id " + f"{context.connection_record.connection_id}. Oob record " + f"{oob_record.oob_id} associated with connection id " + f"{oob_record.connection_id}" ) return None - # If the state is await response, and there are attachments we want to update the connection id - # on the oob record. In case no request_attach is present, this is handled by the reuse handlers + # If the state is await response, and there are attachments we want to update + # the connection id on the oob record. In case no request_attach is present, + # this is handled by the reuse handlers if ( oob_record.invitation.requests_attach and oob_record.state == OobRecord.STATE_AWAIT_RESPONSE ): LOGGER.debug( - f"Removing stale connection {oob_record.connection_id} due to connection reuse" + f"Removing stale connection {oob_record.connection_id} due " + "to connection reuse" ) # Remove stale connection due to connection reuse if oob_record.connection_id: @@ -201,18 +215,21 @@ async def find_oob_record_for_inbound_message( oob_record.connection_id = context.connection_record.connection_id - # If no attach_thread_id is stored yet we need to match the current message thread_id against the attached messages - # in the oob invitation + # If no attach_thread_id is stored yet we need to match the current message + # thread_id against the attached messages in the oob invitation if not oob_record.attach_thread_id and oob_record.invitation.requests_attach: - # Check if the current message thread_id corresponds to one of the invitation ~thread.thid + # Check if the current message thread_id corresponds to one of the invitation + # ~thread.thid allowed_thread_ids = [ self.get_thread_id(attachment.content) for attachment in oob_record.invitation.requests_attach ] - if not context.message_receipt.thread_id in allowed_thread_ids: + if context.message_receipt.thread_id not in allowed_thread_ids: LOGGER.debug( - f"Inbound message is for not allowed thread {context.message_receipt.thread_id}. Allowed threads are {allowed_thread_ids}" + "Inbound message is for not allowed thread " + f"{context.message_receipt.thread_id}. Allowed " + f"threads are {allowed_thread_ids}" ) return None @@ -222,7 +239,8 @@ async def find_oob_record_for_inbound_message( and context.message_receipt.thread_id != oob_record.attach_thread_id ): LOGGER.debug( - f"Inbound message thread id {context.message_receipt.thread_id} does not match oob record thread id {oob_record.attach_thread_id}" + f"Inbound message thread id {context.message_receipt.thread_id} does not" + f" match oob record thread id {oob_record.attach_thread_id}" ) return None @@ -248,11 +266,13 @@ async def find_oob_record_for_inbound_message( ) ): LOGGER.debug( - "Inbound message sender verkey does not match stored service on oob record" + "Inbound message sender verkey does not match stored service on oob" + " record" ) return None - # If the message has a ~service decorator we save it in the oob record so we can reply to this message + # If the message has a ~service decorator we save it in the oob record so we + # can reply to this message if context._message._service: LOGGER.debug( "Storing service decorator in oob record %s", @@ -261,8 +281,8 @@ async def find_oob_record_for_inbound_message( oob_record.their_service = context.message._service.serialize() async with context.profile.session() as session: - # We can now remove the oob record as the connection should now be stored in the - # exchange record itself. + # We can now remove the oob record as the connection should now be stored in + # the exchange record itself. if oob_record.connection_id: oob_record.state = OobRecord.STATE_DONE await oob_record.emit_event(session) @@ -298,7 +318,8 @@ async def handle_message( if not supported_messages: raise Exception( - f"None of the oob attached messages supported. Supported message types are {supported_types}" + f"None of the oob attached messages supported. Supported message types " + f"are {supported_types}" ) message = supported_messages[0] @@ -325,4 +346,5 @@ async def handle_message( self._inbound_message_router(profile, inbound_message, False) def get_thread_id(self, message: Dict[str, Any]) -> str: + """Extract thread id from agent message dict.""" return message.get("~thread", {}).get("thid") or message.get("@id") diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py index efbc66b9cf..bc5f4088df 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py @@ -16,7 +16,6 @@ from ....core.oob_processor import OobMessageProcessor from ....core.profile import Profile from ....did.did_key import DIDKey -from ....messaging.decorators.attach_decorator import AttachDecorator from ....messaging.responder import BaseResponder from ....multitenant.base import BaseMultitenantManager from ....storage.error import StorageNotFoundError @@ -338,7 +337,9 @@ async def create_invitation( session, MediationManager.METADATA_KEY, { - MediationManager.METADATA_ID: mediation_record.mediation_id + MediationManager.METADATA_ID: ( + mediation_record.mediation_id + ) }, ) @@ -465,7 +466,8 @@ async def receive_invitation( public_did is not None and use_existing_connection ): # invite has public DID: seek existing connection LOGGER.debug( - f"Trying to find existing connection for oob invitation with did {public_did}" + "Trying to find existing connection for oob invitation with " + f"did {public_did}" ) async with self._profile.session() as session: conn_rec = await ConnRecord.find_existing_connection( @@ -500,8 +502,8 @@ async def receive_invitation( # Will make new connection conn_rec = None - # Try to create a connection. Either if the reuse failed or we didn't have a connection yet - # Throws an error if connection could not be created + # Try to create a connection. Either if the reuse failed or we didn't have a + # connection yet. Throws an error if connection could not be created if not conn_rec and invitation.handshake_protocols: oob_record = await self._perform_handshake( oob_record=oob_record, @@ -521,11 +523,13 @@ async def receive_invitation( # Handle any attachments if invitation.requests_attach: LOGGER.debug( - f"Process attached messages for oob exchange {oob_record.oob_id} (connection_id {oob_record.connection_id})" + f"Process attached messages for oob exchange {oob_record.oob_id} " + f"(connection_id {oob_record.connection_id})" ) - # FIXME: this should ideally be handled using an event handler. Once the connection is ready - # we start processing the attached messages. For now we use the timeout method + # FIXME: this should ideally be handled using an event handler. Once the + # connection is ready we start processing the attached messages. + # For now we use the timeout method if ( conn_rec and not conn_rec.is_ready @@ -553,8 +557,8 @@ async def receive_invitation( await self._respond_request_attach(oob_record) # If a connection record is associated with the oob record we can remove it now as - # we can leverage the connection for all exchanges. Otherwise we need to keep it around - # for the connectionless exchange + # we can leverage the connection for all exchanges. Otherwise we need to keep it + # around for the connectionless exchange if conn_rec: oob_record.state = OobRecord.STATE_DONE async with self.profile.session() as session: @@ -614,9 +618,11 @@ async def _service_decorator_from_service( async def _wait_for_reuse_response( self, oob_id: str, timeout: int = 15 ) -> OobRecord: - """ - Wait for reuse response message state. Either by receiving a reuse accepted or problem - report. If no answer is received withing the timeout, the state will be set to reuse_not_acceted + """Wait for reuse response. + + Wait for reuse response message state. Either by receiving a reuse accepted or + problem report. If no answer is received withing the timeout, the state will be + set to reuse_not_accepted Args: oob_id: Identifier of the oob record @@ -649,8 +655,6 @@ async def _wait_for_state() -> OobRecord: return oob_record LOGGER.debug(f"Wait for oob {oob_id} to receive reuse accepted mesage") - # FIXME: event is not being picked up by the event listener. Is it the event, the cond? - # Wait for oob_record to have reuse_accepted state event = await await_event LOGGER.debug("Received reuse response message") return OobRecord.deserialize(event.payload) @@ -717,7 +721,8 @@ async def _handle_hanshake_reuse( # Wait for the reuse accepted message oob_record = await self._wait_for_reuse_response(oob_record.oob_id) LOGGER.debug( - f"Oob reuse for oob id {oob_record.oob_id} with connection {oob_record.connection_id} finished with state {oob_record.state}" + f"Oob reuse for oob id {oob_record.oob_id} with connection " + f"{oob_record.connection_id} finished with state {oob_record.state}" ) if oob_record.state != OobRecord.STATE_ACCEPTED: @@ -844,7 +849,8 @@ async def _perform_handshake( if not conn_record: raise OutOfBandManagerError( - f"Unable to create connection. Could not perform handshake using any of the handshake_protocols (supported {supported_handshake_protocols})" + f"Unable to create connection. Could not perform handshake using any of " + f"the handshake_protocols (supported {supported_handshake_protocols})" ) async with self.profile.session() as session: @@ -958,8 +964,8 @@ async def receive_reuse_message( oob_record.reuse_msg_id = reuse_msg_id oob_record.connection_id = conn_rec.connection_id - # We don't want to store this state. We either remove the record (no multi-use) - # or we can't update the record (multi-use) + # We don't want to store this state. We either remove the record + # (no multi-use) or we can't update the record (multi-use) await oob_record.emit_event(session) # If the oob_record is not multi-use we can now remove it From e688647efa954d451e3e3cba24c76840ef7821d8 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sun, 3 Apr 2022 18:37:11 +0200 Subject: [PATCH 161/872] test: fix some tests Signed-off-by: Timo Glastra --- .../protocols/connections/v1_0/tests/test_manager.py | 11 ++++++++++- .../protocols/didexchange/v1_0/tests/test_manager.py | 10 +++++++++- aries_cloudagent/vc/vc_ld/tests/test_vc_ld.py | 1 + 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py index c986cb88cf..6b97d635e2 100644 --- a/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py @@ -11,6 +11,7 @@ from .....connections.models.conn_record import ConnRecord from .....connections.models.connection_target import ConnectionTarget from .....connections.models.diddoc import DIDDoc, PublicKey, PublicKeyType, Service +from .....core.oob_processor import OobMessageProcessor from .....core.in_memory import InMemoryProfile from .....core.profile import ProfileSession from .....did.did_key import DIDKey @@ -73,6 +74,12 @@ async def setUp(self): self.responder = MockResponder() + self.oob_mock = async_mock.MagicMock( + clean_finished_oob_record=async_mock.CoroutineMock( + return_value=None + ) + ) + self.profile = InMemoryProfile.test_profile( { "default_endpoint": "http://aries.ca/endpoint", @@ -81,7 +88,7 @@ async def setUp(self): "debug.auto_accept_invites": True, "debug.auto_accept_requests": True, }, - bind={BaseResponder: self.responder, BaseCache: InMemoryCache()}, + bind={BaseResponder: self.responder, BaseCache: InMemoryCache(), OobMessageProcessor: self.oob_mock}, ) self.context = self.profile.context @@ -713,6 +720,8 @@ async def test_receive_request_public_did_oob_invite(self): conn_rec = await self.manager.receive_request(mock_request, receipt) assert conn_rec + self.oob_mock.clean_finished_oob_record.assert_called_once_with(self.profile, mock_request) + async def test_receive_request_public_did_conn_invite(self): async with self.profile.session() as session: mock_request = async_mock.MagicMock() diff --git a/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py index aee31fa30b..f2fdd51af1 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py @@ -12,6 +12,7 @@ PublicKeyType, Service, ) +from .....core.oob_processor import OobMessageProcessor from .....core.in_memory import InMemoryProfile from .....ledger.base import BaseLedger from .....messaging.responder import BaseResponder, MockResponder @@ -77,6 +78,12 @@ class TestDidExchangeManager(AsyncTestCase, TestConfig): async def setUp(self): self.responder = MockResponder() + self.oob_mock = async_mock.MagicMock( + clean_finished_oob_record=async_mock.CoroutineMock( + return_value=None + ) + ) + self.profile = InMemoryProfile.test_profile( { "default_endpoint": "http://aries.ca/endpoint", @@ -87,7 +94,7 @@ async def setUp(self): "multitenant.enabled": True, "wallet.id": True, }, - bind={BaseResponder: self.responder, BaseCache: InMemoryCache()}, + bind={BaseResponder: self.responder, BaseCache: InMemoryCache(), OobMessageProcessor: self.oob_mock}, ) self.context = self.profile.context async with self.profile.session() as session: @@ -542,6 +549,7 @@ async def test_receive_request_explicit_public_did(self): mediation_id=None, ) assert conn_rec + self.oob_mock.clean_finished_oob_record.assert_called_once_with(self.profile, mock_request) async def test_receive_request_invi_not_found(self): async with self.profile.session() as session: diff --git a/aries_cloudagent/vc/vc_ld/tests/test_vc_ld.py b/aries_cloudagent/vc/vc_ld/tests/test_vc_ld.py index 471220e09f..8fe29c799f 100644 --- a/aries_cloudagent/vc/vc_ld/tests/test_vc_ld.py +++ b/aries_cloudagent/vc/vc_ld/tests/test_vc_ld.py @@ -208,6 +208,7 @@ async def test_create_presentation_x_invalid_credential_structures(self): presentation_id="https://presentation_id.com", ) + @pytest.mark.ursa_bbs_signatures async def test_sign_presentation_bbsbls(self): unsigned_presentation = await create_presentation( credentials=[CREDENTIAL_ISSUED] From 2793267f853aa0d7f34259e89734db44ebe2602d Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 4 Apr 2022 16:01:38 +0200 Subject: [PATCH 162/872] test: fix oob tests Signed-off-by: Timo Glastra --- aries_cloudagent/core/oob_processor.py | 7 +- .../protocols/out_of_band/v1_0/manager.py | 31 +- .../v1_0/models/tests/test_invitation.py | 1 + .../out_of_band/v1_0/tests/test_manager.py | 3366 ++++------------- 4 files changed, 773 insertions(+), 2632 deletions(-) diff --git a/aries_cloudagent/core/oob_processor.py b/aries_cloudagent/core/oob_processor.py index 28836be151..ce76aa68e2 100644 --- a/aries_cloudagent/core/oob_processor.py +++ b/aries_cloudagent/core/oob_processor.py @@ -19,11 +19,16 @@ from ..transport.inbound.message import InboundMessage from ..transport.outbound.message import OutboundMessage from ..transport.wire_format import JsonWireFormat +from .error import BaseError from .profile import Profile LOGGER = logging.getLogger(__name__) +class OobMessageProcessorError(BaseError): + """Base error for OobMessageProcessor.""" + + class OobMessageProcessor: """Out of band message processor.""" @@ -317,7 +322,7 @@ async def handle_message( ] if not supported_messages: - raise Exception( + raise OobMessageProcessorError( f"None of the oob attached messages supported. Supported message types " f"are {supported_types}" ) diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py index bc5f4088df..e64f0d5e57 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py @@ -137,6 +137,10 @@ async def create_invitation( and self.profile.settings.get("debug.auto_accept_requests") ) ) + if not hs_protos and metadata: + raise OutOfBandManagerError( + "Cannot store metadata without handshake protocols" + ) if public: if multi_use: raise OutOfBandManagerError( @@ -482,11 +486,6 @@ async def receive_invitation( connection_id=conn_rec.connection_id if conn_rec else None, ) - # # Save record - # # OOB_TODO: I think we can remove this save. Other paths will save the record - # async with self.profile.session() as session: - # await oob_record.save(session) - # Try to reuse the connection. If not accepted sets the conn_rec to None if conn_rec and not invitation.requests_attach: oob_record = await self._handle_hanshake_reuse(oob_record, conn_rec) @@ -536,8 +535,8 @@ async def receive_invitation( and not await self._wait_for_conn_rec_active(conn_rec.connection_id) ): raise OutOfBandManagerError( - "Connection not ready to process attach message" - f"For connection_id: {oob_record.connection_id} and " + "Connection not ready to process attach message " + f"for connection_id: {oob_record.connection_id} and " f"invitation_msg_id {invitation._id}", ) @@ -554,7 +553,7 @@ async def receive_invitation( ).serialize() await oob_record.save(session) - await self._respond_request_attach(oob_record) + await self._process_request_attach(oob_record) # If a connection record is associated with the oob record we can remove it now as # we can leverage the connection for all exchanges. Otherwise we need to keep it @@ -562,12 +561,12 @@ async def receive_invitation( if conn_rec: oob_record.state = OobRecord.STATE_DONE async with self.profile.session() as session: - await oob_record.save(session) + await oob_record.emit_event(session) await oob_record.delete_record(session) return oob_record - async def _respond_request_attach(self, oob_record: OobRecord): + async def _process_request_attach(self, oob_record: OobRecord): invitation = oob_record.invitation message_processor = self.profile.inject(OobMessageProcessor) @@ -1069,10 +1068,8 @@ async def receive_reuse_accepted_message( ) raise OutOfBandManagerError( ( - ( - "Error processing reuse accepted message " - f"for OOB invitation {invi_msg_id}, {e}" - ) + "Error processing reuse accepted message " + f"for OOB invitation {invi_msg_id}, {e}" ) ) @@ -1112,9 +1109,7 @@ async def receive_problem_report( except Exception as e: raise OutOfBandManagerError( ( - ( - "Error processing problem report message " - f"for OOB invitation {invi_msg_id}, {e}" - ) + "Error processing problem report message " + f"for OOB invitation {invi_msg_id}, {e}" ) ) diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/models/tests/test_invitation.py b/aries_cloudagent/protocols/out_of_band/v1_0/models/tests/test_invitation.py index 3c1bc0fac5..db2c2b714d 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/models/tests/test_invitation.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/models/tests/test_invitation.py @@ -23,6 +23,7 @@ def test_invitation_record(self): "invitation_url": None, "state": None, "trace": False, + "oob_id": None } another = InvitationRecord(invi_msg_id="99999") diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py index 297d10c59b..2eeb95cac1 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py @@ -1,105 +1,91 @@ """Test OOB Manager.""" -import asyncio import json - -from asynctest import mock as async_mock, TestCase as AsyncTestCase from copy import deepcopy -from datetime import datetime, timezone, timedelta -from uuid import UUID +from datetime import datetime, timedelta, timezone +from typing import List +from unittest.mock import ANY + +from asynctest import TestCase as AsyncTestCase, mock as async_mock from .....connections.models.conn_record import ConnRecord from .....connections.models.connection_target import ConnectionTarget from .....connections.models.diddoc import DIDDoc, PublicKey, PublicKeyType, Service +from .....core.event_bus import EventBus from .....core.in_memory import InMemoryProfile -from .....core.profile import ProfileSession +from .....core.oob_processor import OobMessageProcessor from .....did.did_key import DIDKey -from .....indy.holder import IndyHolder -from .....indy.models.pres_preview import ( - IndyPresAttrSpec, - IndyPresPredSpec, - IndyPresPreview, -) from .....messaging.decorators.attach_decorator import AttachDecorator +from .....messaging.decorators.service_decorator import ServiceDecorator from .....messaging.responder import BaseResponder, MockResponder -from .....messaging.util import str_to_epoch, datetime_now, datetime_to_str +from .....messaging.util import datetime_now, datetime_to_str, str_to_epoch from .....multitenant.base import BaseMultitenantManager from .....multitenant.manager import MultitenantManager +from .....protocols.coordinate_mediation.v1_0.manager import MediationManager from .....protocols.coordinate_mediation.v1_0.models.mediation_record import ( MediationRecord, ) -from .....protocols.coordinate_mediation.v1_0.manager import MediationManager from .....protocols.didexchange.v1_0.manager import DIDXManager -from .....protocols.issue_credential.v1_0.manager import ( - CredentialManager as V10CredManager, -) from .....protocols.issue_credential.v1_0.messages.credential_offer import ( CredentialOffer as V10CredOffer, ) from .....protocols.issue_credential.v1_0.messages.inner.credential_preview import ( - CredentialPreview as V10CredentialPreview, CredAttrSpec as V10CredAttrSpec, ) -from .....protocols.issue_credential.v1_0.tests import ( - INDY_OFFER, - INDY_CRED_REQ, +from .....protocols.issue_credential.v1_0.messages.inner.credential_preview import ( + CredentialPreview as V10CredentialPreview, +) +from .....protocols.issue_credential.v1_0.tests import INDY_OFFER +from .....protocols.issue_credential.v2_0.message_types import ( + ATTACHMENT_FORMAT as V20_CRED_ATTACH_FORMAT, ) -from .....protocols.issue_credential.v2_0.manager import V20CredManager +from .....protocols.issue_credential.v2_0.message_types import CRED_20_OFFER from .....protocols.issue_credential.v2_0.messages.cred_format import V20CredFormat +from .....protocols.issue_credential.v2_0.messages.cred_offer import V20CredOffer from .....protocols.issue_credential.v2_0.messages.inner.cred_preview import ( - V20CredPreview, V20CredAttrSpec, + V20CredPreview, ) -from .....protocols.issue_credential.v2_0.messages.cred_offer import V20CredOffer -from .....protocols.issue_credential.v2_0.models.cred_ex_record import V20CredExRecord -from .....protocols.issue_credential.v2_0.message_types import ( - ATTACHMENT_FORMAT as V20_CRED_ATTACH_FORMAT, - CRED_20_OFFER, -) -from .....protocols.present_proof.v1_0.manager import PresentationManager + from .....protocols.present_proof.v1_0.message_types import ( - PRESENTATION_REQUEST, ATTACH_DECO_IDS as V10_PRES_ATTACH_FORMAT, ) -from .....protocols.present_proof.v1_0.messages.presentation import Presentation +from .....protocols.present_proof.v1_0.message_types import PRESENTATION_REQUEST from .....protocols.present_proof.v1_0.messages.presentation_request import ( PresentationRequest, ) -from .....protocols.present_proof.v1_0.models.presentation_exchange import ( - V10PresentationExchange, -) -from .....protocols.present_proof.v2_0.manager import V20PresManager + from .....protocols.present_proof.v2_0.message_types import ( ATTACHMENT_FORMAT as V20_PRES_ATTACH_FORMAT, - PRES_20, - PRES_20_REQUEST, ) -from .....protocols.present_proof.v2_0.messages.pres import V20Pres +from .....protocols.present_proof.v2_0.message_types import PRES_20_REQUEST from .....protocols.present_proof.v2_0.messages.pres_format import V20PresFormat from .....protocols.present_proof.v2_0.messages.pres_request import V20PresRequest from .....storage.error import StorageNotFoundError -from .....storage.vc_holder.base import VCHolder -from .....storage.vc_holder.vc_record import VCRecord from .....transport.inbound.receipt import MessageReceipt from .....wallet.did_info import DIDInfo, KeyInfo from .....wallet.did_method import DIDMethod from .....wallet.in_memory import InMemoryWallet from .....wallet.key_type import KeyType - +from ....connections.v1_0.messages.connection_invitation import ConnectionInvitation from ....didcomm_prefix import DIDCommPrefix +from ....issue_credential.v1_0.message_types import CREDENTIAL_OFFER from ....issue_credential.v1_0.models.credential_exchange import V10CredentialExchange - from .. import manager as test_module from ..manager import ( + REUSE_ACCEPTED_WEBHOOK_TOPIC, + REUSE_WEBHOOK_TOPIC, OutOfBandManager, OutOfBandManagerError, ) -from ..message_types import INVITATION +from ..message_types import INVITATION, MESSAGE_REUSE from ..messages.invitation import HSProto, InvitationMessage +from ..messages.invitation import Service as OobService +from ..messages.problem_report import ProblemReport, ProblemReportReason from ..messages.reuse import HandshakeReuse from ..messages.reuse_accept import HandshakeReuseAccept -from ..messages.problem_report import ProblemReport, ProblemReportReason from ..models.invitation import InvitationRecord +from ..models.oob_record import OobRecord class TestConfig: @@ -109,6 +95,11 @@ class TestConfig: test_endpoint = "http://localhost" test_target_did = "GbuDUYXaUZRfHD2jeDuQuP" their_public_did = "55GkHamhTU1ZbTbV2ab9DE" + test_service = OobService( + recipient_keys=[test_verkey], + routing_keys=[], + service_endpoint=test_endpoint, + ) NOW_8601 = datetime.utcnow().replace(tzinfo=timezone.utc).isoformat(" ", "seconds") NOW_EPOCH = str_to_epoch(NOW_8601) CD_ID = "GMm4vMw8LLrLJjp81kRRLp:3:CL:12:tag" @@ -200,23 +191,6 @@ class TestConfig: }, } - PRES_PREVIEW = IndyPresPreview( - attributes=[ - IndyPresAttrSpec(name="player", cred_def_id=CD_ID, value="Richie Knucklez"), - IndyPresAttrSpec( - name="screenCapture", - cred_def_id=CD_ID, - mime_type="image/png", - value="aW1hZ2luZSBhIHNjcmVlbiBjYXB0dXJl", - ), - ], - predicates=[ - IndyPresPredSpec( - name="highScore", cred_def_id=CD_ID, predicate=">=", threshold=1000000 - ) - ], - ) - PRES_REQ_V1 = PresentationRequest( comment="Test", request_presentations_attach=[ @@ -349,6 +323,9 @@ def setUp(self): ) self.profile.context.injector.bind_instance(BaseResponder, self.responder) + self.profile.context.injector.bind_instance( + EventBus, async_mock.MagicMock(notify=async_mock.CoroutineMock()) + ) self.mt_mgr = async_mock.MagicMock() self.mt_mgr = async_mock.create_autospec(MultitenantManager) self.profile.context.injector.bind_instance(BaseMultitenantManager, self.mt_mgr) @@ -366,12 +343,14 @@ def setUp(self): [], ) - self.test_conn_rec = ConnRecord( + self.test_conn_rec = async_mock.MagicMock( + connection_id="dummy", my_did=TestConfig.test_did, their_did=TestConfig.test_target_did, - their_role=None, + their_role=ConnRecord.Role.REQUESTER, state=ConnRecord.State.COMPLETED, their_public_did=self.their_public_did, + save=async_mock.CoroutineMock(), ) self.test_mediator_routing_keys = [ @@ -399,17 +378,15 @@ async def test_create_invitation_handshake_succeeds(self): hs_protos=[HSProto.RFC23], ) - assert invi_rec._invitation.ser["@type"] == DIDCommPrefix.qualify_current( + assert invi_rec.invitation._type == DIDCommPrefix.qualify_current( INVITATION ) - assert not invi_rec._invitation.ser.get("requests~attach") + assert not invi_rec.invitation.requests_attach assert ( DIDCommPrefix.qualify_current(HSProto.RFC23.name) in invi_rec.invitation.handshake_protocols ) - assert invi_rec._invitation.ser["services"] == [ - f"did:sov:{TestConfig.test_did}" - ] + assert invi_rec.invitation.services == [f"did:sov:{TestConfig.test_did}"] async def test_create_invitation_mediation_overwrites_routing_and_endpoint(self): async with self.profile.session() as session: @@ -436,7 +413,7 @@ async def test_create_invitation_mediation_overwrites_routing_and_endpoint(self) mediation_id=mediation_record.mediation_id, ) assert isinstance(invite, InvitationRecord) - assert invite._invitation.ser["@type"] == DIDCommPrefix.qualify_current( + assert invite.invitation._type == DIDCommPrefix.qualify_current( INVITATION ) assert invite.invitation.label == "test123" @@ -539,7 +516,12 @@ async def test_create_invitation_attachment_v1_0_cred_offer(self): attachments=[{"type": "credential-offer", "id": "dummy-id"}], ) + mock_retrieve_cxid.assert_called_once_with(ANY, "dummy-id") assert isinstance(invi_rec, InvitationRecord) + assert invi_rec.invitation.handshake_protocols + assert invi_rec.invitation.requests_attach[0].content[ + "@type" + ] == DIDCommPrefix.qualify_current(CREDENTIAL_OFFER) async def test_create_invitation_attachment_v1_0_cred_offer_no_handshake(self): self.profile.context.update_settings({"public_invites": True}) @@ -568,8 +550,13 @@ async def test_create_invitation_attachment_v1_0_cred_offer_no_handshake(self): attachments=[{"type": "credential-offer", "id": "dummy-id"}], ) + mock_retrieve_cxid.assert_called_once_with(ANY, "dummy-id") assert isinstance(invi_rec, InvitationRecord) - assert not invi_rec._invitation.ser["handshake_protocols"] + assert not invi_rec.invitation.handshake_protocols + assert invi_rec.invitation.requests_attach[0].content == { + **self.CRED_OFFER_V1.serialize(), + "~thread": {"pthid": invi_rec.invi_msg_id}, + } async def test_create_invitation_attachment_v2_0_cred_offer(self): with async_mock.patch.object( @@ -593,9 +580,7 @@ async def test_create_invitation_attachment_v2_0_cred_offer(self): mock_retrieve_cxid_v1.side_effect = test_module.StorageNotFoundError() mock_retrieve_cxid_v2.return_value = async_mock.MagicMock( cred_offer=async_mock.MagicMock( - serialize=async_mock.MagicMock( - return_value=json.dumps({"cred": "offer"}) - ) + serialize=async_mock.MagicMock(return_value={"cred": "offer"}) ) ) invi_rec = await self.manager.create_invitation( @@ -606,7 +591,13 @@ async def test_create_invitation_attachment_v2_0_cred_offer(self): attachments=[{"type": "credential-offer", "id": "dummy-id"}], ) - assert invi_rec._invitation.ser["requests~attach"] + mock_retrieve_cxid_v2.assert_called_once_with(ANY, "dummy-id") + assert isinstance(invi_rec, InvitationRecord) + assert not invi_rec.invitation.handshake_protocols + assert invi_rec.invitation.requests_attach[0].content == { + "cred": "offer", + "~thread": {"pthid": invi_rec.invi_msg_id}, + } async def test_create_invitation_attachment_present_proof_v1_0(self): self.profile.context.update_settings({"public_invites": True}) @@ -635,10 +626,13 @@ async def test_create_invitation_attachment_present_proof_v1_0(self): attachments=[{"type": "present-proof", "id": "dummy-id"}], ) - assert invi_rec._invitation.ser["requests~attach"] - mock_retrieve_pxid.assert_called_once() - assert isinstance(mock_retrieve_pxid.call_args[0][0], ProfileSession) - assert mock_retrieve_pxid.call_args[0][1] == "dummy-id" + mock_retrieve_pxid.assert_called_once_with(ANY, "dummy-id") + assert isinstance(invi_rec, InvitationRecord) + assert invi_rec.invitation.handshake_protocols + assert invi_rec.invitation.requests_attach[0].content == { + **self.PRES_REQ_V1.serialize(), + "~thread": {"pthid": invi_rec.invi_msg_id}, + } async def test_create_invitation_attachment_present_proof_v2_0(self): self.profile.context.update_settings({"public_invites": True}) @@ -672,348 +666,13 @@ async def test_create_invitation_attachment_present_proof_v2_0(self): attachments=[{"type": "present-proof", "id": "dummy-id"}], ) - assert invi_rec._invitation.ser["requests~attach"] - mock_retrieve_pxid_1.assert_called_once() - assert isinstance(mock_retrieve_pxid_1.call_args[0][0], ProfileSession) - assert mock_retrieve_pxid_1.call_args[0][1] == "dummy-id" - mock_retrieve_pxid_2.assert_called_once() - assert isinstance(mock_retrieve_pxid_2.call_args[0][0], ProfileSession) - assert mock_retrieve_pxid_2.call_args[0][1] == "dummy-id" - - async def test_dif_req_v2_attach_pres_existing_conn_auto_present_pres_msg_with_challenge( - self, - ): - async with self.profile.session() as session: - self.profile.context.update_settings({"public_invites": True}) - self.profile.context.update_settings( - {"debug.auto_respond_presentation_request": True} - ) - test_exist_conn = ConnRecord( - my_did=TestConfig.test_did, - their_did=TestConfig.test_target_did, - their_public_did=TestConfig.test_target_did, - invitation_msg_id="12345678-0123-4567-1234-567812345678", - their_role=ConnRecord.Role.REQUESTER, - ) - await test_exist_conn.save(session) - await test_exist_conn.metadata_set(session, "reuse_msg_state", "initial") - await test_exist_conn.metadata_set(session, "reuse_msg_id", "test_123") - receipt = MessageReceipt( - recipient_did=TestConfig.test_did, - recipient_did_public=False, - sender_did=TestConfig.test_target_did, - ) - dif_proof_req = deepcopy(TestConfig.DIF_PROOF_REQ) - dif_proof_req["options"] = {} - dif_proof_req["options"][ - "challenge" - ] = "3fa85f64-5717-4562-b3fc-2c963f66afa7" - dif_pres_req_v2 = V20PresRequest( - comment="some comment", - will_confirm=True, - formats=[ - V20PresFormat( - attach_id="dif", - format_=V20_PRES_ATTACH_FORMAT[PRES_20_REQUEST][ - V20PresFormat.Format.DIF.api - ], - ) - ], - request_presentations_attach=[ - AttachDecorator.data_json(mapping=dif_proof_req, ident="dif") - ], - ) - - px2_rec = test_module.V20PresExRecord( - auto_present=True, - pres_request=dif_pres_req_v2.serialize(), - ) - - dif_req_attach_v2 = AttachDecorator.data_json( - mapping=dif_pres_req_v2.serialize(), - ident="request-0", - ).serialize() - - with async_mock.patch.object( - DIDXManager, - "receive_invitation", - autospec=True, - ) as didx_mgr_receive_invitation, async_mock.patch.object( - V20PresManager, - "receive_pres_request", - autospec=True, - ) as pres_mgr_receive_pres_req, async_mock.patch( - "aries_cloudagent.protocols.out_of_band.v1_0.manager.InvitationMessage", - autospec=True, - ) as inv_message_cls, async_mock.patch.object( - OutOfBandManager, - "fetch_connection_targets", - autospec=True, - ) as oob_mgr_fetch_conn, async_mock.patch.object( - ConnRecord, - "find_existing_connection", - async_mock.CoroutineMock(), - ) as oob_mgr_find_existing_conn, async_mock.patch.object( - OutOfBandManager, - "check_reuse_msg_state", - autospec=True, - ) as oob_mgr_check_reuse_state, async_mock.patch.object( - OutOfBandManager, - "create_handshake_reuse_message", - autospec=True, - ) as oob_mgr_create_reuse_msg, async_mock.patch.object( - OutOfBandManager, - "receive_reuse_message", - autospec=True, - ) as oob_mgr_receive_reuse_msg, async_mock.patch.object( - OutOfBandManager, - "receive_reuse_accepted_message", - autospec=True, - ) as oob_mgr_receive_accept_msg, async_mock.patch.object( - OutOfBandManager, - "receive_problem_report", - autospec=True, - ) as oob_mgr_receive_problem_report, async_mock.patch.object( - V20PresManager, - "create_pres", - autospec=True, - ) as pres_mgr_create_pres: - oob_mgr_find_existing_conn.return_value = test_exist_conn - pres_mgr_receive_pres_req.return_value = px2_rec - pres_mgr_create_pres.return_value = ( - px2_rec, - V20Pres( - formats=[ - V20PresFormat( - attach_id="dif", - format_=V20_PRES_ATTACH_FORMAT[PRES_20][ - V20PresFormat.Format.DIF.api - ], - ) - ], - presentations_attach=[ - AttachDecorator.data_json( - mapping={"bogus": "proof"}, - ident="dif", - ) - ], - ), - ) - self.profile.context.injector.bind_instance( - VCHolder, - async_mock.MagicMock( - search_credentials=async_mock.MagicMock( - return_value=async_mock.MagicMock( - fetch=async_mock.CoroutineMock( - return_value=[ - VCRecord( - contexts=[ - "https://www.w3.org/2018/credentials/v1", - "https://www.w3.org/2018/credentials/examples/v1", - ], - expanded_types=[ - "https://www.w3.org/2018/credentials#VerifiableCredential", - "https://example.org/examples#UniversityDegreeCredential", - ], - issuer_id="https://example.edu/issuers/565049", - subject_ids=[ - "did:example:ebfeb1f712ebc6f1c276e12ec21" - ], - proof_types=["Ed25519Signature2018"], - schema_ids=[ - "https://example.org/examples/degree.json" - ], - cred_value={"...": "..."}, - given_id="http://example.edu/credentials/3732", - cred_tags={"some": "tag"}, - ) - ] - ) - ) - ) - ), - ) - mock_oob_invi = async_mock.MagicMock( - handshake_protocols=[ - pfx.qualify(HSProto.RFC23.name) for pfx in DIDCommPrefix - ], - services=[TestConfig.test_target_did], - requests_attach=[AttachDecorator.deserialize(dif_req_attach_v2)], - ) - - inv_message_cls.deserialize.return_value = mock_oob_invi - - conn_rec = await self.manager.receive_invitation( - mock_oob_invi, use_existing_connection=True - ) - assert conn_rec is not None - - async def test_dif_req_v2_attach_pres_existing_conn_auto_present_pres_msg_with_nonce( - self, - ): - async with self.profile.session() as session: - self.profile.context.update_settings({"public_invites": True}) - self.profile.context.update_settings( - {"debug.auto_respond_presentation_request": True} - ) - test_exist_conn = ConnRecord( - my_did=TestConfig.test_did, - their_did=TestConfig.test_target_did, - their_public_did=TestConfig.test_target_did, - invitation_msg_id="12345678-0123-4567-1234-567812345678", - their_role=ConnRecord.Role.REQUESTER, - ) - await test_exist_conn.save(session) - await test_exist_conn.metadata_set(session, "reuse_msg_state", "initial") - await test_exist_conn.metadata_set(session, "reuse_msg_id", "test_123") - receipt = MessageReceipt( - recipient_did=TestConfig.test_did, - recipient_did_public=False, - sender_did=TestConfig.test_target_did, - ) - - dif_proof_req = deepcopy(TestConfig.DIF_PROOF_REQ) - dif_proof_req["options"] = {} - dif_proof_req["options"]["nonce"] = "12345" - dif_pres_req_v2 = V20PresRequest( - comment="some comment", - will_confirm=True, - formats=[ - V20PresFormat( - attach_id="dif", - format_=V20_PRES_ATTACH_FORMAT[PRES_20_REQUEST][ - V20PresFormat.Format.DIF.api - ], - ) - ], - request_presentations_attach=[ - AttachDecorator.data_json(mapping=dif_proof_req, ident="dif") - ], - ) - - px2_rec = test_module.V20PresExRecord( - auto_present=True, - pres_request=dif_pres_req_v2.serialize(), - ) - - dif_req_attach_v2 = AttachDecorator.data_json( - mapping=dif_pres_req_v2.serialize(), - ident="request-0", - ).serialize() - - with async_mock.patch.object( - DIDXManager, - "receive_invitation", - autospec=True, - ) as didx_mgr_receive_invitation, async_mock.patch.object( - V20PresManager, - "receive_pres_request", - autospec=True, - ) as pres_mgr_receive_pres_req, async_mock.patch( - "aries_cloudagent.protocols.out_of_band.v1_0.manager.InvitationMessage", - autospec=True, - ) as inv_message_cls, async_mock.patch.object( - OutOfBandManager, - "fetch_connection_targets", - autospec=True, - ) as oob_mgr_fetch_conn, async_mock.patch.object( - ConnRecord, - "find_existing_connection", - async_mock.CoroutineMock(), - ) as oob_mgr_find_existing_conn, async_mock.patch.object( - OutOfBandManager, - "check_reuse_msg_state", - autospec=True, - ) as oob_mgr_check_reuse_state, async_mock.patch.object( - OutOfBandManager, - "create_handshake_reuse_message", - autospec=True, - ) as oob_mgr_create_reuse_msg, async_mock.patch.object( - OutOfBandManager, - "receive_reuse_message", - autospec=True, - ) as oob_mgr_receive_reuse_msg, async_mock.patch.object( - OutOfBandManager, - "receive_reuse_accepted_message", - autospec=True, - ) as oob_mgr_receive_accept_msg, async_mock.patch.object( - OutOfBandManager, - "receive_problem_report", - autospec=True, - ) as oob_mgr_receive_problem_report, async_mock.patch.object( - V20PresManager, - "create_pres", - autospec=True, - ) as pres_mgr_create_pres: - oob_mgr_find_existing_conn.return_value = test_exist_conn - pres_mgr_receive_pres_req.return_value = px2_rec - pres_mgr_create_pres.return_value = ( - px2_rec, - V20Pres( - formats=[ - V20PresFormat( - attach_id="dif", - format_=V20_PRES_ATTACH_FORMAT[PRES_20][ - V20PresFormat.Format.DIF.api - ], - ) - ], - presentations_attach=[ - AttachDecorator.data_json( - mapping={"bogus": "proof"}, - ident="dif", - ) - ], - ), - ) - self.profile.context.injector.bind_instance( - VCHolder, - async_mock.MagicMock( - search_credentials=async_mock.MagicMock( - return_value=async_mock.MagicMock( - fetch=async_mock.CoroutineMock( - return_value=[ - VCRecord( - contexts=[ - "https://www.w3.org/2018/credentials/v1", - "https://www.w3.org/2018/credentials/examples/v1", - ], - expanded_types=[ - "https://www.w3.org/2018/credentials#VerifiableCredential", - "https://example.org/examples#UniversityDegreeCredential", - ], - issuer_id="https://example.edu/issuers/565049", - subject_ids=[ - "did:example:ebfeb1f712ebc6f1c276e12ec21" - ], - proof_types=["Ed25519Signature2018"], - schema_ids=[ - "https://example.org/examples/degree.json" - ], - cred_value={"...": "..."}, - given_id="http://example.edu/credentials/3732", - cred_tags={"some": "tag"}, - ) - ] - ) - ) - ) - ), - ) - mock_oob_invi = async_mock.MagicMock( - handshake_protocols=[ - pfx.qualify(HSProto.RFC23.name) for pfx in DIDCommPrefix - ], - services=[TestConfig.test_target_did], - requests_attach=[AttachDecorator.deserialize(dif_req_attach_v2)], - ) - - inv_message_cls.deserialize.return_value = mock_oob_invi - - conn_rec = await self.manager.receive_invitation( - mock_oob_invi, use_existing_connection=True - ) - assert conn_rec is not None + mock_retrieve_pxid_2.assert_called_once_with(ANY, "dummy-id") + assert isinstance(invi_rec, InvitationRecord) + assert invi_rec.invitation.handshake_protocols + assert invi_rec.invitation.requests_attach[0].content == { + **TestConfig.PRES_REQ_V2.serialize(), + "~thread": {"pthid": invi_rec.invi_msg_id}, + } async def test_create_invitation_public_x_no_public_invites(self): self.profile.context.update_settings({"public_invites": False}) @@ -1039,6 +698,20 @@ async def test_create_invitation_public_x_multi_use(self): ) assert "Cannot create public invitation with" in str(context.exception) + async def test_create_invitation_requests_attach_x_multi_use(self): + + with self.assertRaises(OutOfBandManagerError) as context: + await self.manager.create_invitation( + public=False, + my_endpoint="testendpoint", + hs_protos=[test_module.HSProto.RFC23], + attachments=[{"some": "attachment"}], + multi_use=True, + ) + assert "Cannot create multi use invitation with attachments" in str( + context.exception + ) + async def test_create_invitation_public_x_no_public_did(self): self.profile.context.update_settings({"public_invites": True}) @@ -1074,7 +747,7 @@ async def test_create_invitation_attachment_x(self): my_endpoint=TestConfig.test_endpoint, public=False, hs_protos=[test_module.HSProto.RFC23], - multi_use=True, + multi_use=False, attachments=[{"having": "attachment", "is": "no", "good": "here"}], ) assert "Unknown attachment type" in str(context.exception) @@ -1155,720 +828,309 @@ async def test_create_invitation_x_public_metadata(self): ) with self.assertRaises(OutOfBandManagerError) as context: await self.manager.create_invitation( - public=True, - hs_protos=[test_module.HSProto.RFC23], + public=False, + hs_protos=[], + attachments=[{"an": "attachment"}], metadata={"hello": "world"}, multi_use=False, ) - assert "Cannot store metadata on public" in str(context.exception) - - async def test_receive_invitation_with_valid_mediation(self): - async with self.profile.session() as session: - self.profile.context.update_settings({"public_invites": True}) - mediation_record = MediationRecord( - role=MediationRecord.ROLE_CLIENT, - state=MediationRecord.STATE_GRANTED, - connection_id=self.test_mediator_conn_id, - routing_keys=self.test_mediator_routing_keys, - endpoint=self.test_mediator_endpoint, + assert "Cannot store metadata without handshake protocols" in str( + context.exception ) - await mediation_record.save(session) - with async_mock.patch.object( - DIDXManager, "receive_invitation", async_mock.CoroutineMock() - ) as mock_didx_recv_invi: - invite = await self.manager.create_invitation( - my_endpoint=TestConfig.test_endpoint, - my_label="test123", - hs_protos=[HSProto.RFC23], - ) - invi_msg = invite.invitation - invitee_record = await self.manager.receive_invitation( - invitation=invi_msg, - mediation_id=mediation_record._id, - ) - mock_didx_recv_invi.assert_called_once_with( - invitation=invi_msg, - their_public_did=None, - auto_accept=None, - alias=None, - mediation_id=mediation_record._id, - ) - async def test_receive_invitation_with_invalid_mediation(self): - self.profile.context.update_settings({"public_invites": True}) + async def test_wait_for_conn_rec_active_retrieve_by_id(self): with async_mock.patch.object( - DIDXManager, - "receive_invitation", - async_mock.CoroutineMock(), - ) as mock_didx_recv_invi: - invite = await self.manager.create_invitation( - my_endpoint=TestConfig.test_endpoint, - my_label="test123", - hs_protos=[HSProto.RFC23], - ) - invi_msg = invite.invitation - invitee_record = await self.manager.receive_invitation( - invi_msg, - mediation_id="test-mediation-id", - ) - mock_didx_recv_invi.assert_called_once_with( - invitation=invi_msg, - their_public_did=None, - auto_accept=None, - alias=None, - mediation_id=None, - ) + ConnRecord, + "retrieve_by_id", + async_mock.CoroutineMock( + return_value=async_mock.MagicMock( + connection_id="the-retrieved-connection-id" + ) + ), + ): + conn_rec = await self.manager._wait_for_conn_rec_active("a-connection-id") + assert conn_rec.connection_id == "the-retrieved-connection-id" - async def test_receive_invitation_didx_services_with_service_block(self): + async def test_create_handshake_reuse_msg(self): self.profile.context.update_settings({"public_invites": True}) + with async_mock.patch.object( - test_module, "DIDXManager", autospec=True - ) as didx_mgr_cls, async_mock.patch.object( - test_module, - "InvitationMessage", + OutOfBandManager, + "fetch_connection_targets", autospec=True, - ) as invi_msg_cls: - didx_mgr_cls.return_value = async_mock.MagicMock( - receive_invitation=async_mock.CoroutineMock() + ) as oob_mgr_fetch_conn, async_mock.patch.object( + ConnRecord, + "retrieve_by_id", + async_mock.CoroutineMock(return_value=self.test_conn_rec), + ): + oob_mgr_fetch_conn.return_value = ConnectionTarget( + did=TestConfig.test_did, + endpoint=TestConfig.test_endpoint, + recipient_keys=[TestConfig.test_verkey], + sender_key=TestConfig.test_verkey, ) - mock_oob_invi = async_mock.MagicMock( - requests_attach=[], - handshake_protocols=[ - pfx.qualify(HSProto.RFC23.name) for pfx in DIDCommPrefix - ], - services=[ - async_mock.MagicMock( - recipient_keys=["dummy"], - routing_keys=[], - ) - ], + + invitation = InvitationMessage() + oob_record = OobRecord( + invitation=invitation, + invi_msg_id=invitation._id, + role=OobRecord.ROLE_RECEIVER, + connection_id=self.test_conn_rec.connection_id, + state=OobRecord.STATE_INITIAL, ) - invi_msg_cls.deserialize.return_value = mock_oob_invi - await self.manager.receive_invitation(mock_oob_invi) + oob_record = await self.manager._create_handshake_reuse_message( + oob_record, self.test_conn_rec + ) + + _, kwargs = self.responder.send.call_args + reuse_message: HandshakeReuse = kwargs.get("message") + + assert oob_record.state == OobRecord.STATE_AWAIT_RESPONSE + + # Assert responder has been called with the reuse message + assert reuse_message._type == DIDCommPrefix.qualify_current(MESSAGE_REUSE) + assert oob_record.reuse_msg_id == reuse_message._id - async def test_receive_invitation_connection_mock(self): + async def test_create_handshake_reuse_msg_catch_exception(self): self.profile.context.update_settings({"public_invites": True}) with async_mock.patch.object( - test_module, "ConnectionManager", autospec=True - ) as conn_mgr_cls, async_mock.patch.object( - test_module, - "InvitationMessage", + OutOfBandManager, + "fetch_connection_targets", autospec=True, - ) as invi_msg_cls, async_mock.patch.object( - self.manager, - "receive_invitation", - async_mock.CoroutineMock(), - ) as mock_receive_invitation: - mock_receive_invitation.return_value = self.test_conn_rec.serialize() - conn_mgr_cls.return_value = async_mock.MagicMock( - receive_invitation=async_mock.CoroutineMock() - ) - mock_oob_invi = async_mock.MagicMock( - handshake_protocols=[ - pfx.qualify(HSProto.RFC160.name) for pfx in DIDCommPrefix - ], - label="test", - _id="test123", - services=[ - async_mock.MagicMock( - recipient_keys=[ - DIDKey.from_public_key_b58( - "9WCgWKUaAJj3VWxxtzvvMQN3AoFxoBtBDo9ntwJnVVCC", - KeyType.ED25519, - ).did - ], - routing_keys=[], - service_endpoint="http://localhost", - ) - ], - requests_attach=[], + ) as oob_mgr_fetch_conn: + oob_mgr_fetch_conn.side_effect = StorageNotFoundError() + with self.assertRaises(OutOfBandManagerError) as context: + await self.manager._create_handshake_reuse_message( + async_mock.MagicMock(), self.test_conn_rec + ) + assert "Error on creating and sending a handshake reuse message" in str( + context.exception ) - invi_msg_cls.deserialize.return_value = mock_oob_invi - result = await self.manager.receive_invitation(mock_oob_invi) - assert result == self.test_conn_rec.serialize() - async def test_receive_invitation_connection(self): + async def test_receive_reuse_message_existing_found(self): self.profile.context.update_settings({"public_invites": True}) - oob_invi_rec = await self.manager.create_invitation( - auto_accept=True, - public=False, - hs_protos=[test_module.HSProto.RFC160], - multi_use=False, - ) - result = await self.manager.receive_invitation( - invitation=oob_invi_rec.invitation, - use_existing_connection=True, - auto_accept=True, - ) - connection_id = UUID(result.connection_id, version=4) - assert ( - connection_id.hex == result.connection_id.replace("-", "") - and len(result.connection_id) > 5 + receipt = MessageReceipt( + recipient_did=TestConfig.test_did, + recipient_did_public=False, ) - async def test_receive_invitation_services_with_neither_service_blocks_nor_dids( - self, - ): - self.profile.context.update_settings({"public_invites": True}) + reuse_msg = HandshakeReuse() + reuse_msg.assign_thread_id(thid="the-thread-id", pthid="the-pthid") + + self.test_conn_rec.invitation_msg_id = "test_123" + self.test_conn_rec.state = ConnRecord.State.COMPLETED.rfc160 + with async_mock.patch.object( - test_module, "InvitationMessage", async_mock.MagicMock() - ) as invi_msg_cls: - mock_invi_msg = async_mock.MagicMock( - services=[], + OutOfBandManager, + "fetch_connection_targets", + autospec=True, + ) as oob_mgr_fetch_conn, async_mock.patch.object( + OobRecord, + "retrieve_by_tag_filter", + autospec=True, + ) as mock_retrieve_oob, async_mock.patch.object( + self.profile, "notify", autospec=True + ) as mock_notify: + mock_retrieve_oob.return_value = async_mock.MagicMock( + emit_event=async_mock.CoroutineMock(), + delete_record=async_mock.CoroutineMock(), + multi_use=False, ) - invi_msg_cls.deserialize.return_value = mock_invi_msg - with self.assertRaises(OutOfBandManagerError): - await self.manager.receive_invitation(mock_invi_msg) - async def test_receive_invitation_services_with_service_did(self): - self.profile.context.update_settings({"public_invites": True}) - with async_mock.patch.object( - test_module, "DIDXManager", autospec=True - ) as didx_mgr_cls, async_mock.patch.object( - test_module, "InvitationMessage", autospec=True - ) as invi_msg_cls: - didx_mgr_cls.return_value = async_mock.MagicMock( - receive_invitation=async_mock.CoroutineMock() + await self.manager.receive_reuse_message( + reuse_msg, receipt, self.test_conn_rec ) - mock_oob_invi = async_mock.MagicMock( - handshake_protocols=[ - pfx.qualify(HSProto.RFC23.name) for pfx in DIDCommPrefix - ], - services=[TestConfig.test_did], - requests_attach=[], + mock_notify.assert_called_once_with( + REUSE_WEBHOOK_TOPIC, + { + "thread_id": "the-thread-id", + "connection_id": self.test_conn_rec.connection_id, + "comment": "Connection dummy is being reused for invitation the-pthid", + }, ) - invi_msg_cls.deserialize.return_value = mock_oob_invi - - invi_rec = await self.manager.receive_invitation(mock_oob_invi) - assert invi_rec._invitation.ser["services"] - async def test_receive_invitation_attachment_x(self): - self.profile.context.update_settings({"public_invites": True}) - with async_mock.patch.object( - DIDXManager, "receive_invitation", autospec=True - ) as didx_mgr_receive_invitation, async_mock.patch( - "aries_cloudagent.protocols.out_of_band.v1_0.manager.InvitationMessage", - autospec=True, - ) as inv_message_cls: - - mock_oob_invi = async_mock.MagicMock( - services=[TestConfig.test_did], - handshake_protocols=[ - pfx.qualify(HSProto.RFC23.name) for pfx in DIDCommPrefix - ], - requests_attach=[{"having": "attachment", "is": "no", "good": "here"}], + # delete should be called if multi_use == False + mock_retrieve_oob.return_value.delete_record.assert_called_once() + mock_retrieve_oob.return_value.emit_event.assert_called_once() + self.responder.send.assert_called_once_with( + message=ANY, target_list=oob_mgr_fetch_conn.return_value ) - inv_message_cls.deserialize.return_value = mock_oob_invi - with self.assertRaises(OutOfBandManagerError) as context: - await self.manager.receive_invitation(mock_oob_invi) - assert "requests~attach is not properly formatted" in str(context.exception) + assert mock_retrieve_oob.return_value.state == OobRecord.STATE_DONE + assert mock_retrieve_oob.return_value.reuse_msg_id == reuse_msg._thread_id + assert ( + mock_retrieve_oob.return_value.connection_id + == self.test_conn_rec.connection_id + ) - async def test_receive_invitation_req_pres_v1_0_attachment_x(self): + async def test_receive_reuse_message_existing_found_multi_use(self): self.profile.context.update_settings({"public_invites": True}) - with async_mock.patch.object( - DIDXManager, "receive_invitation", autospec=True - ) as didx_mgr_receive_invitation, async_mock.patch( - "aries_cloudagent.protocols.out_of_band.v1_0.manager.InvitationMessage", - autospec=True, - ) as inv_message_cls: - mock_oob_invi = async_mock.MagicMock( - handshake_protocols=[ - pfx.qualify(HSProto.RFC23.name) for pfx in DIDCommPrefix - ], - services=[TestConfig.test_did], - requests_attach=[ - async_mock.MagicMock( - data=async_mock.MagicMock( - json={ - "@type": DIDCommPrefix.qualify_current( - PRESENTATION_REQUEST - ) - } - ) - ), - ], - ) - inv_message_cls.deserialize.return_value = mock_oob_invi - with self.assertRaises(OutOfBandManagerError) as context: - result = await self.manager.receive_invitation(mock_oob_invi) - connection_id = UUID(result.connection_id, version=4) - assert ( - connection_id.hex == result.connection_id - and len(result.connection_id) > 5 - ) - assert "requests~attach is not properly formatted" in str(context.exception) + receipt = MessageReceipt( + recipient_did=TestConfig.test_did, + recipient_did_public=False, + ) + + reuse_msg = HandshakeReuse() + reuse_msg.assign_thread_id(thid="the-thread-id", pthid="the-pthid") + + self.test_conn_rec.invitation_msg_id = "test_123" + self.test_conn_rec.state = ConnRecord.State.COMPLETED.rfc160 - async def test_receive_invitation_invalid_request_type_x(self): - self.profile.context.update_settings({"public_invites": True}) with async_mock.patch.object( - DIDXManager, "receive_invitation", autospec=True - ) as didx_mgr_receive_invitation, async_mock.patch( - "aries_cloudagent.protocols.out_of_band.v1_0.manager.InvitationMessage", + OutOfBandManager, + "fetch_connection_targets", autospec=True, - ) as inv_message_cls: - - mock_oob_invi = async_mock.MagicMock( - services=[TestConfig.test_did], - handshake_protocols=[], - requests_attach=[], + ) as oob_mgr_fetch_conn, async_mock.patch.object( + OobRecord, + "retrieve_by_tag_filter", + autospec=True, + ) as mock_retrieve_oob, async_mock.patch.object( + self.profile, "notify", autospec=True + ) as mock_notify: + mock_retrieve_oob.return_value = async_mock.MagicMock( + emit_event=async_mock.CoroutineMock(), + delete_record=async_mock.CoroutineMock(), + multi_use=True, ) - inv_message_cls.deserialize.return_value = mock_oob_invi - - with self.assertRaises(OutOfBandManagerError): - await self.manager.receive_invitation(mock_oob_invi) - async def test_find_existing_connection(self): - async with self.profile.session() as session: - test_conn_rec = ConnRecord( - my_did=TestConfig.test_did, - their_did=TestConfig.test_target_did, - their_role=None, - state=ConnRecord.State.COMPLETED, - their_public_did=self.their_public_did, + await self.manager.receive_reuse_message( + reuse_msg, receipt, self.test_conn_rec ) - await test_conn_rec.save(session) - conn_record = await ConnRecord.find_existing_connection( - session=session, their_public_did="not_addded" + mock_notify.assert_called_once_with( + REUSE_WEBHOOK_TOPIC, + { + "thread_id": "the-thread-id", + "connection_id": self.test_conn_rec.connection_id, + "comment": "Connection dummy is being reused for invitation the-pthid", + }, ) - assert conn_record == None - conn_record = await ConnRecord.find_existing_connection( - session=session, their_public_did=self.their_public_did + # delete should be called if multi_use == False + mock_retrieve_oob.return_value.delete_record.assert_not_called() + mock_retrieve_oob.return_value.emit_event.assert_called_once() + self.responder.send.assert_called_once_with( + message=ANY, target_list=oob_mgr_fetch_conn.return_value ) - assert conn_record == test_conn_rec - await test_conn_rec.delete_record(session) - async def test_check_reuse_msg_state(self): - async with self.profile.session() as session: - await self.test_conn_rec.save(session) - await self.test_conn_rec.metadata_set( - session, "reuse_msg_state", "accepted" + assert mock_retrieve_oob.return_value.state == OobRecord.STATE_DONE + assert mock_retrieve_oob.return_value.reuse_msg_id == reuse_msg._thread_id + assert ( + mock_retrieve_oob.return_value.connection_id + == self.test_conn_rec.connection_id ) - assert await self.manager.check_reuse_msg_state(self.test_conn_rec) is None - async def test_create_handshake_reuse_msg(self): - async with self.profile.session() as session: - self.profile.context.update_settings({"public_invites": True}) - await self.test_conn_rec.save(session) - with async_mock.patch.object( - DIDXManager, "receive_invitation", autospec=True - ) as didx_mgr_receive_invitation, async_mock.patch( - "aries_cloudagent.protocols.out_of_band.v1_0.manager.InvitationMessage", - autospec=True, - ) as inv_message_cls, async_mock.patch.object( - OutOfBandManager, - "fetch_connection_targets", - autospec=True, - ) as oob_mgr_fetch_conn: - oob_mgr_fetch_conn.return_value = ConnectionTarget( - did=TestConfig.test_did, - endpoint=TestConfig.test_endpoint, - recipient_keys=TestConfig.test_verkey, - sender_key=TestConfig.test_verkey, - ) - oob_invi = InvitationMessage() + async def test_receive_reuse_accepted(self): + self.profile.context.update_settings({"public_invites": True}) - await self.manager.create_handshake_reuse_message( - oob_invi, self.test_conn_rec - ) - assert ( - len(await self.test_conn_rec.metadata_get(session, "reuse_msg_id")) - > 6 - ) - assert ( - await self.test_conn_rec.metadata_get(session, "reuse_msg_state") - == "initial" - ) + receipt = MessageReceipt( + recipient_did=TestConfig.test_did, + recipient_did_public=False, + sender_did="test_did", + ) + reuse_msg_accepted = HandshakeReuseAccept() + reuse_msg_accepted.assign_thread_id(thid="the-thread-id", pthid="the-pthid") - async def test_create_handshake_reuse_msg_catch_exception(self): - async with self.profile.session() as session: - self.profile.context.update_settings({"public_invites": True}) - await self.test_conn_rec.save(session) - with async_mock.patch.object( - DIDXManager, "receive_invitation", autospec=True - ) as didx_mgr_receive_invitation, async_mock.patch( - "aries_cloudagent.protocols.out_of_band.v1_0.manager.InvitationMessage", - autospec=True, - ) as inv_message_cls, async_mock.patch.object( - OutOfBandManager, - "fetch_connection_targets", - autospec=True, - ) as oob_mgr_fetch_conn: - oob_mgr_fetch_conn.side_effect = StorageNotFoundError() - oob_invi = InvitationMessage() - with self.assertRaises(OutOfBandManagerError) as context: - await self.manager.create_handshake_reuse_message( - oob_invi, self.test_conn_rec - ) - assert "Error on creating and sending a handshake reuse message" in str( - context.exception - ) + with async_mock.patch.object( + self.profile, "notify", autospec=True + ) as mock_notify, async_mock.patch.object( + OobRecord, "retrieve_by_tag_filter", autospec=True + ) as mock_retrieve_oob: + mock_retrieve_oob.return_value = async_mock.MagicMock( + emit_event=async_mock.CoroutineMock(), + delete_record=async_mock.CoroutineMock(), + ) - async def test_receive_reuse_message_existing_found(self): - async with self.profile.session() as session: - self.profile.context.update_settings({"public_invites": True}) - receipt = MessageReceipt( - recipient_did=TestConfig.test_did, - recipient_did_public=False, - ) - reuse_msg = HandshakeReuse() - reuse_msg.assign_thread_id(thid="test_123", pthid="test_123") - self.test_conn_rec.invitation_msg_id = "test_123" - self.test_conn_rec.state = ConnRecord.State.COMPLETED.rfc160 - await self.test_conn_rec.save(session) - with async_mock.patch.object( - DIDXManager, "receive_invitation", autospec=True - ) as didx_mgr_receive_invitation, async_mock.patch( - "aries_cloudagent.protocols.out_of_band.v1_0.manager.InvitationMessage", - autospec=True, - ) as inv_message_cls, async_mock.patch.object( - OutOfBandManager, - "fetch_connection_targets", - autospec=True, - ) as oob_mgr_fetch_conn, async_mock.patch.object( - ConnRecord, - "find_existing_connection", - async_mock.CoroutineMock(), - ) as oob_mgr_find_existing_conn, async_mock.patch.object( - InvitationRecord, - "retrieve_by_tag_filter", - autospec=True, - ) as retrieve_invi_rec, async_mock.patch.object( - self.profile, "notify", autospec=True - ) as mock_notify: - oob_mgr_find_existing_conn.return_value = self.test_conn_rec - oob_mgr_fetch_conn.return_value = ConnectionTarget( - did=TestConfig.test_did, - endpoint=TestConfig.test_endpoint, - recipient_keys=TestConfig.test_verkey, - sender_key=TestConfig.test_verkey, - ) - oob_invi = InvitationMessage() - retrieve_invi_rec.return_value = InvitationRecord( - invi_msg_id="test_123" - ) - await self.manager.receive_reuse_message( - reuse_msg, receipt, self.test_conn_rec - ) - mock_notify.assert_called_once() - assert ( - len( - await ConnRecord.query( - session=session, - tag_filter={"invitation_msg_id": "test_123"}, - post_filter_positive={}, - alt=True, - ) - ) - == 1 - ) + await self.manager.receive_reuse_accepted_message( + reuse_msg_accepted, receipt, self.test_conn_rec + ) - async def test_receive_reuse_message_existing_not_found(self): - async with self.profile.session() as session: - self.profile.context.update_settings({"public_invites": True}) - receipt = MessageReceipt( - recipient_did=TestConfig.test_did, - recipient_did_public=False, - sender_did="test_did", - ) - reuse_msg = HandshakeReuse() - reuse_msg.assign_thread_id(thid="test_123", pthid="test_123") - self.test_conn_rec.invitation_msg_id = "test_123" - self.test_conn_rec.state = ConnRecord.State.REQUEST.rfc160 - await self.test_conn_rec.save(session) - with async_mock.patch.object( - DIDXManager, "receive_invitation", autospec=True - ) as didx_mgr_receive_invitation, async_mock.patch( - "aries_cloudagent.protocols.out_of_band.v1_0.manager.InvitationMessage", - autospec=True, - ) as inv_message_cls, async_mock.patch.object( - OutOfBandManager, - "fetch_connection_targets", - autospec=True, - ) as oob_mgr_fetch_conn, async_mock.patch.object( - InvitationRecord, - "retrieve_by_tag_filter", - autospec=True, - ) as retrieve_invi_rec, async_mock.patch.object( - ConnRecord, - "find_existing_connection", - async_mock.CoroutineMock(), - ) as oob_mgr_find_existing_conn, async_mock.patch.object( - self.profile, "notify", autospec=True - ) as mock_notify: - oob_mgr_find_existing_conn.return_value = None - oob_mgr_fetch_conn.return_value = ConnectionTarget( - did=TestConfig.test_did, - endpoint=TestConfig.test_endpoint, - recipient_keys=TestConfig.test_verkey, - sender_key=TestConfig.test_verkey, - ) - oob_invi = InvitationMessage() - retrieve_invi_rec.return_value = InvitationRecord( - invi_msg_id="test_123" - ) - await self.manager.receive_reuse_message( - reuse_msg, receipt, self.test_conn_rec - ) - mock_notify.assert_called_once() - assert len(self.responder.messages) == 0 + mock_retrieve_oob.return_value.emit_event.assert_called_once() + mock_retrieve_oob.return_value.delete_record.assert_called_once() + mock_notify.assert_called_once_with( + REUSE_ACCEPTED_WEBHOOK_TOPIC, + { + "thread_id": "the-thread-id", + "connection_id": self.test_conn_rec.connection_id, + "state": "accepted", + "comment": f"Connection {self.test_conn_rec.connection_id} is being reused for invitation the-pthid", + }, + ) - async def test_receive_reuse_message_problem_report_logic(self): - async with self.profile.session() as session: - self.profile.context.update_settings({"public_invites": True}) - receipt = MessageReceipt( - recipient_did=TestConfig.test_did, - recipient_did_public=False, - sender_did="test_did", - ) - reuse_msg = HandshakeReuse() - reuse_msg.assign_thread_id(thid="test_123", pthid="test_123") - self.test_conn_rec.invitation_msg_id = "test_456" - self.test_conn_rec.their_did = "test_did" - self.test_conn_rec.state = ConnRecord.State.COMPLETED.rfc160 - await self.test_conn_rec.save(session) - with async_mock.patch.object( - OutOfBandManager, - "fetch_connection_targets", - autospec=True, - ) as oob_mgr_fetch_conn, async_mock.patch.object( - self.profile, "notify", autospec=True - ) as mock_notify: - oob_mgr_fetch_conn.return_value = ConnectionTarget( - did=TestConfig.test_did, - endpoint=TestConfig.test_endpoint, - recipient_keys=TestConfig.test_verkey, - sender_key=TestConfig.test_verkey, - ) - await self.manager.receive_reuse_message( - reuse_msg, receipt, self.test_conn_rec - ) - mock_notify.assert_called_once() + async def test_receive_reuse_accepted_x(self): + self.profile.context.update_settings({"public_invites": True}) - async def test_receive_reuse_accepted(self): - async with self.profile.session() as session: - self.profile.context.update_settings({"public_invites": True}) - receipt = MessageReceipt( - recipient_did=TestConfig.test_did, - recipient_did_public=False, - sender_did="test_did", - ) - reuse_msg_accepted = HandshakeReuseAccept() - reuse_msg_accepted.assign_thread_id(thid="test_123", pthid="test_123") - self.test_conn_rec.invitation_msg_id = "test_123" - self.test_conn_rec.state = ConnRecord.State.COMPLETED.rfc160 - await self.test_conn_rec.save(session) - await self.test_conn_rec.metadata_set(session, "reuse_msg_id", "test_123") - await self.test_conn_rec.metadata_set(session, "reuse_msg_state", "initial") - with async_mock.patch.object( - DIDXManager, "receive_invitation", autospec=True - ) as didx_mgr_receive_invitation, async_mock.patch( - "aries_cloudagent.protocols.out_of_band.v1_0.manager.InvitationMessage", - autospec=True, - ) as inv_message_cls, async_mock.patch.object( - OutOfBandManager, - "fetch_connection_targets", - autospec=True, - ) as oob_mgr_fetch_conn, async_mock.patch.object( - self.profile, "notify", autospec=True - ) as mock_notify: + receipt = MessageReceipt( + recipient_did=TestConfig.test_did, + recipient_did_public=False, + sender_did="test_did", + ) + reuse_msg_accepted = HandshakeReuseAccept() + reuse_msg_accepted.assign_thread_id(thid="the-thread-id", pthid="the-pthid") - await self.manager.receive_reuse_accepted_message( - reuse_msg_accepted, receipt, self.test_conn_rec - ) - mock_notify.assert_called_once() - assert ( - await self.test_conn_rec.metadata_get(session, "reuse_msg_state") - == "accepted" - ) + with async_mock.patch.object( + self.profile, "notify", autospec=True + ) as mock_notify, async_mock.patch.object( + OobRecord, "retrieve_by_tag_filter", autospec=True + ) as mock_retrieve_oob: + mock_retrieve_oob.side_effect = (StorageNotFoundError,) - async def test_receive_reuse_accepted(self): - async with self.profile.session() as session: - self.profile.context.update_settings({"public_invites": True}) - receipt = MessageReceipt( - recipient_did=TestConfig.test_did, - recipient_did_public=False, - sender_did="test_did", - ) - reuse_msg_accepted = HandshakeReuseAccept() - reuse_msg_accepted.assign_thread_id(thid="test_123", pthid="test_123") - self.test_conn_rec.invitation_msg_id = "test_123" - self.test_conn_rec.state = ConnRecord.State.COMPLETED.rfc160 - await self.test_conn_rec.save(session) - await self.test_conn_rec.metadata_set(session, "reuse_msg_id", "test_123") - await self.test_conn_rec.metadata_set(session, "reuse_msg_state", "initial") - with async_mock.patch.object( - DIDXManager, "receive_invitation", autospec=True - ) as didx_mgr_receive_invitation, async_mock.patch( - "aries_cloudagent.protocols.out_of_band.v1_0.manager.InvitationMessage", - autospec=True, - ) as inv_message_cls, async_mock.patch.object( - OutOfBandManager, - "fetch_connection_targets", - autospec=True, - ) as oob_mgr_fetch_conn, async_mock.patch.object( - self.profile, "notify", autospec=True - ) as mock_notify: + with self.assertRaises(test_module.OutOfBandManagerError) as err: await self.manager.receive_reuse_accepted_message( reuse_msg_accepted, receipt, self.test_conn_rec ) - mock_notify.assert_called_once() - assert ( - await self.test_conn_rec.metadata_get(session, "reuse_msg_state") - == "accepted" - ) + assert "Error processing reuse accepted message " in err.exception.message + + mock_notify.assert_called_once_with( + REUSE_ACCEPTED_WEBHOOK_TOPIC, + { + "thread_id": "the-thread-id", + "state": "rejected", + "connection_id": self.test_conn_rec.connection_id, + "comment": f"Unable to process HandshakeReuseAccept message, connection {self.test_conn_rec.connection_id} and invitation the-pthid", + }, + ) - async def test_receive_reuse_accepted_invalid_conn(self): + async def test_receive_problem_report(self): self.profile.context.update_settings({"public_invites": True}) + receipt = MessageReceipt( recipient_did=TestConfig.test_did, recipient_did_public=False, sender_did="test_did", ) - reuse_msg_accepted = HandshakeReuseAccept() - reuse_msg_accepted.assign_thread_id(thid="test_123", pthid="test_123") - test_invalid_conn = ConnRecord( - my_did="Test", - their_did="Test", - invitation_msg_id="test_456", - connection_id="12345678-0123-4567-1234-567812345678", + problem_report = ProblemReport( + description={ + "en": "test", + "code": ProblemReportReason.EXISTING_CONNECTION_NOT_ACTIVE.value, + } ) - with async_mock.patch.object( - DIDXManager, "receive_invitation", autospec=True - ) as didx_mgr_receive_invitation, async_mock.patch( - "aries_cloudagent.protocols.out_of_band.v1_0.manager.InvitationMessage", - autospec=True, - ) as inv_message_cls, async_mock.patch.object( - OutOfBandManager, - "fetch_connection_targets", - autospec=True, - ) as oob_mgr_fetch_conn, async_mock.patch.object( - self.profile, "notify", autospec=True - ) as mock_notify: - with self.assertRaises(OutOfBandManagerError) as context: - await self.manager.receive_reuse_accepted_message( - reuse_msg_accepted, receipt, test_invalid_conn - ) - mock_notify.assert_called_once() - assert "Error processing reuse accepted message" in str(context.exception) - - async def test_receive_reuse_accepted_message_catch_exception(self): - async with self.profile.session() as session: - self.profile.context.update_settings({"public_invites": True}) - receipt = MessageReceipt( - recipient_did=TestConfig.test_did, - recipient_did_public=False, - sender_did="test_did", - ) - reuse_msg_accepted = HandshakeReuseAccept() - reuse_msg_accepted.assign_thread_id(thid="test_123", pthid="test_123") - self.test_conn_rec.invitation_msg_id = "test_123" - self.test_conn_rec.state = ConnRecord.State.COMPLETED.rfc160 - await self.test_conn_rec.save(session) - await self.test_conn_rec.metadata_set(session, "reuse_msg_id", "test_123") - await self.test_conn_rec.metadata_set(session, "reuse_msg_state", "initial") + problem_report.assign_thread_id(thid="the-thread-id", pthid="the-pthid") - with async_mock.patch.object( - self.test_conn_rec, - "metadata_set", - async_mock.CoroutineMock(side_effect=StorageNotFoundError), - ), async_mock.patch.object( - self.profile, "notify", autospec=True - ) as mock_notify: - with self.assertRaises(OutOfBandManagerError) as context: - await self.manager.receive_reuse_accepted_message( - reuse_msg_accepted, receipt, self.test_conn_rec - ) - mock_notify.assert_called_once() - assert "Error processing reuse accepted message" in str( - context.exception - ) - - async def test_problem_report_received_not_active(self): - async with self.profile.session() as session: - self.profile.context.update_settings({"public_invites": True}) - receipt = MessageReceipt( - recipient_did=TestConfig.test_did, - recipient_did_public=False, - sender_did="test_did", - ) - problem_report = ProblemReport( - description={ - "en": "test", - "code": ProblemReportReason.EXISTING_CONNECTION_NOT_ACTIVE.value, - } + with async_mock.patch.object( + OobRecord, "retrieve_by_tag_filter", autospec=True + ) as mock_retrieve_oob: + mock_retrieve_oob.return_value = async_mock.MagicMock( + emit_event=async_mock.CoroutineMock(), + delete_record=async_mock.CoroutineMock(), + save=async_mock.CoroutineMock(), ) - problem_report.assign_thread_id(thid="test_123", pthid="test_123") - self.test_conn_rec.invitation_msg_id = "test_123" - self.test_conn_rec.state = ConnRecord.State.COMPLETED.rfc160 - await self.test_conn_rec.save(session) - await self.test_conn_rec.metadata_set(session, "reuse_msg_id", "test_123") - await self.test_conn_rec.metadata_set(session, "reuse_msg_state", "initial") - with async_mock.patch.object( - DIDXManager, "receive_invitation", autospec=True - ) as didx_mgr_receive_invitation, async_mock.patch( - "aries_cloudagent.protocols.out_of_band.v1_0.manager.InvitationMessage", - autospec=True, - ) as inv_message_cls, async_mock.patch.object( - OutOfBandManager, - "fetch_connection_targets", - autospec=True, - ) as oob_mgr_fetch_conn: - - await self.manager.receive_problem_report( - problem_report, receipt, self.test_conn_rec - ) - assert ( - await self.test_conn_rec.metadata_get(session, "reuse_msg_state") - == "not_accepted" - ) - async def test_problem_report_received_not_exists(self): - async with self.profile.session() as session: - self.profile.context.update_settings({"public_invites": True}) - receipt = MessageReceipt( - recipient_did=TestConfig.test_did, - recipient_did_public=False, - sender_did="test_did", - ) - problem_report = ProblemReport( - description={ - "en": "test", - "code": ProblemReportReason.NO_EXISTING_CONNECTION.value, - } + await self.manager.receive_problem_report( + problem_report, receipt, self.test_conn_rec ) - problem_report.assign_thread_id(thid="test_123", pthid="test_123") - self.test_conn_rec.invitation_msg_id = "test_123" - self.test_conn_rec.state = ConnRecord.State.COMPLETED.rfc160 - await self.test_conn_rec.save(session) - await self.test_conn_rec.metadata_set(session, "reuse_msg_id", "test_123") - await self.test_conn_rec.metadata_set(session, "reuse_msg_state", "initial") - with async_mock.patch.object( - DIDXManager, "receive_invitation", autospec=True - ) as didx_mgr_receive_invitation, async_mock.patch( - "aries_cloudagent.protocols.out_of_band.v1_0.manager.InvitationMessage", - autospec=True, - ) as inv_message_cls, async_mock.patch.object( - OutOfBandManager, - "fetch_connection_targets", - autospec=True, - ) as oob_mgr_fetch_conn: - await self.manager.receive_problem_report( - problem_report, receipt, self.test_conn_rec - ) - assert ( - await self.test_conn_rec.metadata_get(session, "reuse_msg_state") - == "not_accepted" - ) + mock_retrieve_oob.assert_called_once_with( + ANY, {"invi_msg_id": "the-pthid", "reuse_msg_id": "the-thread-id"} + ) + assert mock_retrieve_oob.return_value.state == OobRecord.STATE_NOT_ACCEPTED - async def test_problem_report_received_invalid_conn(self): + async def test_receive_problem_report_x(self): self.profile.context.update_settings({"public_invites": True}) + receipt = MessageReceipt( recipient_did=TestConfig.test_did, recipient_did_public=False, @@ -1877,1594 +1139,492 @@ async def test_problem_report_received_invalid_conn(self): problem_report = ProblemReport( description={ "en": "test", - "code": ProblemReportReason.NO_EXISTING_CONNECTION.value, + "code": ProblemReportReason.EXISTING_CONNECTION_NOT_ACTIVE.value, } ) - problem_report.assign_thread_id(thid="test_123", pthid="test_123") - test_invalid_conn = ConnRecord( - my_did="Test", - their_did="Test", - invitation_msg_id="test_456", - connection_id="12345678-0123-4567-1234-567812345678", - ) + problem_report.assign_thread_id(thid="the-thread-id", pthid="the-pthid") + with async_mock.patch.object( - DIDXManager, "receive_invitation", autospec=True - ) as didx_mgr_receive_invitation, async_mock.patch( - "aries_cloudagent.protocols.out_of_band.v1_0.manager.InvitationMessage", - autospec=True, - ) as inv_message_cls, async_mock.patch.object( - OutOfBandManager, - "fetch_connection_targets", - autospec=True, - ) as oob_mgr_fetch_conn: + OobRecord, "retrieve_by_tag_filter", autospec=True + ) as mock_retrieve_oob: + mock_retrieve_oob.side_effect = (StorageNotFoundError(),) - with self.assertRaises(OutOfBandManagerError) as context: + with self.assertRaises(OutOfBandManagerError) as err: await self.manager.receive_problem_report( - problem_report, receipt, test_invalid_conn + problem_report, receipt, self.test_conn_rec ) - assert "Error processing problem report message" in str(context.exception) + assert "Error processing problem report message " in err.exception.message + + async def test_receive_invitation_with_valid_mediation(self): + mock_conn = async_mock.MagicMock(connection_id="dummy-connection") - async def test_existing_conn_record_public_did(self): async with self.profile.session() as session: self.profile.context.update_settings({"public_invites": True}) - test_exist_conn = ConnRecord( - my_did=TestConfig.test_did, - their_did=TestConfig.test_target_did, - their_public_did=TestConfig.test_target_did, - invitation_msg_id="12345678-0123-4567-1234-567812345678", - their_role=ConnRecord.Role.REQUESTER, - ) - await test_exist_conn.save(session) - await test_exist_conn.metadata_set(session, "reuse_msg_state", "initial") - await test_exist_conn.metadata_set(session, "reuse_msg_id", "test_123") - receipt = MessageReceipt( - recipient_did=TestConfig.test_did, - recipient_did_public=False, - sender_did=TestConfig.test_target_did, + mediation_record = MediationRecord( + role=MediationRecord.ROLE_CLIENT, + state=MediationRecord.STATE_GRANTED, + connection_id=self.test_mediator_conn_id, + routing_keys=self.test_mediator_routing_keys, + endpoint=self.test_mediator_endpoint, ) - + await mediation_record.save(session) with async_mock.patch.object( - DIDXManager, "receive_invitation", autospec=True - ) as didx_mgr_receive_invitation, async_mock.patch( - "aries_cloudagent.protocols.out_of_band.v1_0.manager.InvitationMessage", - autospec=True, - ) as inv_message_cls, async_mock.patch.object( - OutOfBandManager, - "fetch_connection_targets", - autospec=True, - ) as oob_mgr_fetch_conn, async_mock.patch.object( - ConnRecord, - "find_existing_connection", - async_mock.CoroutineMock(), - ) as oob_mgr_find_existing_conn, async_mock.patch.object( - OutOfBandManager, - "check_reuse_msg_state", - autospec=True, - ) as oob_mgr_check_reuse_state, async_mock.patch.object( - OutOfBandManager, - "create_handshake_reuse_message", - autospec=True, - ) as oob_mgr_create_reuse_msg, async_mock.patch.object( - OutOfBandManager, - "receive_reuse_message", - autospec=True, - ) as oob_mgr_receive_reuse_msg, async_mock.patch.object( - OutOfBandManager, - "receive_reuse_accepted_message", - autospec=True, - ) as oob_mgr_receive_accept_msg, async_mock.patch.object( - OutOfBandManager, - "receive_problem_report", - autospec=True, - ) as oob_mgr_receive_problem_report: - oob_mgr_find_existing_conn.return_value = test_exist_conn - oob_mgr_check_reuse_state.return_value = None - oob_mgr_create_reuse_msg.return_value = None - oob_mgr_receive_reuse_msg.return_value = None - oob_mgr_receive_accept_msg.return_value = None - oob_mgr_receive_problem_report.return_value = None - await test_exist_conn.metadata_set( - session, "reuse_msg_state", "accepted" - ) - mock_oob_invi = async_mock.MagicMock( - handshake_protocols=[ - pfx.qualify(HSProto.RFC23.name) for pfx in DIDCommPrefix - ], - services=[TestConfig.test_target_did], - requests_attach=[], + DIDXManager, "receive_invitation", async_mock.CoroutineMock() + ) as mock_didx_recv_invi, async_mock.patch.object( + ConnRecord, "retrieve_by_id", async_mock.CoroutineMock() + ) as mock_retrieve_conn_by_id: + invite = await self.manager.create_invitation( + my_endpoint=TestConfig.test_endpoint, + my_label="test123", + hs_protos=[HSProto.RFC23], ) - inv_message_cls.deserialize.return_value = mock_oob_invi - result = await self.manager.receive_invitation( - mock_oob_invi, use_existing_connection=True - ) - retrieved_conn_records = await ConnRecord.query( - session=session, - tag_filter={ - "invitation_msg_id": "12345678-0123-4567-1234-567812345678" - }, - post_filter_positive={}, - alt=True, + mock_retrieve_conn_by_id.return_value = mock_conn + mock_didx_recv_invi.return_value = mock_conn + invi_msg = invite.invitation + await self.manager.receive_invitation( + invitation=invi_msg, + mediation_id=mediation_record._id, ) - assert ( - await retrieved_conn_records[0].metadata_get( - session, "reuse_msg_id" - ) - is None + mock_didx_recv_invi.assert_called_once_with( + invitation=invi_msg, + their_public_did=None, + auto_accept=None, + alias=None, + mediation_id=mediation_record._id, ) - assert ( - await retrieved_conn_records[0].metadata_get( - session, "reuse_msg_state" - ) - is None - ) - assert result.connection_id == retrieved_conn_records[0].connection_id - async def test_existing_conn_record_public_did_not_accepted(self): - async with self.profile.session() as session: - self.profile.context.update_settings({"public_invites": True}) - test_exist_conn = ConnRecord( - my_did=TestConfig.test_did, - their_did="did:sov:LjgpST2rjsoxYegQDRm7EL", - their_public_did="did:sov:LjgpST2rjsoxYegQDRm7EL", - invitation_msg_id="12345678-0123-4567-1234-567812345678", - their_role=ConnRecord.Role.REQUESTER, - ) - await test_exist_conn.save(session) - await test_exist_conn.metadata_set(session, "reuse_msg_id", "test_123") - await test_exist_conn.metadata_set(session, "reuse_msg_state", "initial") - - test_new_conn = ConnRecord( - my_did=TestConfig.test_did, - their_did="did:sov:LjgpST2rjsoxYegQDRm7EL", - their_public_did="did:sov:LjgpST2rjsoxYegQDRm7EL", - invitation_msg_id="12345678-0123-4567-1234-1234545454487", - their_role=ConnRecord.Role.REQUESTER, - ) - - receipt = MessageReceipt( - recipient_did=TestConfig.test_did, - recipient_did_public=False, - sender_did=TestConfig.test_target_did, - ) - - with async_mock.patch.object( - DIDXManager, "receive_invitation", autospec=True - ) as didx_mgr_receive_invitation, async_mock.patch( - "aries_cloudagent.protocols.out_of_band.v1_0.manager.InvitationMessage", - autospec=True, - ) as inv_message_cls, async_mock.patch.object( - OutOfBandManager, - "fetch_connection_targets", - autospec=True, - ) as oob_mgr_fetch_conn, async_mock.patch.object( - ConnRecord, - "find_existing_connection", - async_mock.CoroutineMock(), - ) as oob_mgr_find_existing_conn, async_mock.patch.object( - OutOfBandManager, - "check_reuse_msg_state", - autospec=True, - ) as oob_mgr_check_reuse_state, async_mock.patch.object( - OutOfBandManager, - "create_handshake_reuse_message", - autospec=True, - ) as oob_mgr_create_reuse_msg, async_mock.patch.object( - OutOfBandManager, - "receive_reuse_message", - autospec=True, - ) as oob_mgr_receive_reuse_msg, async_mock.patch.object( - OutOfBandManager, - "receive_reuse_accepted_message", - autospec=True, - ) as oob_mgr_receive_accept_msg, async_mock.patch.object( - OutOfBandManager, - "receive_problem_report", - autospec=True, - ) as oob_mgr_receive_problem_report: - oob_mgr_find_existing_conn.return_value = test_exist_conn - oob_mgr_check_reuse_state.return_value = None - oob_mgr_create_reuse_msg.return_value = None - oob_mgr_receive_reuse_msg.return_value = None - oob_mgr_receive_accept_msg.return_value = None - oob_mgr_receive_problem_report.return_value = None - await test_exist_conn.metadata_set( - session, "reuse_msg_state", "not_accepted" - ) - didx_mgr_receive_invitation.return_value = test_new_conn - mock_oob_invi = async_mock.MagicMock( - handshake_protocols=[ - pfx.qualify(HSProto.RFC23.name) for pfx in DIDCommPrefix - ], - services=[TestConfig.test_target_did], - requests_attach=[], - ) - inv_message_cls.deserialize.return_value = mock_oob_invi - - result = await self.manager.receive_invitation( - mock_oob_invi, use_existing_connection=True - ) - retrieved_conn_records = await ConnRecord.query( - session=session, - tag_filter={ - "invitation_msg_id": "12345678-0123-4567-1234-567812345678" - }, - post_filter_positive={}, - alt=True, - ) - assert ( - await retrieved_conn_records[0].metadata_get( - session, "reuse_msg_state" - ) - == "not_accepted" - ) - assert result.connection_id != retrieved_conn_records[0].connection_id + async def test_receive_invitation_with_invalid_mediation(self): + mock_conn = async_mock.MagicMock(connection_id="dummy-connection") - async def test_existing_conn_record_public_did_inverse_cases(self): - async with self.profile.session() as session: - self.profile.context.update_settings({"public_invites": True}) - test_exist_conn = ConnRecord( - my_did=TestConfig.test_did, - their_did=TestConfig.test_target_did, - their_public_did=TestConfig.test_target_did, - invitation_msg_id="12345678-0123-4567-1234-567812345678", - their_role=ConnRecord.Role.REQUESTER, + with async_mock.patch.object( + DIDXManager, + "receive_invitation", + async_mock.CoroutineMock(), + ) as mock_didx_recv_invi, async_mock.patch.object( + ConnRecord, + "retrieve_by_id", + async_mock.CoroutineMock(), + ) as mock_retrieve_conn_by_id: + invite = await self.manager.create_invitation( + my_endpoint=TestConfig.test_endpoint, + my_label="test123", + hs_protos=[HSProto.RFC23], ) - await self.test_conn_rec.save(session) - await test_exist_conn.save(session) - await test_exist_conn.metadata_set(session, "reuse_msg_state", "initial") - await test_exist_conn.metadata_set(session, "reuse_msg_id", "test_123") - - with async_mock.patch.object( - DIDXManager, "receive_invitation", autospec=True - ) as didx_mgr_receive_invitation, async_mock.patch( - "aries_cloudagent.protocols.out_of_band.v1_0.manager.InvitationMessage", - autospec=True, - ) as inv_message_cls, async_mock.patch.object( - OutOfBandManager, - "fetch_connection_targets", - autospec=True, - ) as oob_mgr_fetch_conn, async_mock.patch.object( - ConnRecord, - "find_existing_connection", - async_mock.CoroutineMock(), - ) as oob_mgr_find_existing_conn, async_mock.patch.object( - OutOfBandManager, - "check_reuse_msg_state", - autospec=True, - ) as oob_mgr_check_reuse_state: - oob_mgr_find_existing_conn.return_value = test_exist_conn - didx_mgr_receive_invitation.return_value = self.test_conn_rec - mock_oob_invi = async_mock.MagicMock( - handshake_protocols=[ - pfx.qualify(HSProto.RFC23.name) for pfx in DIDCommPrefix - ], - services=[TestConfig.test_target_did], - requests_attach=[], - ) - inv_message_cls.deserialize.return_value = mock_oob_invi - - result = await self.manager.receive_invitation( - mock_oob_invi, use_existing_connection=False - ) - retrieved_conn_records = await ConnRecord.query( - session=session, - tag_filter={ - "invitation_msg_id": "12345678-0123-4567-1234-567812345678" - }, - post_filter_positive={}, - alt=True, - ) - assert result.connection_id != retrieved_conn_records[0].connection_id - - async def test_existing_conn_record_public_did_timeout(self): - async with self.profile.session() as session: - self.profile.context.update_settings({"public_invites": True}) - test_exist_conn = ConnRecord( - my_did=TestConfig.test_did, - their_did=TestConfig.test_target_did, - their_public_did=TestConfig.test_target_did, - invitation_msg_id="12345678-0123-4567-1234-567812345678", - their_role=ConnRecord.Role.REQUESTER, + mock_didx_recv_invi.return_value = mock_conn + mock_retrieve_conn_by_id.return_value = mock_conn + invi_msg = invite.invitation + await self.manager.receive_invitation( + invi_msg, + mediation_id="test-mediation-id", ) - await test_exist_conn.save(session) - await test_exist_conn.metadata_set(session, "reuse_msg_state", "initial") - await test_exist_conn.metadata_set(session, "reuse_msg_id", "test_123") - receipt = MessageReceipt( - recipient_did=TestConfig.test_did, - recipient_did_public=False, - sender_did=TestConfig.test_target_did, + mock_didx_recv_invi.assert_called_once_with( + invitation=invi_msg, + their_public_did=None, + auto_accept=None, + alias=None, + mediation_id=None, ) - with async_mock.patch.object( - DIDXManager, "receive_invitation", autospec=True - ) as didx_mgr_receive_invitation, async_mock.patch( - "aries_cloudagent.protocols.out_of_band.v1_0.manager.InvitationMessage", - autospec=True, - ) as inv_message_cls, async_mock.patch.object( - OutOfBandManager, - "fetch_connection_targets", - autospec=True, - ) as oob_mgr_fetch_conn, async_mock.patch.object( - ConnRecord, - "find_existing_connection", - async_mock.CoroutineMock(), - ) as oob_mgr_find_existing_conn, async_mock.patch.object( - OutOfBandManager, - "check_reuse_msg_state", - autospec=True, - ) as oob_mgr_check_reuse_state, async_mock.patch.object( - self.profile, "notify", autospec=True - ) as mock_notify: - oob_mgr_find_existing_conn.return_value = test_exist_conn - oob_mgr_check_reuse_state.side_effect = asyncio.TimeoutError - mock_oob_invi = async_mock.MagicMock( - handshake_protocols=[ - pfx.qualify(HSProto.RFC23.name) for pfx in DIDCommPrefix - ], - services=[TestConfig.test_target_did], - requests_attach=[], - ) - inv_message_cls.deserialize.return_value = mock_oob_invi - - result = await self.manager.receive_invitation( - mock_oob_invi, use_existing_connection=True - ) - mock_notify.assert_called() - retrieved_conn_records = await ConnRecord.query( - session=session, - tag_filter={"their_public_did": TestConfig.test_target_did}, - ) - assert ( - retrieved_conn_records[0].state == ConnRecord.State.ABANDONED.rfc160 - ) - - async def test_existing_conn_record_public_did_timeout_no_handshake_protocol(self): - async with self.profile.session() as session: - self.profile.context.update_settings({"public_invites": True}) - test_exist_conn = ConnRecord( - my_did=TestConfig.test_did, - their_did=TestConfig.test_target_did, - their_public_did=TestConfig.test_target_did, - invitation_msg_id="12345678-0123-4567-1234-567812345678", - their_role=ConnRecord.Role.REQUESTER, - ) - await test_exist_conn.save(session) - await test_exist_conn.metadata_set(session, "reuse_msg_state", "initial") - await test_exist_conn.metadata_set(session, "reuse_msg_id", "test_123") - receipt = MessageReceipt( - recipient_did=TestConfig.test_did, - recipient_did_public=False, - sender_did=TestConfig.test_target_did, - ) + async def test_receive_invitation_didx_services_with_service_block(self): + self.profile.context.update_settings({"public_invites": True}) - with async_mock.patch.object( - DIDXManager, "receive_invitation", autospec=True - ) as didx_mgr_receive_invitation, async_mock.patch( - "aries_cloudagent.protocols.out_of_band.v1_0.manager.InvitationMessage", - autospec=True, - ) as inv_message_cls, async_mock.patch.object( - OutOfBandManager, - "fetch_connection_targets", - autospec=True, - ) as oob_mgr_fetch_conn, async_mock.patch.object( - ConnRecord, - "find_existing_connection", - async_mock.CoroutineMock(), - ) as oob_mgr_find_existing_conn: - oob_mgr_find_existing_conn.return_value = test_exist_conn - mock_oob_invi = async_mock.MagicMock( - handshake_protocols=[], - services=[TestConfig.test_target_did], - requests_attach=[ - {"having": "attachment", "is": "no", "good": "here"} - ], - ) - inv_message_cls.deserialize.return_value = mock_oob_invi - with self.assertRaises(OutOfBandManagerError) as context: - result = await self.manager.receive_invitation( - mock_oob_invi, use_existing_connection=False - ) - assert "No existing connection exists and " in str(context.exception) + mock_conn = async_mock.MagicMock(connection_id="dummy-connection") - async def test_req_v1_attach_presentation_existing_conn_no_auto_present(self): - async with self.profile.session() as session: - self.profile.context.update_settings({"public_invites": True}) - test_exist_conn = ConnRecord( - my_did=TestConfig.test_did, - their_did=TestConfig.test_target_did, - their_public_did=TestConfig.test_target_did, - invitation_msg_id="12345678-0123-4567-1234-567812345678", - their_role=ConnRecord.Role.REQUESTER, - ) - await test_exist_conn.save(session) - await test_exist_conn.metadata_set(session, "reuse_msg_state", "initial") - await test_exist_conn.metadata_set(session, "reuse_msg_id", "test_123") - receipt = MessageReceipt( - recipient_did=TestConfig.test_did, - recipient_did_public=False, - sender_did=TestConfig.test_target_did, + with async_mock.patch.object( + test_module, "DIDXManager", autospec=True + ) as didx_mgr_cls, async_mock.patch.object( + ConnRecord, "retrieve_by_id", async_mock.CoroutineMock() + ) as mock_retrieve_conn_by_id: + didx_mgr_cls.return_value = async_mock.MagicMock( + receive_invitation=async_mock.CoroutineMock(return_value=mock_conn) ) - - exchange_rec = V10PresentationExchange() - - with async_mock.patch.object( - DIDXManager, "receive_invitation", autospec=True - ) as didx_mgr_receive_invitation, async_mock.patch.object( - PresentationManager, "receive_request", autospec=True - ) as pres_mgr_receive_request, async_mock.patch( - "aries_cloudagent.protocols.out_of_band.v1_0.manager.InvitationMessage", - autospec=True, - ) as inv_message_cls, async_mock.patch.object( - OutOfBandManager, - "fetch_connection_targets", - autospec=True, - ) as oob_mgr_fetch_conn, async_mock.patch.object( - ConnRecord, - "find_existing_connection", - async_mock.CoroutineMock(), - ) as oob_mgr_find_existing_conn, async_mock.patch.object( - OutOfBandManager, - "check_reuse_msg_state", - autospec=True, - ) as oob_mgr_check_reuse_state, async_mock.patch.object( - OutOfBandManager, - "create_handshake_reuse_message", - autospec=True, - ) as oob_mgr_create_reuse_msg, async_mock.patch.object( - OutOfBandManager, - "receive_reuse_message", - autospec=True, - ) as oob_mgr_receive_reuse_msg, async_mock.patch.object( - OutOfBandManager, - "receive_reuse_accepted_message", - autospec=True, - ) as oob_mgr_receive_accept_msg, async_mock.patch.object( - OutOfBandManager, - "receive_problem_report", - autospec=True, - ) as oob_mgr_receive_problem_report: - oob_mgr_find_existing_conn.return_value = test_exist_conn - pres_mgr_receive_request.return_value = exchange_rec - - mock_oob_invi = async_mock.MagicMock( - handshake_protocols=[ - pfx.qualify(HSProto.RFC23.name) for pfx in DIDCommPrefix - ], - services=[TestConfig.test_target_did], - requests_attach=[ - AttachDecorator.deserialize(TestConfig.req_attach_v1) - ], - ) - - inv_message_cls.deserialize.return_value = mock_oob_invi - - with self.assertRaises(OutOfBandManagerError) as context: - result = await self.manager.receive_invitation( - mock_oob_invi, use_existing_connection=True + mock_retrieve_conn_by_id.return_value = mock_conn + oob_invitation = InvitationMessage( + requests_attach=[], + handshake_protocols=[ + pfx.qualify(HSProto.RFC23.name) for pfx in DIDCommPrefix + ], + services=[ + OobService( + recipient_keys=["dummy"], + routing_keys=[], ) - assert "Configuration sets auto_present false" in str(context.exception) - - async def test_req_v1_attach_presentation_existing_conn_auto_present_pres_msg(self): - async with self.profile.session() as session: - self.profile.context.update_settings({"public_invites": True}) - self.profile.context.update_settings( - {"debug.auto_respond_presentation_request": True} - ) - test_exist_conn = ConnRecord( - my_did=TestConfig.test_did, - their_did=TestConfig.test_target_did, - their_public_did=TestConfig.test_target_did, - invitation_msg_id="12345678-0123-4567-1234-567812345678", - their_role=ConnRecord.Role.REQUESTER, - ) - await test_exist_conn.save(session) - await test_exist_conn.metadata_set(session, "reuse_msg_state", "initial") - await test_exist_conn.metadata_set(session, "reuse_msg_id", "test_123") - receipt = MessageReceipt( - recipient_did=TestConfig.test_did, - recipient_did_public=False, - sender_did=TestConfig.test_target_did, + ], ) - exchange_rec = V10PresentationExchange() - exchange_rec.auto_present = True - exchange_rec.presentation_request = TestConfig.INDY_PROOF_REQ - - with async_mock.patch.object( - DIDXManager, - "receive_invitation", - autospec=True, - ) as didx_mgr_receive_invitation, async_mock.patch.object( - PresentationManager, - "receive_request", - autospec=True, - ) as pres_mgr_receive_request, async_mock.patch( - "aries_cloudagent.protocols.out_of_band.v1_0.manager.InvitationMessage", - autospec=True, - ) as inv_message_cls, async_mock.patch.object( - OutOfBandManager, - "fetch_connection_targets", - autospec=True, - ) as oob_mgr_fetch_conn, async_mock.patch.object( - ConnRecord, - "find_existing_connection", - async_mock.CoroutineMock(), - ) as oob_mgr_find_existing_conn, async_mock.patch.object( - OutOfBandManager, - "check_reuse_msg_state", - autospec=True, - ) as oob_mgr_check_reuse_state, async_mock.patch.object( - OutOfBandManager, - "create_handshake_reuse_message", - autospec=True, - ) as oob_mgr_create_reuse_msg, async_mock.patch.object( - OutOfBandManager, - "receive_reuse_message", - autospec=True, - ) as oob_mgr_receive_reuse_msg, async_mock.patch.object( - OutOfBandManager, - "receive_reuse_accepted_message", - autospec=True, - ) as oob_mgr_receive_accept_msg, async_mock.patch.object( - OutOfBandManager, - "receive_problem_report", - autospec=True, - ) as oob_mgr_receive_problem_report, async_mock.patch.object( - PresentationManager, - "create_presentation", - autospec=True, - ) as pres_mgr_create_presentation: - oob_mgr_find_existing_conn.return_value = test_exist_conn - pres_mgr_receive_request.return_value = exchange_rec - pres_mgr_create_presentation.return_value = ( - exchange_rec, - Presentation( - presentations_attach=[ - AttachDecorator.data_base64({"bogus": "proof"}) - ] - ), - ) - holder = async_mock.MagicMock(IndyHolder, autospec=True) - get_creds = async_mock.CoroutineMock( - return_value=( - { - "cred_info": {"referent": "dummy_reft"}, - "attrs": { - "player": "Richie Knucklez", - "screenCapture": "aW1hZ2luZSBhIHNjcmVlbiBjYXB0dXJl", - "highScore": "1234560", - }, - }, - ) - ) - holder.get_credentials_for_presentation_request_by_referent = get_creds - holder.create_credential_request = async_mock.CoroutineMock( - return_value=( - json.dumps(TestConfig.indy_cred_req), - json.dumps(TestConfig.cred_req_meta), - ) - ) - self.profile.context.injector.bind_instance(IndyHolder, holder) - mock_oob_invi = async_mock.MagicMock( - handshake_protocols=[ - pfx.qualify(HSProto.RFC23.name) for pfx in DIDCommPrefix - ], - services=[TestConfig.test_target_did], - requests_attach=[ - AttachDecorator.deserialize(TestConfig.req_attach_v1) - ], - ) + await self.manager.receive_invitation(oob_invitation) - inv_message_cls.deserialize.return_value = mock_oob_invi + async def test_receive_invitation_connection_protocol(self): + self.profile.context.update_settings({"public_invites": True}) - conn_rec = await self.manager.receive_invitation( - mock_oob_invi, use_existing_connection=True - ) - assert conn_rec is not None + mock_conn = async_mock.MagicMock(connection_id="dummy-connection") - async def test_req_v1_attach_pres_catch_value_error(self): - async with self.profile.session() as session: - self.profile.context.update_settings({"public_invites": True}) - self.profile.context.update_settings( - {"debug.auto_respond_presentation_request": True} - ) - test_exist_conn = ConnRecord( - my_did=TestConfig.test_did, - their_did=TestConfig.test_target_did, - their_public_did=TestConfig.test_target_did, - invitation_msg_id="12345678-0123-4567-1234-567812345678", - their_role=ConnRecord.Role.REQUESTER, - ) - await test_exist_conn.save(session) - await test_exist_conn.metadata_set(session, "reuse_msg_state", "initial") - await test_exist_conn.metadata_set(session, "reuse_msg_id", "test_123") - receipt = MessageReceipt( - recipient_did=TestConfig.test_did, - recipient_did_public=False, - sender_did=TestConfig.test_target_did, + with async_mock.patch.object( + test_module, "ConnectionManager", autospec=True + ) as conn_mgr_cls, async_mock.patch.object( + ConnRecord, "retrieve_by_id", async_mock.CoroutineMock() + ) as mock_conn_retrieve_by_id: + conn_mgr_cls.return_value = async_mock.MagicMock( + receive_invitation=async_mock.CoroutineMock(return_value=mock_conn) ) - - exchange_rec = V10PresentationExchange() - exchange_rec.auto_present = True - exchange_rec.presentation_request = TestConfig.INDY_PROOF_REQ - - with async_mock.patch.object( - DIDXManager, - "receive_invitation", - autospec=True, - ) as didx_mgr_receive_invitation, async_mock.patch.object( - PresentationManager, - "receive_request", - autospec=True, - ) as pres_mgr_receive_request, async_mock.patch( - "aries_cloudagent.protocols.out_of_band.v1_0.manager.InvitationMessage", - autospec=True, - ) as inv_message_cls, async_mock.patch.object( - OutOfBandManager, - "fetch_connection_targets", - autospec=True, - ) as oob_mgr_fetch_conn, async_mock.patch.object( - ConnRecord, - "find_existing_connection", - async_mock.CoroutineMock(), - ) as oob_mgr_find_existing_conn, async_mock.patch.object( - OutOfBandManager, - "check_reuse_msg_state", - autospec=True, - ) as oob_mgr_check_reuse_state, async_mock.patch.object( - OutOfBandManager, - "create_handshake_reuse_message", - autospec=True, - ) as oob_mgr_create_reuse_msg, async_mock.patch.object( - OutOfBandManager, - "receive_reuse_message", - autospec=True, - ) as oob_mgr_receive_reuse_msg, async_mock.patch.object( - OutOfBandManager, - "receive_reuse_accepted_message", - autospec=True, - ) as oob_mgr_receive_accept_msg, async_mock.patch.object( - OutOfBandManager, - "receive_problem_report", - autospec=True, - ) as oob_mgr_receive_problem_report, async_mock.patch.object( - PresentationManager, - "create_presentation", - autospec=True, - ) as pres_mgr_create_presentation: - oob_mgr_find_existing_conn.return_value = test_exist_conn - pres_mgr_receive_request.return_value = exchange_rec - pres_mgr_create_presentation.return_value = ( - exchange_rec, - Presentation(comment="this is test"), - ) - holder = async_mock.MagicMock(IndyHolder, autospec=True) - get_creds = async_mock.CoroutineMock(return_value=()) - holder.get_credentials_for_presentation_request_by_referent = get_creds - holder.create_credential_request = async_mock.CoroutineMock( - return_value=( - json.dumps(TestConfig.indy_cred_req), - json.dumps(TestConfig.cred_req_meta), - ) - ) - self.profile.context.injector.bind_instance(IndyHolder, holder) - mock_oob_invi = async_mock.MagicMock( - handshake_protocols=[ - pfx.qualify(HSProto.RFC23.name) for pfx in DIDCommPrefix - ], - services=[TestConfig.test_target_did], - requests_attach=[ - AttachDecorator.deserialize(TestConfig.req_attach_v1) - ], - ) - - inv_message_cls.deserialize.return_value = mock_oob_invi - with self.assertRaises(OutOfBandManagerError) as context: - await self.manager.receive_invitation( - mock_oob_invi, use_existing_connection=True + mock_conn_retrieve_by_id.return_value = mock_conn + oob_invitation = InvitationMessage( + handshake_protocols=[ + pfx.qualify(HSProto.RFC160.name) for pfx in DIDCommPrefix + ], + label="test", + _id="test123", + services=[ + OobService( + recipient_keys=[ + DIDKey.from_public_key_b58( + "9WCgWKUaAJj3VWxxtzvvMQN3AoFxoBtBDo9ntwJnVVCC", + KeyType.ED25519, + ).did + ], + routing_keys=[], + service_endpoint="http://localhost", ) - assert "Cannot auto-respond" in str(context.exception) - - async def test_req_v2_attach_presentation_existing_conn_no_auto_present(self): - async with self.profile.session() as session: - self.profile.context.update_settings({"public_invites": True}) - test_exist_conn = ConnRecord( - my_did=TestConfig.test_did, - their_did=TestConfig.test_target_did, - their_public_did=TestConfig.test_target_did, - invitation_msg_id="12345678-0123-4567-1234-567812345678", - their_role=ConnRecord.Role.REQUESTER, + ], + requests_attach=[], ) - await test_exist_conn.save(session) - await test_exist_conn.metadata_set(session, "reuse_msg_state", "initial") - await test_exist_conn.metadata_set(session, "reuse_msg_id", "test_123") - receipt = MessageReceipt( - recipient_did=TestConfig.test_did, - recipient_did_public=False, - sender_did=TestConfig.test_target_did, + await self.manager.receive_invitation(oob_invitation) + conn_mgr_cls.return_value.receive_invitation.assert_called_once_with( + invitation=ANY, + their_public_did=None, + auto_accept=None, + alias=None, + mediation_id=None, ) + _, kwargs = conn_mgr_cls.return_value.receive_invitation.call_args + invitation = kwargs["invitation"] + assert isinstance(invitation, ConnectionInvitation) - px2_rec = test_module.V20PresExRecord() + assert invitation.endpoint == "http://localhost" + assert invitation.recipient_keys == [ + "9WCgWKUaAJj3VWxxtzvvMQN3AoFxoBtBDo9ntwJnVVCC" + ] + assert not invitation.routing_keys - with async_mock.patch.object( - DIDXManager, "receive_invitation", autospec=True - ) as didx_mgr_receive_invitation, async_mock.patch.object( - V20PresManager, "receive_pres_request", autospec=True - ) as pres_mgr_receive_pres_req, async_mock.patch( - "aries_cloudagent.protocols.out_of_band.v1_0.manager.InvitationMessage", - autospec=True, - ) as inv_message_cls, async_mock.patch.object( - OutOfBandManager, - "fetch_connection_targets", - autospec=True, - ) as oob_mgr_fetch_conn, async_mock.patch.object( - ConnRecord, - "find_existing_connection", - async_mock.CoroutineMock(), - ) as oob_mgr_find_existing_conn, async_mock.patch.object( - OutOfBandManager, - "check_reuse_msg_state", - autospec=True, - ) as oob_mgr_check_reuse_state, async_mock.patch.object( - OutOfBandManager, - "create_handshake_reuse_message", - autospec=True, - ) as oob_mgr_create_reuse_msg, async_mock.patch.object( - OutOfBandManager, - "receive_reuse_message", - autospec=True, - ) as oob_mgr_receive_reuse_msg, async_mock.patch.object( - OutOfBandManager, - "receive_reuse_accepted_message", - autospec=True, - ) as oob_mgr_receive_accept_msg, async_mock.patch.object( - OutOfBandManager, - "receive_problem_report", - autospec=True, - ) as oob_mgr_receive_problem_report: - oob_mgr_find_existing_conn.return_value = test_exist_conn - pres_mgr_receive_pres_req.return_value = px2_rec + async def test_receive_invitation_services_with_neither_service_blocks_nor_dids( + self, + ): + self.profile.context.update_settings({"public_invites": True}) + oob_invitation = InvitationMessage( + services=[], + ) + with self.assertRaises(OutOfBandManagerError) as err: + await self.manager.receive_invitation(oob_invitation) - mock_oob_invi = async_mock.MagicMock( - handshake_protocols=[ - pfx.qualify(HSProto.RFC23.name) for pfx in DIDCommPrefix - ], - services=[TestConfig.test_target_did], - requests_attach=[ - AttachDecorator.deserialize(TestConfig.req_attach_v2) - ], - ) + assert "service array must have exactly one element" in err.exception.message - inv_message_cls.deserialize.return_value = mock_oob_invi + async def test_receive_invitation_no_hs_protos_no_attach( + self, + ): + self.profile.context.update_settings({"public_invites": True}) + oob_invitation = InvitationMessage( + services=["did:sov:something"], + ) + with self.assertRaises(OutOfBandManagerError) as err: + await self.manager.receive_invitation(oob_invitation) - with self.assertRaises(OutOfBandManagerError) as context: - await self.manager.receive_invitation( - mock_oob_invi, use_existing_connection=True - ) - assert ( - "Configuration set auto_present false: cannot respond automatically to presentation requests" - == str(context.exception) - ) + assert ( + "Invitation must specify handshake_protocols, requests_attach, or both" + in err.exception.message + ) - async def test_req_v2_attach_presentation_existing_conn_auto_present_pres_msg(self): - async with self.profile.session() as session: - self.profile.context.update_settings({"public_invites": True}) - self.profile.context.update_settings( - {"debug.auto_respond_presentation_request": True} - ) - test_exist_conn = ConnRecord( - my_did=TestConfig.test_did, - their_did=TestConfig.test_target_did, - their_public_did=TestConfig.test_target_did, - invitation_msg_id="12345678-0123-4567-1234-567812345678", - their_role=ConnRecord.Role.REQUESTER, + async def test_existing_conn_record_public_did(self): + self.profile.context.update_settings({"public_invites": True}) + + test_exist_conn = ConnRecord( + connection_id="connection_id", + my_did=TestConfig.test_did, + their_did=TestConfig.test_target_did, + their_public_did=TestConfig.test_target_did, + invitation_msg_id="12345678-0123-4567-1234-567812345678", + their_role=ConnRecord.Role.REQUESTER, + ) + + with async_mock.patch.object( + ConnRecord, + "find_existing_connection", + async_mock.CoroutineMock(), + ) as oob_mgr_find_existing_conn, async_mock.patch.object( + OobRecord, "save", async_mock.CoroutineMock() + ) as oob_record_save, async_mock.patch.object( + OobRecord, "retrieve_by_id", async_mock.CoroutineMock() + ) as oob_record_retrieve_by_id, async_mock.patch.object( + OutOfBandManager, "fetch_connection_targets", autospec=True + ) as oob_mgr_fetch_conn: + oob_mgr_find_existing_conn.return_value = test_exist_conn + oob_mgr_fetch_conn.return_value = [] + oob_invitation = InvitationMessage( + handshake_protocols=[ + pfx.qualify(HSProto.RFC23.name) for pfx in DIDCommPrefix + ], + services=[TestConfig.test_target_did], + requests_attach=[], ) - await test_exist_conn.save(session) - await test_exist_conn.metadata_set(session, "reuse_msg_state", "initial") - await test_exist_conn.metadata_set(session, "reuse_msg_id", "test_123") - receipt = MessageReceipt( - recipient_did=TestConfig.test_did, - recipient_did_public=False, - sender_did=TestConfig.test_target_did, + + oob_record_retrieve_by_id.return_value = async_mock.MagicMock( + state=OobRecord.STATE_ACCEPTED ) - px2_rec = test_module.V20PresExRecord( - auto_present=True, - pres_request=TestConfig.PRES_REQ_V2.serialize(), + result = await self.manager.receive_invitation( + oob_invitation, use_existing_connection=True ) - with async_mock.patch.object( - DIDXManager, - "receive_invitation", - autospec=True, - ) as didx_mgr_receive_invitation, async_mock.patch.object( - V20PresManager, - "receive_pres_request", - autospec=True, - ) as pres_mgr_receive_pres_req, async_mock.patch( - "aries_cloudagent.protocols.out_of_band.v1_0.manager.InvitationMessage", - autospec=True, - ) as inv_message_cls, async_mock.patch.object( - OutOfBandManager, - "fetch_connection_targets", - autospec=True, - ) as oob_mgr_fetch_conn, async_mock.patch.object( - ConnRecord, - "find_existing_connection", - async_mock.CoroutineMock(), - ) as oob_mgr_find_existing_conn, async_mock.patch.object( - OutOfBandManager, - "check_reuse_msg_state", - autospec=True, - ) as oob_mgr_check_reuse_state, async_mock.patch.object( - OutOfBandManager, - "create_handshake_reuse_message", - autospec=True, - ) as oob_mgr_create_reuse_msg, async_mock.patch.object( - OutOfBandManager, - "receive_reuse_message", - autospec=True, - ) as oob_mgr_receive_reuse_msg, async_mock.patch.object( - OutOfBandManager, - "receive_reuse_accepted_message", - autospec=True, - ) as oob_mgr_receive_accept_msg, async_mock.patch.object( - OutOfBandManager, - "receive_problem_report", - autospec=True, - ) as oob_mgr_receive_problem_report, async_mock.patch.object( - V20PresManager, - "create_pres", - autospec=True, - ) as pres_mgr_create_pres: - oob_mgr_find_existing_conn.return_value = test_exist_conn - pres_mgr_receive_pres_req.return_value = px2_rec - pres_mgr_create_pres.return_value = ( - px2_rec, - V20Pres( - formats=[ - V20PresFormat( - attach_id="indy", - format_=V20_PRES_ATTACH_FORMAT[PRES_20][ - V20PresFormat.Format.INDY.api - ], - ) - ], - presentations_attach=[ - AttachDecorator.data_base64( - mapping={"bogus": "proof"}, - ident="indy", - ) - ], - ), - ) - holder = async_mock.MagicMock(IndyHolder, autospec=True) - get_creds = async_mock.CoroutineMock( - return_value=( - { - "cred_info": {"referent": "dummy_reft"}, - "attrs": { - "player": "Richie Knucklez", - "screenCapture": "aW1hZ2luZSBhIHNjcmVlbiBjYXB0dXJl", - "highScore": "1234560", - }, - }, - ) - ) - holder.get_credentials_for_presentation_request_by_referent = get_creds - holder.create_credential_request = async_mock.CoroutineMock( - return_value=( - json.dumps(TestConfig.indy_cred_req), - json.dumps(TestConfig.cred_req_meta), - ) - ) - self.profile.context.injector.bind_instance(IndyHolder, holder) - mock_oob_invi = async_mock.MagicMock( - handshake_protocols=[ - pfx.qualify(HSProto.RFC23.name) for pfx in DIDCommPrefix - ], - services=[TestConfig.test_target_did], - requests_attach=[ - AttachDecorator.deserialize(TestConfig.req_attach_v2) - ], - ) + oob_mgr_find_existing_conn.assert_called_once() + assert result.state == OobRecord.STATE_ACCEPTED + oob_record_save.assert_called_once_with( + ANY, reason="Storing reuse msg data" + ) - inv_message_cls.deserialize.return_value = mock_oob_invi + async def test_receive_invitation_handshake_reuse(self): + self.profile.context.update_settings({"public_invites": True}) - conn_rec = await self.manager.receive_invitation( - mock_oob_invi, use_existing_connection=True - ) - assert conn_rec is not None + test_exist_conn = ConnRecord( + connection_id="connection_id", + my_did=TestConfig.test_did, + their_did=TestConfig.test_target_did, + their_public_did=TestConfig.test_target_did, + invitation_msg_id="12345678-0123-4567-1234-567812345678", + their_role=ConnRecord.Role.REQUESTER, + ) - async def test_req_v2_attach_pres_catch_value_error(self): - async with self.profile.session() as session: - self.profile.context.update_settings({"public_invites": True}) - self.profile.context.update_settings( - {"debug.auto_respond_presentation_request": False} - ) - test_exist_conn = ConnRecord( - my_did=TestConfig.test_did, - their_did=TestConfig.test_target_did, - their_public_did=TestConfig.test_target_did, - invitation_msg_id="12345678-0123-4567-1234-567812345678", - their_role=ConnRecord.Role.REQUESTER, + with async_mock.patch.object( + test_module.OutOfBandManager, + "_handle_hanshake_reuse", + async_mock.CoroutineMock(), + ) as handle_handshake_reuse, async_mock.patch.object( + test_module.OutOfBandManager, + "_perform_handshake", + async_mock.CoroutineMock(), + ) as perform_handshake, async_mock.patch.object( + ConnRecord, + "find_existing_connection", + async_mock.CoroutineMock(return_value=test_exist_conn), + ): + oob_invitation = InvitationMessage( + handshake_protocols=[ + pfx.qualify(HSProto.RFC23.name) for pfx in DIDCommPrefix + ], + services=[TestConfig.test_target_did], + requests_attach=[], ) - await test_exist_conn.save(session) - await test_exist_conn.metadata_set(session, "reuse_msg_state", "initial") - await test_exist_conn.metadata_set(session, "reuse_msg_id", "test_123") - receipt = MessageReceipt( - recipient_did=TestConfig.test_did, - recipient_did_public=False, - sender_did=TestConfig.test_target_did, + + handle_handshake_reuse.return_value = async_mock.MagicMock( + state=OobRecord.STATE_ACCEPTED ) - px2_rec = test_module.V20PresExRecord( - auto_present=False, - pres_request=TestConfig.PRES_REQ_V2.serialize(), + result = await self.manager.receive_invitation( + oob_invitation, use_existing_connection=True ) - with async_mock.patch.object( - DIDXManager, - "receive_invitation", - autospec=True, - ) as didx_mgr_receive_invitation, async_mock.patch.object( - V20PresManager, - "receive_pres_request", - autospec=True, - ) as pres_mgr_receive_pres_req, async_mock.patch( - "aries_cloudagent.protocols.out_of_band.v1_0.manager.InvitationMessage", - autospec=True, - ) as inv_message_cls, async_mock.patch.object( - OutOfBandManager, - "fetch_connection_targets", - autospec=True, - ) as oob_mgr_fetch_conn, async_mock.patch.object( - ConnRecord, - "find_existing_connection", - async_mock.CoroutineMock(), - ) as oob_mgr_find_existing_conn, async_mock.patch.object( - OutOfBandManager, - "check_reuse_msg_state", - autospec=True, - ) as oob_mgr_check_reuse_state, async_mock.patch.object( - OutOfBandManager, - "create_handshake_reuse_message", - autospec=True, - ) as oob_mgr_create_reuse_msg, async_mock.patch.object( - OutOfBandManager, - "receive_reuse_message", - autospec=True, - ) as oob_mgr_receive_reuse_msg, async_mock.patch.object( - OutOfBandManager, - "receive_reuse_accepted_message", - autospec=True, - ) as oob_mgr_receive_accept_msg, async_mock.patch.object( - OutOfBandManager, - "receive_problem_report", - autospec=True, - ) as oob_mgr_receive_problem_report, async_mock.patch.object( - V20PresManager, - "create_pres", - autospec=True, - ) as pres_mgr_create_pres: - oob_mgr_find_existing_conn.return_value = test_exist_conn - pres_mgr_receive_pres_req.return_value = px2_rec - pres_mgr_create_pres.return_value = ( - px2_rec, - V20Pres( - formats=[ - V20PresFormat( - attach_id="indy", - format_=V20_PRES_ATTACH_FORMAT[PRES_20][ - V20PresFormat.Format.INDY.api - ], - ) - ], - presentations_attach=[ - AttachDecorator.data_base64( - mapping={"bogus": "proof"}, - ident="indy", - ) - ], - ), - ) - holder = async_mock.MagicMock(IndyHolder, autospec=True) - get_creds = async_mock.CoroutineMock(return_value=()) - holder.get_credentials_for_presentation_request_by_referent = get_creds - holder.create_credential_request = async_mock.CoroutineMock( - return_value=( - json.dumps(TestConfig.indy_cred_req), - json.dumps(TestConfig.cred_req_meta), - ) - ) - self.profile.context.injector.bind_instance(IndyHolder, holder) - mock_oob_invi = async_mock.MagicMock( - handshake_protocols=[ - pfx.qualify(HSProto.RFC23.name) for pfx in DIDCommPrefix - ], - services=[TestConfig.test_target_did], - requests_attach=[ - AttachDecorator.deserialize(TestConfig.req_attach_v2) - ], - ) + perform_handshake.assert_not_called() + handle_handshake_reuse.assert_called_once_with(ANY, test_exist_conn) - inv_message_cls.deserialize.return_value = mock_oob_invi - with self.assertRaises(OutOfBandManagerError) as context: - await self.manager.receive_invitation( - mock_oob_invi, use_existing_connection=True - ) - assert "cannot respond automatically" in str(context.exception) + assert result.state == OobRecord.STATE_ACCEPTED - async def test_req_attach_cred_offer_v1(self): - async with self.profile.session() as session: - self.profile.context.update_settings({"public_invites": True}) - self.profile.context.update_settings( - {"debug.auto_respond_credential_offer": True} - ) - test_exist_conn = ConnRecord( - my_did=TestConfig.test_did, - their_did=TestConfig.test_target_did, - their_public_did=TestConfig.test_target_did, - invitation_msg_id="12345678-0123-4567-1234-567812345678", - their_role=ConnRecord.Role.REQUESTER, - state=ConnRecord.State.COMPLETED, - ) - await test_exist_conn.save(session) - await test_exist_conn.metadata_set(session, "reuse_msg_state", "initial") - await test_exist_conn.metadata_set(session, "reuse_msg_id", "test_123") + async def test_receive_invitation_handshake_reuse_failed(self): + self.profile.context.update_settings({"public_invites": True}) + + test_exist_conn = ConnRecord( + connection_id="connection_id", + my_did=TestConfig.test_did, + their_did=TestConfig.test_target_did, + their_public_did=TestConfig.test_target_did, + invitation_msg_id="12345678-0123-4567-1234-567812345678", + their_role=ConnRecord.Role.REQUESTER, + ) - receipt = MessageReceipt( - recipient_did=TestConfig.test_did, - recipient_did_public=False, - sender_did=TestConfig.test_target_did, + with async_mock.patch.object( + test_module.OutOfBandManager, + "_handle_hanshake_reuse", + async_mock.CoroutineMock(), + ) as handle_handshake_reuse, async_mock.patch.object( + test_module.OutOfBandManager, + "_perform_handshake", + async_mock.CoroutineMock(), + ) as perform_handshake, async_mock.patch.object( + ConnRecord, + "find_existing_connection", + async_mock.CoroutineMock(return_value=test_exist_conn), + ), async_mock.patch.object( + ConnRecord, + "retrieve_by_id", + async_mock.CoroutineMock(return_value=test_exist_conn), + ), async_mock.patch.object( + test_module, "mediation_record_if_id", async_mock.CoroutineMock() + ): + oob_invitation = InvitationMessage( + handshake_protocols=[ + pfx.qualify(HSProto.RFC23.name) for pfx in DIDCommPrefix + ], + services=[TestConfig.test_target_did], + requests_attach=[], ) - req_attach = deepcopy(TestConfig.req_attach_v1) - del req_attach["data"]["json"] - req_attach["data"]["json"] = TestConfig.CRED_OFFER_V1.serialize() - exchange_rec = V10CredentialExchange() - exchange_rec.credential_offer = TestConfig.CRED_OFFER_V1 - with async_mock.patch.object( - DIDXManager, - "receive_invitation", - autospec=True, - ) as didx_mgr_receive_invitation, async_mock.patch.object( - V10CredManager, - "receive_offer", - autospec=True, - ) as cred_mgr_offer_receive, async_mock.patch( - "aries_cloudagent.protocols.out_of_band.v1_0.manager.InvitationMessage", - autospec=True, - ) as inv_message_cls, async_mock.patch.object( - OutOfBandManager, - "fetch_connection_targets", - autospec=True, - ) as oob_mgr_fetch_conn, async_mock.patch.object( - ConnRecord, - "find_existing_connection", - async_mock.CoroutineMock(), - ) as oob_mgr_find_existing_conn, async_mock.patch.object( - OutOfBandManager, - "check_reuse_msg_state", - autospec=True, - ) as oob_mgr_check_reuse_state, async_mock.patch.object( - OutOfBandManager, - "conn_rec_is_active", - autospec=True, - ) as oob_mgr_check_conn_rec_active, async_mock.patch.object( - OutOfBandManager, - "create_handshake_reuse_message", - autospec=True, - ) as oob_mgr_create_reuse_msg, async_mock.patch.object( - OutOfBandManager, - "receive_reuse_message", - autospec=True, - ) as oob_mgr_receive_reuse_msg, async_mock.patch.object( - OutOfBandManager, - "receive_reuse_accepted_message", - autospec=True, - ) as oob_mgr_receive_accept_msg, async_mock.patch.object( - OutOfBandManager, - "receive_problem_report", - autospec=True, - ) as oob_mgr_receive_problem_report, async_mock.patch.object( - V10CredManager, - "create_request", - autospec=True, - ) as cred_mgr_request_receive: - oob_mgr_find_existing_conn.return_value = test_exist_conn - oob_mgr_check_conn_rec_active.return_value = test_exist_conn - cred_mgr_offer_receive.return_value = exchange_rec - cred_mgr_request_receive.return_value = (exchange_rec, INDY_CRED_REQ) - mock_oob_invi = async_mock.MagicMock( - handshake_protocols=[ - pfx.qualify(HSProto.RFC23.name) for pfx in DIDCommPrefix - ], - services=[TestConfig.test_target_did], - requests_attach=[AttachDecorator.deserialize(req_attach)], - ) - inv_message_cls.deserialize.return_value = mock_oob_invi - conn_rec = await self.manager.receive_invitation( - mock_oob_invi, use_existing_connection=True - ) - assert conn_rec is not None + mock_oob = async_mock.MagicMock( + delete_record=async_mock.CoroutineMock(), + emit_event=async_mock.CoroutineMock(), + ) + perform_handshake.return_value = mock_oob - async def test_req_attach_cred_offer_v1_no_issue(self): - self.profile.context.update_settings({"public_invites": True}) - self.profile.context.update_settings( - {"debug.auto_respond_credential_offer": False} + handle_handshake_reuse.return_value = async_mock.MagicMock( + state=OobRecord.STATE_NOT_ACCEPTED ) - test_exist_conn = ConnRecord( - my_did=TestConfig.test_did, - their_did=TestConfig.test_target_did, - their_public_did=TestConfig.test_target_did, - invitation_msg_id="12345678-0123-4567-1234-567812345678", - their_role=ConnRecord.Role.REQUESTER, - state=ConnRecord.State.COMPLETED, + + result = await self.manager.receive_invitation( + oob_invitation, + use_existing_connection=True, + alias="alias", + auto_accept=True, + mediation_id="mediation_id", ) - await test_exist_conn.save(session) - await test_exist_conn.metadata_set(session, "reuse_msg_state", "initial") - await test_exist_conn.metadata_set(session, "reuse_msg_id", "test_123") - receipt = MessageReceipt( - recipient_did=TestConfig.test_did, - recipient_did_public=False, - sender_did=TestConfig.test_target_did, + handle_handshake_reuse.assert_called_once_with(ANY, test_exist_conn) + perform_handshake.assert_called_once_with( + oob_record=ANY, + alias="alias", + auto_accept=True, + mediation_id="mediation_id", ) - req_attach = deepcopy(TestConfig.req_attach_v1) - del req_attach["data"]["json"] - req_attach["data"]["json"] = TestConfig.CRED_OFFER_V1.serialize() - exchange_rec = V10CredentialExchange() - exchange_rec.credential_offer = TestConfig.CRED_OFFER_V1 + assert mock_oob.state == OobRecord.STATE_DONE + assert result is mock_oob + mock_oob.emit_event.assert_called_once() + mock_oob.delete_record.assert_called_once() - with async_mock.patch.object( - DIDXManager, - "receive_invitation", - autospec=True, - ) as didx_mgr_receive_invitation, async_mock.patch.object( - V10CredManager, - "receive_offer", - autospec=True, - ) as cred_mgr_offer_receive, async_mock.patch( - "aries_cloudagent.protocols.out_of_band.v1_0.manager.InvitationMessage", - autospec=True, - ) as inv_message_cls, async_mock.patch.object( - OutOfBandManager, - "fetch_connection_targets", - autospec=True, - ) as oob_mgr_fetch_conn, async_mock.patch.object( - ConnRecord, - "find_existing_connection", - async_mock.CoroutineMock(), - ) as oob_mgr_find_existing_conn, async_mock.patch.object( - OutOfBandManager, - "check_reuse_msg_state", - autospec=True, - ) as oob_mgr_check_reuse_state, async_mock.patch.object( - OutOfBandManager, - "conn_rec_is_active", - autospec=True, - ) as oob_mgr_check_conn_rec_active, async_mock.patch.object( - OutOfBandManager, - "create_handshake_reuse_message", - autospec=True, - ) as oob_mgr_create_reuse_msg, async_mock.patch.object( - OutOfBandManager, - "receive_reuse_message", - autospec=True, - ) as oob_mgr_receive_reuse_msg, async_mock.patch.object( - OutOfBandManager, - "receive_reuse_accepted_message", - autospec=True, - ) as oob_mgr_receive_accept_msg, async_mock.patch.object( - OutOfBandManager, - "receive_problem_report", - autospec=True, - ) as oob_mgr_receive_problem_report: - oob_mgr_find_existing_conn.return_value = test_exist_conn - cred_mgr_offer_receive.return_value = exchange_rec - mock_oob_invi = async_mock.MagicMock( - handshake_protocols=[ - pfx.qualify(HSProto.RFC23.name) for pfx in DIDCommPrefix - ], - services=[TestConfig.test_target_did], - requests_attach=[AttachDecorator.deserialize(req_attach)], - ) - inv_message_cls.deserialize.return_value = mock_oob_invi - with self.assertRaises(OutOfBandManagerError) as context: - await self.manager.receive_invitation( - mock_oob_invi, use_existing_connection=True - ) - assert "Configuration sets auto_offer false" in str(context.exception) + async def test_receive_invitation_services_with_service_did(self): + self.profile.context.update_settings({"public_invites": True}) - async def test_req_attach_cred_offer_v2(self): - async with self.profile.session() as session: - self.profile.context.update_settings({"public_invites": True}) - self.profile.context.update_settings( - {"debug.auto_respond_credential_offer": True} + mock_conn = async_mock.MagicMock(connection_id="dummy") + + with async_mock.patch.object( + test_module, "DIDXManager", autospec=True + ) as didx_mgr_cls, async_mock.patch.object( + ConnRecord, + "retrieve_by_id", + async_mock.CoroutineMock(return_value=mock_conn), + ): + didx_mgr_cls.return_value = async_mock.MagicMock( + receive_invitation=async_mock.CoroutineMock(return_value=mock_conn) ) - test_exist_conn = ConnRecord( - my_did=TestConfig.test_did, - their_did=TestConfig.test_target_did, - their_public_did=TestConfig.test_target_did, - invitation_msg_id="12345678-0123-4567-1234-567812345678", - their_role=ConnRecord.Role.REQUESTER, - state=ConnRecord.State.COMPLETED, + oob_invitation = InvitationMessage( + handshake_protocols=[ + pfx.qualify(HSProto.RFC23.name) for pfx in DIDCommPrefix + ], + services=[TestConfig.test_did], + requests_attach=[], ) - await test_exist_conn.save(session) - await test_exist_conn.metadata_set(session, "reuse_msg_state", "initial") - await test_exist_conn.metadata_set(session, "reuse_msg_id", "test_123") - receipt = MessageReceipt( - recipient_did=TestConfig.test_did, - recipient_did_public=False, - sender_did=TestConfig.test_target_did, - ) - req_attach = deepcopy(TestConfig.req_attach_v1) - del req_attach["data"]["json"] - req_attach["data"]["json"] = TestConfig.CRED_OFFER_V2.serialize() + invitation_record = await self.manager.receive_invitation(oob_invitation) + assert invitation_record.invitation.services - exchange_rec = V20CredExRecord() - exchange_rec.cred_offer = TestConfig.CRED_OFFER_V2 + async def test_request_attach_oob_message_processor_connectionless(self): + requests_attach: List[AttachDecorator] = [ + AttachDecorator.deserialize(deepcopy(TestConfig.req_attach_v1)) + ] - with async_mock.patch.object( - DIDXManager, - "receive_invitation", - autospec=True, - ) as didx_mgr_receive_invitation, async_mock.patch.object( - V20CredManager, - "receive_offer", - autospec=True, - ) as cred_mgr_offer_receive, async_mock.patch( - "aries_cloudagent.protocols.out_of_band.v1_0.manager.InvitationMessage", - autospec=True, - ) as inv_message_cls, async_mock.patch.object( - OutOfBandManager, - "fetch_connection_targets", - autospec=True, - ) as oob_mgr_fetch_conn, async_mock.patch.object( - ConnRecord, - "find_existing_connection", - async_mock.CoroutineMock(), - ) as oob_mgr_find_existing_conn, async_mock.patch.object( - OutOfBandManager, - "check_reuse_msg_state", - autospec=True, - ) as oob_mgr_check_reuse_state, async_mock.patch.object( - OutOfBandManager, - "conn_rec_is_active", - autospec=True, - ) as oob_mgr_check_conn_rec_active, async_mock.patch.object( - OutOfBandManager, - "create_handshake_reuse_message", - autospec=True, - ) as oob_mgr_create_reuse_msg, async_mock.patch.object( - OutOfBandManager, - "receive_reuse_message", - autospec=True, - ) as oob_mgr_receive_reuse_msg, async_mock.patch.object( - OutOfBandManager, - "receive_reuse_accepted_message", - autospec=True, - ) as oob_mgr_receive_accept_msg, async_mock.patch.object( - OutOfBandManager, - "receive_problem_report", - autospec=True, - ) as oob_mgr_receive_problem_report, async_mock.patch.object( - V20CredManager, - "create_request", - autospec=True, - ) as cred_mgr_request_receive: - oob_mgr_find_existing_conn.return_value = test_exist_conn - oob_mgr_check_conn_rec_active.return_value = test_exist_conn - cred_mgr_offer_receive.return_value = exchange_rec - cred_mgr_request_receive.return_value = (exchange_rec, INDY_CRED_REQ) - mock_oob_invi = async_mock.MagicMock( - handshake_protocols=[ - pfx.qualify(HSProto.RFC23.name) for pfx in DIDCommPrefix - ], - services=[TestConfig.test_target_did], - requests_attach=[AttachDecorator.deserialize(req_attach)], - ) - inv_message_cls.deserialize.return_value = mock_oob_invi - conn_rec = await self.manager.receive_invitation( - mock_oob_invi, use_existing_connection=True - ) - assert conn_rec is not None + mock_oob_processor = async_mock.MagicMock( + handle_message=async_mock.CoroutineMock() + ) + self.profile.context.injector.bind_instance( + OobMessageProcessor, mock_oob_processor + ) - async def test_req_attach_cred_offer_v2_no_issue(self): - async with self.profile.session() as session: - self.profile.context.update_settings({"public_invites": True}) - self.profile.context.update_settings( - {"debug.auto_respond_credential_offer": False} + mock_service_decorator = ServiceDecorator( + endpoint=self.test_endpoint, recipient_keys=[self.test_verkey] + ) + + with async_mock.patch.object( + InMemoryWallet, + "create_signing_key", + async_mock.CoroutineMock(), + ) as mock_create_signing_key, async_mock.patch.object( + OutOfBandManager, + "_service_decorator_from_service", + async_mock.CoroutineMock(), + ) as mock_service_decorator_from_service: + mock_create_signing_key.return_value = KeyInfo( + verkey="a-verkey", metadata={}, key_type=KeyType.ED25519 ) - test_exist_conn = ConnRecord( - my_did=TestConfig.test_did, - their_did=TestConfig.test_target_did, - their_public_did=TestConfig.test_target_did, - invitation_msg_id="12345678-0123-4567-1234-567812345678", - their_role=ConnRecord.Role.REQUESTER, - state=ConnRecord.State.COMPLETED, + mock_service_decorator_from_service.return_value = mock_service_decorator + oob_invitation = InvitationMessage( + handshake_protocols=[], + services=[self.test_service], + requests_attach=requests_attach, ) - await test_exist_conn.save(session) - await test_exist_conn.metadata_set(session, "reuse_msg_state", "initial") - await test_exist_conn.metadata_set(session, "reuse_msg_id", "test_123") - receipt = MessageReceipt( - recipient_did=TestConfig.test_did, - recipient_did_public=False, - sender_did=TestConfig.test_target_did, + oob_record = await self.manager.receive_invitation( + oob_invitation, use_existing_connection=True ) - req_attach = deepcopy(TestConfig.req_attach_v1) - del req_attach["data"]["json"] - req_attach["data"]["json"] = TestConfig.CRED_OFFER_V2.serialize() - exchange_rec = V20CredExRecord() - exchange_rec.cred_offer = TestConfig.CRED_OFFER_V2 + assert oob_record.our_recipient_key == "a-verkey" + assert oob_record.our_service - with async_mock.patch.object( - DIDXManager, - "receive_invitation", - autospec=True, - ) as didx_mgr_receive_invitation, async_mock.patch.object( - V20CredManager, - "receive_offer", - autospec=True, - ) as cred_mgr_offer_receive, async_mock.patch( - "aries_cloudagent.protocols.out_of_band.v1_0.manager.InvitationMessage", - autospec=True, - ) as inv_message_cls, async_mock.patch.object( - OutOfBandManager, - "fetch_connection_targets", - autospec=True, - ) as oob_mgr_fetch_conn, async_mock.patch.object( - ConnRecord, - "find_existing_connection", - async_mock.CoroutineMock(), - ) as oob_mgr_find_existing_conn, async_mock.patch.object( - OutOfBandManager, - "check_reuse_msg_state", - autospec=True, - ) as oob_mgr_check_reuse_state, async_mock.patch.object( - OutOfBandManager, - "conn_rec_is_active", - autospec=True, - ) as oob_mgr_check_conn_rec_active, async_mock.patch.object( - OutOfBandManager, - "create_handshake_reuse_message", - autospec=True, - ) as oob_mgr_create_reuse_msg, async_mock.patch.object( - OutOfBandManager, - "receive_reuse_message", - autospec=True, - ) as oob_mgr_receive_reuse_msg, async_mock.patch.object( - OutOfBandManager, - "receive_reuse_accepted_message", - autospec=True, - ) as oob_mgr_receive_accept_msg, async_mock.patch.object( - OutOfBandManager, - "receive_problem_report", - autospec=True, - ) as oob_mgr_receive_problem_report: - oob_mgr_find_existing_conn.return_value = test_exist_conn - cred_mgr_offer_receive.return_value = exchange_rec - mock_oob_invi = async_mock.MagicMock( - handshake_protocols=[ - pfx.qualify(HSProto.RFC23.name) for pfx in DIDCommPrefix - ], - services=[TestConfig.test_target_did], - requests_attach=[AttachDecorator.deserialize(req_attach)], - ) - inv_message_cls.deserialize.return_value = mock_oob_invi - with self.assertRaises(OutOfBandManagerError) as context: - await self.manager.receive_invitation( - mock_oob_invi, use_existing_connection=True - ) - assert "Configuration sets auto_offer false" in str(context.exception) + mock_create_signing_key.assert_called_once_with(KeyType.ED25519) + mock_oob_processor.handle_message.assert_called_once_with( + self.profile, + [attachment.content for attachment in requests_attach], + oob_record=oob_record, + their_service=mock_service_decorator, + ) - async def test_catch_unsupported_request_attach(self): + async def test_request_attach_oob_message_processor_connection(self): async with self.profile.session() as session: self.profile.context.update_settings({"public_invites": True}) - self.profile.context.update_settings( - {"debug.auto_respond_credential_offer": False} - ) test_exist_conn = ConnRecord( + connection_id="a-connection-id", my_did=TestConfig.test_did, their_did=TestConfig.test_target_did, their_public_did=TestConfig.test_target_did, invitation_msg_id="12345678-0123-4567-1234-567812345678", their_role=ConnRecord.Role.REQUESTER, + state=ConnRecord.State.COMPLETED.rfc160, ) - await test_exist_conn.save(session) - await test_exist_conn.metadata_set(session, "reuse_msg_state", "initial") - await test_exist_conn.metadata_set(session, "reuse_msg_id", "test_123") - - receipt = MessageReceipt( - recipient_did=TestConfig.test_did, - recipient_did_public=False, - sender_did=TestConfig.test_target_did, - ) - req_attach = deepcopy(TestConfig.req_attach_v1) - del req_attach["data"]["json"] - req_attach["data"]["json"] = TestConfig.CRED_OFFER_V1.serialize() - req_attach["data"]["json"]["@type"] = "test" - - with async_mock.patch.object( - DIDXManager, - "receive_invitation", - autospec=True, - ) as didx_mgr_receive_invitation, async_mock.patch( - "aries_cloudagent.protocols.out_of_band.v1_0.manager.InvitationMessage", - autospec=True, - ) as inv_message_cls, async_mock.patch.object( - OutOfBandManager, - "fetch_connection_targets", - autospec=True, - ) as oob_mgr_fetch_conn, async_mock.patch.object( - ConnRecord, - "find_existing_connection", - async_mock.CoroutineMock(), - ) as oob_mgr_find_existing_conn, async_mock.patch.object( - OutOfBandManager, - "check_reuse_msg_state", - autospec=True, - ) as oob_mgr_check_reuse_state, async_mock.patch.object( - OutOfBandManager, - "conn_rec_is_active", - autospec=True, - ) as oob_mgr_check_conn_rec_active, async_mock.patch.object( - OutOfBandManager, - "create_handshake_reuse_message", - autospec=True, - ) as oob_mgr_create_reuse_msg, async_mock.patch.object( - OutOfBandManager, - "receive_reuse_message", - autospec=True, - ) as oob_mgr_receive_reuse_msg, async_mock.patch.object( - OutOfBandManager, - "receive_reuse_accepted_message", - autospec=True, - ) as oob_mgr_receive_accept_msg, async_mock.patch.object( - OutOfBandManager, - "receive_problem_report", - autospec=True, - ) as oob_mgr_receive_problem_report: - oob_mgr_find_existing_conn.return_value = test_exist_conn - mock_oob_invi = async_mock.MagicMock( - handshake_protocols=[ - pfx.qualify(HSProto.RFC23.name) for pfx in DIDCommPrefix - ], - services=[TestConfig.test_target_did], - requests_attach=[AttachDecorator.deserialize(req_attach)], - ) - inv_message_cls.deserialize.return_value = mock_oob_invi - with self.assertRaises(OutOfBandManagerError) as context: - await self.manager.receive_invitation( - mock_oob_invi, use_existing_connection=True - ) - assert "Unsupported requests~attach type" in str(context.exception) - async def test_check_conn_rec_active_a(self): - async with self.profile.session() as session: - await self.test_conn_rec.save(session) - conn_rec = await self.manager.conn_rec_is_active( - self.test_conn_rec.connection_id - ) - assert conn_rec.connection_id == self.test_conn_rec.connection_id - - async def test_check_conn_rec_active_b(self): - connection_id = self.test_conn_rec.connection_id - conn_rec_request = deepcopy(self.test_conn_rec) - conn_rec_request.state = "request" - conn_rec_active = deepcopy(self.test_conn_rec) - conn_rec_active.state = "active" - with async_mock.patch.object( - test_module.ConnRecord, - "retrieve_by_id", - autospec=True, - ) as mock_conn_rec_retrieve: - mock_conn_rec_retrieve.side_effect = [conn_rec_request, conn_rec_active] - conn_rec = await self.manager.conn_rec_is_active(connection_id) - assert conn_rec.state == "active" + requests_attach: List[AttachDecorator] = [ + AttachDecorator.deserialize(deepcopy(TestConfig.req_attach_v1)) + ] - async def test_request_attach_cred_offer_v1_check_conn_rec_active_timeout(self): - async with self.profile.session() as session: - self.profile.context.update_settings({"public_invites": True}) - self.profile.context.update_settings( - {"debug.auto_respond_credential_offer": True} + mock_oob_processor = async_mock.MagicMock( + handle_message=async_mock.CoroutineMock() ) - test_exist_conn = ConnRecord( - my_did=TestConfig.test_did, - their_did=TestConfig.test_target_did, - their_public_did=TestConfig.test_target_did, - invitation_msg_id="12345678-0123-4567-1234-567812345678", - their_role=ConnRecord.Role.REQUESTER, + self.profile.context.injector.bind_instance( + OobMessageProcessor, mock_oob_processor ) - await test_exist_conn.save(session) - await test_exist_conn.metadata_set(session, "reuse_msg_state", "initial") - await test_exist_conn.metadata_set(session, "reuse_msg_id", "test_123") - req_attach = deepcopy(TestConfig.req_attach_v1) - del req_attach["data"]["json"] - req_attach["data"]["json"] = TestConfig.CRED_OFFER_V1.serialize() - exchange_rec = V20CredExRecord() - exchange_rec.cred_offer = TestConfig.CRED_OFFER_V1 with async_mock.patch.object( - DIDXManager, - "receive_invitation", - autospec=True, - ), async_mock.patch.object( - V10CredManager, - "receive_offer", - autospec=True, - ) as cred_mgr_offer_receive, async_mock.patch( - "aries_cloudagent.protocols.out_of_band.v1_0.manager.InvitationMessage", - autospec=True, - ) as inv_message_cls, async_mock.patch.object( - OutOfBandManager, - "fetch_connection_targets", - autospec=True, - ), async_mock.patch.object( ConnRecord, "find_existing_connection", async_mock.CoroutineMock(), - ) as oob_mgr_find_existing_conn, async_mock.patch.object( - OutOfBandManager, - "check_reuse_msg_state", - autospec=True, - ), async_mock.patch.object( - OutOfBandManager, - "conn_rec_is_active", - autospec=True, - ) as oob_mgr_check_conn_rec_active, async_mock.patch.object( - OutOfBandManager, - "create_handshake_reuse_message", - autospec=True, - ), async_mock.patch.object( - OutOfBandManager, - "receive_reuse_message", - autospec=True, - ), async_mock.patch.object( - OutOfBandManager, - "receive_reuse_accepted_message", - autospec=True, - ), async_mock.patch.object( - OutOfBandManager, - "receive_problem_report", - autospec=True, - ), async_mock.patch.object( - V10CredManager, - "create_request", - autospec=True, - ) as cred_mgr_request_receive, async_mock.patch.object( - test_module.LOGGER, "warning", async_mock.MagicMock() - ) as mock_logger_warning: + ) as oob_mgr_find_existing_conn: oob_mgr_find_existing_conn.return_value = test_exist_conn - cred_mgr_offer_receive.return_value = exchange_rec - cred_mgr_request_receive.return_value = (exchange_rec, INDY_CRED_REQ) - oob_mgr_check_conn_rec_active.side_effect = asyncio.TimeoutError - mock_oob_invi = async_mock.MagicMock( + oob_invitation = InvitationMessage( handshake_protocols=[ pfx.qualify(HSProto.RFC23.name) for pfx in DIDCommPrefix ], services=[TestConfig.test_target_did], - requests_attach=[AttachDecorator.deserialize(req_attach)], + requests_attach=requests_attach, + ) + + oob_record = await self.manager.receive_invitation( + oob_invitation, use_existing_connection=True ) - inv_message_cls.deserialize.return_value = mock_oob_invi - conn_rec = await self.manager.receive_invitation( - mock_oob_invi, use_existing_connection=True + + mock_oob_processor.handle_message.assert_called_once_with( + self.profile, + [attachment.content for attachment in requests_attach], + oob_record=oob_record, + their_service=None, ) - mock_logger_warning.assert_called_once() - assert conn_rec is not None - async def test_request_attach_cred_offer_v2_check_conn_rec_active_timeout(self): + async def test_request_attach_wait_for_conn_rec_active(self): async with self.profile.session() as session: self.profile.context.update_settings({"public_invites": True}) - self.profile.context.update_settings( - {"debug.auto_respond_credential_offer": True} - ) test_exist_conn = ConnRecord( my_did=TestConfig.test_did, their_did=TestConfig.test_target_did, @@ -3472,87 +1632,67 @@ async def test_request_attach_cred_offer_v2_check_conn_rec_active_timeout(self): invitation_msg_id="12345678-0123-4567-1234-567812345678", their_role=ConnRecord.Role.REQUESTER, ) - await test_exist_conn.save(session) - await test_exist_conn.metadata_set(session, "reuse_msg_state", "initial") - await test_exist_conn.metadata_set(session, "reuse_msg_id", "test_123") - receipt = MessageReceipt( - recipient_did=TestConfig.test_did, - recipient_did_public=False, - sender_did=TestConfig.test_target_did, - ) - req_attach = deepcopy(TestConfig.req_attach_v1) - del req_attach["data"]["json"] - req_attach["data"]["json"] = TestConfig.CRED_OFFER_V2.serialize() - exchange_rec = V20CredExRecord() - exchange_rec.cred_offer = TestConfig.CRED_OFFER_V2 with async_mock.patch.object( - DIDXManager, - "receive_invitation", - autospec=True, - ) as didx_mgr_receive_invitation, async_mock.patch.object( - V20CredManager, - "receive_offer", - autospec=True, - ) as cred_mgr_offer_receive, async_mock.patch( - "aries_cloudagent.protocols.out_of_band.v1_0.manager.InvitationMessage", - autospec=True, - ) as inv_message_cls, async_mock.patch.object( - OutOfBandManager, - "fetch_connection_targets", - autospec=True, - ) as oob_mgr_fetch_conn, async_mock.patch.object( + OutOfBandManager, "_wait_for_conn_rec_active" + ) as mock_wait_for_conn_rec_active, async_mock.patch.object( ConnRecord, "find_existing_connection", async_mock.CoroutineMock(), - ) as oob_mgr_find_existing_conn, async_mock.patch.object( - OutOfBandManager, - "check_reuse_msg_state", - autospec=True, - ) as oob_mgr_check_reuse_state, async_mock.patch.object( - OutOfBandManager, - "conn_rec_is_active", - autospec=True, - ) as oob_mgr_check_conn_rec_active, async_mock.patch.object( - OutOfBandManager, - "create_handshake_reuse_message", - autospec=True, - ) as oob_mgr_create_reuse_msg, async_mock.patch.object( - OutOfBandManager, - "receive_reuse_message", - autospec=True, - ) as oob_mgr_receive_reuse_msg, async_mock.patch.object( - OutOfBandManager, - "receive_reuse_accepted_message", - autospec=True, - ) as oob_mgr_receive_accept_msg, async_mock.patch.object( - OutOfBandManager, - "receive_problem_report", - autospec=True, - ) as oob_mgr_receive_problem_report, async_mock.patch.object( - V20CredManager, - "create_request", - autospec=True, - ) as cred_mgr_request_receive, async_mock.patch.object( - test_module.LOGGER, "warning", async_mock.MagicMock() - ) as mock_logger_warning: + ) as oob_mgr_find_existing_conn: oob_mgr_find_existing_conn.return_value = test_exist_conn - cred_mgr_offer_receive.return_value = exchange_rec - cred_mgr_request_receive.return_value = (exchange_rec, INDY_CRED_REQ) - oob_mgr_check_conn_rec_active.side_effect = asyncio.TimeoutError - mock_oob_invi = async_mock.MagicMock( + mock_wait_for_conn_rec_active.return_value = None + oob_invitation = InvitationMessage( handshake_protocols=[ pfx.qualify(HSProto.RFC23.name) for pfx in DIDCommPrefix ], services=[TestConfig.test_target_did], - requests_attach=[AttachDecorator.deserialize(req_attach)], + requests_attach=[ + AttachDecorator.deserialize(deepcopy(TestConfig.req_attach_v1)) + ], ) - inv_message_cls.deserialize.return_value = mock_oob_invi - conn_rec = await self.manager.receive_invitation( - mock_oob_invi, use_existing_connection=True + + with self.assertRaises(test_module.OutOfBandManagerError) as err: + oob_record = await self.manager.receive_invitation( + oob_invitation, use_existing_connection=True + ) + assert ( + "Connection not ready to process attach message for connection_id:" + in err.exception.message ) - mock_logger_warning.assert_called_once() - assert conn_rec is not None + + async def test_service_decorator_from_service_did(self): + did = "did:sov:something" + + self.manager.resolve_invitation = async_mock.CoroutineMock() + self.manager.resolve_invitation.return_value = ( + TestConfig.test_endpoint, + [TestConfig.test_verkey], + self.test_mediator_routing_keys, + ) + + service = await self.manager._service_decorator_from_service(did) + + assert service.endpoint == TestConfig.test_endpoint + assert service.recipient_keys == [TestConfig.test_verkey] + assert service.routing_keys == self.test_mediator_routing_keys + + async def test_service_decorator_from_service_object(self): + oob_service = OobService( + service_endpoint=TestConfig.test_endpoint, + recipient_keys=[ + DIDKey.from_public_key_b58(TestConfig.test_verkey, KeyType.ED25519).did + ], + routing_keys=[ + DIDKey.from_public_key_b58(verkey, KeyType.ED25519).did + for verkey in self.test_mediator_routing_keys + ], + ) + service = await self.manager._service_decorator_from_service(oob_service) + + assert service.endpoint == TestConfig.test_endpoint + assert service.recipient_keys == [TestConfig.test_verkey] + assert service.routing_keys == self.test_mediator_routing_keys async def test_delete_stale_connection_by_invitation(self): current_datetime = datetime_now() From 65ea610a4d6ff21522cca1654ef74045ebe75248 Mon Sep 17 00:00:00 2001 From: Adam Burdett Date: Mon, 4 Apr 2022 17:25:17 -0600 Subject: [PATCH 163/872] atomic subwallet creation Signed-off-by: Adam Burdett --- aries_cloudagent/config/argparse.py | 5 +-- aries_cloudagent/multitenant/admin/routes.py | 10 ++--- .../multitenant/admin/tests/test_routes.py | 33 ++++++++++++++-- aries_cloudagent/multitenant/base.py | 39 ++++++++++--------- 4 files changed, 56 insertions(+), 31 deletions(-) diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index e9a3a82641..b3d7d7f3dc 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -1623,7 +1623,7 @@ def add_arguments(self, parser: ArgumentParser): help=( 'Specify multitenancy configuration ("wallet_type" and "wallet_name"). ' 'For example: "{"wallet_type":"askar-profile","wallet_name":' - '"askar-profile-name", "key":"key", "key_derivation_method":"RAW"}"' + '"askar-profile-name", "key_derivation_method":"RAW"}"' '"wallet_name" is only used when "wallet_type" is "askar-profile"' ), ) @@ -1657,9 +1657,6 @@ def get_settings(self, args: Namespace): "wallet_name" ) - if multitenancyConfig.get("key"): - settings["multitenant.key"] = multitenancyConfig.get("key") - if multitenancyConfig.get("key_derivation_method"): settings[ "multitenant.key_derivation_method" diff --git a/aries_cloudagent/multitenant/admin/routes.py b/aries_cloudagent/multitenant/admin/routes.py index 890eb29cc1..98ebaa2b36 100644 --- a/aries_cloudagent/multitenant/admin/routes.py +++ b/aries_cloudagent/multitenant/admin/routes.py @@ -58,8 +58,8 @@ class CreateWalletRequestSchema(OpenAPISchema): wallet_key_derivation = fields.Str( description="Key derivation", + required=False, example="RAW", - default="ARGON2I_MOD", validate=validate.OneOf(["ARGON2I_MOD", "ARGON2I_INT", "RAW"]), ) @@ -107,7 +107,7 @@ class CreateWalletRequestSchema(OpenAPISchema): description="Key management method to use for this wallet.", example=WalletRecord.MODE_MANAGED, default=WalletRecord.MODE_MANAGED, - # MTODO: add unmanaged mode once implemented + # TODO: add unmanaged mode once implemented validate=validate.OneOf((WalletRecord.MODE_MANAGED,)), ) @@ -302,19 +302,19 @@ async def wallet_create(request: web.BaseRequest): "wallet.type": body.get("wallet_type") or "in_memory", "wallet.name": body.get("wallet_name"), "wallet.key": wallet_key, - "wallet.key_derivation_method": body.get( - "wallet_key_derivation", "ARGON2I_MOD" - ), "wallet.webhook_urls": wallet_webhook_urls, "wallet.dispatch_type": wallet_dispatch_type, } label = body.get("label") image_url = body.get("image_url") + key_derivation = body.get("wallet_key_derivation") if label: settings["default_label"] = label if image_url: settings["image_url"] = image_url + if key_derivation: # allow lower levels to handle default + settings["wallet.key_derivation_method"] = key_derivation try: multitenant_mgr = context.profile.inject(BaseMultitenantManager) diff --git a/aries_cloudagent/multitenant/admin/tests/test_routes.py b/aries_cloudagent/multitenant/admin/tests/test_routes.py index d2299b8ff5..cf447e5cbb 100644 --- a/aries_cloudagent/multitenant/admin/tests/test_routes.py +++ b/aries_cloudagent/multitenant/admin/tests/test_routes.py @@ -142,9 +142,9 @@ async def test_wallets_list_query(self): async def test_wallet_create(self): body = { "wallet_name": "test", + "default_label": "test_label", "wallet_type": "indy", "wallet_key": "test", - "key_derivation_method": "ARGON2I_MOD", "key_management_mode": "managed", "wallet_webhook_urls": [], "wallet_dispatch_type": "base", @@ -176,7 +176,6 @@ async def test_wallet_create(self): "wallet.name": body["wallet_name"], "wallet.type": body["wallet_type"], "wallet.key": body["wallet_key"], - 'wallet.key_derivation_method': body["key_derivation_method"], "wallet.webhook_urls": body["wallet_webhook_urls"], "wallet.dispatch_type": body["wallet_dispatch_type"], }, @@ -208,7 +207,7 @@ async def test_wallet_create_optional_default_fields(self): body = { "wallet_name": "test", "wallet_key": "test", - "key_derivation_method": "ARGON2I_MOD", + "wallet_key_derivation": "ARGON2I_MOD", "wallet_webhook_urls": [], "wallet_dispatch_type": "base", "label": "my_test_label", @@ -226,11 +225,37 @@ async def test_wallet_create_optional_default_fields(self): "wallet.name": body["wallet_name"], "wallet.type": "in_memory", "wallet.key": body["wallet_key"], - 'wallet.key_derivation_method': body["key_derivation_method"], "default_label": body["label"], "image_url": body["image_url"], "wallet.webhook_urls": body["wallet_webhook_urls"], "wallet.dispatch_type": body["wallet_dispatch_type"], + "wallet.key_derivation_method": body["wallet_key_derivation"], + }, + WalletRecord.MODE_MANAGED, + ) + + + async def test_wallet_create_raw_key_derivation(self): + body = { + "wallet_name": "test", + "wallet_key": "test", + "wallet_key_derivation": "RAW", + } + self.request.json = async_mock.CoroutineMock(return_value=body) + + with async_mock.patch.object(test_module.web, "json_response") as mock_response: + self.mock_multitenant_mgr.create_wallet = async_mock.CoroutineMock() + self.mock_multitenant_mgr.create_auth_token = async_mock.Mock() + + await test_module.wallet_create(self.request) + self.mock_multitenant_mgr.create_wallet.assert_called_once_with( + { + "wallet.type": "in_memory", + "wallet.name": body["wallet_name"], + "wallet.key": body["wallet_key"], + "wallet.key_derivation_method": body["wallet_key_derivation"], + "wallet.webhook_urls": [], + "wallet.dispatch_type": "base", }, WalletRecord.MODE_MANAGED, ) diff --git a/aries_cloudagent/multitenant/base.py b/aries_cloudagent/multitenant/base.py index 5bdd9a9787..35cf332b9d 100644 --- a/aries_cloudagent/multitenant/base.py +++ b/aries_cloudagent/multitenant/base.py @@ -183,26 +183,29 @@ async def create_wallet( ) await wallet_record.save(session) + try: + # provision wallet + profile = await self.get_wallet_profile( + self._profile.context, + wallet_record, + { + "wallet.key": wallet_key, + }, + provision=True, + ) - # provision wallet - profile = await self.get_wallet_profile( - self._profile.context, - wallet_record, - { - "wallet.key": wallet_key, - }, - provision=True, - ) - - # subwallet context - async with profile.session() as session: - wallet = session.inject(BaseWallet) - public_did_info = await wallet.get_public_did() + # subwallet context + async with profile.session() as session: + wallet = session.inject(BaseWallet) + public_did_info = await wallet.get_public_did() - if public_did_info: - await self.add_key( - wallet_record.wallet_id, public_did_info.verkey, skip_if_exists=True - ) + if public_did_info: + await self.add_key( + wallet_record.wallet_id, public_did_info.verkey, skip_if_exists=True + ) + except Exception: + await wallet_record.delete_record(session) + raise return wallet_record From d90c141b9223e7be81b2b19101e79cba389e65e8 Mon Sep 17 00:00:00 2001 From: Adam Burdett Date: Mon, 4 Apr 2022 17:31:29 -0600 Subject: [PATCH 164/872] Todo for subwallets Signed-off-by: Adam Burdett --- aries_cloudagent/multitenant/admin/routes.py | 2 +- aries_cloudagent/wallet/models/wallet_record.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/multitenant/admin/routes.py b/aries_cloudagent/multitenant/admin/routes.py index 98ebaa2b36..5d016b9fb6 100644 --- a/aries_cloudagent/multitenant/admin/routes.py +++ b/aries_cloudagent/multitenant/admin/routes.py @@ -107,7 +107,7 @@ class CreateWalletRequestSchema(OpenAPISchema): description="Key management method to use for this wallet.", example=WalletRecord.MODE_MANAGED, default=WalletRecord.MODE_MANAGED, - # TODO: add unmanaged mode once implemented + # MTODO: add unmanaged mode once implemented validate=validate.OneOf((WalletRecord.MODE_MANAGED,)), ) diff --git a/aries_cloudagent/wallet/models/wallet_record.py b/aries_cloudagent/wallet/models/wallet_record.py index 3620fb54f3..52aa8496ab 100644 --- a/aries_cloudagent/wallet/models/wallet_record.py +++ b/aries_cloudagent/wallet/models/wallet_record.py @@ -36,7 +36,7 @@ def __init__( wallet_id: str = None, key_management_mode: str = None, settings: dict = None, - # TODO: how to make this a tag without making it + # MTODO: how to make this a tag without making it # a constructor param wallet_name: str = None, **kwargs, From ae0ab2283f12a0a7d1425c5969b4686b972b985b Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 5 Apr 2022 15:00:57 +0200 Subject: [PATCH 165/872] test: oob processor tests Signed-off-by: Timo Glastra --- aries_cloudagent/core/oob_processor.py | 7 +- .../core/tests/test_oob_processor.py | 741 ++++++++++++++++++ 2 files changed, 745 insertions(+), 3 deletions(-) create mode 100644 aries_cloudagent/core/tests/test_oob_processor.py diff --git a/aries_cloudagent/core/oob_processor.py b/aries_cloudagent/core/oob_processor.py index ce76aa68e2..ab770a5446 100644 --- a/aries_cloudagent/core/oob_processor.py +++ b/aries_cloudagent/core/oob_processor.py @@ -136,7 +136,7 @@ async def find_oob_record_for_inbound_message( # Fine if record is not found pass # Otherwise try to find it using the attach thread id. This is only needed - # for connectionless exchanges where every handlers needs the context of the + # for connectionless exchanges where every handler needs the context of the # oob record for verification. We could attach the oob_record to all messages, # even if we have a connection, but it would add another query to all inbound # messages. @@ -304,7 +304,7 @@ async def handle_message( profile: Profile, messages: List[Dict[str, Any]], oob_record: OobRecord, - their_service: Optional[ServiceDecorator], + their_service: Optional[ServiceDecorator] = None, ): """Message handler for inbound messages.""" @@ -322,9 +322,10 @@ async def handle_message( ] if not supported_messages: + message_str = ", ".join(supported_types) raise OobMessageProcessorError( f"None of the oob attached messages supported. Supported message types " - f"are {supported_types}" + f"are {message_str}" ) message = supported_messages[0] diff --git a/aries_cloudagent/core/tests/test_oob_processor.py b/aries_cloudagent/core/tests/test_oob_processor.py new file mode 100644 index 0000000000..a5c336c351 --- /dev/null +++ b/aries_cloudagent/core/tests/test_oob_processor.py @@ -0,0 +1,741 @@ +import json + +from asynctest import ANY +from asynctest import TestCase as AsyncTestCase +from asynctest import mock as async_mock + +from ...connections.models.conn_record import ConnRecord +from ...messaging.decorators.attach_decorator import AttachDecorator +from ...messaging.decorators.service_decorator import ServiceDecorator +from ...messaging.request_context import RequestContext +from ...protocols.connections.v1_0.messages.connection_invitation import ( + ConnectionInvitation, +) +from ...protocols.out_of_band.v1_0.messages.invitation import InvitationMessage +from ...protocols.out_of_band.v1_0.models.oob_record import OobRecord +from ...storage.error import StorageNotFoundError +from ...transport.inbound.receipt import MessageReceipt +from ...transport.outbound.message import OutboundMessage +from ..in_memory.profile import InMemoryProfile +from ..oob_processor import OobMessageProcessor, OobMessageProcessorError + + +class TestOobProcessor(AsyncTestCase): + async def setUp(self): + self.profile = InMemoryProfile.test_profile() + self.inbound_message_router = async_mock.CoroutineMock() + self.oob_processor = OobMessageProcessor( + inbound_message_router=self.inbound_message_router + ) + + self.oob_record = async_mock.MagicMock( + connection_id="a-connection-id", + attach_thread_id="the-thid", + their_service={ + "recipientKeys": ["9WCgWKUaAJj3VWxxtzvvMQN3AoFxoBtBDo9ntwJnVVCC"], + "routingKeys": ["6QSduYdf8Bi6t8PfNm5vNomGWDtXhmMmTRzaciudBXYJ"], + "serviceEndpoint": "http://their-service-endpoint.com", + }, + emit_event=async_mock.CoroutineMock(), + delete_record=async_mock.CoroutineMock(), + save=async_mock.CoroutineMock(), + ) + self.context = RequestContext.test_context() + self.context.message = ConnectionInvitation() + + async def test_clean_finished_oob_record_no_multi_use_no_request_attach(self): + test_message = InvitationMessage() + test_message.assign_thread_id("the-thid", "the-pthid") + + mock_oob = async_mock.MagicMock( + emit_event=async_mock.CoroutineMock(), + delete_record=async_mock.CoroutineMock(), + multi_use=False, + invitation=async_mock.MagicMock(requests_attach=[]), + ) + + with async_mock.patch.object( + OobRecord, + "retrieve_by_tag_filter", + async_mock.CoroutineMock(return_value=mock_oob), + ) as mock_retrieve_oob: + + await self.oob_processor.clean_finished_oob_record( + self.profile, test_message + ) + + assert mock_oob.state == OobRecord.STATE_DONE + mock_oob.emit_event.assert_called_once() + mock_oob.delete_record.assert_called_once() + + mock_retrieve_oob.assert_called_once_with( + ANY, {"invi_msg_id": "the-pthid"}, {"role": OobRecord.ROLE_SENDER} + ) + + async def test_clean_finished_oob_record_multi_use(self): + test_message = InvitationMessage() + test_message.assign_thread_id("the-thid", "the-pthid") + + mock_oob = async_mock.MagicMock( + emit_event=async_mock.CoroutineMock(), + delete_record=async_mock.CoroutineMock(), + multi_use=True, + invitation=async_mock.MagicMock(requests_attach=[]), + ) + + with async_mock.patch.object( + OobRecord, + "retrieve_by_tag_filter", + async_mock.CoroutineMock(return_value=mock_oob), + ) as mock_retrieve_oob: + + await self.oob_processor.clean_finished_oob_record( + self.profile, test_message + ) + + mock_oob.emit_event.assert_not_called() + mock_oob.delete_record.assert_not_called() + + mock_retrieve_oob.assert_called_once_with( + ANY, {"invi_msg_id": "the-pthid"}, {"role": OobRecord.ROLE_SENDER} + ) + + async def test_clean_finished_oob_record_x(self): + test_message = InvitationMessage() + test_message.assign_thread_id("the-thid", "the-pthid") + + with async_mock.patch.object( + OobRecord, + "retrieve_by_tag_filter", + async_mock.CoroutineMock(), + ) as mock_retrieve_oob: + mock_retrieve_oob.side_effect = (StorageNotFoundError(),) + + await self.oob_processor.clean_finished_oob_record( + self.profile, test_message + ) + + async def test_find_oob_target_for_outbound_message(self): + mock_oob = async_mock.MagicMock( + emit_event=async_mock.CoroutineMock(), + delete_record=async_mock.CoroutineMock(), + multi_use=True, + invitation=async_mock.MagicMock(requests_attach=[]), + invi_msg_id="the-pthid", + our_recipient_key="3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx", + their_service={ + "recipientKeys": ["9WCgWKUaAJj3VWxxtzvvMQN3AoFxoBtBDo9ntwJnVVCC"], + "serviceEndpoint": "http://their-service-endpoint.com", + "routingKeys": ["6QSduYdf8Bi6t8PfNm5vNomGWDtXhmMmTRzaciudBXYJ"], + }, + our_service={ + "recipientKeys": ["3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx"], + "serviceEndpoint": "http://our-service-endpoint.com", + "routingKeys": [], + }, + ) + + message = json.dumps({"~thread": {"thid": "the-thid"}}) + outbound = OutboundMessage(reply_thread_id="the-thid", payload=message) + + with async_mock.patch.object( + OobRecord, + "retrieve_by_tag_filter", + async_mock.CoroutineMock(return_value=mock_oob), + ) as mock_retrieve_oob: + + target = await self.oob_processor.find_oob_target_for_outbound_message( + self.profile, outbound + ) + + assert target + assert target.endpoint == "http://their-service-endpoint.com" + assert target.recipient_keys == [ + "9WCgWKUaAJj3VWxxtzvvMQN3AoFxoBtBDo9ntwJnVVCC" + ] + assert target.routing_keys == [ + "6QSduYdf8Bi6t8PfNm5vNomGWDtXhmMmTRzaciudBXYJ" + ] + assert target.sender_key == "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx" + + payload = json.loads(outbound.payload) + + assert payload == { + "~thread": {"thid": "the-thid", "pthid": "the-pthid"}, + "~service": { + "recipientKeys": ["3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx"], + "serviceEndpoint": "http://our-service-endpoint.com", + "routingKeys": [], + }, + } + + mock_retrieve_oob.assert_called_once_with( + ANY, {"attach_thread_id": "the-thid"} + ) + + async def test_find_oob_target_for_outbound_message_oob_not_found(self): + message = json.dumps({}) + outbound = OutboundMessage(reply_thread_id="the-thid", payload=message) + + with async_mock.patch.object( + OobRecord, + "retrieve_by_tag_filter", + async_mock.CoroutineMock(side_effect=(StorageNotFoundError(),)), + ) as mock_retrieve_oob: + + target = await self.oob_processor.find_oob_target_for_outbound_message( + self.profile, outbound + ) + + assert not target + mock_retrieve_oob.assert_called_once_with( + ANY, {"attach_thread_id": "the-thid"} + ) + + async def test_find_oob_target_for_outbound_message_update_service_thread(self): + mock_oob = async_mock.MagicMock( + emit_event=async_mock.CoroutineMock(), + delete_record=async_mock.CoroutineMock(), + multi_use=True, + invitation=async_mock.MagicMock(requests_attach=[]), + invi_msg_id="the-pthid", + our_recipient_key="3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx", + their_service={ + "recipientKeys": ["9WCgWKUaAJj3VWxxtzvvMQN3AoFxoBtBDo9ntwJnVVCC"], + "serviceEndpoint": "http://their-service-endpoint.com", + "routingKeys": ["6QSduYdf8Bi6t8PfNm5vNomGWDtXhmMmTRzaciudBXYJ"], + }, + our_service={ + "recipientKeys": ["3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx"], + "serviceEndpoint": "http://our-service-endpoint.com", + "routingKeys": [], + }, + ) + + with async_mock.patch.object( + OobRecord, + "retrieve_by_tag_filter", + async_mock.CoroutineMock(return_value=mock_oob), + ): + + message = json.dumps({}) + outbound = OutboundMessage(reply_thread_id="the-thid", payload=message) + await self.oob_processor.find_oob_target_for_outbound_message( + self.profile, outbound + ) + payload = json.loads(outbound.payload) + + assert payload == { + "~thread": {"pthid": "the-pthid"}, + "~service": { + "recipientKeys": ["3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx"], + "serviceEndpoint": "http://our-service-endpoint.com", + "routingKeys": [], + }, + } + + message = json.dumps( + { + "~service": {"already": "present"}, + } + ) + outbound = OutboundMessage(reply_thread_id="the-thid", payload=message) + await self.oob_processor.find_oob_target_for_outbound_message( + self.profile, outbound + ) + payload = json.loads(outbound.payload) + + assert payload == { + "~thread": {"pthid": "the-pthid"}, + "~service": {"already": "present"}, + } + + async def test_find_oob_record_for_inbound_message_parent_thread_id(self): + # With pthid + with async_mock.patch.object( + OobRecord, + "retrieve_by_tag_filter", + async_mock.CoroutineMock(return_value=self.oob_record), + ) as mock_retrieve: + self.context.message_receipt = MessageReceipt( + thread_id="the-thid", parent_thread_id="the-pthid" + ) + + assert await self.oob_processor.find_oob_record_for_inbound_message( + self.context + ) + mock_retrieve.assert_called_once() + mock_retrieve.assert_called_once_with(ANY, {"invi_msg_id": "the-pthid"}) + + # With pthid, throws error + with async_mock.patch.object( + OobRecord, + "retrieve_by_tag_filter", + async_mock.CoroutineMock(side_effect=(StorageNotFoundError(),)), + ) as mock_retrieve: + self.context.message_receipt = MessageReceipt( + thread_id="the-thid", parent_thread_id="the-pthid" + ) + + assert not await self.oob_processor.find_oob_record_for_inbound_message( + self.context + ) + mock_retrieve.assert_called_once() + mock_retrieve.assert_called_once_with(ANY, {"invi_msg_id": "the-pthid"}) + + # Without pthid + with async_mock.patch.object( + OobRecord, + "retrieve_by_tag_filter", + async_mock.CoroutineMock(), + ) as mock_retrieve: + self.context.message_receipt = MessageReceipt() + + assert not await self.oob_processor.find_oob_record_for_inbound_message( + self.context + ) + mock_retrieve.assert_not_called() + + async def test_find_oob_record_for_inbound_message_connectionless_retrieve_oob( + self, + ): + # With thread_id and recipient_verkey + with async_mock.patch.object( + OobRecord, + "retrieve_by_tag_filter", + async_mock.CoroutineMock(return_value=self.oob_record), + ) as mock_retrieve: + self.context.message_receipt = MessageReceipt( + thread_id="the-thid", + recipient_verkey="our-recipient-key", + sender_verkey=self.oob_record.their_service["recipientKeys"][0], + ) + + assert await self.oob_processor.find_oob_record_for_inbound_message( + self.context + ) + mock_retrieve.assert_called_once() + mock_retrieve.assert_called_once_with( + ANY, + { + "attach_thread_id": "the-thid", + "our_recipient_key": "our-recipient-key", + }, + ) + + # With thread_id and recipient_verkey, throws error + with async_mock.patch.object( + OobRecord, + "retrieve_by_tag_filter", + async_mock.CoroutineMock(side_effect=(StorageNotFoundError(),)), + ) as mock_retrieve: + self.context.message_receipt = MessageReceipt( + thread_id="the-thid", recipient_verkey="our-recipient-key" + ) + + assert not await self.oob_processor.find_oob_record_for_inbound_message( + self.context + ) + mock_retrieve.assert_called_once() + mock_retrieve.assert_called_once_with( + ANY, + { + "attach_thread_id": "the-thid", + "our_recipient_key": "our-recipient-key", + }, + ) + + # With connection + with async_mock.patch.object( + OobRecord, + "retrieve_by_tag_filter", + async_mock.CoroutineMock(return_value=None), + ) as mock_retrieve: + self.context.message_receipt = MessageReceipt( + thread_id="the-thid", recipient_verkey="our-recipient-key" + ) + self.context.connection_record = async_mock.MagicMock() + + assert not await self.oob_processor.find_oob_record_for_inbound_message( + self.context + ) + mock_retrieve.assert_not_called() + + # Without thread_id and recipient_verkey + with async_mock.patch.object( + OobRecord, + "retrieve_by_tag_filter", + async_mock.CoroutineMock(return_value=None), + ) as mock_retrieve: + self.context.message_receipt = MessageReceipt() + + assert not await self.oob_processor.find_oob_record_for_inbound_message( + self.context + ) + mock_retrieve.assert_not_called() + + async def test_find_oob_record_for_inbound_message_sender_connection_id_no_match( + self, + ): + with async_mock.patch.object( + OobRecord, + "retrieve_by_tag_filter", + async_mock.CoroutineMock(return_value=self.oob_record), + ) as mock_retrieve: + self.oob_record.role = OobRecord.ROLE_SENDER + self.oob_record.state = OobRecord.STATE_AWAIT_RESPONSE + self.context.connection_record = async_mock.MagicMock( + connection_id="a-connection-id" + ) + self.context.message_receipt = MessageReceipt( + thread_id="the-thid", parent_thread_id="the-pthid" + ) + + assert await self.oob_processor.find_oob_record_for_inbound_message( + self.context + ) + mock_retrieve.assert_called_once() + mock_retrieve.assert_called_once_with(ANY, {"invi_msg_id": "the-pthid"}) + + # Connection id is different + with async_mock.patch.object( + OobRecord, + "retrieve_by_tag_filter", + async_mock.CoroutineMock(return_value=self.oob_record), + ) as mock_retrieve: + self.oob_record.role = OobRecord.ROLE_SENDER + self.oob_record.state = OobRecord.STATE_ACCEPTED + self.context.connection_record = async_mock.MagicMock( + connection_id="another-connection-id" + ) + self.context.message_receipt = MessageReceipt( + thread_id="the-thid", parent_thread_id="the-pthid" + ) + + assert not await self.oob_processor.find_oob_record_for_inbound_message( + self.context + ) + mock_retrieve.assert_called_once() + mock_retrieve.assert_called_once_with(ANY, {"invi_msg_id": "the-pthid"}) + + # Connection id is not the same, state is not await response + with async_mock.patch.object( + OobRecord, + "retrieve_by_tag_filter", + async_mock.CoroutineMock(return_value=self.oob_record), + ) as mock_retrieve: + self.oob_record.role = OobRecord.ROLE_SENDER + self.oob_record.state = OobRecord.STATE_ACCEPTED + self.context.connection_record = async_mock.MagicMock( + connection_id="another-connection-id" + ) + self.context.message_receipt = MessageReceipt( + thread_id="the-thid", parent_thread_id="the-pthid" + ) + + assert not await self.oob_processor.find_oob_record_for_inbound_message( + self.context + ) + mock_retrieve.assert_called_once() + mock_retrieve.assert_called_once_with(ANY, {"invi_msg_id": "the-pthid"}) + + # Connection id is not the same, state is AWAIT_RESPONSE. oob has connection_id + with async_mock.patch.object( + OobRecord, + "retrieve_by_tag_filter", + async_mock.CoroutineMock(return_value=self.oob_record), + ) as mock_retrieve, async_mock.patch.object( + ConnRecord, + "retrieve_by_id", + async_mock.CoroutineMock( + return_value=async_mock.MagicMock( + delete_record=async_mock.CoroutineMock() + ) + ), + ) as mock_retrieve_conn: + self.oob_record.role = OobRecord.ROLE_SENDER + self.oob_record.state = OobRecord.STATE_AWAIT_RESPONSE + self.context.connection_record = async_mock.MagicMock( + connection_id="another-connection-id" + ) + self.context.message_receipt = MessageReceipt( + thread_id="the-thid", parent_thread_id="the-pthid" + ) + + assert await self.oob_processor.find_oob_record_for_inbound_message( + self.context + ) + mock_retrieve.assert_called_once() + mock_retrieve.assert_called_once_with(ANY, {"invi_msg_id": "the-pthid"}) + mock_retrieve_conn.assert_called_once_with(ANY, "a-connection-id") + mock_retrieve_conn.return_value.delete_record.assert_called_once() + + assert self.oob_record.connection_id == "another-connection-id" + + async def test_find_oob_record_for_inbound_message_attach_thread_id_set(self): + # No attach thread_id + self.oob_record.attach_thread_id = None + + self.oob_record.invitation.requests_attach = [ + AttachDecorator.data_json({"@id": "the-thid"}) + ] + + with async_mock.patch.object( + OobRecord, + "retrieve_by_tag_filter", + async_mock.CoroutineMock(return_value=self.oob_record), + ) as mock_retrieve: + self.context.message_receipt = MessageReceipt( + thread_id="the-thid", parent_thread_id="the-pthid" + ) + + assert await self.oob_processor.find_oob_record_for_inbound_message( + self.context + ) + mock_retrieve.assert_called_once() + mock_retrieve.assert_called_once_with(ANY, {"invi_msg_id": "the-pthid"}) + + assert self.oob_record.attach_thread_id == "the-thid" + + async def test_find_oob_record_for_inbound_message_attach_thread_id_not_in_list( + self, + ): + # No attach thread_id + self.oob_record.attach_thread_id = None + + self.oob_record.invitation.requests_attach = [ + AttachDecorator.data_json({"@id": "another-thid"}) + ] + + with async_mock.patch.object( + OobRecord, + "retrieve_by_tag_filter", + async_mock.CoroutineMock(return_value=self.oob_record), + ) as mock_retrieve: + self.context.message_receipt = MessageReceipt( + thread_id="the-thid", parent_thread_id="the-pthid" + ) + + assert not await self.oob_processor.find_oob_record_for_inbound_message( + self.context + ) + mock_retrieve.assert_called_once() + mock_retrieve.assert_called_once_with(ANY, {"invi_msg_id": "the-pthid"}) + + async def test_find_oob_record_for_inbound_message_not_attach_thread_id_matching( + self, + ): + + with async_mock.patch.object( + OobRecord, + "retrieve_by_tag_filter", + async_mock.CoroutineMock(return_value=self.oob_record), + ) as mock_retrieve: + self.context.message_receipt = MessageReceipt( + thread_id="the-thid", parent_thread_id="the-pthid" + ) + + assert await self.oob_processor.find_oob_record_for_inbound_message( + self.context + ) + mock_retrieve.assert_called_once() + mock_retrieve.assert_called_once_with(ANY, {"invi_msg_id": "the-pthid"}) + + async def test_find_oob_record_for_inbound_message_not_attach_thread_id_not_matching( + self, + ): + self.oob_record.attach_thread_id = "another-thid" + + with async_mock.patch.object( + OobRecord, + "retrieve_by_tag_filter", + async_mock.CoroutineMock(return_value=self.oob_record), + ) as mock_retrieve: + self.context.message_receipt = MessageReceipt( + thread_id="the-thid", parent_thread_id="the-pthid" + ) + + assert not await self.oob_processor.find_oob_record_for_inbound_message( + self.context + ) + mock_retrieve.assert_called_once() + mock_retrieve.assert_called_once_with(ANY, {"invi_msg_id": "the-pthid"}) + + async def test_find_oob_record_for_inbound_message_recipient_verkey_not_in_their_service( + self, + ): + with async_mock.patch.object( + OobRecord, + "retrieve_by_tag_filter", + async_mock.CoroutineMock(return_value=self.oob_record), + ) as mock_retrieve: + self.context.message_receipt = MessageReceipt( + thread_id="the-thid", + parent_thread_id="the-pthid", + recipient_verkey="recipient-verkey", + sender_verkey="a-sender-verkey", + ) + + assert not await self.oob_processor.find_oob_record_for_inbound_message( + self.context + ) + mock_retrieve.assert_called_once() + mock_retrieve.assert_called_once_with(ANY, {"invi_msg_id": "the-pthid"}) + + async def test_find_oob_record_for_inbound_message_their_service_matching_with_message_receipt( + self, + ): + with async_mock.patch.object( + OobRecord, + "retrieve_by_tag_filter", + async_mock.CoroutineMock(return_value=self.oob_record), + ) as mock_retrieve: + self.context.message_receipt = MessageReceipt( + thread_id="the-thid", + parent_thread_id="the-pthid", + recipient_verkey="recipient-verkey", + sender_verkey="9WCgWKUaAJj3VWxxtzvvMQN3AoFxoBtBDo9ntwJnVVCC", + ) + + assert await self.oob_processor.find_oob_record_for_inbound_message( + self.context + ) + mock_retrieve.assert_called_once() + mock_retrieve.assert_called_once_with(ANY, {"invi_msg_id": "the-pthid"}) + + async def test_find_oob_record_for_inbound_message_their_service_set_on_oob_record( + self, + ): + self.context._message._service = ServiceDecorator( + endpoint="http://example.com/endpoint", + recipient_keys=["9WCgWKUaAJj3VWxxtzvvMQN3AoFxoBtBDo9ntwJnVVCC"], + routing_keys=["9WCgWKUaAJj3VWxxtzvvMQN3AoFxoBtBDo9ntwJnVVCC"], + ) + + self.oob_record.their_service = None + + with async_mock.patch.object( + OobRecord, + "retrieve_by_tag_filter", + async_mock.CoroutineMock(return_value=self.oob_record), + ) as mock_retrieve: + self.context.message_receipt = MessageReceipt( + thread_id="the-thid", + parent_thread_id="the-pthid", + ) + + assert await self.oob_processor.find_oob_record_for_inbound_message( + self.context + ) + mock_retrieve.assert_called_once() + mock_retrieve.assert_called_once_with(ANY, {"invi_msg_id": "the-pthid"}) + + assert self.oob_record.their_service == { + "serviceEndpoint": "http://example.com/endpoint", + "recipientKeys": ["9WCgWKUaAJj3VWxxtzvvMQN3AoFxoBtBDo9ntwJnVVCC"], + "routingKeys": ["9WCgWKUaAJj3VWxxtzvvMQN3AoFxoBtBDo9ntwJnVVCC"], + } + + async def test_find_oob_record_for_inbound_message_session_emit_delete( + self, + ): + + with async_mock.patch.object( + OobRecord, + "retrieve_by_tag_filter", + async_mock.CoroutineMock(return_value=self.oob_record), + ) as mock_retrieve: + self.context.message_receipt = MessageReceipt( + thread_id="the-thid", + parent_thread_id="the-pthid", + ) + + assert await self.oob_processor.find_oob_record_for_inbound_message( + self.context + ) + mock_retrieve.assert_called_once() + mock_retrieve.assert_called_once_with(ANY, {"invi_msg_id": "the-pthid"}) + + assert self.oob_record.state == OobRecord.STATE_DONE + self.oob_record.emit_event.assert_called_once() + self.oob_record.delete_record.assert_called_once() + self.oob_record.save.assert_not_called() + + async def test_find_oob_record_for_inbound_message_session_connectionless_save( + self, + ): + + self.oob_record.connection_id = None + + with async_mock.patch.object( + OobRecord, + "retrieve_by_tag_filter", + async_mock.CoroutineMock(return_value=self.oob_record), + ) as mock_retrieve: + self.context.message_receipt = MessageReceipt( + thread_id="the-thid", + parent_thread_id="the-pthid", + ) + + assert await self.oob_processor.find_oob_record_for_inbound_message( + self.context + ) + mock_retrieve.assert_called_once() + mock_retrieve.assert_called_once_with(ANY, {"invi_msg_id": "the-pthid"}) + + self.oob_record.emit_event.assert_not_called() + self.oob_record.delete_record.assert_not_called() + self.oob_record.save.assert_called_once() + + async def test_handle_message(self): + oob_record = async_mock.MagicMock( + connection_id="the-conn-id", save=async_mock.CoroutineMock() + ) + + await self.oob_processor.handle_message( + self.profile, + [ + { + "@type": "issue-credential/1.0/offer-credential", + "@id": "4a580490-a9d8-44f5-a3f6-14e0b8a219b0", + } + ], + oob_record, + their_service=ServiceDecorator( + endpoint="http://their-service-endpoint.com", + recipient_keys=["9WCgWKUaAJj3VWxxtzvvMQN3AoFxoBtBDo9ntwJnVVCC"], + routing_keys=["6QSduYdf8Bi6t8PfNm5vNomGWDtXhmMmTRzaciudBXYJ"], + ), + ) + + assert oob_record.attach_thread_id == "4a580490-a9d8-44f5-a3f6-14e0b8a219b0" + assert oob_record.their_service == { + "serviceEndpoint": "http://their-service-endpoint.com", + "recipientKeys": ["9WCgWKUaAJj3VWxxtzvvMQN3AoFxoBtBDo9ntwJnVVCC"], + "routingKeys": ["6QSduYdf8Bi6t8PfNm5vNomGWDtXhmMmTRzaciudBXYJ"], + } + + oob_record.save.assert_called_once() + + self.inbound_message_router.assert_called_once_with(self.profile, ANY, False) + + async def test_handle_message_unsupported_message_type(self): + + with self.assertRaises(OobMessageProcessorError) as err: + await self.oob_processor.handle_message( + self.profile, [{"@type": "unsupported"}], async_mock.MagicMock() + ) + assert ( + "None of the oob attached messages supported. Supported message types are issue-credential/1.0/offer-credential, issue-credential/2.0/offer-credential, present-proof/1.0/request-presentation, present-proof/2.0/request-presentation" + in err.exception.message + ) + + async def test_get_thread_id(self): + message_w_thread = { + "@id": "the-message-id", + "~thread": {"thid": "the-thread-id"}, + } + message_wo_thread = {"@id": "the-message-id"} + + assert self.oob_processor.get_thread_id(message_w_thread) == "the-thread-id" + assert self.oob_processor.get_thread_id(message_wo_thread) == "the-message-id" From b3a20b6bc707f0bdf89cd4b1f05ce946a1b2fa0c Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 5 Apr 2022 15:27:16 +0200 Subject: [PATCH 166/872] fix: fix after merge Signed-off-by: Timo Glastra --- aries_cloudagent/protocols/present_proof/v2_0/manager.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/aries_cloudagent/protocols/present_proof/v2_0/manager.py b/aries_cloudagent/protocols/present_proof/v2_0/manager.py index 67049b92cf..48dc3c312d 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/manager.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/manager.py @@ -359,8 +359,6 @@ async def receive_pres( ) pres_ex_record.pres = message pres_ex_record.state = V20PresExRecord.STATE_PRESENTATION_RECEIVED - if not pres_ex_record.connection_id: - pres_ex_record.connection_id = conn_record.connection_id async with self._profile.session() as session: await pres_ex_record.save(session, reason="receive v2.0 presentation") From 10dddebd2a73e86b95a86fdd778d68fc091aea85 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 5 Apr 2022 15:38:00 +0200 Subject: [PATCH 167/872] style: black formatting Signed-off-by: Timo Glastra --- .../connections/v1_0/tests/test_manager.py | 14 +++++++++----- .../didexchange/v1_0/tests/test_manager.py | 14 +++++++++----- .../v1_0/models/tests/test_invitation.py | 2 +- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py index 6b97d635e2..3e3cb78d72 100644 --- a/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py @@ -75,9 +75,7 @@ async def setUp(self): self.responder = MockResponder() self.oob_mock = async_mock.MagicMock( - clean_finished_oob_record=async_mock.CoroutineMock( - return_value=None - ) + clean_finished_oob_record=async_mock.CoroutineMock(return_value=None) ) self.profile = InMemoryProfile.test_profile( @@ -88,7 +86,11 @@ async def setUp(self): "debug.auto_accept_invites": True, "debug.auto_accept_requests": True, }, - bind={BaseResponder: self.responder, BaseCache: InMemoryCache(), OobMessageProcessor: self.oob_mock}, + bind={ + BaseResponder: self.responder, + BaseCache: InMemoryCache(), + OobMessageProcessor: self.oob_mock, + }, ) self.context = self.profile.context @@ -720,7 +722,9 @@ async def test_receive_request_public_did_oob_invite(self): conn_rec = await self.manager.receive_request(mock_request, receipt) assert conn_rec - self.oob_mock.clean_finished_oob_record.assert_called_once_with(self.profile, mock_request) + self.oob_mock.clean_finished_oob_record.assert_called_once_with( + self.profile, mock_request + ) async def test_receive_request_public_did_conn_invite(self): async with self.profile.session() as session: diff --git a/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py index f2fdd51af1..6df0b348b2 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py @@ -79,9 +79,7 @@ async def setUp(self): self.responder = MockResponder() self.oob_mock = async_mock.MagicMock( - clean_finished_oob_record=async_mock.CoroutineMock( - return_value=None - ) + clean_finished_oob_record=async_mock.CoroutineMock(return_value=None) ) self.profile = InMemoryProfile.test_profile( @@ -94,7 +92,11 @@ async def setUp(self): "multitenant.enabled": True, "wallet.id": True, }, - bind={BaseResponder: self.responder, BaseCache: InMemoryCache(), OobMessageProcessor: self.oob_mock}, + bind={ + BaseResponder: self.responder, + BaseCache: InMemoryCache(), + OobMessageProcessor: self.oob_mock, + }, ) self.context = self.profile.context async with self.profile.session() as session: @@ -549,7 +551,9 @@ async def test_receive_request_explicit_public_did(self): mediation_id=None, ) assert conn_rec - self.oob_mock.clean_finished_oob_record.assert_called_once_with(self.profile, mock_request) + self.oob_mock.clean_finished_oob_record.assert_called_once_with( + self.profile, mock_request + ) async def test_receive_request_invi_not_found(self): async with self.profile.session() as session: diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/models/tests/test_invitation.py b/aries_cloudagent/protocols/out_of_band/v1_0/models/tests/test_invitation.py index db2c2b714d..12dae75f89 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/models/tests/test_invitation.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/models/tests/test_invitation.py @@ -23,7 +23,7 @@ def test_invitation_record(self): "invitation_url": None, "state": None, "trace": False, - "oob_id": None + "oob_id": None, } another = InvitationRecord(invi_msg_id="99999") From ad5140119a77c1ce8f62ce7f36af1d654f393f61 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Tue, 5 Apr 2022 08:07:47 -0700 Subject: [PATCH 168/872] argparse -it update Signed-off-by: Shaanjot Gill --- aries_cloudagent/config/argparse.py | 2 -- aries_cloudagent/core/conductor.py | 47 +++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index f39659552d..355caebccc 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -1246,8 +1246,6 @@ def get_settings(self, args: Namespace): settings = {} if args.inbound_transports: settings["transport.inbound_configs"] = args.inbound_transports - else: - raise ArgsParseError("-it/--inbound-transport is required") if args.outbound_transports: settings["transport.outbound_configs"] = args.outbound_transports diff --git a/aries_cloudagent/core/conductor.py b/aries_cloudagent/core/conductor.py index e47a6fc135..dc156a5e73 100644 --- a/aries_cloudagent/core/conductor.py +++ b/aries_cloudagent/core/conductor.py @@ -23,6 +23,7 @@ ledger_config, load_multiple_genesis_transactions_from_config, ) +from ..config.error import ArgsParseError from ..config.logging import LoggingConfigurator from ..config.wallet import wallet_config from ..core.profile import Profile @@ -105,6 +106,52 @@ async def setup(self): """Initialize the global request context.""" context = await self.context_builder.build_context() + ext_plugin = context.settings.get("external_plugins") + ext_plugin_config = context.settings.get("plugin_config") + in_transport_config_exception = False + if not context.settings.get("transport.inbound_configs"): + if ext_plugin and ext_plugin_config: + if ( + "redis_queue" in ext_plugin_config + and "inbound" in ext_plugin_config["redis_queue"] + ): + pass + else: + in_transport_config_exception = False + if ext_plugin and ext_plugin_config: + if ( + "kafka_queue" in ext_plugin_config + and "consumer-config" in ext_plugin_config["kafka_queue"] + ): + pass + else: + in_transport_config_exception = False + if in_transport_config_exception: + raise ArgsParseError( + "No --inbound-transport/-it and external transport config specified" + ) + out_transport_config_exception = False + if not context.settings.get("transport.outbound_configs"): + if ext_plugin and ext_plugin_config: + if ( + "redis_queue" in ext_plugin_config + and "outbound" in ext_plugin_config["redis_queue"] + ): + pass + else: + out_transport_config_exception = False + if ext_plugin and ext_plugin_config: + if ( + "kafka_queue" in ext_plugin_config + and "producer-config" in ext_plugin_config["kafka_queue"] + ): + pass + else: + out_transport_config_exception = False + if out_transport_config_exception: + raise ArgsParseError( + "No --outbound-transport/-ot and external transport config specified" + ) # Fetch genesis transactions if necessary if context.settings.get("ledger.ledger_config_list"): From e2df969000cff0a59285fdb602c94f328edad77d Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Tue, 5 Apr 2022 11:07:40 -0700 Subject: [PATCH 169/872] changes from PR#1610 Signed-off-by: Shaanjot Gill --- aries_cloudagent/revocation/routes.py | 2 +- aries_cloudagent/tails/indy_tails_server.py | 17 +++++- aries_cloudagent/tails/tests/test_indy.py | 62 +++++++++++++++++++++ requirements.bbs.txt | 2 +- 4 files changed, 78 insertions(+), 5 deletions(-) diff --git a/aries_cloudagent/revocation/routes.py b/aries_cloudagent/revocation/routes.py index b68a187e9d..ea2f83427a 100644 --- a/aries_cloudagent/revocation/routes.py +++ b/aries_cloudagent/revocation/routes.py @@ -1252,7 +1252,7 @@ async def on_revocation_tails_file_event(profile: Profile, event: Event): revoc_reg_id = event.payload["context"]["rev_reg_id"] tails_local_path = tails_path(revoc_reg_id) (upload_success, reason) = await tails_server.upload_tails_file( - profile, + profile.context, revoc_reg_id, tails_local_path, interval=0.8, diff --git a/aries_cloudagent/tails/indy_tails_server.py b/aries_cloudagent/tails/indy_tails_server.py index 8646a83ce7..a7aee90a58 100644 --- a/aries_cloudagent/tails/indy_tails_server.py +++ b/aries_cloudagent/tails/indy_tails_server.py @@ -2,6 +2,8 @@ from typing import Tuple +from ..config.injection_context import InjectionContext +from ..ledger.multiple_ledger.base_manager import BaseMultipleLedgerManager from ..utils.http import put_file, PutError from .base import BaseTailsServer @@ -13,7 +15,7 @@ class IndyTailsServer(BaseTailsServer): async def upload_tails_file( self, - context, + context: InjectionContext, rev_reg_id: str, tails_file_path: str, interval: float = 1.0, @@ -30,9 +32,18 @@ async def upload_tails_file( backoff: exponential backoff in retry interval max_attempts: maximum number of attempts to make """ - - genesis_transactions = context.settings.get("ledger.genesis_transactions") tails_server_upload_url = context.settings.get("tails_server_upload_url") + genesis_transactions = context.settings.get("ledger.genesis_transactions") + + if not genesis_transactions: + ledger_manager = context.injector.inject(BaseMultipleLedgerManager) + write_ledgers = await ledger_manager.get_write_ledger() + pool = write_ledgers[1].pool + + try: + genesis_transactions = pool.genesis_transactions + except AttributeError: + genesis_transactions = pool.genesis_txns_cache if not tails_server_upload_url: raise TailsServerNotConfiguredError( diff --git a/aries_cloudagent/tails/tests/test_indy.py b/aries_cloudagent/tails/tests/test_indy.py index 4dc479c5c6..ca9d1d6729 100644 --- a/aries_cloudagent/tails/tests/test_indy.py +++ b/aries_cloudagent/tails/tests/test_indy.py @@ -1,6 +1,8 @@ from asynctest import mock as async_mock, TestCase as AsyncTestCase from ...config.injection_context import InjectionContext +from ...core.in_memory import InMemoryProfile +from ...ledger.multiple_ledger.base_manager import BaseMultipleLedgerManager from .. import indy_tails_server as test_module @@ -38,6 +40,66 @@ async def test_upload(self): assert ok assert text == "tails-hash" + async def test_upload_indy_sdk(self): + profile = InMemoryProfile.test_profile() + profile.settings["tails_server_upload_url"] = "http://1.2.3.4:8088" + profile.context.injector.bind_instance( + BaseMultipleLedgerManager, + async_mock.MagicMock( + get_write_ledger=async_mock.CoroutineMock( + return_value=( + "test_ledger_id", + async_mock.MagicMock( + pool=async_mock.MagicMock(genesis_transactions="dummy") + ), + ) + ) + ), + ) + indy_tails = test_module.IndyTailsServer() + + with async_mock.patch.object( + test_module, "put_file", async_mock.CoroutineMock() + ) as mock_put: + mock_put.return_value = "tails-hash" + (ok, text) = await indy_tails.upload_tails_file( + profile.context, + REV_REG_ID, + "/tmp/dummy/path", + ) + assert ok + assert text == "tails-hash" + + async def test_upload_indy_vdr(self): + profile = InMemoryProfile.test_profile() + profile.settings["tails_server_upload_url"] = "http://1.2.3.4:8088" + profile.context.injector.bind_instance( + BaseMultipleLedgerManager, + async_mock.MagicMock( + get_write_ledger=async_mock.CoroutineMock( + return_value=( + "test_ledger_id", + async_mock.MagicMock( + pool=async_mock.MagicMock(genesis_txns_cache="dummy") + ), + ) + ) + ), + ) + indy_tails = test_module.IndyTailsServer() + + with async_mock.patch.object( + test_module, "put_file", async_mock.CoroutineMock() + ) as mock_put: + mock_put.return_value = "tails-hash" + (ok, text) = await indy_tails.upload_tails_file( + profile.context, + REV_REG_ID, + "/tmp/dummy/path", + ) + assert ok + assert text == "tails-hash" + async def test_upload_x(self): context = InjectionContext( settings={ diff --git a/requirements.bbs.txt b/requirements.bbs.txt index 8debc1f11e..972fa90c8a 100644 --- a/requirements.bbs.txt +++ b/requirements.bbs.txt @@ -1 +1 @@ -ursa-bbs-signatures==1.0.1 \ No newline at end of file +ursa-bbs-signatures~=1.0.1 \ No newline at end of file From 87acb4ef0a92c00959026cb9e479d7249099cbb5 Mon Sep 17 00:00:00 2001 From: DaevMithran Date: Wed, 6 Apr 2022 17:11:45 +0530 Subject: [PATCH 170/872] Update schema and error handling Signed-off-by: DaevMithran --- aries_cloudagent/wallet/routes.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/aries_cloudagent/wallet/routes.py b/aries_cloudagent/wallet/routes.py index a7772be595..0332a408f8 100644 --- a/aries_cloudagent/wallet/routes.py +++ b/aries_cloudagent/wallet/routes.py @@ -188,6 +188,12 @@ class DIDCreateSchema(OpenAPISchema): description="To define a key type for a did:key", ) + seed = fields.Str( + required=False, + description="Optional seed to use for DID", + example="000000000000000000000000Trustee1", + ) + class CreateAttribTxnForEndorserOptionSchema(OpenAPISchema): """Class for user to input whether to create a transaction for endorser or not.""" @@ -355,9 +361,9 @@ async def wallet_create_did(request: web.BaseRequest): f" support key type {key_type.key_type}" ) ) - seed = None - if context.settings.get("wallet.allow_insecure_seed"): - seed = body.get("seed") or None + seed = body.get("seed") or None + if seed and not context.settings.get("wallet.allow_insecure_seed"): + raise web.HTTPBadRequest(reason="Seed support is not enabled") info = None async with context.session() as session: wallet = session.inject_or(BaseWallet) From 444eba08cd8d373b9890c0cf72362960b7873887 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Wed, 6 Apr 2022 07:33:16 -0700 Subject: [PATCH 171/872] pass as ENV_VARS to run_docker Signed-off-by: Shaanjot Gill --- scripts/run_docker | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/run_docker b/scripts/run_docker index e1c99a0b9d..f16334ebc9 100755 --- a/scripts/run_docker +++ b/scripts/run_docker @@ -14,6 +14,9 @@ for PORT in $PORTS; do ARGS="${ARGS} -p $PORT" done +for ENV_VAR in $ENV_VARS; do + ARGS="${ARGS} -e $ENV_VAR" +done PTVSD_PORT="${PTVSD_PORT-5678}" for arg in "$@"; do From 6f97a4553a7e806be1231f603a4d0dc12fb82e35 Mon Sep 17 00:00:00 2001 From: DaevMithran Date: Wed, 6 Apr 2022 20:14:35 +0530 Subject: [PATCH 172/872] Update schema description Signed-off-by: DaevMithran --- aries_cloudagent/wallet/routes.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/wallet/routes.py b/aries_cloudagent/wallet/routes.py index 0332a408f8..b5d6d198cc 100644 --- a/aries_cloudagent/wallet/routes.py +++ b/aries_cloudagent/wallet/routes.py @@ -190,7 +190,10 @@ class DIDCreateSchema(OpenAPISchema): seed = fields.Str( required=False, - description="Optional seed to use for DID", + description=( + "Optional seed to use for DID, Must be" + "enabled in configuration before use." + ), example="000000000000000000000000Trustee1", ) From a09ce4b0ce181b7cbc139e08d3a979dd6e3e1319 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Wed, 6 Apr 2022 08:40:57 -0700 Subject: [PATCH 173/872] Fix order of operations connecting faber to endorser Signed-off-by: Ian Costanzo --- demo/runners/support/agent.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/demo/runners/support/agent.py b/demo/runners/support/agent.py index 2a8cf1ddb5..041c71d7d5 100644 --- a/demo/runners/support/agent.py +++ b/demo/runners/support/agent.py @@ -572,6 +572,12 @@ async def register_or_switch_wallet( new_wallet = await self.agency_admin_POST("/multitenancy/wallet", wallet_params) self.log("New wallet params:", new_wallet) self.managed_wallet_params = new_wallet + + # if endorser, endorse the wallet ledger operations + if endorser_agent: + if not await connect_wallet_to_endorser(self, endorser_agent): + raise Exception("Endorser setup FAILED :-(") + if public_did: if cred_type == CRED_FORMAT_INDY: # assign public did @@ -599,11 +605,6 @@ async def register_or_switch_wallet( log_msg("Mediation setup FAILED :-(") raise Exception("Mediation setup FAILED :-(") - # if endorser, endorse the wallet ledger operations - if endorser_agent: - if not await connect_wallet_to_endorser(self, endorser_agent): - raise Exception("Endorser setup FAILED :-(") - self.log(f"Created NEW wallet {target_wallet_name}") return True From a6f49f8ca65910ee2ea4d46163338098f270f255 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Wed, 6 Apr 2022 10:51:59 -0700 Subject: [PATCH 174/872] Update logic to public did of author in demo Signed-off-by: Ian Costanzo --- demo/runners/support/agent.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/demo/runners/support/agent.py b/demo/runners/support/agent.py index 041c71d7d5..64e17be6cf 100644 --- a/demo/runners/support/agent.py +++ b/demo/runners/support/agent.py @@ -587,7 +587,11 @@ async def register_or_switch_wallet( did=new_did["result"]["did"], verkey=new_did["result"]["verkey"], ) - await self.admin_POST("/wallet/did/public?did=" + self.did) + if self.endorser_role and self.endorser_role == "author": + if endorser_agent: + await self.admin_POST("/wallet/did/public?did=" + self.did) + else: + await self.admin_POST("/wallet/did/public?did=" + self.did) elif cred_type == CRED_FORMAT_JSON_LD: # create did of appropriate type data = {"method": DID_METHOD_KEY, "options": {"key_type": KEY_TYPE_BLS}} From 9ce96be741540ff259ad28c848b6b3308fca74e5 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Wed, 6 Apr 2022 11:59:07 -0700 Subject: [PATCH 175/872] update attach_decorator verify method Signed-off-by: Shaanjot Gill --- aries_cloudagent/messaging/decorators/attach_decorator.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/aries_cloudagent/messaging/decorators/attach_decorator.py b/aries_cloudagent/messaging/decorators/attach_decorator.py index 9f173e72ff..309ebc6a68 100644 --- a/aries_cloudagent/messaging/decorators/attach_decorator.py +++ b/aries_cloudagent/messaging/decorators/attach_decorator.py @@ -428,7 +428,7 @@ async def verify(self, wallet: BaseWallet, signer_verkey: str = None) -> bool: assert self.jws b64_payload = unpad(set_urlsafe_b64(self.base64, True)) - verkey_to_check = None + verkey_to_check = [] for sig in [self.jws] if self.signatures == 1 else self.jws.signatures: b64_protected = sig.protected b64_sig = sig.signature @@ -439,7 +439,7 @@ async def verify(self, wallet: BaseWallet, signer_verkey: str = None) -> bool: b_sig = b64_to_bytes(b64_sig, urlsafe=True) verkey = bytes_to_b58(b64_to_bytes(protected["jwk"]["x"], urlsafe=True)) encoded_pk = DIDKey.from_did(protected["jwk"]["kid"]).public_key_b58 - verkey_to_check = encoded_pk + verkey_to_check.append(encoded_pk) if not await wallet.verify_message( sign_input, b_sig, verkey, KeyType.ED25519 ): @@ -448,7 +448,7 @@ async def verify(self, wallet: BaseWallet, signer_verkey: str = None) -> bool: sign_input, b_sig, encoded_pk, KeyType.ED25519 ): return False - if signer_verkey and signer_verkey != verkey_to_check: + if signer_verkey and signer_verkey not in verkey_to_check: return False return True From c32d9079fc287c2cd15b7760e4f4d390e84f5f08 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Thu, 7 Apr 2022 09:44:54 -0400 Subject: [PATCH 176/872] fix: create task for profile finalizer Signed-off-by: Daniel Bluhm --- aries_cloudagent/askar/profile.py | 3 +- aries_cloudagent/config/argparse.py | 15 ++++-- aries_cloudagent/indy/sdk/profile.py | 3 +- aries_cloudagent/multitenant/cache.py | 40 +++++++++------ aries_cloudagent/multitenant/manager.py | 17 ++++--- .../multitenant/tests/test_cache.py | 49 ++++++++++--------- 6 files changed, 75 insertions(+), 52 deletions(-) diff --git a/aries_cloudagent/askar/profile.py b/aries_cloudagent/askar/profile.py index 6277a936df..a03c7bbd3e 100644 --- a/aries_cloudagent/askar/profile.py +++ b/aries_cloudagent/askar/profile.py @@ -166,7 +166,8 @@ def finalizer(self) -> Optional[finalize]: def _finalize(opened: Optional[AskarOpenStore]): if opened: - asyncio.get_event_loop().run_until_complete(opened.close()) + LOGGER.debug("Profile finalizer called; closing wallet") + asyncio.get_event_loop().create_task(opened.close()) return finalize(self, _finalize, self.opened) diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index a8b6ae5110..dfcba82dd1 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -1645,18 +1645,23 @@ def get_settings(self, args: Namespace): settings["multitenant.admin_enabled"] = True if args.multitenancy_config: - multitenancyConfig = json.loads(args.multitenancy_config) + multitenancy_config = json.loads(args.multitenancy_config) - if multitenancyConfig.get("wallet_type"): - settings["multitenant.wallet_type"] = multitenancyConfig.get( + if multitenancy_config.get("wallet_type"): + settings["multitenant.wallet_type"] = multitenancy_config.get( "wallet_type" ) - if multitenancyConfig.get("wallet_name"): - settings["multitenant.wallet_name"] = multitenancyConfig.get( + if multitenancy_config.get("wallet_name"): + settings["multitenant.wallet_name"] = multitenancy_config.get( "wallet_name" ) + if multitenancy_config.get("cache_size"): + settings["multitenant.cache_size"] = multitenancy_config.get( + "cache_size" + ) + return settings diff --git a/aries_cloudagent/indy/sdk/profile.py b/aries_cloudagent/indy/sdk/profile.py index b5fb130b3d..5034408938 100644 --- a/aries_cloudagent/indy/sdk/profile.py +++ b/aries_cloudagent/indy/sdk/profile.py @@ -125,7 +125,8 @@ def finalizer(self) -> Optional[finalize]: def _finalize(opened: Optional[IndyOpenWallet]): if opened: - asyncio.get_event_loop().run_until_complete(opened.close()) + LOGGER.debug("Profile finalizer called; closing wallet") + asyncio.get_event_loop().create_task(opened.close()) return finalize(self, _finalize, self.opened) diff --git a/aries_cloudagent/multitenant/cache.py b/aries_cloudagent/multitenant/cache.py index 8a00120ca8..7fb636843b 100644 --- a/aries_cloudagent/multitenant/cache.py +++ b/aries_cloudagent/multitenant/cache.py @@ -21,19 +21,21 @@ def __init__(self, capacity: int): profiles are closed. """ - self.profiles: OrderedDict[str, Profile] = OrderedDict() - self._open_profiles: WeakValueDictionary[str, Profile] = WeakValueDictionary() + LOGGER.debug(f"Profile cache initialized with capacity {capacity}") + + self._cache: OrderedDict[str, Profile] = OrderedDict() + self.profiles: WeakValueDictionary[str, Profile] = WeakValueDictionary() self.capacity = capacity def _cleanup(self): """Prune cache until size matches defined capacity.""" - if len(self.profiles) > self.capacity: + if len(self._cache) > self.capacity: LOGGER.debug( f"Profile limit of {self.capacity} reached." " Evicting least recently used profiles..." ) - while len(self.profiles) > self.capacity: - key, _ = self.profiles.popitem(last=False) + while len(self._cache) > self.capacity: + key, _ = self._cache.popitem(last=False) LOGGER.debug(f"Evicted profile with key {key}") def get(self, key: str) -> Optional[Profile]: @@ -50,19 +52,18 @@ def get(self, key: str) -> Optional[Profile]: Optional[Profile]: Profile if found in cache. """ - if key not in self._open_profiles: - return None - else: - value = self._open_profiles[key] - if key not in self.profiles: + value = self.profiles.get(key) + if value: + if key not in self._cache: LOGGER.debug( f"Rescuing profile {key} from eviction from cache; profile " "will be reinserted into cache" ) - self.profiles[key] = value - self.profiles.move_to_end(key) + self._cache[key] = value + self._cache.move_to_end(key) self._cleanup() - return value + + return value def has(self, key: str) -> bool: """Check whether there is a profile with associated key in the cache. @@ -86,11 +87,19 @@ def put(self, key: str, value: Profile) -> None: key (str): the key to set value (Profile): the profile to set """ + + # Close the profile when it falls out of scope value.finalizer() - self._open_profiles[key] = value + + # Keep track of currently opened profiles using weak references self.profiles[key] = value + + # Strong reference to profile to hold open until evicted LOGGER.debug(f"Setting profile with id {key} in profile cache") - self.profiles.move_to_end(key) + self._cache[key] = value + + # Refresh profile livliness + self._cache.move_to_end(key) self._cleanup() def remove(self, key: str): @@ -100,3 +109,4 @@ def remove(self, key: str): key (str): The key to remove from the cache. """ del self.profiles[key] + del self._cache[key] diff --git a/aries_cloudagent/multitenant/manager.py b/aries_cloudagent/multitenant/manager.py index 210d799a06..e7bf2d9447 100644 --- a/aries_cloudagent/multitenant/manager.py +++ b/aries_cloudagent/multitenant/manager.py @@ -1,16 +1,17 @@ """Manager for multitenancy.""" +import logging from typing import Iterable -from ..core.profile import ( - Profile, -) -from ..config.wallet import wallet_config + from ..config.injection_context import InjectionContext -from ..wallet.models.wallet_record import WalletRecord +from ..config.wallet import wallet_config +from ..core.profile import Profile from ..multitenant.base import BaseMultitenantManager - +from ..wallet.models.wallet_record import WalletRecord from .cache import ProfileCache +LOGGER = logging.getLogger(__name__) + class MultitenantManager(BaseMultitenantManager): """Class for handling multitenancy.""" @@ -22,7 +23,9 @@ def __init__(self, profile: Profile): profile: The profile for this manager """ super().__init__(profile) - self._profiles = ProfileCache(100) + self._profiles = ProfileCache( + profile.settings.get_int("multitenant.cache_size") or 100 + ) @property def open_profiles(self) -> Iterable[Profile]: diff --git a/aries_cloudagent/multitenant/tests/test_cache.py b/aries_cloudagent/multitenant/tests/test_cache.py index 451e465051..ce90488512 100644 --- a/aries_cloudagent/multitenant/tests/test_cache.py +++ b/aries_cloudagent/multitenant/tests/test_cache.py @@ -1,8 +1,19 @@ -from asynctest import mock as async_mock +from ...core.profile import Profile from ..cache import ProfileCache +class MockProfile(Profile): + def session(self, context = None): + ... + + def transaction(self, context = None): + ... + + def finalizer(self): + return None + + def test_get_not_in_cache(): cache = ProfileCache(1) @@ -12,7 +23,7 @@ def test_get_not_in_cache(): def test_put_get_in_cache(): cache = ProfileCache(1) - profile = async_mock.MagicMock() + profile = MockProfile() cache.put("1", profile) assert cache.get("1") is profile @@ -21,7 +32,7 @@ def test_put_get_in_cache(): def test_remove(): cache = ProfileCache(1) - profile = async_mock.MagicMock() + profile = MockProfile() cache.put("1", profile) assert cache.get("1") is profile @@ -34,7 +45,7 @@ def test_remove(): def test_has_true(): cache = ProfileCache(1) - profile = async_mock.MagicMock() + profile = MockProfile() assert cache.has("1") is False cache.put("1", profile) @@ -44,14 +55,11 @@ def test_has_true(): def test_cleanup(): cache = ProfileCache(1) - profile1 = async_mock.MagicMock() - profile2 = async_mock.MagicMock() - - cache.put("1", profile1) + cache.put("1", MockProfile()) assert len(cache.profiles) == 1 - cache.put("2", profile2) + cache.put("2", MockProfile()) assert len(cache.profiles) == 1 assert cache.get("1") == None @@ -60,23 +68,18 @@ def test_cleanup(): def test_cleanup_lru(): cache = ProfileCache(3) - profile1 = async_mock.MagicMock() - profile2 = async_mock.MagicMock() - profile3 = async_mock.MagicMock() - profile4 = async_mock.MagicMock() - - cache.put("1", profile1) - cache.put("2", profile2) - cache.put("3", profile3) + cache.put("1", MockProfile()) + cache.put("2", MockProfile()) + cache.put("3", MockProfile()) assert len(cache.profiles) == 3 cache.get("1") - cache.put("4", profile4) + cache.put("4", MockProfile()) - assert len(cache.profiles) == 3 - assert cache.get("1") == profile1 - assert cache.get("2") == None - assert cache.get("3") == profile3 - assert cache.get("4") == profile4 + assert len(cache._cache) == 3 + assert cache.get("1") + assert cache.get("2") is None + assert cache.get("3") + assert cache.get("4") From 352840921f89c69dd389a1feb63f94c4afcd2048 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Thu, 7 Apr 2022 09:47:42 -0400 Subject: [PATCH 177/872] style: black fixes Signed-off-by: Daniel Bluhm --- aries_cloudagent/multitenant/tests/test_cache.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/multitenant/tests/test_cache.py b/aries_cloudagent/multitenant/tests/test_cache.py index ce90488512..7350cdc1f0 100644 --- a/aries_cloudagent/multitenant/tests/test_cache.py +++ b/aries_cloudagent/multitenant/tests/test_cache.py @@ -4,10 +4,10 @@ class MockProfile(Profile): - def session(self, context = None): + def session(self, context=None): ... - def transaction(self, context = None): + def transaction(self, context=None): ... def finalizer(self): From 34d0913c236d912a3b7a287f317750579ba4330d Mon Sep 17 00:00:00 2001 From: Akiff Manji Date: Thu, 7 Apr 2022 07:28:03 -0700 Subject: [PATCH 178/872] Adds support to faber demo for returning json response in connectionless proof request Signed-off-by: Akiff Manji --- demo/runners/faber.py | 10 +++------- demo/runners/support/agent.py | 4 +++- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/demo/runners/faber.py b/demo/runners/faber.py index d7e7ce6ab5..99af10a9f6 100644 --- a/demo/runners/faber.py +++ b/demo/runners/faber.py @@ -608,14 +608,10 @@ async def main(args): ) pres_req_id = proof_request["presentation_exchange_id"] url = ( - "http://" - + os.getenv("DOCKERHOST").replace( - "{PORT}", str(faber_agent.agent.admin_port + 1) - ) - + "/webhooks/pres_req/" - + pres_req_id - + "/" + os.getenv("WEBHOOK_TARGET") + or ("http://" + os.getenv("DOCKERHOST").replace("{PORT}", str(faber_agent.agent.admin_port + 1)) + "/webhooks") ) + + f"/pres_req/{pres_req_id}/" log_msg(f"Proof request url: {url}") qr = QRCode(border=1) qr.add_data(url) diff --git a/demo/runners/support/agent.py b/demo/runners/support/agent.py index 2a8cf1ddb5..5300cc9ffd 100644 --- a/demo/runners/support/agent.py +++ b/demo/runners/support/agent.py @@ -715,7 +715,7 @@ async def listen_webhooks(self, webhook_port): if RUN_MODE == "pwd": self.webhook_url = f"http://localhost:{str(webhook_port)}/webhooks" else: - self.webhook_url = ( + self.webhook_url = self.external_webhook_target or ( f"http://{self.external_host}:{str(webhook_port)}/webhooks" ) app = web.Application() @@ -764,6 +764,8 @@ async def _send_connectionless_proof_req(self, request: ClientRequest): return web.Response(status=404) proof_reg_txn = proof_exch["presentation_request_dict"] proof_reg_txn["~service"] = await self.service_decorator() + if request.headers['Accept'] == 'application/json': + return web.json_response(proof_reg_txn) objJsonStr = json.dumps(proof_reg_txn) objJsonB64 = base64.b64encode(objJsonStr.encode("ascii")) service_url = self.webhook_url From 24bfbab095d52d420c7f32a872829a713867785a Mon Sep 17 00:00:00 2001 From: Akiff Manji Date: Thu, 7 Apr 2022 07:47:15 -0700 Subject: [PATCH 179/872] Format code Signed-off-by: Akiff Manji --- demo/runners/faber.py | 11 +++++++---- demo/runners/support/agent.py | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/demo/runners/faber.py b/demo/runners/faber.py index 99af10a9f6..ba46d9eec0 100644 --- a/demo/runners/faber.py +++ b/demo/runners/faber.py @@ -607,11 +607,14 @@ async def main(args): "/present-proof/create-request", proof_request_web_request ) pres_req_id = proof_request["presentation_exchange_id"] - url = ( - os.getenv("WEBHOOK_TARGET") - or ("http://" + os.getenv("DOCKERHOST").replace("{PORT}", str(faber_agent.agent.admin_port + 1)) + "/webhooks") + url = os.getenv("WEBHOOK_TARGET") or ( + "http://" + + os.getenv("DOCKERHOST").replace( + "{PORT}", str(faber_agent.agent.admin_port + 1) + ) + + "/webhooks" ) - + f"/pres_req/{pres_req_id}/" + +f"/pres_req/{pres_req_id}/" log_msg(f"Proof request url: {url}") qr = QRCode(border=1) qr.add_data(url) diff --git a/demo/runners/support/agent.py b/demo/runners/support/agent.py index cd2af724c8..abf3e7fd6e 100644 --- a/demo/runners/support/agent.py +++ b/demo/runners/support/agent.py @@ -769,7 +769,7 @@ async def _send_connectionless_proof_req(self, request: ClientRequest): return web.Response(status=404) proof_reg_txn = proof_exch["presentation_request_dict"] proof_reg_txn["~service"] = await self.service_decorator() - if request.headers['Accept'] == 'application/json': + if request.headers["Accept"] == "application/json": return web.json_response(proof_reg_txn) objJsonStr = json.dumps(proof_reg_txn) objJsonB64 = base64.b64encode(objJsonStr.encode("ascii")) From d725a139e8794ae38a4619ee52b58310a1119df1 Mon Sep 17 00:00:00 2001 From: Adam Burdett Date: Thu, 7 Apr 2022 09:02:07 -0600 Subject: [PATCH 180/872] formatting Signed-off-by: Adam Burdett --- aries_cloudagent/multitenant/admin/tests/test_routes.py | 1 - 1 file changed, 1 deletion(-) diff --git a/aries_cloudagent/multitenant/admin/tests/test_routes.py b/aries_cloudagent/multitenant/admin/tests/test_routes.py index cf447e5cbb..faae8d6d3b 100644 --- a/aries_cloudagent/multitenant/admin/tests/test_routes.py +++ b/aries_cloudagent/multitenant/admin/tests/test_routes.py @@ -234,7 +234,6 @@ async def test_wallet_create_optional_default_fields(self): WalletRecord.MODE_MANAGED, ) - async def test_wallet_create_raw_key_derivation(self): body = { "wallet_name": "test", From 9d862ada1edc8dc899b8931354d37c754ad4b0fe Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Thu, 7 Apr 2022 11:27:36 -0700 Subject: [PATCH 181/872] Duplicate checking for schema and cred def Signed-off-by: Ian Costanzo --- .../messaging/credential_definitions/routes.py | 17 +++++++++++++++++ aries_cloudagent/messaging/schemas/routes.py | 12 ++++++++++++ 2 files changed, 29 insertions(+) diff --git a/aries_cloudagent/messaging/credential_definitions/routes.py b/aries_cloudagent/messaging/credential_definitions/routes.py index cd78c623f1..7205ed5ef9 100644 --- a/aries_cloudagent/messaging/credential_definitions/routes.py +++ b/aries_cloudagent/messaging/credential_definitions/routes.py @@ -186,6 +186,23 @@ async def credential_definitions_send_credential_definition(request: web.BaseReq tag = body.get("tag") rev_reg_size = body.get("revocation_registry_size") + tag_query = {"schema_id": schema_id} + async with profile.session() as session: + storage = session.inject(BaseStorage) + found = await storage.find_all_records( + type_filter=CRED_DEF_SENT_RECORD_TYPE, + tag_query=tag_query, + ) + if 0 < len(found): + # need to check te 'tag' value + for record in found: + cred_def_id = record.value + cred_def_id_parts = cred_def_id.split(":") + if tag == cred_def_id_parts[4]: + raise web.HTTPBadRequest( + reason=f"Cred def for {schema_id} {tag} already exists" + ) + # check if we need to endorse if is_author_role(context.profile): # authors cannot write to the ledger diff --git a/aries_cloudagent/messaging/schemas/routes.py b/aries_cloudagent/messaging/schemas/routes.py index c494adda5b..aa3f48b0fa 100644 --- a/aries_cloudagent/messaging/schemas/routes.py +++ b/aries_cloudagent/messaging/schemas/routes.py @@ -184,6 +184,18 @@ async def schemas_send_schema(request: web.BaseRequest): schema_version = body.get("schema_version") attributes = body.get("attributes") + tag_query = {"schema_name": schema_name, "schema_version": schema_version} + async with profile.session() as session: + storage = session.inject(BaseStorage) + found = await storage.find_all_records( + type_filter=SCHEMA_SENT_RECORD_TYPE, + tag_query=tag_query, + ) + if 0 < len(found): + raise web.HTTPBadRequest( + reason=f"Schema {schema_name} {schema_version} already exists" + ) + # check if we need to endorse if is_author_role(context.profile): # authors cannot write to the ledger From 499da8cb1b9afa3645d96cd4d24eeb5a3666918f Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Thu, 7 Apr 2022 13:06:59 -0700 Subject: [PATCH 182/872] Prep for adding the 0.7.4-rc0 tag Signed-off-by: Stephen Curran --- CHANGELOG.md | 107 ++++++++++++++++++++++++++++++++++++ PUBLISHING.md | 18 +++--- aries_cloudagent/version.py | 2 +- open-api/openapi.json | 2 +- 4 files changed, 118 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04faab87df..fb2b01a765 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,110 @@ +# 0.7.4-RC0 + +This release is largely a release for internal fixes with a few minor +enhancements. There has been a lot of groups exercising ACA-Py and the updates +made in this release are a reflection of those updates. We have PRs that have +been contributed by 16 different people, which is very likely a record for a +single ACA-Py release. + +The largest enhancement is in the area of the Hyperledger Indy endorser, +enabling an instance of ACA-Py to act as an Endorser for Indy authors needed +endorsing to write objects to an Indy ledger. We're hoping to see an +"aries-endorser-service" come from that work, an Endorser to be easily operated +by an organization, ideally with a controller starter kit to allow an approvals +business flow. + +A lot of work has been put in for this release related to performance and load +testing, with updates being made to the key "shared component" ACA-Py +dependencies ([Aries Askar](https://github.com/bcgov/aries-askar), [Indy +VDR](https://github.comyperledger/indy-vdr)) and [Indy Shared RS (including +CredX)](https://github.com/hyperledger/indy-shared-rs). We now recommend using +those components (by using Askar for the wallet type in the startup parameters) +for new ACA-Py deployments. A wallet migration tool from indy-sdk storage to +Askar storage is still needed before migrating existing deployment to Askar. A +big thanks to those creating/reporting on stress test scenarios, and especially +the team at LISSI for creating the +[aries-cloudagent-loadgenerator](https://github.com/lissi-id/aries-cloudagent-loadgenerator) +to make load testing so easy! And of course to the core ACA-Py team for +addressing the findings. + +Several new ways to control ACA-Py configurations were added, including new +startup parameters, Admin API parameters to control instances of protocols, and +additional web hook notifications. + +A number of fixes were made to the Credential Exchange protocols, both for V1 +and V2, and for both AnonCreds and W3C format VCs. Nothing new and no changes in +the APIs. + +As well there were a number of internal fixes, dependency updates, documentation +and demo changes, developer tools and release management updates. All the usual +stuff needed for a growing codebase. + +## April 7, 2022 + +- Hyperledger Indy Endorser related updates: + - Fix order of operations connecting faber to endorser [\#1716](https://github.com/hyperledger/aries-cloudagent-python/pull/1716) ([ianco](https://github.com/ianco)) + - Endorser support for updating DID endpoints on ledger [\#1696](https://github.com/hyperledger/aries-cloudagent-python/pull/1696) ([frostyfrog](https://github.com/frostyfrog)) + - Add "sent" key to both Schema and Cred Defs when using Endorsers [\#1663](https://github.com/hyperledger/aries-cloudagent-python/pull/1663) ([frostyfrog](https://github.com/frostyfrog)) + - Add cred\_def\_id to metadata when using an Endorser [\#1655](https://github.com/hyperledger/aries-cloudagent-python/pull/1655) ([frostyfrog](https://github.com/frostyfrog)) + - Update Endorser documentation [\#1646](https://github.com/hyperledger/aries-cloudagent-python/pull/1646) ([chumbert](https://github.com/chumbert)) + - Auto-promote author did to public after endorsing [\#1607](https://github.com/hyperledger/aries-cloudagent-python/pull/1607) ([ianco](https://github.com/ianco)) + - DID updates for endorser [\#1601](https://github.com/hyperledger/aries-cloudagent-python/pull/1601) ([ianco](https://github.com/ianco)) + - Qualify did exch connection lookup by role [\#1670](https://github.com/hyperledger/aries-cloudagent-python/pull/1670) ([ianco](https://github.com/ianco)) + +- Additions to the startup parameters, Admin API and Web Hooks + - feat: accept taa using startup parameter --accept-taa [\#1643](https://github.com/hyperledger/aries-cloudagent-python/pull/1643) ([TimoGlastra](https://github.com/TimoGlastra)) + - Add auto\_verify flag in present-proof protocol [\#1702](https://github.com/hyperledger/aries-cloudagent-python/pull/1702) ([DaevMithran](https://github.com/DaevMithran)) + - feat: query connections by their\_public\_did [\#1637](https://github.com/hyperledger/aries-cloudagent-python/pull/1637) ([TimoGlastra](https://github.com/TimoGlastra)) + - feat: enable webhook events for mediation records [\#1614](https://github.com/hyperledger/aries-cloudagent-python/pull/1614) ([TimoGlastra](https://github.com/TimoGlastra)) + - Feature/undelivered events [\#1694](https://github.com/hyperledger/aries-cloudagent-python/pull/1694) ([mepeltier](https://github.com/mepeltier)) + - Allow use of SEED when creating local wallet DID Issue-1682 Issue-1682 [\#1705](https://github.com/hyperledger/aries-cloudagent-python/pull/1705) ([DaevMithran](https://github.com/DaevMithran)) + +- Issue Credential, Revocation, Present Proof updates/fixes + - Fix: DIF proof proposal when creating bound presentation request \[Issue\#1687\] [\#1690](https://github.com/hyperledger/aries-cloudagent-python/pull/1690) ([shaangill025](https://github.com/shaangill025)) + - Fix DIF PresExch and OOB request\_attach delete unused connection [\#1676](https://github.com/hyperledger/aries-cloudagent-python/pull/1676) ([shaangill025](https://github.com/shaangill025)) + - Fix DIFPresFormatHandler returning invalid V20PresExRecord on presentation verification [\#1645](https://github.com/hyperledger/aries-cloudagent-python/pull/1645) ([rmnre](https://github.com/rmnre)) + - Update aries-askar patch version to at least 0.2.4 as 0.2.3 does not include backward compatibility [\#1603](https://github.com/hyperledger/aries-cloudagent-python/pull/1603) ([acuderman](https://github.com/acuderman)) + - Fixes for credential details in issue-credential webhook responses [\#1668](https://github.com/hyperledger/aries-cloudagent-python/pull/1668) ([andrewwhitehead](https://github.com/andrewwhitehead)) + - Fix: present-proof v2 send-proposal [issue\#1474](https://github.com/hyperledger/aries-cloudagent-python/issues/1474) [\#1667](https://github.com/hyperledger/aries-cloudagent-python/pull/1667) ([shaangill025](https://github.com/shaangill025)) + - Fixes Issue 3b from [\#1597](https://github.com/hyperledger/aries-cloudagent-python/issues/1597): V2 Credential exchange ignores the auto-respond-credential-request + - fix: always notify if revocation notification record exists [\#1665](https://github.com/hyperledger/aries-cloudagent-python/pull/1665) ([TimoGlastra](https://github.com/TimoGlastra)) + - Revert change to send\_credential\_ack return value [\#1660](https://github.com/hyperledger/aries-cloudagent-python/pull/1660) ([andrewwhitehead](https://github.com/andrewwhitehead)) + - Fix usage of send\_credential\_ack [\#1653](https://github.com/hyperledger/aries-cloudagent-python/pull/1653) ([andrewwhitehead](https://github.com/andrewwhitehead)) + - Replace blank credential/presentation exchange states with abandoned state [\#1605](https://github.com/hyperledger/aries-cloudagent-python/pull/1605) ([andrewwhitehead](https://github.com/andrewwhitehead)) + - Fixes Issue 4 from [\#1597](https://github.com/hyperledger/aries-cloudagent-python/issues/1597): Wallet type askar has issues when receiving V1 credentials + - Fix for AnonCreds non-revoc proof with no timestamp [\#1628](https://github.com/hyperledger/aries-cloudagent-python/pull/1628) ([ianco](https://github.com/ianco)) + - Fixes and cleanups for issue-credential 1.0 [\#1619](https://github.com/hyperledger/aries-cloudagent-python/pull/1619) ([andrewwhitehead](https://github.com/andrewwhitehead)) + - Fixes for v7.3.0 - Issue [\#1597](https://github.com/hyperledger/aries-cloudagent-python/issues/1597) [\#1711](https://github.com/hyperledger/aries-cloudagent-python/pull/1711) ([shaangill025](https://github.com/shaangill025)) + - Fixes Issue 1 from [\#1597](https://github.com/hyperledger/aries-cloudagent-python/issues/1597): Tails file upload fails when a credDef is created and multi ledger support is enabled + +- Dependencies and internal code updates/fixes + - Fix: update IndyLedgerRequestsExecutor logic - multitenancy and basic base wallet type [\#1700](https://github.com/hyperledger/aries-cloudagent-python/pull/1700) ([shaangill025](https://github.com/shaangill025)) + - Move database operations inside the session context [\#1633](https://github.com/hyperledger/aries-cloudagent-python/pull/1633) ([acuderman](https://github.com/acuderman)) + - Upgrade ConfigArgParse to version 1.5.3 [\#1627](https://github.com/hyperledger/aries-cloudagent-python/pull/1627) ([WadeBarnes](https://github.com/WadeBarnes)) + - Update aiohttp dependency [\#1606](https://github.com/hyperledger/aries-cloudagent-python/pull/1606) ([acuderman](https://github.com/acuderman)) + - did-exchange implicit request pthid update & invitation key verification [\#1599](https://github.com/hyperledger/aries-cloudagent-python/pull/1599) ([shaangill025](https://github.com/shaangill025)) + - Fix auto connection response not being properly mediated [\#1638](https://github.com/hyperledger/aries-cloudagent-python/pull/1638) ([dbluhm](https://github.com/dbluhm)) + - platform target in run tests. [\#1697](https://github.com/hyperledger/aries-cloudagent-python/pull/1697) ([burdettadam](https://github.com/burdettadam)) + - Add an integration test for mixed proof with a revocable cred and a n… [\#1672](https://github.com/hyperledger/aries-cloudagent-python/pull/1672) ([ianco](https://github.com/ianco)) + +- Documentation and Demo Updates + - Multitenancy Docs Update [\#1706](https://github.com/hyperledger/aries-cloudagent-python/pull/1706) ([MonolithicMonk](https://github.com/MonolithicMonk)) + - [\#1674](https://github.com/hyperledger/aries-cloudagent-python/issue/1674) Add basic DOCKER\_ENV logging for run\_demo [\#1675](https://github.com/hyperledger/aries-cloudagent-python/pull/1675) ([tdiesler](https://github.com/tdiesler)) + - Performance demo updates [\#1647](https://github.com/hyperledger/aries-cloudagent-python/pull/1647) ([ianco](https://github.com/ianco)) + - docs: supported features attribution [\#1654](https://github.com/hyperledger/aries-cloudagent-python/pull/1654) ([TimoGlastra](https://github.com/TimoGlastra)) + +- Code management and contributor/developer support updates + - Pin markupsafe at version 2.0.1 [\#1642](https://github.com/hyperledger/aries-cloudagent-python/pull/1642) ([andrewwhitehead](https://github.com/andrewwhitehead)) + - style: format with stable black release [\#1615](https://github.com/hyperledger/aries-cloudagent-python/pull/1615) ([TimoGlastra](https://github.com/TimoGlastra)) + - Remove references to play with von [\#1688](https://github.com/hyperledger/aries-cloudagent-python/pull/1688) ([ianco](https://github.com/ianco)) + - Add pre-commit as optional developer tool [\#1671](https://github.com/hyperledger/aries-cloudagent-python/pull/1671) ([dbluhm](https://github.com/dbluhm)) + - run\_docker start - pass environment variables [\#1715](https://github.com/hyperledger/aries-cloudagent-python/pull/1715) ([shaangill025](https://github.com/shaangill025)) + +- Release management-related updates + - Added missed new module -- upgrade -- to the RTD generated docs [\#1593](https://github.com/hyperledger/aries-cloudagent-python/pull/1593) ([swcurran](https://github.com/swcurran)) + - Doh....update the date in the Changelog for 0.7.3 [\#1592](https://github.com/hyperledger/aries-cloudagent-python/pull/1592) ([swcurran](https://github.com/swcurran)) + + # 0.7.3 ## January 10, 2022 diff --git a/PUBLISHING.md b/PUBLISHING.md index 8846d840aa..625a7f5673 100644 --- a/PUBLISHING.md +++ b/PUBLISHING.md @@ -10,22 +10,22 @@ Once ready to do a release, create a local branch that includes the following up 2. Include details of the merged PRs included in this release. General process to follow: -- Gather the set of PRs since the last release and put them into a list. - - An example query to use to get the list of PRs is: [https://github.com/hyperledger/aries-cloudagent-python/pulls?q=is%3Apr+is%3Amerged+sort%3Aupdated+merged%3A%3E2021-11-15](https://github.com/hyperledger/aries-cloudagent-python/pulls?q=is%3Apr+is%3Amerged+sort%3Aupdated+merged%3A%3E2021-11-15), where the date at the end is the date of the previous release. - - Organize the list into suitable categories, update (if necessary) the PR description and add notes to clarify the changes. - - Add a link to each PR on the PR number. - - A regular expression you can use in VS Code to add the links to the list (assuming each line ends with the PR number) is `#([0-9]*)` (find) and `[#$1](https://github.com/hyperledger/aries-cloudagent-python/pull/$1)` (replace). Use regular expressions in the search, highlight the list and choose "Find in Selection" before replacing. - - Add a narrative about the release above the PR that highlights what has gone into the release. +- Gather the set of PRs since the last release and put them into a list. A good tool to use for this is the [github-changelog-generator](https://github.com/github-changelog-generator/github-changelog-generator). Steps: + - Create a read only GitHub token for your account on this page: [https://github.com/settings/tokens](https://github.com/settings/tokens/new?description=GitHub%20Changelog%20Generator%20token) with a scope of `repo` / `public_repo`. + - Use a command like the following, adjusting the tag parameters as appropriate. `docker run -it --rm -v "$(pwd)":/usr/local/src/your-app githubchangeloggenerator/github-changelog-generator --user hyperledger --project aries-cloudagent-python --output 0.7.4-rc0.md --since-tag 0.7.3 --future-release 0.7.4-rc0 --release-branch main --token ` + - In the generated file, use only the PR list -- we don't include the list of closed issues in the Change Log. +- Organize the list into suitable categories, update (if necessary) the PR description and add notes to clarify the changes. See previous release entries to understand the style -- a format should help developers. +- Add a narrative about the release above the PR that highlights what has gone into the release. 3. Update the ReadTheDocs in the `/docs` folder by following the instructions in the `docs/README.md` file. That will likely add a number of new and modified files to the PR. Eliminate all of the errors in the generation process, either by mocking external dependencies or by fixing ACA-Py code. If necessary, create an issue with the errors and assign it to the appropriate developer. Experience has demonstrated to use that documentation generation errors should be fixed in the code. 4. Update the version number listed in [aries_cloudagent/version.py](aries_cloudagent/version.py) and, prefixed with a "v" in [open-api/openapi.json](open-api/openapi.json) (e.g. "0.7.2" in the version.py file and "v0.7.2" in the openapi.json file). The incremented version number should adhere to the [Semantic Versioning Specification](https://semver.org/#semantic-versioning-specification-semver) based on the changes since the last published release. For Release Candidates, the form of the tag is "0.7.2-rc0". -5. An extra search of the repo for the existing tag is recommended to see if there are any other instances of the tag in the repo. If any are found to be required, finding a way to not need them is best, but if they are needed, please update this document to note where the tag can be found. +5. An extra search of the repo for the existing tag is recommended to see if there are any other instances of the tag in the repo. If any are found to be required (other than in CHANGELOG.md and the examples in this file, of course), finding a way to not need them is best, but if they are needed, please update this document to note where the tag can be found. -6. Double check all of these steps above, and then create a PR from the branch. If there are still further changes to be merged, mark the PR as "Draft", repeat **ALL** of the steps again, and then mark this PR as ready. +6. Double check all of these steps above, and then create a PR from the branch. If there are still further changes to be merged, mark the PR as "Draft", repeat **ALL** of the steps again, and then mark this PR as ready and then wait until it is merged. -7. Create a new GitHub tag representing the version. The tag name and title of the release should be the same as the version in [aries_cloudagent/version.py](aries_cloudagent/version.py). Use the "Generate Release Notes" capability to get a sequential listing of the PRs in the release, to complement the manually created Changelog. Verify on PyPi that the version is published. +7. Immediately after it is merged, create a new GitHub tag representing the version. The tag name and title of the release should be the same as the version in [aries_cloudagent/version.py](aries_cloudagent/version.py). Use the "Generate Release Notes" capability to get a sequential listing of the PRs in the release, to complement the manually curated Changelog. Verify on PyPi that the version is published. 8. Publish a new docker container on Docker Hub ([bcgovimages/aries-cloudagent](https://hub.docker.com/r/bcgovimages/aries-cloudagent/)) by following the README.md instructions to create a PR for the release in the repository [https://github.com/bcgov/aries-cloudagent-container](https://github.com/bcgov/aries-cloudagent-container). Appropriate permissions are required to publish the image. diff --git a/aries_cloudagent/version.py b/aries_cloudagent/version.py index 653ad55847..273fd7a0e6 100644 --- a/aries_cloudagent/version.py +++ b/aries_cloudagent/version.py @@ -1,4 +1,4 @@ """Library version information.""" -__version__ = "0.7.3" +__version__ = "0.7.4-rc0" RECORD_TYPE_ACAPY_VERSION = "acapy_version" diff --git a/open-api/openapi.json b/open-api/openapi.json index 999ab9c622..3f56197218 100644 --- a/open-api/openapi.json +++ b/open-api/openapi.json @@ -1,7 +1,7 @@ { "swagger" : "2.0", "info" : { - "version" : "v0.7.3", + "version" : "v0.7.4-rc0", "title" : "Aries Cloud Agent" }, "tags" : [ { From e9ba13d4ad3ce034ecf8312968960611e3adc822 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Thu, 7 Apr 2022 13:39:39 -0700 Subject: [PATCH 183/872] Opps typo Signed-off-by: Ian Costanzo --- aries_cloudagent/messaging/credential_definitions/routes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aries_cloudagent/messaging/credential_definitions/routes.py b/aries_cloudagent/messaging/credential_definitions/routes.py index 7205ed5ef9..82ffa09fab 100644 --- a/aries_cloudagent/messaging/credential_definitions/routes.py +++ b/aries_cloudagent/messaging/credential_definitions/routes.py @@ -194,7 +194,7 @@ async def credential_definitions_send_credential_definition(request: web.BaseReq tag_query=tag_query, ) if 0 < len(found): - # need to check te 'tag' value + # need to check the 'tag' value for record in found: cred_def_id = record.value cred_def_id_parts = cred_def_id.split(":") From 2cde6005c52c89d870dabd4a029ce23f0d383b04 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sun, 10 Apr 2022 16:39:38 +0200 Subject: [PATCH 184/872] feat: add token revocation Signed-off-by: Timo Glastra --- aries_cloudagent/multitenant/admin/routes.py | 4 +- .../multitenant/admin/tests/test_routes.py | 8 +- aries_cloudagent/multitenant/base.py | 15 ++- .../multitenant/tests/test_base.py | 114 ++++++++++++++++-- .../wallet/models/wallet_record.py | 5 +- 5 files changed, 130 insertions(+), 16 deletions(-) diff --git a/aries_cloudagent/multitenant/admin/routes.py b/aries_cloudagent/multitenant/admin/routes.py index fd0a8a6b3e..5df6f84cfd 100644 --- a/aries_cloudagent/multitenant/admin/routes.py +++ b/aries_cloudagent/multitenant/admin/routes.py @@ -315,7 +315,7 @@ async def wallet_create(request: web.BaseRequest): settings, key_management_mode ) - token = multitenant_mgr.create_auth_token(wallet_record, wallet_key) + token = await multitenant_mgr.create_auth_token(wallet_record, wallet_key) except BaseError as err: raise web.HTTPBadRequest(reason=err.roll_up) from err @@ -413,7 +413,7 @@ async def wallet_create_token(request: web.BaseRequest): " the wallet key to be provided" ) - token = multitenant_mgr.create_auth_token(wallet_record, wallet_key) + token = await multitenant_mgr.create_auth_token(wallet_record, wallet_key) except StorageNotFoundError as err: raise web.HTTPNotFound(reason=err.roll_up) from err except WalletKeyMissingError as err: diff --git a/aries_cloudagent/multitenant/admin/tests/test_routes.py b/aries_cloudagent/multitenant/admin/tests/test_routes.py index c1f50f49c3..1ffa316e2b 100644 --- a/aries_cloudagent/multitenant/admin/tests/test_routes.py +++ b/aries_cloudagent/multitenant/admin/tests/test_routes.py @@ -164,7 +164,7 @@ async def test_wallet_create(self): return_value=wallet_mock ) - self.mock_multitenant_mgr.create_auth_token = async_mock.Mock( + self.mock_multitenant_mgr.create_auth_token = async_mock.CoroutineMock( return_value="test_token" ) print(self.request["context"]) @@ -215,7 +215,7 @@ async def test_wallet_create_optional_default_fields(self): with async_mock.patch.object(test_module.web, "json_response") as mock_response: self.mock_multitenant_mgr.create_wallet = async_mock.CoroutineMock() - self.mock_multitenant_mgr.create_auth_token = async_mock.Mock() + self.mock_multitenant_mgr.create_auth_token = async_mock.CoroutineMock() await test_module.wallet_create(self.request) self.mock_multitenant_mgr.create_wallet.assert_called_once_with( @@ -440,7 +440,7 @@ async def test_wallet_create_token_managed(self): ) as mock_response: mock_wallet_record_retrieve_by_id.return_value = mock_wallet_record - self.mock_multitenant_mgr.create_auth_token = async_mock.Mock( + self.mock_multitenant_mgr.create_auth_token = async_mock.CoroutineMock( return_value="test_token" ) @@ -468,7 +468,7 @@ async def test_wallet_create_token_unmanaged(self): ) as mock_response: mock_wallet_record_retrieve_by_id.return_value = mock_wallet_record - self.mock_multitenant_mgr.create_auth_token = async_mock.Mock( + self.mock_multitenant_mgr.create_auth_token = async_mock.CoroutineMock( return_value="test_token" ) diff --git a/aries_cloudagent/multitenant/base.py b/aries_cloudagent/multitenant/base.py index 5bdd9a9787..a7abb4c31d 100644 --- a/aries_cloudagent/multitenant/base.py +++ b/aries_cloudagent/multitenant/base.py @@ -1,5 +1,6 @@ """Manager for multitenancy.""" +from datetime import datetime import logging from abc import abstractmethod @@ -333,7 +334,7 @@ async def add_key( keylist_updates, connection_id=mediation_record.connection_id ) - def create_auth_token( + async def create_auth_token( self, wallet_record: WalletRecord, wallet_key: str = None ) -> str: """Create JWT auth token for specified wallet record. @@ -351,8 +352,9 @@ def create_auth_token( str: JWT auth token """ + iat = int(round(datetime.utcnow().timestamp())) - jwt_payload = {"wallet_id": wallet_record.wallet_id} + jwt_payload = {"wallet_id": wallet_record.wallet_id, "iat": iat} jwt_secret = self._profile.settings.get("multitenant.jwt_secret") if wallet_record.requires_external_key: @@ -363,6 +365,11 @@ def create_auth_token( token = jwt.encode(jwt_payload, jwt_secret, algorithm="HS256").decode() + # Store iat for verification later on + wallet_record.jwt_iat = iat + async with self._profile.session() as session: + await wallet_record.save(session) + return token async def get_profile_for_token( @@ -389,6 +396,7 @@ async def get_profile_for_token( wallet_id = token_body.get("wallet_id") wallet_key = token_body.get("wallet_key") + iat = token_body.get("iat") async with self._profile.session() as session: wallet = await WalletRecord.retrieve_by_id(session, wallet_id) @@ -399,6 +407,9 @@ async def get_profile_for_token( extra_settings["wallet.key"] = wallet_key + if wallet.jwt_iat and wallet.jwt_iat != iat: + raise MultitenantManagerError("Token not valid") + profile = await self.get_wallet_profile(context, wallet, extra_settings) return profile diff --git a/aries_cloudagent/multitenant/tests/test_base.py b/aries_cloudagent/multitenant/tests/test_base.py index 4f6cb8dabf..5d858dd6f9 100644 --- a/aries_cloudagent/multitenant/tests/test_base.py +++ b/aries_cloudagent/multitenant/tests/test_base.py @@ -3,6 +3,8 @@ import jwt +from datetime import datetime + from ...core.in_memory import InMemoryProfile from ...config.base import InjectionError from ...messaging.responder import BaseResponder @@ -21,6 +23,7 @@ from ...wallet.did_method import DIDMethod from ..base import BaseMultitenantManager, MultitenantManagerError from ..error import WalletKeyMissingError +from .. import base as test_module class TestBaseMultitenantManager(AsyncTestCase): @@ -414,35 +417,55 @@ async def test_create_auth_token_fails_no_wallet_key_but_required(self): async def test_create_auth_token_managed(self): self.profile.settings["multitenant.jwt_secret"] = "very_secret_jwt" - wallet_record = WalletRecord( + wallet_record = async_mock.MagicMock( wallet_id="test_wallet", key_management_mode=WalletRecord.MODE_MANAGED, + requires_external_key=False, settings={}, + save=async_mock.CoroutineMock(), ) + utc_now = datetime(2020, 1, 1, 0, 0, 0) + iat = int(round(utc_now.timestamp())) + expected_token = jwt.encode( - {"wallet_id": wallet_record.wallet_id}, "very_secret_jwt" + {"wallet_id": wallet_record.wallet_id, "iat": iat}, "very_secret_jwt" ).decode() - token = self.manager.create_auth_token(wallet_record) + with async_mock.patch.object(test_module, "datetime") as mock_datetime: + mock_datetime.utcnow.return_value = utc_now + token = await self.manager.create_auth_token(wallet_record) + assert wallet_record.jwt_iat == iat assert expected_token == token async def test_create_auth_token_unmanaged(self): self.profile.settings["multitenant.jwt_secret"] = "very_secret_jwt" - wallet_record = WalletRecord( + wallet_record = async_mock.MagicMock( wallet_id="test_wallet", key_management_mode=WalletRecord.MODE_UNMANAGED, + requires_external_key=True, settings={"wallet.type": "indy"}, + save=async_mock.CoroutineMock(), ) + utc_now = datetime(2020, 1, 1, 0, 0, 0) + iat = int(round(utc_now.timestamp())) + expected_token = jwt.encode( - {"wallet_id": wallet_record.wallet_id, "wallet_key": "test_key"}, + { + "wallet_id": wallet_record.wallet_id, + "iat": iat, + "wallet_key": "test_key", + }, "very_secret_jwt", ).decode() - token = self.manager.create_auth_token(wallet_record, "test_key") + with async_mock.patch.object(test_module, "datetime") as mock_datetime: + mock_datetime.utcnow.return_value = utc_now + token = await self.manager.create_auth_token(wallet_record, "test_key") + assert wallet_record.jwt_iat == iat assert expected_token == token async def test_get_profile_for_token_invalid_token_raises(self): @@ -468,7 +491,7 @@ async def test_get_profile_for_token_wallet_key_missing_raises(self): with self.assertRaises(WalletKeyMissingError): await self.manager.get_profile_for_token(self.profile.context, token) - async def test_get_profile_for_token_managed_wallet(self): + async def test_get_profile_for_token_managed_wallet_no_iat(self): self.profile.settings["multitenant.jwt_secret"] = "very_secret_jwt" wallet_record = WalletRecord( key_management_mode=WalletRecord.MODE_MANAGED, @@ -500,6 +523,83 @@ async def test_get_profile_for_token_managed_wallet(self): assert profile == mock_profile + async def test_get_profile_for_token_managed_wallet_iat(self): + iat = 100 + + self.profile.settings["multitenant.jwt_secret"] = "very_secret_jwt" + wallet_record = WalletRecord( + key_management_mode=WalletRecord.MODE_MANAGED, + settings={"wallet.type": "indy", "wallet.key": "wallet_key"}, + jwt_iat=iat, + ) + + session = await self.profile.session() + await wallet_record.save(session) + + token = jwt.encode( + {"wallet_id": wallet_record.wallet_id, "iat": iat}, + "very_secret_jwt", + algorithm="HS256", + ).decode() + + with async_mock.patch.object( + BaseMultitenantManager, "get_wallet_profile" + ) as get_wallet_profile: + mock_profile = InMemoryProfile.test_profile() + get_wallet_profile.return_value = mock_profile + + profile = await self.manager.get_profile_for_token( + self.profile.context, token + ) + + get_wallet_profile.assert_called_once_with( + self.profile.context, + wallet_record, + {}, + ) + + assert profile == mock_profile + + async def test_get_profile_for_token_managed_wallet_x_iat_no_match(self): + iat = 100 + + self.profile.settings["multitenant.jwt_secret"] = "very_secret_jwt" + wallet_record = WalletRecord( + key_management_mode=WalletRecord.MODE_MANAGED, + settings={"wallet.type": "indy", "wallet.key": "wallet_key"}, + jwt_iat=iat, + ) + + session = await self.profile.session() + await wallet_record.save(session) + + token = jwt.encode( + # Change iat from record value + {"wallet_id": wallet_record.wallet_id, "iat": 200}, + "very_secret_jwt", + algorithm="HS256", + ).decode() + + with async_mock.patch.object( + BaseMultitenantManager, "get_wallet_profile" + ) as get_wallet_profile, self.assertRaises( + MultitenantManagerError, msg="Token not valid" + ): + mock_profile = InMemoryProfile.test_profile() + get_wallet_profile.return_value = mock_profile + + profile = await self.manager.get_profile_for_token( + self.profile.context, token + ) + + get_wallet_profile.assert_called_once_with( + self.profile.context, + wallet_record, + {}, + ) + + assert profile == mock_profile + async def test_get_profile_for_token_unmanaged_wallet(self): self.profile.settings["multitenant.jwt_secret"] = "very_secret_jwt" wallet_record = WalletRecord( diff --git a/aries_cloudagent/wallet/models/wallet_record.py b/aries_cloudagent/wallet/models/wallet_record.py index 3421968d34..6240ea3ea1 100644 --- a/aries_cloudagent/wallet/models/wallet_record.py +++ b/aries_cloudagent/wallet/models/wallet_record.py @@ -39,11 +39,13 @@ def __init__( # MTODO: how to make this a tag without making it # a constructor param wallet_name: str = None, + jwt_iat: Optional[int] = None, **kwargs, ): """Initialize a new WalletRecord.""" super().__init__(wallet_id, **kwargs) self.key_management_mode = key_management_mode + self.jwt_iat = jwt_iat self._settings = settings @property @@ -85,7 +87,8 @@ def wallet_key(self) -> Optional[str]: def record_value(self) -> dict: """Accessor for the JSON record value generated for this record.""" return { - prop: getattr(self, prop) for prop in ("settings", "key_management_mode") + prop: getattr(self, prop) + for prop in ("settings", "key_management_mode", "jwt_iat") } @property From dbe0f09e2c89d2da2da744d1d751da0dd40498ad Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sun, 10 Apr 2022 16:39:38 +0200 Subject: [PATCH 185/872] feat: add token revocation Signed-off-by: Timo Glastra --- aries_cloudagent/multitenant/admin/routes.py | 4 +- .../multitenant/admin/tests/test_routes.py | 8 +- aries_cloudagent/multitenant/base.py | 15 ++- .../multitenant/tests/test_base.py | 114 ++++++++++++++++-- .../wallet/models/wallet_record.py | 5 +- 5 files changed, 130 insertions(+), 16 deletions(-) diff --git a/aries_cloudagent/multitenant/admin/routes.py b/aries_cloudagent/multitenant/admin/routes.py index fd0a8a6b3e..5df6f84cfd 100644 --- a/aries_cloudagent/multitenant/admin/routes.py +++ b/aries_cloudagent/multitenant/admin/routes.py @@ -315,7 +315,7 @@ async def wallet_create(request: web.BaseRequest): settings, key_management_mode ) - token = multitenant_mgr.create_auth_token(wallet_record, wallet_key) + token = await multitenant_mgr.create_auth_token(wallet_record, wallet_key) except BaseError as err: raise web.HTTPBadRequest(reason=err.roll_up) from err @@ -413,7 +413,7 @@ async def wallet_create_token(request: web.BaseRequest): " the wallet key to be provided" ) - token = multitenant_mgr.create_auth_token(wallet_record, wallet_key) + token = await multitenant_mgr.create_auth_token(wallet_record, wallet_key) except StorageNotFoundError as err: raise web.HTTPNotFound(reason=err.roll_up) from err except WalletKeyMissingError as err: diff --git a/aries_cloudagent/multitenant/admin/tests/test_routes.py b/aries_cloudagent/multitenant/admin/tests/test_routes.py index c1f50f49c3..1ffa316e2b 100644 --- a/aries_cloudagent/multitenant/admin/tests/test_routes.py +++ b/aries_cloudagent/multitenant/admin/tests/test_routes.py @@ -164,7 +164,7 @@ async def test_wallet_create(self): return_value=wallet_mock ) - self.mock_multitenant_mgr.create_auth_token = async_mock.Mock( + self.mock_multitenant_mgr.create_auth_token = async_mock.CoroutineMock( return_value="test_token" ) print(self.request["context"]) @@ -215,7 +215,7 @@ async def test_wallet_create_optional_default_fields(self): with async_mock.patch.object(test_module.web, "json_response") as mock_response: self.mock_multitenant_mgr.create_wallet = async_mock.CoroutineMock() - self.mock_multitenant_mgr.create_auth_token = async_mock.Mock() + self.mock_multitenant_mgr.create_auth_token = async_mock.CoroutineMock() await test_module.wallet_create(self.request) self.mock_multitenant_mgr.create_wallet.assert_called_once_with( @@ -440,7 +440,7 @@ async def test_wallet_create_token_managed(self): ) as mock_response: mock_wallet_record_retrieve_by_id.return_value = mock_wallet_record - self.mock_multitenant_mgr.create_auth_token = async_mock.Mock( + self.mock_multitenant_mgr.create_auth_token = async_mock.CoroutineMock( return_value="test_token" ) @@ -468,7 +468,7 @@ async def test_wallet_create_token_unmanaged(self): ) as mock_response: mock_wallet_record_retrieve_by_id.return_value = mock_wallet_record - self.mock_multitenant_mgr.create_auth_token = async_mock.Mock( + self.mock_multitenant_mgr.create_auth_token = async_mock.CoroutineMock( return_value="test_token" ) diff --git a/aries_cloudagent/multitenant/base.py b/aries_cloudagent/multitenant/base.py index 5bdd9a9787..a7abb4c31d 100644 --- a/aries_cloudagent/multitenant/base.py +++ b/aries_cloudagent/multitenant/base.py @@ -1,5 +1,6 @@ """Manager for multitenancy.""" +from datetime import datetime import logging from abc import abstractmethod @@ -333,7 +334,7 @@ async def add_key( keylist_updates, connection_id=mediation_record.connection_id ) - def create_auth_token( + async def create_auth_token( self, wallet_record: WalletRecord, wallet_key: str = None ) -> str: """Create JWT auth token for specified wallet record. @@ -351,8 +352,9 @@ def create_auth_token( str: JWT auth token """ + iat = int(round(datetime.utcnow().timestamp())) - jwt_payload = {"wallet_id": wallet_record.wallet_id} + jwt_payload = {"wallet_id": wallet_record.wallet_id, "iat": iat} jwt_secret = self._profile.settings.get("multitenant.jwt_secret") if wallet_record.requires_external_key: @@ -363,6 +365,11 @@ def create_auth_token( token = jwt.encode(jwt_payload, jwt_secret, algorithm="HS256").decode() + # Store iat for verification later on + wallet_record.jwt_iat = iat + async with self._profile.session() as session: + await wallet_record.save(session) + return token async def get_profile_for_token( @@ -389,6 +396,7 @@ async def get_profile_for_token( wallet_id = token_body.get("wallet_id") wallet_key = token_body.get("wallet_key") + iat = token_body.get("iat") async with self._profile.session() as session: wallet = await WalletRecord.retrieve_by_id(session, wallet_id) @@ -399,6 +407,9 @@ async def get_profile_for_token( extra_settings["wallet.key"] = wallet_key + if wallet.jwt_iat and wallet.jwt_iat != iat: + raise MultitenantManagerError("Token not valid") + profile = await self.get_wallet_profile(context, wallet, extra_settings) return profile diff --git a/aries_cloudagent/multitenant/tests/test_base.py b/aries_cloudagent/multitenant/tests/test_base.py index 4f6cb8dabf..5d858dd6f9 100644 --- a/aries_cloudagent/multitenant/tests/test_base.py +++ b/aries_cloudagent/multitenant/tests/test_base.py @@ -3,6 +3,8 @@ import jwt +from datetime import datetime + from ...core.in_memory import InMemoryProfile from ...config.base import InjectionError from ...messaging.responder import BaseResponder @@ -21,6 +23,7 @@ from ...wallet.did_method import DIDMethod from ..base import BaseMultitenantManager, MultitenantManagerError from ..error import WalletKeyMissingError +from .. import base as test_module class TestBaseMultitenantManager(AsyncTestCase): @@ -414,35 +417,55 @@ async def test_create_auth_token_fails_no_wallet_key_but_required(self): async def test_create_auth_token_managed(self): self.profile.settings["multitenant.jwt_secret"] = "very_secret_jwt" - wallet_record = WalletRecord( + wallet_record = async_mock.MagicMock( wallet_id="test_wallet", key_management_mode=WalletRecord.MODE_MANAGED, + requires_external_key=False, settings={}, + save=async_mock.CoroutineMock(), ) + utc_now = datetime(2020, 1, 1, 0, 0, 0) + iat = int(round(utc_now.timestamp())) + expected_token = jwt.encode( - {"wallet_id": wallet_record.wallet_id}, "very_secret_jwt" + {"wallet_id": wallet_record.wallet_id, "iat": iat}, "very_secret_jwt" ).decode() - token = self.manager.create_auth_token(wallet_record) + with async_mock.patch.object(test_module, "datetime") as mock_datetime: + mock_datetime.utcnow.return_value = utc_now + token = await self.manager.create_auth_token(wallet_record) + assert wallet_record.jwt_iat == iat assert expected_token == token async def test_create_auth_token_unmanaged(self): self.profile.settings["multitenant.jwt_secret"] = "very_secret_jwt" - wallet_record = WalletRecord( + wallet_record = async_mock.MagicMock( wallet_id="test_wallet", key_management_mode=WalletRecord.MODE_UNMANAGED, + requires_external_key=True, settings={"wallet.type": "indy"}, + save=async_mock.CoroutineMock(), ) + utc_now = datetime(2020, 1, 1, 0, 0, 0) + iat = int(round(utc_now.timestamp())) + expected_token = jwt.encode( - {"wallet_id": wallet_record.wallet_id, "wallet_key": "test_key"}, + { + "wallet_id": wallet_record.wallet_id, + "iat": iat, + "wallet_key": "test_key", + }, "very_secret_jwt", ).decode() - token = self.manager.create_auth_token(wallet_record, "test_key") + with async_mock.patch.object(test_module, "datetime") as mock_datetime: + mock_datetime.utcnow.return_value = utc_now + token = await self.manager.create_auth_token(wallet_record, "test_key") + assert wallet_record.jwt_iat == iat assert expected_token == token async def test_get_profile_for_token_invalid_token_raises(self): @@ -468,7 +491,7 @@ async def test_get_profile_for_token_wallet_key_missing_raises(self): with self.assertRaises(WalletKeyMissingError): await self.manager.get_profile_for_token(self.profile.context, token) - async def test_get_profile_for_token_managed_wallet(self): + async def test_get_profile_for_token_managed_wallet_no_iat(self): self.profile.settings["multitenant.jwt_secret"] = "very_secret_jwt" wallet_record = WalletRecord( key_management_mode=WalletRecord.MODE_MANAGED, @@ -500,6 +523,83 @@ async def test_get_profile_for_token_managed_wallet(self): assert profile == mock_profile + async def test_get_profile_for_token_managed_wallet_iat(self): + iat = 100 + + self.profile.settings["multitenant.jwt_secret"] = "very_secret_jwt" + wallet_record = WalletRecord( + key_management_mode=WalletRecord.MODE_MANAGED, + settings={"wallet.type": "indy", "wallet.key": "wallet_key"}, + jwt_iat=iat, + ) + + session = await self.profile.session() + await wallet_record.save(session) + + token = jwt.encode( + {"wallet_id": wallet_record.wallet_id, "iat": iat}, + "very_secret_jwt", + algorithm="HS256", + ).decode() + + with async_mock.patch.object( + BaseMultitenantManager, "get_wallet_profile" + ) as get_wallet_profile: + mock_profile = InMemoryProfile.test_profile() + get_wallet_profile.return_value = mock_profile + + profile = await self.manager.get_profile_for_token( + self.profile.context, token + ) + + get_wallet_profile.assert_called_once_with( + self.profile.context, + wallet_record, + {}, + ) + + assert profile == mock_profile + + async def test_get_profile_for_token_managed_wallet_x_iat_no_match(self): + iat = 100 + + self.profile.settings["multitenant.jwt_secret"] = "very_secret_jwt" + wallet_record = WalletRecord( + key_management_mode=WalletRecord.MODE_MANAGED, + settings={"wallet.type": "indy", "wallet.key": "wallet_key"}, + jwt_iat=iat, + ) + + session = await self.profile.session() + await wallet_record.save(session) + + token = jwt.encode( + # Change iat from record value + {"wallet_id": wallet_record.wallet_id, "iat": 200}, + "very_secret_jwt", + algorithm="HS256", + ).decode() + + with async_mock.patch.object( + BaseMultitenantManager, "get_wallet_profile" + ) as get_wallet_profile, self.assertRaises( + MultitenantManagerError, msg="Token not valid" + ): + mock_profile = InMemoryProfile.test_profile() + get_wallet_profile.return_value = mock_profile + + profile = await self.manager.get_profile_for_token( + self.profile.context, token + ) + + get_wallet_profile.assert_called_once_with( + self.profile.context, + wallet_record, + {}, + ) + + assert profile == mock_profile + async def test_get_profile_for_token_unmanaged_wallet(self): self.profile.settings["multitenant.jwt_secret"] = "very_secret_jwt" wallet_record = WalletRecord( diff --git a/aries_cloudagent/wallet/models/wallet_record.py b/aries_cloudagent/wallet/models/wallet_record.py index 3421968d34..6240ea3ea1 100644 --- a/aries_cloudagent/wallet/models/wallet_record.py +++ b/aries_cloudagent/wallet/models/wallet_record.py @@ -39,11 +39,13 @@ def __init__( # MTODO: how to make this a tag without making it # a constructor param wallet_name: str = None, + jwt_iat: Optional[int] = None, **kwargs, ): """Initialize a new WalletRecord.""" super().__init__(wallet_id, **kwargs) self.key_management_mode = key_management_mode + self.jwt_iat = jwt_iat self._settings = settings @property @@ -85,7 +87,8 @@ def wallet_key(self) -> Optional[str]: def record_value(self) -> dict: """Accessor for the JSON record value generated for this record.""" return { - prop: getattr(self, prop) for prop in ("settings", "key_management_mode") + prop: getattr(self, prop) + for prop in ("settings", "key_management_mode", "jwt_iat") } @property From c2d94fbe30531bc7af3770a45a2652c42ac5ac20 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sun, 10 Apr 2022 17:22:12 +0200 Subject: [PATCH 186/872] fix: add prepare-response state Signed-off-by: Timo Glastra --- .../protocols/out_of_band/v1_0/manager.py | 24 +++++++++++-------- .../out_of_band/v1_0/models/oob_record.py | 7 +++--- .../out_of_band/v1_0/tests/test_manager.py | 5 +++- 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py index e64f0d5e57..0bbfc668bc 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py @@ -519,6 +519,19 @@ async def receive_invitation( session, oob_record.connection_id ) + # If a connection record is associated with the oob record we can remove it now as + # we can leverage the connection for all exchanges. Otherwise we need to keep it + # around for the connectionless exchange + if conn_rec: + oob_record.state = OobRecord.STATE_DONE + async with self.profile.session() as session: + await oob_record.emit_event(session) + await oob_record.delete_record(session) + else: + oob_record.state = OobRecord.STATE_PREPARE_RESPONSE + async with self.profile.session() as session: + await oob_record.save(session) + # Handle any attachments if invitation.requests_attach: LOGGER.debug( @@ -555,15 +568,6 @@ async def receive_invitation( await self._process_request_attach(oob_record) - # If a connection record is associated with the oob record we can remove it now as - # we can leverage the connection for all exchanges. Otherwise we need to keep it - # around for the connectionless exchange - if conn_rec: - oob_record.state = OobRecord.STATE_DONE - async with self.profile.session() as session: - await oob_record.emit_event(session) - await oob_record.delete_record(session) - return oob_record async def _process_request_attach(self, oob_record: OobRecord): @@ -631,7 +635,7 @@ async def _wait_for_reuse_response( """ OOB_REUSE_RESPONSE_STATE = re.compile( - "^acapy::record::out_of_band::(reuse_accepted|reuse_not_accepted)$" + "^acapy::record::out_of_band::(reuse-accepted|reuse-not-accepted)$" ) async def _wait_for_state() -> OobRecord: diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/models/oob_record.py b/aries_cloudagent/protocols/out_of_band/v1_0/models/oob_record.py index f1d21d7631..f0b247ebb7 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/models/oob_record.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/models/oob_record.py @@ -35,9 +35,10 @@ class Meta: } STATE_INITIAL = "initial" - STATE_AWAIT_RESPONSE = "await_response" - STATE_NOT_ACCEPTED = "reuse_not_accepted" - STATE_ACCEPTED = "reuse_accepted" + STATE_PREPARE_RESPONSE = "prepare-response" + STATE_AWAIT_RESPONSE = "await-response" + STATE_NOT_ACCEPTED = "reuse-not-accepted" + STATE_ACCEPTED = "reuse-accepted" STATE_DONE = "done" ROLE_SENDER = "sender" diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py index 2eeb95cac1..f7ff19144a 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py @@ -1289,7 +1289,7 @@ async def test_receive_invitation_connection_protocol(self): ], requests_attach=[], ) - await self.manager.receive_invitation(oob_invitation) + oob_record = await self.manager.receive_invitation(oob_invitation) conn_mgr_cls.return_value.receive_invitation.assert_called_once_with( invitation=ANY, their_public_did=None, @@ -1307,6 +1307,8 @@ async def test_receive_invitation_connection_protocol(self): ] assert not invitation.routing_keys + assert oob_record.state == OobRecord.STATE_DONE + async def test_receive_invitation_services_with_neither_service_blocks_nor_dids( self, ): @@ -1564,6 +1566,7 @@ async def test_request_attach_oob_message_processor_connectionless(self): assert oob_record.our_recipient_key == "a-verkey" assert oob_record.our_service + assert oob_record.state == OobRecord.STATE_PREPARE_RESPONSE mock_create_signing_key.assert_called_once_with(KeyType.ED25519) mock_oob_processor.handle_message.assert_called_once_with( From ba23b5b8c03674f8b4afcf3b10bf251eabfabaf6 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sun, 10 Apr 2022 17:45:02 +0200 Subject: [PATCH 187/872] fix: only store service and attach thid if connless Signed-off-by: Timo Glastra --- aries_cloudagent/core/oob_processor.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/aries_cloudagent/core/oob_processor.py b/aries_cloudagent/core/oob_processor.py index ab770a5446..0bb8c440ee 100644 --- a/aries_cloudagent/core/oob_processor.py +++ b/aries_cloudagent/core/oob_processor.py @@ -342,12 +342,17 @@ async def handle_message( receipt=receipt, ) - oob_record.attach_thread_id = self.get_thread_id(message) - if their_service: - LOGGER.debug("Storing their service in oob record %s", their_service) - oob_record.their_service = their_service.serialize() + # We only need to store this data for connectionless + # (it could be the oob record is already deleted) + if not oob_record.connection_id: + oob_record.attach_thread_id = self.get_thread_id(message) + if their_service: + LOGGER.debug( + "Storing their service in oob record %s", their_service + ) + oob_record.their_service = their_service.serialize() - await oob_record.save(session) + await oob_record.save(session) self._inbound_message_router(profile, inbound_message, False) From c97f2547f698a7bc65b8ded8e5e554fda8d98303 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sun, 10 Apr 2022 17:53:26 +0200 Subject: [PATCH 188/872] test: fix oob processor tests Signed-off-by: Timo Glastra --- .../core/tests/test_oob_processor.py | 35 +++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/core/tests/test_oob_processor.py b/aries_cloudagent/core/tests/test_oob_processor.py index a5c336c351..5197daedbd 100644 --- a/aries_cloudagent/core/tests/test_oob_processor.py +++ b/aries_cloudagent/core/tests/test_oob_processor.py @@ -687,9 +687,40 @@ async def test_find_oob_record_for_inbound_message_session_connectionless_save( self.oob_record.delete_record.assert_not_called() self.oob_record.save.assert_called_once() - async def test_handle_message(self): + async def test_handle_message_connection(self): oob_record = async_mock.MagicMock( - connection_id="the-conn-id", save=async_mock.CoroutineMock() + connection_id="the-conn-id", + save=async_mock.CoroutineMock(), + attach_thread_id=None, + their_service=None, + ) + + await self.oob_processor.handle_message( + self.profile, + [ + { + "@type": "issue-credential/1.0/offer-credential", + "@id": "4a580490-a9d8-44f5-a3f6-14e0b8a219b0", + } + ], + oob_record, + their_service=ServiceDecorator( + endpoint="http://their-service-endpoint.com", + recipient_keys=["9WCgWKUaAJj3VWxxtzvvMQN3AoFxoBtBDo9ntwJnVVCC"], + routing_keys=["6QSduYdf8Bi6t8PfNm5vNomGWDtXhmMmTRzaciudBXYJ"], + ), + ) + + assert oob_record.attach_thread_id == None + assert oob_record.their_service == None + + oob_record.save.assert_not_called() + + self.inbound_message_router.assert_called_once_with(self.profile, ANY, False) + + async def test_handle_message_connectionless(self): + oob_record = async_mock.MagicMock( + save=async_mock.CoroutineMock(), connection_id=None ) await self.oob_processor.handle_message( From b69587a69f2c9852bc378c71a56f5b2f04a03aad Mon Sep 17 00:00:00 2001 From: Akiff Manji Date: Mon, 11 Apr 2022 11:28:06 -0700 Subject: [PATCH 189/872] Fix formatting issue on webhook url Signed-off-by: Akiff Manji --- demo/runners/faber.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/demo/runners/faber.py b/demo/runners/faber.py index ba46d9eec0..1d127cefcb 100644 --- a/demo/runners/faber.py +++ b/demo/runners/faber.py @@ -607,14 +607,17 @@ async def main(args): "/present-proof/create-request", proof_request_web_request ) pres_req_id = proof_request["presentation_exchange_id"] - url = os.getenv("WEBHOOK_TARGET") or ( - "http://" - + os.getenv("DOCKERHOST").replace( - "{PORT}", str(faber_agent.agent.admin_port + 1) + url = ( + os.getenv("WEBHOOK_TARGET") + or ( + "http://" + + os.getenv("DOCKERHOST").replace( + "{PORT}", str(faber_agent.agent.admin_port + 1) + ) + + "/webhooks" ) - + "/webhooks" + + f"/pres_req/{pres_req_id}/" ) - +f"/pres_req/{pres_req_id}/" log_msg(f"Proof request url: {url}") qr = QRCode(border=1) qr.add_data(url) From 9e43317ba96a4e12a336849975e472a0dec6f5d6 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Mon, 11 Apr 2022 14:39:17 -0700 Subject: [PATCH 190/872] Use provided connection_id if provided Signed-off-by: Ian Costanzo --- aries_cloudagent/wallet/routes.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/aries_cloudagent/wallet/routes.py b/aries_cloudagent/wallet/routes.py index a7772be595..1c23f01fbd 100644 --- a/aries_cloudagent/wallet/routes.py +++ b/aries_cloudagent/wallet/routes.py @@ -441,7 +441,12 @@ async def wallet_set_public_did(request: web.BaseRequest): info: DIDInfo = None try: info, attrib_def = await promote_wallet_public_did( - context.profile, context, context.session, did, write_ledger=write_ledger + context.profile, + context, + context.session, + did, + write_ledger=write_ledger, + connection_id=connection_id, ) except LookupError as err: raise web.HTTPNotFound(reason=str(err)) from err @@ -487,6 +492,7 @@ async def promote_wallet_public_did( session_fn, did: str, write_ledger: bool = False, + connection_id: str = None, ) -> DIDInfo: """Promote supplied DID to the wallet public DID.""" @@ -512,7 +518,8 @@ async def promote_wallet_public_did( write_ledger = False # author has not provided a connection id, so determine which to use - connection_id = await get_endorser_connection_id(context.profile) + if not connection_id: + connection_id = await get_endorser_connection_id(context.profile) if not connection_id: raise web.HTTPBadRequest(reason="No endorser connection found") @@ -785,10 +792,12 @@ async def on_register_nym_event(profile: Profile, event: Event): if is_author_role(profile) and profile.context.settings.get_value( "endorser.auto_promote_author_did" ): + print(">>> payload =", event.payload) did = event.payload["did"] + connection_id = event.payload.get("connection_id") try: await promote_wallet_public_did( - profile, profile.context, profile.session, did + profile, profile.context, profile.session, did, connection_id ) except Exception: # log the error, but continue From f424dbe8568781bb55b56173e26bdc04bd77b1ef Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Mon, 11 Apr 2022 20:52:05 -0400 Subject: [PATCH 191/872] fix: prevent circular references from breaking cache Signed-off-by: Daniel Bluhm --- aries_cloudagent/admin/server.py | 17 +++++++++++++--- aries_cloudagent/core/dispatcher.py | 30 ++++++++++++++++++++--------- 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/aries_cloudagent/admin/server.py b/aries_cloudagent/admin/server.py index 3043f10ca4..096f0fd680 100644 --- a/aries_cloudagent/admin/server.py +++ b/aries_cloudagent/admin/server.py @@ -7,6 +7,7 @@ from typing import Callable, Coroutine import uuid import warnings +import weakref from aiohttp import web from aiohttp_apispec import ( @@ -115,7 +116,11 @@ def __init__( """ super().__init__(**kwargs) - self._profile = profile + # Weakly hold the profile so this reference doesn't prevent profiles + # from being cleaned up when appropriate. + # Binding this AdminResponder to the profile's context creates a circular + # reference. + self._profile = weakref.ref(profile) self._send = send async def send_outbound(self, message: OutboundMessage) -> OutboundSendStatus: @@ -125,7 +130,10 @@ async def send_outbound(self, message: OutboundMessage) -> OutboundSendStatus: Args: message: The `OutboundMessage` to be sent """ - return await self._send(self._profile, message) + profile = self._profile() + if not profile: + raise RuntimeError("weakref to profile has expired") + return await self._send(profile, message) async def send_webhook(self, topic: str, payload: dict): """ @@ -139,7 +147,10 @@ async def send_webhook(self, topic: str, payload: dict): "responder.send_webhook is deprecated; please use the event bus instead.", DeprecationWarning, ) - await self._profile.notify("acapy::webhook::" + topic, payload) + profile = self._profile() + if not profile: + raise RuntimeError("weakref to profile has expired") + await profile.notify("acapy::webhook::" + topic, payload) @property def send_fn(self) -> Coroutine: diff --git a/aries_cloudagent/core/dispatcher.py b/aries_cloudagent/core/dispatcher.py index 98df420472..f7663e64dd 100644 --- a/aries_cloudagent/core/dispatcher.py +++ b/aries_cloudagent/core/dispatcher.py @@ -11,6 +11,7 @@ import warnings from typing import Callable, Coroutine, Union +import weakref from aiohttp.web import HTTPException @@ -272,7 +273,10 @@ def __init__( """ super().__init__(**kwargs) - self._context = context + # Weakly hold the context so it can be properly garbage collected. + # Binding this DispatcherResponder into the context creates a circular + # reference. + self._context = weakref.ref(context) self._inbound_message = inbound_message self._send = send_outbound @@ -285,13 +289,13 @@ async def create_outbound( Args: message: The message payload """ - if isinstance(message, AgentMessage) and self._context.settings.get( - "timing.enabled" - ): + context = self._context() + if not context: + raise RuntimeError("weakref to context has expired") + + if isinstance(message, AgentMessage) and context.settings.get("timing.enabled"): # Inject the timing decorator - in_time = ( - self._context.message_receipt and self._context.message_receipt.in_time - ) + in_time = context.message_receipt and context.message_receipt.in_time if not message._decorators.get("timing"): message._decorators["timing"] = { "in_time": in_time, @@ -307,7 +311,11 @@ async def send_outbound(self, message: OutboundMessage) -> OutboundSendStatus: Args: message: The `OutboundMessage` to be sent """ - return await self._send(self._context.profile, message, self._inbound_message) + context = self._context() + if not context: + raise RuntimeError("weakref to context has expired") + + return await self._send(context.profile, message, self._inbound_message) async def send_webhook(self, topic: str, payload: dict): """ @@ -321,4 +329,8 @@ async def send_webhook(self, topic: str, payload: dict): "responder.send_webhook is deprecated; please use the event bus instead.", DeprecationWarning, ) - await self._context.profile.notify("acapy::webhook::" + topic, payload) + context = self._context() + if not context: + raise RuntimeError("weakref to context has expired") + + await context.profile.notify("acapy::webhook::" + topic, payload) From a091c63190f88d2479026010a0933c1741e9511c Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Mon, 11 Apr 2022 21:13:11 -0400 Subject: [PATCH 192/872] feat: rework multitenancy-config arg Signed-off-by: Daniel Bluhm --- aries_cloudagent/config/argparse.py | 50 +++++++++++++++++------------ 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index 5551a85389..1bdb3396dd 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -1629,12 +1629,13 @@ def add_arguments(self, parser: ArgumentParser): parser.add_argument( "--multitenancy-config", type=str, - metavar="", + nargs="+", + metavar="key=value", env_var="ACAPY_MULTITENANCY_CONFIGURATION", help=( - 'Specify multitenancy configuration ("wallet_type" and "wallet_name"). ' - 'For example: "{"wallet_type":"askar-profile","wallet_name":' - '"askar-profile-name"}"' + "Specify multitenancy configuration in key=value pairs. " + 'For example: "wallet_type=askar-profile wallet_name=askar-profile-name" ' + "Possible values: wallet_name, wallet_key, cache_size. " '"wallet_name" is only used when "wallet_type" is "askar-profile"' ), ) @@ -1656,22 +1657,31 @@ def get_settings(self, args: Namespace): settings["multitenant.admin_enabled"] = True if args.multitenancy_config: - multitenancy_config = json.loads(args.multitenancy_config) - - if multitenancy_config.get("wallet_type"): - settings["multitenant.wallet_type"] = multitenancy_config.get( - "wallet_type" - ) - - if multitenancy_config.get("wallet_name"): - settings["multitenant.wallet_name"] = multitenancy_config.get( - "wallet_name" - ) - - if multitenancy_config.get("cache_size"): - settings["multitenant.cache_size"] = multitenancy_config.get( - "cache_size" - ) + # Legacy support + if ( + len(args.multitenancy_config) == 1 + and args.multitenancy_config[0][0] == "{" + ): + multitenancy_config = json.loads(args.multitenancy_config) + if multitenancy_config.get("wallet_type"): + settings["multitenant.wallet_type"] = multitenancy_config.get( + "wallet_type" + ) + + if multitenancy_config.get("wallet_name"): + settings["multitenant.wallet_name"] = multitenancy_config.get( + "wallet_name" + ) + + if multitenancy_config.get("cache_size"): + settings["multitenant.cache_size"] = multitenancy_config.get( + "cache_size" + ) + else: + for value_str in chain(*args.multitenancy_config): + key, value = value_str.split("=", maxsplit=1) + value = yaml.safe_load(value) + settings[f"multitenant.{key}"] = value return settings From 2ffd52163e826ce6e022c37979b19df08bea751d Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Mon, 11 Apr 2022 21:22:44 -0400 Subject: [PATCH 193/872] fix: parsing multiple multitenancy-config options Signed-off-by: Daniel Bluhm --- aries_cloudagent/config/argparse.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index 1bdb3396dd..2d9b2f6b13 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -1678,7 +1678,7 @@ def get_settings(self, args: Namespace): "cache_size" ) else: - for value_str in chain(*args.multitenancy_config): + for value_str in args.multitenancy_config: key, value = value_str.split("=", maxsplit=1) value = yaml.safe_load(value) settings[f"multitenant.{key}"] = value From b5b09371392583f105e69c11c89b4d1c094f25ff Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Tue, 12 Apr 2022 09:16:29 -0400 Subject: [PATCH 194/872] fix: multitenancy config json parsing and tests Signed-off-by: Daniel Bluhm --- aries_cloudagent/config/argparse.py | 2 +- aries_cloudagent/config/tests/test_argparse.py | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index 2d9b2f6b13..3abfa363f2 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -1662,7 +1662,7 @@ def get_settings(self, args: Namespace): len(args.multitenancy_config) == 1 and args.multitenancy_config[0][0] == "{" ): - multitenancy_config = json.loads(args.multitenancy_config) + multitenancy_config = json.loads(args.multitenancy_config[0]) if multitenancy_config.get("wallet_type"): settings["multitenant.wallet_type"] = multitenancy_config.get( "wallet_type" diff --git a/aries_cloudagent/config/tests/test_argparse.py b/aries_cloudagent/config/tests/test_argparse.py index 4e76a681d2..e8672f7a1e 100644 --- a/aries_cloudagent/config/tests/test_argparse.py +++ b/aries_cloudagent/config/tests/test_argparse.py @@ -263,6 +263,24 @@ async def test_multitenancy_settings(self): assert settings.get("multitenant.wallet_type") == "askar" assert settings.get("multitenant.wallet_name") == "test" + result = parser.parse_args( + [ + "--multitenant", + "--jwt-secret", + "secret", + "--multitenancy-config", + "wallet_type=askar", + "wallet_name=test", + ] + ) + + settings = group.get_settings(result) + + assert settings.get("multitenant.enabled") == True + assert settings.get("multitenant.jwt_secret") == "secret" + assert settings.get("multitenant.wallet_type") == "askar" + assert settings.get("multitenant.wallet_name") == "test" + async def test_endorser_settings(self): """Test required argument parsing.""" From 9296ff35a78a14c9f643d1d0cfa991f0b98115b1 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Tue, 12 Apr 2022 09:56:14 -0700 Subject: [PATCH 195/872] Remove debugging Signed-off-by: Ian Costanzo --- aries_cloudagent/wallet/routes.py | 1 - 1 file changed, 1 deletion(-) diff --git a/aries_cloudagent/wallet/routes.py b/aries_cloudagent/wallet/routes.py index 1c23f01fbd..0acff9d4cc 100644 --- a/aries_cloudagent/wallet/routes.py +++ b/aries_cloudagent/wallet/routes.py @@ -792,7 +792,6 @@ async def on_register_nym_event(profile: Profile, event: Event): if is_author_role(profile) and profile.context.settings.get_value( "endorser.auto_promote_author_did" ): - print(">>> payload =", event.payload) did = event.payload["did"] connection_id = event.payload.get("connection_id") try: From b48b34d9bb5ba9f4967a80c82672cc93a2a0dc82 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 13 Apr 2022 10:22:48 +0200 Subject: [PATCH 196/872] temp Signed-off-by: Timo Glastra --- .../wallet/tests/test_askar_wallet.py | 244 ------------------ 1 file changed, 244 deletions(-) delete mode 100644 aries_cloudagent/wallet/tests/test_askar_wallet.py diff --git a/aries_cloudagent/wallet/tests/test_askar_wallet.py b/aries_cloudagent/wallet/tests/test_askar_wallet.py deleted file mode 100644 index 4d0c5d93bd..0000000000 --- a/aries_cloudagent/wallet/tests/test_askar_wallet.py +++ /dev/null @@ -1,244 +0,0 @@ -import pytest - -from asynctest import mock as async_mock - -from aries_askar import AskarError, AskarErrorCode - -from ...askar.profile import AskarProfileManager -from ...config.injection_context import InjectionContext -from ...core.in_memory import InMemoryProfile -from ...ledger.endpoint_type import EndpointType - -from ..base import BaseWallet -from ..did_method import DIDMethod -from ..in_memory import InMemoryWallet -from ..key_type import KeyType -from .. import askar as test_module - -from . import test_in_memory_wallet - - -@pytest.fixture() -async def in_memory_wallet(): - profile = InMemoryProfile.test_profile() - wallet = InMemoryWallet(profile) - yield wallet - - -@pytest.fixture() -async def wallet(): - context = InjectionContext() - profile = await AskarProfileManager().provision( - context, - { - # "auto_recreate": True, - # "auto_remove": True, - "name": ":memory:", - "key": await AskarProfileManager.generate_store_key(), - "key_derivation_method": "RAW", # much faster than using argon-hashed keys - }, - ) - async with profile.session() as session: - yield session.inject(BaseWallet) - del session - # this will block indefinitely if session or profile references remain - # await profile.close() - - -@pytest.mark.askar -class TestAskarWallet(test_in_memory_wallet.TestInMemoryWallet): - """Apply all InMemoryWallet tests against AskarWallet""" - - # overriding derived values - Askar follows bls signatures draft 4 in key generation - test_key_bls12381g2_did = "did:key:zUC74E9UD2W6Q1MgPexCEdpstiCsY1Vbnyqepygk7McZVce38L1tGX7qZ2SgY4Zz2m9FUB4Xb5cEHSujks9XeKDzqe4QzW3CyyJ1cv8iBLNqU61EfkBoW2yEkg6VgqHTDtANYRS" - test_bls12381g2_verkey = "pPbb9Lqs3PVTyiHM4h8fbQqxHjBPm1Hixb6vdW9kkjHEij4FZrigkaV1P5DjWTbcKxeeYfkQuZMmozRQV3tH1gXhCA972LAXMGSKH7jxz8sNJqrCR6o8asgXDeYZeL1W3p8" - - @pytest.mark.skip - @pytest.mark.asyncio - async def test_rotate_did_keypair_x(self, wallet): - info = await wallet.create_local_did( - DIDMethod.SOV, KeyType.ED25519, self.test_seed, self.test_did - ) - - with async_mock.patch.object( - indy.did, "replace_keys_start", async_mock.CoroutineMock() - ) as mock_repl_start: - mock_repl_start.side_effect = test_module.IndyError( - test_module.ErrorCode.CommonIOError, {"message": "outlier"} - ) - with pytest.raises(test_module.WalletError) as excinfo: - await wallet.rotate_did_keypair_start(self.test_did) - assert "outlier" in str(excinfo.value) - - with async_mock.patch.object( - indy.did, "replace_keys_apply", async_mock.CoroutineMock() - ) as mock_repl_apply: - mock_repl_apply.side_effect = test_module.IndyError( - test_module.ErrorCode.CommonIOError, {"message": "outlier"} - ) - with pytest.raises(test_module.WalletError) as excinfo: - await wallet.rotate_did_keypair_apply(self.test_did) - assert "outlier" in str(excinfo.value) - - @pytest.mark.skip - @pytest.mark.asyncio - async def test_create_signing_key_x(self, wallet): - with async_mock.patch.object( - indy.crypto, "create_key", async_mock.CoroutineMock() - ) as mock_create_key: - mock_create_key.side_effect = test_module.IndyError( - test_module.ErrorCode.CommonIOError, {"message": "outlier"} - ) - with pytest.raises(test_module.WalletError) as excinfo: - await wallet.create_signing_key() - assert "outlier" in str(excinfo.value) - - @pytest.mark.skip - @pytest.mark.asyncio - async def test_create_local_did_x(self, wallet): - with async_mock.patch.object( - indy.did, "create_and_store_my_did", async_mock.CoroutineMock() - ) as mock_create: - mock_create.side_effect = test_module.IndyError( - test_module.ErrorCode.CommonIOError, {"message": "outlier"} - ) - with pytest.raises(test_module.WalletError) as excinfo: - await wallet.create_local_did( - DIDMethod.SOV, - KeyType.ED25519, - ) - assert "outlier" in str(excinfo.value) - - @pytest.mark.asyncio - async def test_set_did_endpoint_ledger(self, wallet): - mock_ledger = async_mock.MagicMock( - read_only=False, update_endpoint_for_did=async_mock.CoroutineMock() - ) - info_pub = await wallet.create_public_did( - DIDMethod.SOV, - KeyType.ED25519, - ) - await wallet.set_did_endpoint(info_pub.did, "http://1.2.3.4:8021", mock_ledger) - mock_ledger.update_endpoint_for_did.assert_called_once_with( - info_pub.did, - "http://1.2.3.4:8021", - EndpointType.ENDPOINT, - endorser_did=None, - write_ledger=True, - ) - info_pub2 = await wallet.get_public_did() - assert info_pub2.metadata["endpoint"] == "http://1.2.3.4:8021" - - with pytest.raises(test_module.LedgerConfigError) as excinfo: - await wallet.set_did_endpoint(info_pub.did, "http://1.2.3.4:8021", None) - assert "No ledger available" in str(excinfo.value) - - @pytest.mark.asyncio - async def test_set_did_endpoint_readonly_ledger(self, wallet): - mock_ledger = async_mock.MagicMock( - read_only=True, update_endpoint_for_did=async_mock.CoroutineMock() - ) - info_pub = await wallet.create_public_did( - DIDMethod.SOV, - KeyType.ED25519, - ) - await wallet.set_did_endpoint(info_pub.did, "http://1.2.3.4:8021", mock_ledger) - mock_ledger.update_endpoint_for_did.assert_not_called() - info_pub2 = await wallet.get_public_did() - assert info_pub2.metadata["endpoint"] == "http://1.2.3.4:8021" - - with pytest.raises(test_module.LedgerConfigError) as excinfo: - await wallet.set_did_endpoint(info_pub.did, "http://1.2.3.4:8021", None) - assert "No ledger available" in str(excinfo.value) - - @pytest.mark.skip - @pytest.mark.asyncio - async def test_get_signing_key_x(self, wallet): - with async_mock.patch.object( - indy.crypto, "get_key_metadata", async_mock.CoroutineMock() - ) as mock_signing: - mock_signing.side_effect = test_module.IndyError( - test_module.ErrorCode.CommonIOError, {"message": "outlier"} - ) - with pytest.raises(test_module.WalletError) as excinfo: - await wallet.get_signing_key(None) - assert "outlier" in str(excinfo.value) - - @pytest.mark.skip - @pytest.mark.asyncio - async def test_get_local_did_x(self, wallet): - with async_mock.patch.object( - indy.did, "get_my_did_with_meta", async_mock.CoroutineMock() - ) as mock_my: - mock_my.side_effect = test_module.IndyError( - test_module.ErrorCode.CommonIOError, {"message": "outlier"} - ) - with pytest.raises(test_module.WalletError) as excinfo: - await wallet.get_local_did(None) - assert "outlier" in str(excinfo.value) - - @pytest.mark.asyncio - async def test_verify_message_x(self, wallet): - with async_mock.patch.object( - test_module.Key, "verify_signature" - ) as mock_verify: - mock_verify.side_effect = test_module.AskarError( # outlier - AskarErrorCode.BACKEND, {"message": "outlier"} - ) - with pytest.raises(test_module.WalletError) as excinfo: - await wallet.verify_message( - b"hello world", - b"signature", - self.test_ed25519_verkey, - KeyType.ED25519, - ) - - @pytest.mark.asyncio - async def test_pack_message_x(self, wallet): - with async_mock.patch.object( - test_module, - "pack_message", - ) as mock_pack: - mock_pack.side_effect = AskarError( # outlier - AskarErrorCode.BACKEND, {"message": "outlier"} - ) - with pytest.raises(test_module.WalletError) as excinfo: - await wallet.pack_message( - b"hello world", - [ - self.test_ed25519_verkey, - ], - ) - - -@pytest.mark.askar -class TestWalletCompat: - """Tests for wallet compatibility.""" - - test_seed = "testseed000000000000000000000001" - test_did = "55GkHamhTU1ZbTbV2ab9DE" - test_verkey = "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx" - test_message = "test message" - - @pytest.mark.asyncio - async def test_compare_pack_unpack(self, in_memory_wallet, wallet): - """ - Ensure that python-based pack/unpack is compatible with indy-sdk implementation - """ - await in_memory_wallet.create_local_did( - DIDMethod.SOV, KeyType.ED25519, self.test_seed - ) - py_packed = await in_memory_wallet.pack_message( - self.test_message, [self.test_verkey], self.test_verkey - ) - - await wallet.create_local_did(DIDMethod.SOV, KeyType.ED25519, self.test_seed) - packed = await wallet.pack_message( - self.test_message, [self.test_verkey], self.test_verkey - ) - - py_unpacked, from_vk, to_vk = await in_memory_wallet.unpack_message(packed) - assert self.test_message == py_unpacked - - unpacked, from_vk, to_vk = await wallet.unpack_message(py_packed) - assert self.test_message == unpacked From 33153ea6f3945fae33ad66e2604302b726b49586 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 13 Apr 2022 10:25:41 +0200 Subject: [PATCH 197/872] docs(multitenancy): add token invlidation to docs Signed-off-by: Timo Glastra --- Multitenancy.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Multitenancy.md b/Multitenancy.md index ce61bcfd73..cf5027cd84 100644 --- a/Multitenancy.md +++ b/Multitenancy.md @@ -260,7 +260,7 @@ echo $new_tenant | curl -X POST "${ACAPY_ADMIN_URL}/multitenancy/wallet" \ #### Method 2: Get tenant token -This method allows you to retrieve a tenant `token` for an already registered tenant. To retrieve a token you will need an Admin API key (if your admin is protected with one), `wallet_key` and the `wallet_id` of the tenant. +This method allows you to retrieve a tenant `token` for an already registered tenant. To retrieve a token you will need an Admin API key (if your admin is protected with one), `wallet_key` and the `wallet_id` of the tenant. Not that calling the get tenant token endpoint will **invalidate** the old token. This is useful if the old token needs to be revoked, but does mean that you can't have multiple authentication tokens for the same wallet. Only the last generated token will always be valid. Example From 5ce0d3ee220fa4202199a9a22f261abd633133ea Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 13 Apr 2022 10:27:31 +0200 Subject: [PATCH 198/872] docs(multitenancy): fix typo Signed-off-by: Timo Glastra --- Multitenancy.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Multitenancy.md b/Multitenancy.md index cf5027cd84..d3d7d9805a 100644 --- a/Multitenancy.md +++ b/Multitenancy.md @@ -260,7 +260,7 @@ echo $new_tenant | curl -X POST "${ACAPY_ADMIN_URL}/multitenancy/wallet" \ #### Method 2: Get tenant token -This method allows you to retrieve a tenant `token` for an already registered tenant. To retrieve a token you will need an Admin API key (if your admin is protected with one), `wallet_key` and the `wallet_id` of the tenant. Not that calling the get tenant token endpoint will **invalidate** the old token. This is useful if the old token needs to be revoked, but does mean that you can't have multiple authentication tokens for the same wallet. Only the last generated token will always be valid. +This method allows you to retrieve a tenant `token` for an already registered tenant. To retrieve a token you will need an Admin API key (if your admin is protected with one), `wallet_key` and the `wallet_id` of the tenant. Note that calling the get tenant token endpoint will **invalidate** the old token. This is useful if the old token needs to be revoked, but does mean that you can't have multiple authentication tokens for the same wallet. Only the last generated token will always be valid. Example From 590724a20da2616918bccec1be3a70bf034a78d0 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Tue, 12 Apr 2022 14:37:54 -0400 Subject: [PATCH 199/872] feat: allow querying default mediator from base wallet Signed-off-by: Daniel Bluhm --- aries_cloudagent/admin/server.py | 1 + 1 file changed, 1 insertion(+) diff --git a/aries_cloudagent/admin/server.py b/aries_cloudagent/admin/server.py index 3043f10ca4..0bbada1da6 100644 --- a/aries_cloudagent/admin/server.py +++ b/aries_cloudagent/admin/server.py @@ -323,6 +323,7 @@ async def check_multitenant_authorization(request: web.Request, handler): f"{UUIDFour.PATTERN}/default-mediator)", path, ) + or path.startswith("/mediation/default-mediator") ) # base wallet is not allowed to perform ssi related actions. From 1c218041a843f9428926f712f06f46278af43c0f Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Thu, 14 Apr 2022 15:27:26 -0400 Subject: [PATCH 200/872] test: askar profile finalizer And make tests follow pytest convention Signed-off-by: Daniel Bluhm --- aries_cloudagent/askar/tests/test_profile.py | 153 ++++++++++--------- 1 file changed, 85 insertions(+), 68 deletions(-) diff --git a/aries_cloudagent/askar/tests/test_profile.py b/aries_cloudagent/askar/tests/test_profile.py index 4c4c646a84..319f0b64c7 100644 --- a/aries_cloudagent/askar/tests/test_profile.py +++ b/aries_cloudagent/askar/tests/test_profile.py @@ -1,7 +1,8 @@ import asyncio +import logging import pytest -from asynctest import TestCase as AsyncTestCase, mock +from asynctest import mock from ...askar.profile import AskarProfile from ...config.injection_context import InjectionContext @@ -9,73 +10,89 @@ from .. import profile as test_module -class TestProfile(AsyncTestCase): - @mock.patch("aries_cloudagent.askar.store.AskarOpenStore") - async def test_init_success(self, AskarOpenStore): +@pytest.fixture +def open_store(): + yield mock.MagicMock() + + +async def test_init_success(open_store): + askar_profile = AskarProfile( + open_store, + ) + + assert askar_profile.opened == open_store + + +async def test_remove_success(open_store): + openStore = open_store + context = InjectionContext() + profile_id = "profile_id" + context.settings = { + "multitenant.wallet_type": "askar-profile", + "wallet.askar_profile": profile_id, + "ledger.genesis_transactions": mock.MagicMock(), + } + askar_profile = AskarProfile(openStore, context, profile_id=profile_id) + remove_profile_stub = asyncio.Future() + remove_profile_stub.set_result(True) + openStore.store.remove_profile.return_value = remove_profile_stub + + await askar_profile.remove() + + openStore.store.remove_profile.assert_called_once_with(profile_id) + + +async def test_remove_profile_not_removed_if_wallet_type_not_askar_profile(open_store): + openStore = open_store + context = InjectionContext() + context.settings = {"multitenant.wallet_type": "basic"} + askar_profile = AskarProfile(openStore, context) + + await askar_profile.remove() + + openStore.store.remove_profile.assert_not_called() + + +@pytest.mark.asyncio +async def test_profile_manager_transaction(): + profile = "profileId" + + with mock.patch("aries_cloudagent.askar.profile.AskarProfile") as AskarProfile: + askar_profile = AskarProfile(None, True, profile_id=profile) + askar_profile.profile_id = profile + askar_profile_transaction = mock.MagicMock() + askar_profile.store.transaction.return_value = askar_profile_transaction + + transactionProfile = test_module.AskarProfileSession(askar_profile, True) + + assert transactionProfile._opener == askar_profile_transaction + askar_profile.store.transaction.assert_called_once_with(profile) + + +@pytest.mark.asyncio +async def test_profile_manager_store(): + profile = "profileId" + + with mock.patch("aries_cloudagent.askar.profile.AskarProfile") as AskarProfile: + askar_profile = AskarProfile(None, False, profile_id=profile) + askar_profile.profile_id = profile + askar_profile_session = mock.MagicMock() + askar_profile.store.session.return_value = askar_profile_session + + sessionProfile = test_module.AskarProfileSession(askar_profile, False) + + assert sessionProfile._opener == askar_profile_session + askar_profile.store.session.assert_called_once_with(profile) + + +def test_finalizer(open_store, caplog): + def _smaller_scope(): askar_profile = AskarProfile( - AskarOpenStore, + open_store, ) + askar_profile.finalizer() + + with caplog.at_level(logging.DEBUG): + _smaller_scope() - assert askar_profile.opened == AskarOpenStore - - @mock.patch("aries_cloudagent.askar.store.AskarOpenStore") - async def test_remove_success(self, AskarOpenStore): - openStore = AskarOpenStore - context = InjectionContext() - profile_id = "profile_id" - context.settings = { - "multitenant.wallet_type": "askar-profile", - "wallet.askar_profile": profile_id, - "ledger.genesis_transactions": mock.MagicMock(), - } - askar_profile = AskarProfile(openStore, context, profile_id=profile_id) - remove_profile_stub = asyncio.Future() - remove_profile_stub.set_result(True) - openStore.store.remove_profile.return_value = remove_profile_stub - - await askar_profile.remove() - - openStore.store.remove_profile.assert_called_once_with(profile_id) - - @mock.patch("aries_cloudagent.askar.store.AskarOpenStore") - async def test_remove_profile_not_removed_if_wallet_type_not_askar_profile( - self, AskarOpenStore - ): - openStore = AskarOpenStore - context = InjectionContext() - context.settings = {"multitenant.wallet_type": "basic"} - askar_profile = AskarProfile(openStore, context) - - await askar_profile.remove() - - openStore.store.remove_profile.assert_not_called() - - @pytest.mark.asyncio - async def test_profile_manager_transaction(self): - profile = "profileId" - - with mock.patch("aries_cloudagent.askar.profile.AskarProfile") as AskarProfile: - askar_profile = AskarProfile(None, True, profile_id=profile) - askar_profile.profile_id = profile - askar_profile_transaction = mock.MagicMock() - askar_profile.store.transaction.return_value = askar_profile_transaction - - transactionProfile = test_module.AskarProfileSession(askar_profile, True) - - assert transactionProfile._opener == askar_profile_transaction - askar_profile.store.transaction.assert_called_once_with(profile) - - @pytest.mark.asyncio - async def test_profile_manager_store(self): - profile = "profileId" - - with mock.patch("aries_cloudagent.askar.profile.AskarProfile") as AskarProfile: - askar_profile = AskarProfile(None, False, profile_id=profile) - askar_profile.profile_id = profile - askar_profile_session = mock.MagicMock() - askar_profile.store.session.return_value = askar_profile_session - - sessionProfile = test_module.AskarProfileSession(askar_profile, False) - - assert sessionProfile._opener == askar_profile_session - askar_profile.store.session.assert_called_once_with(profile) + assert "finalizer called" in caplog.text From 108ecabc0e1fa309417154411f069d90f3d27e4c Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Thu, 14 Apr 2022 15:30:20 -0400 Subject: [PATCH 201/872] refactor: indy test profile by pytest convention Signed-off-by: Daniel Bluhm --- .../indy/sdk/tests/test_profile.py | 122 +++++++++--------- 1 file changed, 61 insertions(+), 61 deletions(-) diff --git a/aries_cloudagent/indy/sdk/tests/test_profile.py b/aries_cloudagent/indy/sdk/tests/test_profile.py index 6db4425574..aed2f93ef6 100644 --- a/aries_cloudagent/indy/sdk/tests/test_profile.py +++ b/aries_cloudagent/indy/sdk/tests/test_profile.py @@ -25,65 +25,65 @@ async def profile(): ) -class TestIndySdkProfile: - @pytest.mark.asyncio - async def test_properties(self, profile): - assert profile.name == "test-profile" - assert profile.backend == "indy" - assert profile.wallet and profile.wallet.handle == 1 - - assert "IndySdkProfile" in str(profile) - assert profile.created - assert profile.wallet.created - assert profile.wallet.master_secret_id == "master-secret" - - with async_mock.patch.object(profile, "opened", False): - with pytest.raises(ProfileError): - await profile.remove() - - with async_mock.patch.object( - profile.opened, "close", async_mock.CoroutineMock() - ): +@pytest.mark.asyncio +async def test_properties(profile): + assert profile.name == "test-profile" + assert profile.backend == "indy" + assert profile.wallet and profile.wallet.handle == 1 + + assert "IndySdkProfile" in str(profile) + assert profile.created + assert profile.wallet.created + assert profile.wallet.master_secret_id == "master-secret" + + with async_mock.patch.object(profile, "opened", False): + with pytest.raises(ProfileError): await profile.remove() - assert profile.opened is None - - def test_settings_genesis_transactions(self): - context = InjectionContext( - settings={"ledger.genesis_transactions": async_mock.MagicMock()} - ) - context.injector.bind_instance(IndySdkLedgerPool, IndySdkLedgerPool("name")) - profile = IndySdkProfile( - IndyOpenWallet( - config=IndyWalletConfig({"name": "test-profile"}), - created=True, - handle=1, - master_secret_id="master-secret", - ), - context, - ) - - def test_settings_ledger_config(self): - context = InjectionContext(settings={"ledger.ledger_config_list": True}) - context.injector.bind_instance(IndySdkLedgerPool, IndySdkLedgerPool("name")) - profile = IndySdkProfile( - IndyOpenWallet( - config=IndyWalletConfig({"name": "test-profile"}), - created=True, - handle=1, - master_secret_id="master-secret", - ), - context, - ) - - def test_read_only(self): - context = InjectionContext(settings={"ledger.read_only": True}) - context.injector.bind_instance(IndySdkLedgerPool, IndySdkLedgerPool("name")) - ro_profile = IndySdkProfile( - IndyOpenWallet( - config=IndyWalletConfig({"name": "test-profile"}), - created=True, - handle=1, - master_secret_id="master-secret", - ), - context, - ) + + with async_mock.patch.object(profile.opened, "close", async_mock.CoroutineMock()): + await profile.remove() + assert profile.opened is None + + +def test_settings_genesis_transactions(): + context = InjectionContext( + settings={"ledger.genesis_transactions": async_mock.MagicMock()} + ) + context.injector.bind_instance(IndySdkLedgerPool, IndySdkLedgerPool("name")) + profile = IndySdkProfile( + IndyOpenWallet( + config=IndyWalletConfig({"name": "test-profile"}), + created=True, + handle=1, + master_secret_id="master-secret", + ), + context, + ) + + +def test_settings_ledger_config(): + context = InjectionContext(settings={"ledger.ledger_config_list": True}) + context.injector.bind_instance(IndySdkLedgerPool, IndySdkLedgerPool("name")) + profile = IndySdkProfile( + IndyOpenWallet( + config=IndyWalletConfig({"name": "test-profile"}), + created=True, + handle=1, + master_secret_id="master-secret", + ), + context, + ) + + +def test_read_only(): + context = InjectionContext(settings={"ledger.read_only": True}) + context.injector.bind_instance(IndySdkLedgerPool, IndySdkLedgerPool("name")) + ro_profile = IndySdkProfile( + IndyOpenWallet( + config=IndyWalletConfig({"name": "test-profile"}), + created=True, + handle=1, + master_secret_id="master-secret", + ), + context, + ) From 2e80b2991d89b4696b6567af21b0e8bccf6e5526 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Thu, 14 Apr 2022 15:35:56 -0400 Subject: [PATCH 202/872] test: indy sdk profile finalizer Signed-off-by: Daniel Bluhm --- aries_cloudagent/askar/tests/test_profile.py | 4 +- .../indy/sdk/tests/test_profile.py | 70 ++++++++----------- 2 files changed, 31 insertions(+), 43 deletions(-) diff --git a/aries_cloudagent/askar/tests/test_profile.py b/aries_cloudagent/askar/tests/test_profile.py index 319f0b64c7..cc484b109b 100644 --- a/aries_cloudagent/askar/tests/test_profile.py +++ b/aries_cloudagent/askar/tests/test_profile.py @@ -87,9 +87,7 @@ async def test_profile_manager_store(): def test_finalizer(open_store, caplog): def _smaller_scope(): - askar_profile = AskarProfile( - open_store, - ) + askar_profile = AskarProfile(open_store) askar_profile.finalizer() with caplog.at_level(logging.DEBUG): diff --git a/aries_cloudagent/indy/sdk/tests/test_profile.py b/aries_cloudagent/indy/sdk/tests/test_profile.py index aed2f93ef6..7372cd1fea 100644 --- a/aries_cloudagent/indy/sdk/tests/test_profile.py +++ b/aries_cloudagent/indy/sdk/tests/test_profile.py @@ -1,3 +1,4 @@ +import logging import pytest from asynctest import mock as async_mock @@ -10,19 +11,21 @@ from ..wallet_setup import IndyWalletConfig, IndyOpenWallet +@pytest.fixture +async def open_wallet(): + yield IndyOpenWallet( + config=IndyWalletConfig({"name": "test-profile"}), + created=True, + handle=1, + master_secret_id="master-secret", + ) + + @pytest.fixture() -async def profile(): +async def profile(open_wallet): context = InjectionContext() context.injector.bind_instance(IndySdkLedgerPool, IndySdkLedgerPool("name")) - yield IndySdkProfile( - IndyOpenWallet( - config=IndyWalletConfig({"name": "test-profile"}), - created=True, - handle=1, - master_secret_id="master-secret", - ), - context, - ) + yield IndySdkProfile(open_wallet, context) @pytest.mark.asyncio @@ -45,45 +48,32 @@ async def test_properties(profile): assert profile.opened is None -def test_settings_genesis_transactions(): +def test_settings_genesis_transactions(open_wallet): context = InjectionContext( settings={"ledger.genesis_transactions": async_mock.MagicMock()} ) context.injector.bind_instance(IndySdkLedgerPool, IndySdkLedgerPool("name")) - profile = IndySdkProfile( - IndyOpenWallet( - config=IndyWalletConfig({"name": "test-profile"}), - created=True, - handle=1, - master_secret_id="master-secret", - ), - context, - ) + profile = IndySdkProfile(open_wallet, context) -def test_settings_ledger_config(): +def test_settings_ledger_config(open_wallet): context = InjectionContext(settings={"ledger.ledger_config_list": True}) context.injector.bind_instance(IndySdkLedgerPool, IndySdkLedgerPool("name")) - profile = IndySdkProfile( - IndyOpenWallet( - config=IndyWalletConfig({"name": "test-profile"}), - created=True, - handle=1, - master_secret_id="master-secret", - ), - context, - ) + profile = IndySdkProfile(open_wallet, context) -def test_read_only(): +def test_read_only(open_wallet): context = InjectionContext(settings={"ledger.read_only": True}) context.injector.bind_instance(IndySdkLedgerPool, IndySdkLedgerPool("name")) - ro_profile = IndySdkProfile( - IndyOpenWallet( - config=IndyWalletConfig({"name": "test-profile"}), - created=True, - handle=1, - master_secret_id="master-secret", - ), - context, - ) + ro_profile = IndySdkProfile(open_wallet, context) + + +def test_finalizer(open_wallet, caplog): + def _smaller_scope(): + profile = IndySdkProfile(open_wallet) + profile.finalizer() + + with caplog.at_level(logging.DEBUG): + _smaller_scope() + + assert "finalizer called" in caplog.text From 9cfbcda9053357a83f987f83f12091c8613074e5 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Thu, 14 Apr 2022 15:43:54 -0400 Subject: [PATCH 203/872] test: cache_size parsing Signed-off-by: Daniel Bluhm --- aries_cloudagent/config/tests/test_argparse.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/config/tests/test_argparse.py b/aries_cloudagent/config/tests/test_argparse.py index e8672f7a1e..605621857f 100644 --- a/aries_cloudagent/config/tests/test_argparse.py +++ b/aries_cloudagent/config/tests/test_argparse.py @@ -252,7 +252,7 @@ async def test_multitenancy_settings(self): "--jwt-secret", "secret", "--multitenancy-config", - '{"wallet_type":"askar","wallet_name":"test"}', + '{"wallet_type":"askar","wallet_name":"test", "cache_size": 10}', ] ) @@ -271,6 +271,7 @@ async def test_multitenancy_settings(self): "--multitenancy-config", "wallet_type=askar", "wallet_name=test", + "cache_size=10", ] ) From 0422d8aeb43cdf09d15403df3cb9828b8e340cba Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Thu, 14 Apr 2022 15:44:58 -0400 Subject: [PATCH 204/872] test: dispatcher responder context weak ref Signed-off-by: Daniel Bluhm --- aries_cloudagent/core/tests/test_dispatcher.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/aries_cloudagent/core/tests/test_dispatcher.py b/aries_cloudagent/core/tests/test_dispatcher.py index 3b5e245367..b9f00564b1 100644 --- a/aries_cloudagent/core/tests/test_dispatcher.py +++ b/aries_cloudagent/core/tests/test_dispatcher.py @@ -404,3 +404,20 @@ async def test_create_enc_outbound(self): ) as mock_send_outbound: await responder.send(message) assert mock_send_outbound.called_once() + + async def test_expired_context_x(self): + def _smaller_scope(): + profile = make_profile() + context = RequestContext(profile) + message = b"abc123xyz7890000" + return test_module.DispatcherResponder(context, message, None) + + responder = _smaller_scope() + with self.assertRaises(RuntimeError): + await responder.create_outbound(b"test") + + with self.assertRaises(RuntimeError): + await responder.send_outbound(None) + + with self.assertRaises(RuntimeError): + await responder.send_webhook("test", {}) From 94b0c3e6f797d5b471bab580e7434dfa12fc6f4d Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Thu, 14 Apr 2022 15:49:22 -0400 Subject: [PATCH 205/872] test: rescuing still open profile from eviction Signed-off-by: Daniel Bluhm --- .../multitenant/tests/test_cache.py | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/aries_cloudagent/multitenant/tests/test_cache.py b/aries_cloudagent/multitenant/tests/test_cache.py index 7350cdc1f0..7acaaba318 100644 --- a/aries_cloudagent/multitenant/tests/test_cache.py +++ b/aries_cloudagent/multitenant/tests/test_cache.py @@ -83,3 +83,28 @@ def test_cleanup_lru(): assert cache.get("2") is None assert cache.get("3") assert cache.get("4") + + +def test_rescue_open_profile(): + cache = ProfileCache(3) + + cache.put("1", MockProfile()) + cache.put("2", MockProfile()) + cache.put("3", MockProfile()) + + assert len(cache.profiles) == 3 + + held = cache.profiles["1"] + cache.put("4", MockProfile()) + + assert len(cache.profiles) == 4 + assert len(cache._cache) == 3 + + cache.get("1") + + assert len(cache.profiles) == 3 + assert len(cache._cache) == 3 + assert cache.get("1") + assert cache.get("2") is None + assert cache.get("3") + assert cache.get("4") From a9402f47eaec0c96b03f2a97b5a23823d69839d8 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Thu, 14 Apr 2022 15:54:16 -0400 Subject: [PATCH 206/872] test: askar profile manager open profiles Signed-off-by: Daniel Bluhm --- .../tests/test_askar_profile_manager.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/aries_cloudagent/multitenant/tests/test_askar_profile_manager.py b/aries_cloudagent/multitenant/tests/test_askar_profile_manager.py index b93930f2b0..5bbc1d926c 100644 --- a/aries_cloudagent/multitenant/tests/test_askar_profile_manager.py +++ b/aries_cloudagent/multitenant/tests/test_askar_profile_manager.py @@ -157,3 +157,18 @@ async def test_remove_wallet_profile(self): with async_mock.patch.object(InMemoryProfile, "remove") as profile_remove: await self.manager.remove_wallet_profile(test_profile) profile_remove.assert_called_once_with() + + async def test_open_profiles(self): + assert len(list(self.manager.open_profiles)) == 0 + + create_profile_stub = asyncio.Future() + create_profile_stub.set_result("") + with async_mock.patch( + "aries_cloudagent.multitenant.askar_profile_manager.AskarProfile" + ) as AskarProfile: + sub_wallet_profile = AskarProfile(None, None) + sub_wallet_profile.context.copy.return_value = InjectionContext() + sub_wallet_profile.store.create_profile.return_value = create_profile_stub + self.manager._multitenant_profile = sub_wallet_profile + + assert len(list(self.manager.open_profiles)) == 1 From 45de35637ce6cc24799551568ebdb933986122a9 Mon Sep 17 00:00:00 2001 From: Adam Burdett Date: Thu, 14 Apr 2022 15:06:17 -0600 Subject: [PATCH 207/872] async mock,'As it should be' Signed-off-by: Adam Burdett --- aries_cloudagent/multitenant/admin/tests/test_routes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aries_cloudagent/multitenant/admin/tests/test_routes.py b/aries_cloudagent/multitenant/admin/tests/test_routes.py index 37887d5667..98ad47dca2 100644 --- a/aries_cloudagent/multitenant/admin/tests/test_routes.py +++ b/aries_cloudagent/multitenant/admin/tests/test_routes.py @@ -244,7 +244,7 @@ async def test_wallet_create_raw_key_derivation(self): with async_mock.patch.object(test_module.web, "json_response") as mock_response: self.mock_multitenant_mgr.create_wallet = async_mock.CoroutineMock() - self.mock_multitenant_mgr.create_auth_token = async_mock.Mock() + self.mock_multitenant_mgr.create_auth_token = async_mock.CoroutineMock() await test_module.wallet_create(self.request) self.mock_multitenant_mgr.create_wallet.assert_called_once_with( From 4a5f65ddcc37adf0879e036ec69cf99489d8e26f Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Thu, 14 Apr 2022 17:23:03 -0400 Subject: [PATCH 208/872] fix: self.manager for mocking methods in tests Signed-off-by: Daniel Bluhm --- aries_cloudagent/multitenant/tests/test_base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/multitenant/tests/test_base.py b/aries_cloudagent/multitenant/tests/test_base.py index 2a73a3143b..2832fc74ef 100644 --- a/aries_cloudagent/multitenant/tests/test_base.py +++ b/aries_cloudagent/multitenant/tests/test_base.py @@ -556,7 +556,7 @@ async def test_get_profile_for_token_managed_wallet_iat(self): ).decode() with async_mock.patch.object( - BaseMultitenantManager, "get_wallet_profile" + self.manager, "get_wallet_profile" ) as get_wallet_profile: mock_profile = InMemoryProfile.test_profile() get_wallet_profile.return_value = mock_profile @@ -594,7 +594,7 @@ async def test_get_profile_for_token_managed_wallet_x_iat_no_match(self): ).decode() with async_mock.patch.object( - BaseMultitenantManager, "get_wallet_profile" + self.manager, "get_wallet_profile" ) as get_wallet_profile, self.assertRaises( MultitenantManagerError, msg="Token not valid" ): From 4bf26e58845839514d670564897d7043d8f19ab4 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Thu, 14 Apr 2022 17:57:44 -0400 Subject: [PATCH 209/872] test: admin responder profile expired Signed-off-by: Daniel Bluhm --- aries_cloudagent/admin/tests/test_admin_server.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/aries_cloudagent/admin/tests/test_admin_server.py b/aries_cloudagent/admin/tests/test_admin_server.py index 8f5429dead..6482371c9e 100644 --- a/aries_cloudagent/admin/tests/test_admin_server.py +++ b/aries_cloudagent/admin/tests/test_admin_server.py @@ -485,3 +485,17 @@ async def test_on_record_event(server, event_topic, webhook_topic): ) as mock_send_webhook: await server._on_record_event(profile, Event(event_topic, None)) mock_send_webhook.assert_called_once_with(profile, webhook_topic, None) + + +@pytest.mark.asyncio +async def test_admin_responder_profile_expired_x(): + def _smaller_scope(): + profile = InMemoryProfile.test_profile() + return test_module.AdminResponder(profile, None) + + responder = _smaller_scope() + with pytest.raises(RuntimeError): + await responder.send_outbound(None) + + with pytest.raises(RuntimeError): + await responder.send_webhook("test", {}) From e3f3466f2fb0b2f68c827796083aff3e77a6e8af Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Fri, 18 Mar 2022 11:16:34 -0600 Subject: [PATCH 210/872] feat: Revocation Notification V2 I believe this has been implemented as per RFC 0721 from this PR https://github.com/hyperledger/aries-rfcs/pull/721 Signed-off-by: Colton Wolkins (Indicio work address) --- .../revocation_notification/definition.py | 8 +- .../revocation_notification/v2_0/__init__.py | 0 .../v2_0/handlers/__init__.py | 0 .../v2_0/handlers/revoke_handler.py | 45 +++++ .../v2_0/handlers/tests/__init__.py | 0 .../handlers/tests/test_revoke_handler.py | 71 ++++++++ .../v2_0/message_types.py | 20 +++ .../v2_0/messages/__init__.py | 0 .../v2_0/messages/revoke.py | 73 ++++++++ .../v2_0/messages/tests/__init__.py | 0 .../v2_0/messages/tests/test_revoke.py | 10 ++ .../v2_0/models/__init__.py | 0 .../v2_0/models/rev_notification_record.py | 160 ++++++++++++++++++ .../v2_0/models/tests/__init__.py | 0 .../tests/test_rev_notification_record.py | 68 ++++++++ .../revocation_notification/v2_0/routes.py | 76 +++++++++ .../v2_0/tests/__init__.py | 0 .../v2_0/tests/test_routes.py | 140 +++++++++++++++ 18 files changed, 670 insertions(+), 1 deletion(-) create mode 100644 aries_cloudagent/protocols/revocation_notification/v2_0/__init__.py create mode 100644 aries_cloudagent/protocols/revocation_notification/v2_0/handlers/__init__.py create mode 100644 aries_cloudagent/protocols/revocation_notification/v2_0/handlers/revoke_handler.py create mode 100644 aries_cloudagent/protocols/revocation_notification/v2_0/handlers/tests/__init__.py create mode 100644 aries_cloudagent/protocols/revocation_notification/v2_0/handlers/tests/test_revoke_handler.py create mode 100644 aries_cloudagent/protocols/revocation_notification/v2_0/message_types.py create mode 100644 aries_cloudagent/protocols/revocation_notification/v2_0/messages/__init__.py create mode 100644 aries_cloudagent/protocols/revocation_notification/v2_0/messages/revoke.py create mode 100644 aries_cloudagent/protocols/revocation_notification/v2_0/messages/tests/__init__.py create mode 100644 aries_cloudagent/protocols/revocation_notification/v2_0/messages/tests/test_revoke.py create mode 100644 aries_cloudagent/protocols/revocation_notification/v2_0/models/__init__.py create mode 100644 aries_cloudagent/protocols/revocation_notification/v2_0/models/rev_notification_record.py create mode 100644 aries_cloudagent/protocols/revocation_notification/v2_0/models/tests/__init__.py create mode 100644 aries_cloudagent/protocols/revocation_notification/v2_0/models/tests/test_rev_notification_record.py create mode 100644 aries_cloudagent/protocols/revocation_notification/v2_0/routes.py create mode 100644 aries_cloudagent/protocols/revocation_notification/v2_0/tests/__init__.py create mode 100644 aries_cloudagent/protocols/revocation_notification/v2_0/tests/test_routes.py diff --git a/aries_cloudagent/protocols/revocation_notification/definition.py b/aries_cloudagent/protocols/revocation_notification/definition.py index 62bddef6f5..baf2b7b433 100644 --- a/aries_cloudagent/protocols/revocation_notification/definition.py +++ b/aries_cloudagent/protocols/revocation_notification/definition.py @@ -6,5 +6,11 @@ "minimum_minor_version": 0, "current_minor_version": 0, "path": "v1_0", - } + }, + { + "major_version": 2, + "minimum_minor_version": 0, + "current_minor_version": 0, + "path": "v2_0", + }, ] diff --git a/aries_cloudagent/protocols/revocation_notification/v2_0/__init__.py b/aries_cloudagent/protocols/revocation_notification/v2_0/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/aries_cloudagent/protocols/revocation_notification/v2_0/handlers/__init__.py b/aries_cloudagent/protocols/revocation_notification/v2_0/handlers/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/aries_cloudagent/protocols/revocation_notification/v2_0/handlers/revoke_handler.py b/aries_cloudagent/protocols/revocation_notification/v2_0/handlers/revoke_handler.py new file mode 100644 index 0000000000..2e953ed8ae --- /dev/null +++ b/aries_cloudagent/protocols/revocation_notification/v2_0/handlers/revoke_handler.py @@ -0,0 +1,45 @@ +"""Handler for revoke message.""" + +from .....messaging.base_handler import BaseHandler +from .....messaging.request_context import RequestContext +from .....messaging.responder import BaseResponder + +from ..messages.revoke import Revoke + + +class RevokeHandler(BaseHandler): + """Handler for revoke message.""" + + RECIEVED_TOPIC = "acapy::revocation-notification-v2::received" + WEBHOOK_TOPIC = "acapy::webhook::revocation-notification-v2" + + async def handle(self, context: RequestContext, responder: BaseResponder): + """Handle revoke message.""" + assert isinstance(context.message, Revoke) + self._logger.debug( + "Received notification of revocation for %s cred %s " + "with comment: %s", + context.message.revocation_format, + context.message.credential_id, + context.message.comment, + ) + # Emit a webhook + if context.settings.get("revocation.monitor_notification"): + await context.profile.notify( + self.WEBHOOK_TOPIC, + { + "revocation_format": context.message.revocation_format, + "credential_id": context.message.credential_id, + "comment": context.message.comment, + }, + ) + + # Emit an event + await context.profile.notify( + self.RECIEVED_TOPIC, + { + "revocation_format": context.message.revocation_format, + "credential_id": context.message.credential_id, + "comment": context.message.comment, + }, + ) diff --git a/aries_cloudagent/protocols/revocation_notification/v2_0/handlers/tests/__init__.py b/aries_cloudagent/protocols/revocation_notification/v2_0/handlers/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/aries_cloudagent/protocols/revocation_notification/v2_0/handlers/tests/test_revoke_handler.py b/aries_cloudagent/protocols/revocation_notification/v2_0/handlers/tests/test_revoke_handler.py new file mode 100644 index 0000000000..8d5b2357c5 --- /dev/null +++ b/aries_cloudagent/protocols/revocation_notification/v2_0/handlers/tests/test_revoke_handler.py @@ -0,0 +1,71 @@ +"""Test RevokeHandler.""" + +import pytest + +from ......config.settings import Settings +from ......core.event_bus import EventBus, MockEventBus +from ......core.in_memory import InMemoryProfile +from ......core.profile import Profile +from ......messaging.request_context import RequestContext +from ......messaging.responder import MockResponder, BaseResponder +from ...messages.revoke import Revoke +from ..revoke_handler import RevokeHandler + + +@pytest.fixture +def event_bus(): + yield MockEventBus() + + +@pytest.fixture +def responder(): + yield MockResponder() + + +@pytest.fixture +def profile(event_bus): + yield InMemoryProfile.test_profile(bind={EventBus: event_bus}) + + +@pytest.fixture +def message(): + yield Revoke(revocation_format="indy-anoncreds", credential_id="mock_cred_revocation_id", comment="mock_comment") + + +@pytest.fixture +def context(profile: Profile, message: Revoke): + request_context = RequestContext(profile) + request_context.message = message + yield request_context + + +@pytest.mark.asyncio +async def test_handle( + context: RequestContext, responder: BaseResponder, event_bus: MockEventBus +): + await RevokeHandler().handle(context, responder) + assert event_bus.events + [(_, received)] = event_bus.events + assert received.topic == RevokeHandler.RECIEVED_TOPIC + assert "revocation_format" in received.payload + assert "credential_id" in received.payload + assert "comment" in received.payload + + +@pytest.mark.asyncio +async def test_handle_monitor( + context: RequestContext, responder: BaseResponder, event_bus: MockEventBus +): + context.settings["revocation.monitor_notification"] = True + await RevokeHandler().handle(context, responder) + [(_, webhook), (_, received)] = event_bus.events + + assert webhook.topic == RevokeHandler.WEBHOOK_TOPIC + assert "revocation_format" in received.payload + assert "credential_id" in received.payload + assert "comment" in webhook.payload + + assert received.topic == RevokeHandler.RECIEVED_TOPIC + assert "revocation_format" in received.payload + assert "credential_id" in received.payload + assert "comment" in received.payload diff --git a/aries_cloudagent/protocols/revocation_notification/v2_0/message_types.py b/aries_cloudagent/protocols/revocation_notification/v2_0/message_types.py new file mode 100644 index 0000000000..4033d5c8b7 --- /dev/null +++ b/aries_cloudagent/protocols/revocation_notification/v2_0/message_types.py @@ -0,0 +1,20 @@ +"""Message type identifiers for Revocation Notification protocol.""" + +from ...didcomm_prefix import DIDCommPrefix + + +SPEC_URI = ( + "https://github.com/hyperledger/aries-rfcs/blob/main/features/" + "0721-revocation-notification-v2/README.md" +) +PROTOCOL = "revocation_notification" +VERSION = "2.0" +BASE = f"{PROTOCOL}/{VERSION}" + +# Message types +REVOKE = f"{BASE}/revoke" + +PROTOCOL_PACKAGE = "aries_cloudagent.protocols.revocation_notification.v2_0" +MESSAGE_TYPES = DIDCommPrefix.qualify_all( + {REVOKE: f"{PROTOCOL_PACKAGE}.messages.revoke.Revoke"} +) diff --git a/aries_cloudagent/protocols/revocation_notification/v2_0/messages/__init__.py b/aries_cloudagent/protocols/revocation_notification/v2_0/messages/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/aries_cloudagent/protocols/revocation_notification/v2_0/messages/revoke.py b/aries_cloudagent/protocols/revocation_notification/v2_0/messages/revoke.py new file mode 100644 index 0000000000..672bf67871 --- /dev/null +++ b/aries_cloudagent/protocols/revocation_notification/v2_0/messages/revoke.py @@ -0,0 +1,73 @@ +"""Revoke message.""" + +from marshmallow import fields, validate +from .....messaging.agent_message import AgentMessage, AgentMessageSchema +from .....messaging.decorators.please_ack_decorator import ( + PleaseAckDecorator, + PleaseAckDecoratorSchema, +) +from .....messaging.valid import UUIDFour +from ..message_types import PROTOCOL_PACKAGE, REVOKE + +HANDLER_CLASS = f"{PROTOCOL_PACKAGE}.handlers.revoke_handler.RevokeHandler" + + +class Revoke(AgentMessage): + """Class representing revoke message.""" + + class Meta: + """Revoke Meta.""" + + handler_class = HANDLER_CLASS + message_type = REVOKE + schema_class = "RevokeSchema" + + def __init__( + self, + *, + revocation_format: str, + credential_id: str, + please_ack: PleaseAckDecorator = None, + comment: str = None, + **kwargs, + ): + """Initialize revoke message.""" + super().__init__(**kwargs) + self.revocation_format = revocation_format + self.credential_id = credential_id + self.comment = comment + + +class RevokeSchema(AgentMessageSchema): + """Schema of Revoke message.""" + + class Meta: + """RevokeSchema Meta.""" + + model_class = Revoke + + revocation_format = fields.Str( + required=True, + description=( + "The format of the credential revocation ID" + ), + example="indy-anoncreds", + validate=validate.OneOf(["indy-anoncreds"]), + ) + credential_id = fields.Str( + required=True, + description=( + "Credential ID of the issued credential to be revoked" + ), + example=UUIDFour.EXAMPLE, + ) + please_ack = fields.Nested( + PleaseAckDecoratorSchema, + required=False, + description="Whether or not the holder should acknowledge receipt", + data_key="~please_ack", + ) + comment = fields.Str( + required=False, + description="Human readable information about revocation notification", + ) diff --git a/aries_cloudagent/protocols/revocation_notification/v2_0/messages/tests/__init__.py b/aries_cloudagent/protocols/revocation_notification/v2_0/messages/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/aries_cloudagent/protocols/revocation_notification/v2_0/messages/tests/test_revoke.py b/aries_cloudagent/protocols/revocation_notification/v2_0/messages/tests/test_revoke.py new file mode 100644 index 0000000000..14ec6613d6 --- /dev/null +++ b/aries_cloudagent/protocols/revocation_notification/v2_0/messages/tests/test_revoke.py @@ -0,0 +1,10 @@ +"""Test Revoke Message.""" + +from ..revoke import Revoke + + +def test_instantiate(): + msg = Revoke(revocation_format="indy-anoncreds", credential_id="test-id", comment="test") + assert msg.revocation_format == "indy-anoncreds" + assert msg.credential_id == "test-id" + assert msg.comment == "test" diff --git a/aries_cloudagent/protocols/revocation_notification/v2_0/models/__init__.py b/aries_cloudagent/protocols/revocation_notification/v2_0/models/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/aries_cloudagent/protocols/revocation_notification/v2_0/models/rev_notification_record.py b/aries_cloudagent/protocols/revocation_notification/v2_0/models/rev_notification_record.py new file mode 100644 index 0000000000..a95643c2dc --- /dev/null +++ b/aries_cloudagent/protocols/revocation_notification/v2_0/models/rev_notification_record.py @@ -0,0 +1,160 @@ +"""Store revocation notification details until revocation is published.""" + +from typing import Optional, Sequence + +from marshmallow import fields +from marshmallow.utils import EXCLUDE + + +from .....core.profile import ProfileSession +from .....messaging.models.base_record import BaseRecord, BaseRecordSchema +from .....messaging.valid import INDY_CRED_REV_ID, INDY_REV_REG_ID, UUID4 +from .....storage.error import StorageNotFoundError, StorageDuplicateError +from ..messages.revoke import Revoke + + +class RevNotificationRecord(BaseRecord): + """Revocation Notification Record.""" + + class Meta: + """RevNotificationRecord Meta.""" + + schema_class = "RevNotificationRecordSchema" + + RECORD_TYPE = "revocation_notification" + RECORD_ID_NAME = "revocation_notification_id" + TAG_NAMES = { + "rev_reg_id", + "cred_rev_id", + "connection_id", + } + + def __init__( + self, + *, + revocation_notification_id: str = None, + rev_reg_id: str = None, + cred_rev_id: str = None, + connection_id: str = None, + thread_id: str = None, + comment: str = None, + **kwargs, + ): + """Construct record.""" + super().__init__(revocation_notification_id, **kwargs) + self.rev_reg_id = rev_reg_id + self.cred_rev_id = cred_rev_id + self.connection_id = connection_id + self.thread_id = thread_id + self.comment = comment + + @property + def revocation_notification_id(self) -> Optional[str]: + """Return record id.""" + return self._id + + @property + def record_value(self) -> dict: + """Return record value.""" + return {prop: getattr(self, prop) for prop in ("thread_id", "comment")} + + @classmethod + async def query_by_ids( + cls, + session: ProfileSession, + cred_rev_id: str, + rev_reg_id: str, + ) -> "RevNotificationRecord": + """Retrieve revocation notification record by cred rev id and/or rev reg id. + + Args: + session: the profile session to use + cred_rev_id: the cred rev id by which to filter + rev_reg_id: the rev reg id by which to filter + """ + tag_filter = { + **{"cred_rev_id": cred_rev_id for _ in [""] if cred_rev_id}, + **{"rev_reg_id": rev_reg_id for _ in [""] if rev_reg_id}, + } + + result = await cls.query(session, tag_filter) + if len(result) > 1: + raise StorageDuplicateError( + "More than one RevNotificationRecord was found for the given IDs" + ) + if not result: + raise StorageNotFoundError( + "No RevNotificationRecord found for the given IDs" + ) + return result[0] + + @classmethod + async def query_by_rev_reg_id( + cls, + session: ProfileSession, + rev_reg_id: str, + ) -> Sequence["RevNotificationRecord"]: + """Retrieve revocation notification records by rev reg id. + + Args: + session: the profile session to use + rev_reg_id: the rev reg id by which to filter + """ + tag_filter = { + **{"rev_reg_id": rev_reg_id for _ in [""] if rev_reg_id}, + } + + return await cls.query(session, tag_filter) + + def to_message(self): + """Return a revocation notification constructed from this record.""" + if not self.thread_id: + raise ValueError( + "No thread ID set on revocation notification record, " + "cannot create message" + ) + return Revoke( + revocation_format="self.revocation_format", + credential_id=self.cred_rev_id, + comment=self.comment, + ) + + +class RevNotificationRecordSchema(BaseRecordSchema): + """Revocation Notification Record Schema.""" + + class Meta: + """RevNotificationRecordSchema Meta.""" + + model_class = "RevNotificationRecord" + unknown = EXCLUDE + + rev_reg_id = fields.Str( + required=False, + description="Revocation registry identifier", + **INDY_REV_REG_ID, + ) + cred_rev_id = fields.Str( + required=False, + description="Credential revocation identifier", + **INDY_CRED_REV_ID, + ) + connection_id = fields.Str( + description=( + "Connection ID to which the revocation notification will be sent; " + "required if notify is true" + ), + required=False, + **UUID4, + ) + thread_id = fields.Str( + description=( + "Thread ID of the credential exchange message thread resulting in " + "the credential now being revoked; required if notify is true" + ), + required=False, + ) + comment = fields.Str( + description="Optional comment to include in revocation notification", + required=False, + ) diff --git a/aries_cloudagent/protocols/revocation_notification/v2_0/models/tests/__init__.py b/aries_cloudagent/protocols/revocation_notification/v2_0/models/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/aries_cloudagent/protocols/revocation_notification/v2_0/models/tests/test_rev_notification_record.py b/aries_cloudagent/protocols/revocation_notification/v2_0/models/tests/test_rev_notification_record.py new file mode 100644 index 0000000000..b4b9cee438 --- /dev/null +++ b/aries_cloudagent/protocols/revocation_notification/v2_0/models/tests/test_rev_notification_record.py @@ -0,0 +1,68 @@ +"""Test RevNotificationRecord.""" + +import pytest + +from ......core.in_memory import InMemoryProfile +from ......storage.error import StorageDuplicateError, StorageNotFoundError +from ...messages.revoke import Revoke +from ..rev_notification_record import RevNotificationRecord + + +@pytest.fixture +def profile(): + yield InMemoryProfile.test_profile() + + +@pytest.fixture +def rec(): + yield RevNotificationRecord( + rev_reg_id="mock_rev_reg_id", + cred_rev_id="mock_cred_rev_id", + connection_id="mock_connection_id", + thread_id="mock_thread_id", + comment="mock_comment", + ) + + +@pytest.mark.asyncio +async def test_storage(profile, rec): + async with profile.session() as session: + await rec.save(session) + recalled = await RevNotificationRecord.retrieve_by_id( + session, rec.revocation_notification_id + ) + assert recalled == rec + recalled = await RevNotificationRecord.query_by_ids( + session, cred_rev_id="mock_cred_rev_id", rev_reg_id="mock_rev_reg_id" + ) + assert recalled == rec + [recalled] = await RevNotificationRecord.query_by_rev_reg_id( + session, rev_reg_id="mock_rev_reg_id" + ) + assert recalled == rec + + with pytest.raises(StorageNotFoundError): + await RevNotificationRecord.query_by_ids( + session, cred_rev_id="unknown", rev_reg_id="unknown" + ) + + with pytest.raises(StorageDuplicateError): + another = RevNotificationRecord( + rev_reg_id="mock_rev_reg_id", + cred_rev_id="mock_cred_rev_id", + ) + await another.save(session) + await RevNotificationRecord.query_by_ids( + session, cred_rev_id="mock_cred_rev_id", rev_reg_id="mock_rev_reg_id" + ) + + +def test_to_message(rec): + message = rec.to_message() + assert isinstance(message, Revoke) + assert message.credential_id == rec.cred_rev_id + assert message.comment == rec.comment + + with pytest.raises(ValueError): + rec.thread_id = None + rec.to_message() diff --git a/aries_cloudagent/protocols/revocation_notification/v2_0/routes.py b/aries_cloudagent/protocols/revocation_notification/v2_0/routes.py new file mode 100644 index 0000000000..83ba81fe63 --- /dev/null +++ b/aries_cloudagent/protocols/revocation_notification/v2_0/routes.py @@ -0,0 +1,76 @@ +"""Routes for revocation notification.""" +import logging +import re + +from ....core.event_bus import Event, EventBus +from ....core.profile import Profile +from ....messaging.responder import BaseResponder +from ....revocation.util import ( + REVOCATION_CLEAR_PENDING_EVENT, + REVOCATION_PUBLISHED_EVENT, + REVOCATION_EVENT_PREFIX, +) +from ....storage.error import StorageError, StorageNotFoundError +from .models.rev_notification_record import RevNotificationRecord + +LOGGER = logging.getLogger(__name__) + + +def register_events(event_bus: EventBus): + """Register to handle events.""" + event_bus.subscribe( + re.compile(f"^{REVOCATION_EVENT_PREFIX}{REVOCATION_PUBLISHED_EVENT}.*"), + on_revocation_published, + ) + event_bus.subscribe( + re.compile(f"^{REVOCATION_EVENT_PREFIX}{REVOCATION_CLEAR_PENDING_EVENT}.*"), + on_pending_cleared, + ) + + +async def on_revocation_published(profile: Profile, event: Event): + """Handle issuer revoke event.""" + LOGGER.debug("Sending notification of revocation to recipient: %s", event.payload) + + should_notify = profile.settings.get("revocation.notify", False) + responder = profile.inject(BaseResponder) + crids = event.payload.get("crids") or [] + + try: + async with profile.session() as session: + records = await RevNotificationRecord.query_by_rev_reg_id( + session, + rev_reg_id=event.payload["rev_reg_id"], + ) + records = [record for record in records if record.cred_rev_id in crids] + + for record in records: + await record.delete_record(session) + if should_notify: + await responder.send( + record.to_message(), connection_id=record.connection_id + ) + + except StorageNotFoundError: + LOGGER.info( + "No revocation notification record found for revoked credential; " + "no notification will be sent" + ) + except StorageError: + LOGGER.exception("Failed to retrieve revocation notification record") + + +async def on_pending_cleared(profile: Profile, event: Event): + """Handle pending cleared event.""" + + # Query by rev reg ID + async with profile.session() as session: + notifications = await RevNotificationRecord.query_by_rev_reg_id( + session, event.payload["rev_reg_id"] + ) + + # Delete + async with profile.transaction() as txn: + for notification in notifications: + await notification.delete_record(txn) + await txn.commit() diff --git a/aries_cloudagent/protocols/revocation_notification/v2_0/tests/__init__.py b/aries_cloudagent/protocols/revocation_notification/v2_0/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/aries_cloudagent/protocols/revocation_notification/v2_0/tests/test_routes.py b/aries_cloudagent/protocols/revocation_notification/v2_0/tests/test_routes.py new file mode 100644 index 0000000000..6fe38c848b --- /dev/null +++ b/aries_cloudagent/protocols/revocation_notification/v2_0/tests/test_routes.py @@ -0,0 +1,140 @@ +"""Test routes.py""" +from asynctest import mock +import pytest + +from .. import routes as test_module +from .....config.settings import Settings +from .....core.event_bus import Event, MockEventBus +from .....core.in_memory import InMemoryProfile +from .....core.profile import Profile +from .....messaging.responder import BaseResponder, MockResponder +from .....revocation.util import ( + REVOCATION_CLEAR_PENDING_EVENT, + REVOCATION_EVENT_PREFIX, + REVOCATION_PUBLISHED_EVENT, +) +from .....storage.error import StorageError, StorageNotFoundError + + +@pytest.fixture +def responder(): + yield MockResponder() + + +@pytest.fixture +def profile(responder): + yield InMemoryProfile.test_profile(bind={BaseResponder: responder}) + + +def test_register_events(): + """Test handlers are added on register. + + This test need not be particularly in depth to keep it from getting brittle. + """ + event_bus = MockEventBus() + test_module.register_events(event_bus) + assert event_bus.topic_patterns_to_subscribers + + +@pytest.mark.asyncio +async def test_on_revocation_published(profile: Profile, responder: MockResponder): + """Test revocation published event handler.""" + mock_rec = mock.MagicMock() + mock_rec.cred_rev_id = "mock" + mock_rec.delete_record = mock.CoroutineMock() + + MockRec = mock.MagicMock() + MockRec.query_by_rev_reg_id = mock.CoroutineMock(return_value=[mock_rec]) + + topic = f"{REVOCATION_EVENT_PREFIX}{REVOCATION_PUBLISHED_EVENT}::mock" + event = Event(topic, {"rev_reg_id": "mock", "crids": ["mock"]}) + + assert isinstance(profile.settings, Settings) + profile.settings["revocation.notify"] = True + + with mock.patch.object(test_module, "RevNotificationRecord", MockRec): + await test_module.on_revocation_published(profile, event) + + MockRec.query_by_rev_reg_id.assert_called_once() + mock_rec.delete_record.assert_called_once() + assert responder.messages + + +@pytest.mark.asyncio +async def test_on_revocation_published_no_notify( + profile: Profile, responder: MockResponder +): + """Test revocation published event handler.""" + mock_rec = mock.MagicMock() + mock_rec.cred_rev_id = "mock" + mock_rec.delete_record = mock.CoroutineMock() + + MockRec = mock.MagicMock() + MockRec.query_by_rev_reg_id = mock.CoroutineMock(return_value=[mock_rec]) + + topic = f"{REVOCATION_EVENT_PREFIX}{REVOCATION_PUBLISHED_EVENT}::mock" + event = Event(topic, {"rev_reg_id": "mock", "crids": ["mock"]}) + + assert isinstance(profile.settings, Settings) + profile.settings["revocation.notify"] = False + + with mock.patch.object(test_module, "RevNotificationRecord", MockRec): + await test_module.on_revocation_published(profile, event) + + MockRec.query_by_rev_reg_id.assert_called_once() + mock_rec.delete_record.assert_called_once() + assert not responder.messages + + +@pytest.mark.asyncio +async def test_on_revocation_published_x_not_found( + profile: Profile, responder: MockResponder +): + """Test revocation published event handler.""" + MockRec = mock.MagicMock() + MockRec.query_by_rev_reg_id = mock.CoroutineMock(side_effect=StorageNotFoundError) + + topic = f"{REVOCATION_EVENT_PREFIX}{REVOCATION_PUBLISHED_EVENT}::mock" + event = Event(topic, {"rev_reg_id": "mock", "crids": ["mock"]}) + + with mock.patch.object(test_module, "RevNotificationRecord", MockRec): + await test_module.on_revocation_published(profile, event) + + MockRec.query_by_rev_reg_id.assert_called_once() + assert not responder.messages + + +@pytest.mark.asyncio +async def test_on_revocation_published_x_storage_error( + profile: Profile, responder: MockResponder +): + """Test revocation published event handler.""" + MockRec = mock.MagicMock() + MockRec.query_by_rev_reg_id = mock.CoroutineMock(side_effect=StorageError) + + topic = f"{REVOCATION_EVENT_PREFIX}{REVOCATION_PUBLISHED_EVENT}::mock" + event = Event(topic, {"rev_reg_id": "mock", "crids": ["mock"]}) + + with mock.patch.object(test_module, "RevNotificationRecord", MockRec): + await test_module.on_revocation_published(profile, event) + + MockRec.query_by_rev_reg_id.assert_called_once() + assert not responder.messages + + +@pytest.mark.asyncio +async def test_on_pending_cleared(profile: Profile): + """Test pending revocation cleared event.""" + mock_rec = mock.MagicMock() + mock_rec.delete_record = mock.CoroutineMock() + + MockRec = mock.MagicMock() + MockRec.query_by_rev_reg_id = mock.CoroutineMock(return_value=[mock_rec]) + + topic = f"{REVOCATION_EVENT_PREFIX}{REVOCATION_CLEAR_PENDING_EVENT}::mock" + event = Event(topic, {"rev_reg_id": "mock"}) + + with mock.patch.object(test_module, "RevNotificationRecord", MockRec): + await test_module.on_pending_cleared(profile, event) + + mock_rec.delete_record.assert_called_once() From c822250a490e085ae4ee027861c6e93c2d67eee5 Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Tue, 12 Apr 2022 06:17:17 -0600 Subject: [PATCH 211/872] fix: Fix output of Revoc Notif V2 Fix the output message for Revocation Notification V2 Signed-off-by: Colton Wolkins (Indicio work address) --- .../v2_0/models/rev_notification_record.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/protocols/revocation_notification/v2_0/models/rev_notification_record.py b/aries_cloudagent/protocols/revocation_notification/v2_0/models/rev_notification_record.py index a95643c2dc..f1a913a727 100644 --- a/aries_cloudagent/protocols/revocation_notification/v2_0/models/rev_notification_record.py +++ b/aries_cloudagent/protocols/revocation_notification/v2_0/models/rev_notification_record.py @@ -114,8 +114,8 @@ def to_message(self): "cannot create message" ) return Revoke( - revocation_format="self.revocation_format", - credential_id=self.cred_rev_id, + revocation_format="indy-anoncreds", + credential_id=f"{self.rev_reg_id}::{self.cred_rev_id}", comment=self.comment, ) From 26ccb0d3169f3ce88f761e9f4c78a00857f118c7 Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Tue, 19 Apr 2022 08:40:35 -0600 Subject: [PATCH 212/872] ci: Fix black formatting errors Signed-off-by: Colton Wolkins (Indicio work address) --- .../v2_0/handlers/revoke_handler.py | 3 +-- .../v2_0/handlers/tests/test_revoke_handler.py | 6 +++++- .../revocation_notification/v2_0/messages/revoke.py | 8 ++------ .../v2_0/messages/tests/test_revoke.py | 6 +++++- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/aries_cloudagent/protocols/revocation_notification/v2_0/handlers/revoke_handler.py b/aries_cloudagent/protocols/revocation_notification/v2_0/handlers/revoke_handler.py index 2e953ed8ae..f2ffafe7e0 100644 --- a/aries_cloudagent/protocols/revocation_notification/v2_0/handlers/revoke_handler.py +++ b/aries_cloudagent/protocols/revocation_notification/v2_0/handlers/revoke_handler.py @@ -17,8 +17,7 @@ async def handle(self, context: RequestContext, responder: BaseResponder): """Handle revoke message.""" assert isinstance(context.message, Revoke) self._logger.debug( - "Received notification of revocation for %s cred %s " - "with comment: %s", + "Received notification of revocation for %s cred %s with comment: %s", context.message.revocation_format, context.message.credential_id, context.message.comment, diff --git a/aries_cloudagent/protocols/revocation_notification/v2_0/handlers/tests/test_revoke_handler.py b/aries_cloudagent/protocols/revocation_notification/v2_0/handlers/tests/test_revoke_handler.py index 8d5b2357c5..a93a314e23 100644 --- a/aries_cloudagent/protocols/revocation_notification/v2_0/handlers/tests/test_revoke_handler.py +++ b/aries_cloudagent/protocols/revocation_notification/v2_0/handlers/tests/test_revoke_handler.py @@ -29,7 +29,11 @@ def profile(event_bus): @pytest.fixture def message(): - yield Revoke(revocation_format="indy-anoncreds", credential_id="mock_cred_revocation_id", comment="mock_comment") + yield Revoke( + revocation_format="indy-anoncreds", + credential_id="mock_cred_revocation_id", + comment="mock_comment", + ) @pytest.fixture diff --git a/aries_cloudagent/protocols/revocation_notification/v2_0/messages/revoke.py b/aries_cloudagent/protocols/revocation_notification/v2_0/messages/revoke.py index 672bf67871..93c0829a2a 100644 --- a/aries_cloudagent/protocols/revocation_notification/v2_0/messages/revoke.py +++ b/aries_cloudagent/protocols/revocation_notification/v2_0/messages/revoke.py @@ -48,17 +48,13 @@ class Meta: revocation_format = fields.Str( required=True, - description=( - "The format of the credential revocation ID" - ), + description=("The format of the credential revocation ID"), example="indy-anoncreds", validate=validate.OneOf(["indy-anoncreds"]), ) credential_id = fields.Str( required=True, - description=( - "Credential ID of the issued credential to be revoked" - ), + description=("Credential ID of the issued credential to be revoked"), example=UUIDFour.EXAMPLE, ) please_ack = fields.Nested( diff --git a/aries_cloudagent/protocols/revocation_notification/v2_0/messages/tests/test_revoke.py b/aries_cloudagent/protocols/revocation_notification/v2_0/messages/tests/test_revoke.py index 14ec6613d6..c20c93ee1a 100644 --- a/aries_cloudagent/protocols/revocation_notification/v2_0/messages/tests/test_revoke.py +++ b/aries_cloudagent/protocols/revocation_notification/v2_0/messages/tests/test_revoke.py @@ -4,7 +4,11 @@ def test_instantiate(): - msg = Revoke(revocation_format="indy-anoncreds", credential_id="test-id", comment="test") + msg = Revoke( + revocation_format="indy-anoncreds", + credential_id="test-id", + comment="test", + ) assert msg.revocation_format == "indy-anoncreds" assert msg.credential_id == "test-id" assert msg.comment == "test" From 46d402ebd2aa18fd2c425d7c21a3fca2c2834950 Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Tue, 19 Apr 2022 09:21:06 -0600 Subject: [PATCH 213/872] ci: Fix unit test Signed-off-by: Colton Wolkins (Indicio work address) --- .../v2_0/models/tests/test_rev_notification_record.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aries_cloudagent/protocols/revocation_notification/v2_0/models/tests/test_rev_notification_record.py b/aries_cloudagent/protocols/revocation_notification/v2_0/models/tests/test_rev_notification_record.py index b4b9cee438..e06be17091 100644 --- a/aries_cloudagent/protocols/revocation_notification/v2_0/models/tests/test_rev_notification_record.py +++ b/aries_cloudagent/protocols/revocation_notification/v2_0/models/tests/test_rev_notification_record.py @@ -60,7 +60,7 @@ async def test_storage(profile, rec): def test_to_message(rec): message = rec.to_message() assert isinstance(message, Revoke) - assert message.credential_id == rec.cred_rev_id + assert message.credential_id == f"{rec.rev_reg_id}::{rec.cred_rev_id}" assert message.comment == rec.comment with pytest.raises(ValueError): From 91d8d176f60203aea76008794683d4a32c1832ba Mon Sep 17 00:00:00 2001 From: Akiff Manji Date: Tue, 19 Apr 2022 12:50:44 -0700 Subject: [PATCH 214/872] Formatting fix to faber presentation request webwook Signed-off-by: Akiff Manji --- demo/runners/faber.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/demo/runners/faber.py b/demo/runners/faber.py index 1d127cefcb..78a3ad552b 100644 --- a/demo/runners/faber.py +++ b/demo/runners/faber.py @@ -616,8 +616,7 @@ async def main(args): ) + "/webhooks" ) - + f"/pres_req/{pres_req_id}/" - ) + ) + f"/pres_req/{pres_req_id}/" log_msg(f"Proof request url: {url}") qr = QRCode(border=1) qr.add_data(url) From 3077e499a5dc7d313109a5611fafe92997fb6b9f Mon Sep 17 00:00:00 2001 From: Philipp Etschel Date: Wed, 20 Apr 2022 12:31:51 +0200 Subject: [PATCH 215/872] added existing wrappers section Signed-off-by: Philipp Etschel --- UsingOpenAPI.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/UsingOpenAPI.md b/UsingOpenAPI.md index f0a367d596..9523f8245e 100644 --- a/UsingOpenAPI.md +++ b/UsingOpenAPI.md @@ -26,6 +26,22 @@ Two major open source code generation tools are [Swagger](https://github.com/swa Another suggestion for code generation is to keep the `modelPropertyNaming` set to `original` when generating code. Although it is tempting to try and enable marshalling into standard naming formats such as `camelCase`, the reality is that the models represent what is sent on the wire and documented in the [Aries Protocol RFCS](https://github.com/hyperledger/aries-rfcs/tree/master/features). It has proven handy to be able to see code references correspond directly with protocol RFCs when debugging. It will also correspond directly with what the `model` shows when looking at the ACA-py `Swagger UI` in a browser if you need to try something out manually before coding. One final point is that on occasions it has been discovered that the code generation tools don't always get the marshalling correct in all circumstances when changing model name format. +## Existing Language Wrappers for ACA-py + +### Python + +- https://pypi.org/project/aries-cloudcontroller/ + - https://github.com/didx-xyz/aries-cloudcontroller-python +- https://github.com/bcgov/traction/tree/develop/services/traction/acapy_client +- https://github.com/Indicio-tech/acapy-client + +### Go + +- https://github.com/ldej/go-acapy-client + +### Java + +- https://github.com/hyperledger-labs/acapy-java-client From 103c9e3654f5e3a4145f2d019e8e00c8eb3a6a48 Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Wed, 20 Apr 2022 09:58:41 -0700 Subject: [PATCH 216/872] fix iteration over key list Signed-off-by: Andrew Whitehead --- aries_cloudagent/askar/didcomm/v2.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/askar/didcomm/v2.py b/aries_cloudagent/askar/didcomm/v2.py index 514590f626..c74b5cd9e7 100644 --- a/aries_cloudagent/askar/didcomm/v2.py +++ b/aries_cloudagent/askar/didcomm/v2.py @@ -262,7 +262,7 @@ async def unpack_message( recip_kid = None for kid in wrapper.recipient_key_ids: recip_key_entry = next( - await session.fetch_all_keys(tag_filter={"kid": kid}), None + iter(await session.fetch_all_keys(tag_filter={"kid": kid})), None ) if recip_key_entry: recip_kid = kid @@ -289,7 +289,7 @@ async def unpack_message( # FIXME - will need to insert proper sender key resolution method here # instead of looking in the wallet sender_key_entry = next( - await session.fetch_all_keys(tag_filter={"kid": sender_kid}), None + iter(await session.fetch_all_keys(tag_filter={"kid": sender_kid})), None ) if not sender_key_entry: raise DidcommEnvelopeError("Sender public key not found") From 760f1945edca37786f7ed925db60da6dc84cefa3 Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Wed, 20 Apr 2022 09:59:17 -0700 Subject: [PATCH 217/872] update askar minimum version to 0.2.5 Signed-off-by: Andrew Whitehead --- requirements.askar.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.askar.txt b/requirements.askar.txt index 3655606766..6a44c42e39 100644 --- a/requirements.askar.txt +++ b/requirements.askar.txt @@ -1,3 +1,3 @@ -aries-askar~=0.2.4 +aries-askar~=0.2.5 indy-credx~=0.3 indy-vdr~=0.3.3 From 1e17098ed9b40604bed9ffd2eaa1077c83158818 Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Tue, 19 Apr 2022 15:53:35 -0600 Subject: [PATCH 218/872] feat: Add the ability to deny specific plugins from loading As part of #1734, I found that the Revocation Notification V2 code failed to properly run due to the fact that the V1 code was deleting the DB objects before V2 could get to it. @dbluhm and I decided that adding an option to deny specific plugins from loading was one of the better solutions to the problem. Before the plugin has been initialized, any plugins listed within `--deny-plugins` will be unregistered to prevent them from initializing and potentially interferring with other plugins that have conflicts. Signed-off-by: Colton Wolkins (Indicio work address) --- aries_cloudagent/config/argparse.py | 17 +++++++++++++++++ aries_cloudagent/config/default_context.py | 3 +++ aries_cloudagent/core/plugin_registry.py | 7 +++++++ .../core/tests/test_plugin_registry.py | 19 +++++++++++++++++++ 4 files changed, 46 insertions(+) diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index 4b91b22aad..0216ebeaa1 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -534,6 +534,20 @@ def add_arguments(self, parser: ArgumentParser): ), ) + parser.add_argument( + "--deny-plugin", + dest="denied_plugins", + type=str, + action="append", + required=False, + metavar="", + env_var="ACAPY_DENY_PLUGIN", + help=( + "Deny plugin module from loading. Multiple " + "instances of this parameter can be specified." + ), + ) + parser.add_argument( "--plugin-config", dest="plugin_config", @@ -611,6 +625,9 @@ def get_settings(self, args: Namespace) -> dict: if args.external_plugins: settings["external_plugins"] = args.external_plugins + if args.denied_plugins: + settings["denied_plugins"] = args.denied_plugins + if args.plugin_config: with open(args.plugin_config, "r") as stream: settings["plugin_config"] = yaml.safe_load(stream) diff --git a/aries_cloudagent/config/default_context.py b/aries_cloudagent/config/default_context.py index 4e9b0eddef..d92531f1ab 100644 --- a/aries_cloudagent/config/default_context.py +++ b/aries_cloudagent/config/default_context.py @@ -136,5 +136,8 @@ async def load_plugins(self, context: InjectionContext): for plugin_path in self.settings.get("external_plugins", []): plugin_registry.register_plugin(plugin_path) + for plugin_path in self.settings.get("deny_plugins", []): + plugin_registry.unregister_plugin(plugin_path) + # Register message protocols await plugin_registry.init_context(context) diff --git a/aries_cloudagent/core/plugin_registry.py b/aries_cloudagent/core/plugin_registry.py index 193c695241..1a4aa62fc5 100644 --- a/aries_cloudagent/core/plugin_registry.py +++ b/aries_cloudagent/core/plugin_registry.py @@ -177,6 +177,13 @@ def register_plugin(self, module_name: str) -> ModuleType: # self._plugins[module_name] = mod # return mod + def unregister_plugin(self, module_name: str): + """Unregister a plugin module. MUST BE CALLED BEFORE INITIALIZATION.""" + if module_name not in self._plugins: + return + + del self._plugins[module_name] + def register_package(self, package_name: str) -> Sequence[ModuleType]: """Register all modules (sub-packages) under a given package name.""" try: diff --git a/aries_cloudagent/core/tests/test_plugin_registry.py b/aries_cloudagent/core/tests/test_plugin_registry.py index 7213e96b7e..2b16a644e4 100644 --- a/aries_cloudagent/core/tests/test_plugin_registry.py +++ b/aries_cloudagent/core/tests/test_plugin_registry.py @@ -478,6 +478,25 @@ class MODULE: ] assert self.registry.register_plugin("dummy") == obj + async def test_unregister_plugin_has_setup(self): + class MODULE: + setup = "present" + + obj = MODULE() + with async_mock.patch.object( + ClassLoader, "load_module", async_mock.MagicMock() + ) as load_module: + load_module.side_effect = [ + obj, # module + None, # routes + None, # message types + None, # definition without versions attr + ] + assert self.registry.register_plugin("dummy") == obj + assert "dummy" in self.registry._plugins.keys() + self.registry.unregister_plugin("dummy") + assert "dummy" not in self.registry._plugins.keys() + async def test_register_definitions_malformed(self): class MODULE: no_setup = "no setup attr" From 3166d19c1d9173ed67dc56a3f59c043348edd960 Mon Sep 17 00:00:00 2001 From: Thomas Diesler Date: Tue, 26 Apr 2022 14:15:20 +0200 Subject: [PATCH 219/872] [#1745] Fetch from --genesis-url likely to fail in composed container setup Signed-off-by: Thomas Diesler --- aries_cloudagent/config/ledger.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/config/ledger.py b/aries_cloudagent/config/ledger.py index d98b9ad0e5..8ff0f66640 100644 --- a/aries_cloudagent/config/ledger.py +++ b/aries_cloudagent/config/ledger.py @@ -31,7 +31,9 @@ async def fetch_genesis_transactions(genesis_url: str) -> str: headers["Content-Type"] = "application/json" LOGGER.info("Fetching genesis transactions from: %s", genesis_url) try: - return await fetch(genesis_url, headers=headers) + # Fetch from --genesis-url likely to fail in composed container setup + # https://github.com/hyperledger/aries-cloudagent-python/issues/1745 + return await fetch(genesis_url, headers=headers, max_attempts=20) except FetchError as e: raise ConfigError("Error retrieving ledger genesis transactions") from e From c6d79e8cb14ce0ba4cbbd5a720edeee44ce34129 Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Wed, 27 Apr 2022 12:00:14 -0600 Subject: [PATCH 220/872] feat: Implement Plugin Blocklist based on Timo's suggestion Signed-off-by: Colton Wolkins (Indicio work address) --- aries_cloudagent/config/argparse.py | 12 ++++++------ aries_cloudagent/config/default_context.py | 7 +++---- aries_cloudagent/core/plugin_registry.py | 15 ++++++--------- .../core/tests/test_plugin_registry.py | 9 ++++----- 4 files changed, 19 insertions(+), 24 deletions(-) diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index 0216ebeaa1..8dab11d7c5 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -535,15 +535,15 @@ def add_arguments(self, parser: ArgumentParser): ) parser.add_argument( - "--deny-plugin", - dest="denied_plugins", + "--block-plugin", + dest="blocked_plugins", type=str, action="append", required=False, metavar="", - env_var="ACAPY_DENY_PLUGIN", + env_var="ACAPY_BLOCKED_PLUGIN", help=( - "Deny plugin module from loading. Multiple " + "Block plugin module from loading. Multiple " "instances of this parameter can be specified." ), ) @@ -625,8 +625,8 @@ def get_settings(self, args: Namespace) -> dict: if args.external_plugins: settings["external_plugins"] = args.external_plugins - if args.denied_plugins: - settings["denied_plugins"] = args.denied_plugins + if args.blocked_plugins: + settings["blocked_plugins"] = args.blocked_plugins if args.plugin_config: with open(args.plugin_config, "r") as stream: diff --git a/aries_cloudagent/config/default_context.py b/aries_cloudagent/config/default_context.py index d92531f1ab..fb0867cddc 100644 --- a/aries_cloudagent/config/default_context.py +++ b/aries_cloudagent/config/default_context.py @@ -111,7 +111,9 @@ async def bind_providers(self, context: InjectionContext): async def load_plugins(self, context: InjectionContext): """Set up plugin registry and load plugins.""" - plugin_registry = PluginRegistry() + plugin_registry = PluginRegistry( + blocklist=self.settings.get("blocked_plugins", []) + ) context.injector.bind_instance(PluginRegistry, plugin_registry) # Register standard protocol plugins @@ -136,8 +138,5 @@ async def load_plugins(self, context: InjectionContext): for plugin_path in self.settings.get("external_plugins", []): plugin_registry.register_plugin(plugin_path) - for plugin_path in self.settings.get("deny_plugins", []): - plugin_registry.unregister_plugin(plugin_path) - # Register message protocols await plugin_registry.init_context(context) diff --git a/aries_cloudagent/core/plugin_registry.py b/aries_cloudagent/core/plugin_registry.py index 1a4aa62fc5..d75dd103ed 100644 --- a/aries_cloudagent/core/plugin_registry.py +++ b/aries_cloudagent/core/plugin_registry.py @@ -3,7 +3,7 @@ import logging from collections import OrderedDict from types import ModuleType -from typing import Sequence +from typing import Sequence, Iterable from ..config.injection_context import InjectionContext from ..core.event_bus import EventBus @@ -19,9 +19,10 @@ class PluginRegistry: """Plugin registry for indexing application plugins.""" - def __init__(self): + def __init__(self, blocklist: Iterable[str] = []): """Initialize a `PluginRegistry` instance.""" self._plugins = OrderedDict() + self._blocklist = set(blocklist) @property def plugin_names(self) -> Sequence[str]: @@ -119,6 +120,9 @@ def register_plugin(self, module_name: str) -> ModuleType: """Register a plugin module.""" if module_name in self._plugins: mod = self._plugins[module_name] + elif module_name in self._blocklist: + LOGGER.debug(f"Blocked {module_name} from loading due to blocklist") + return None else: try: mod = ClassLoader.load_module(module_name) @@ -177,13 +181,6 @@ def register_plugin(self, module_name: str) -> ModuleType: # self._plugins[module_name] = mod # return mod - def unregister_plugin(self, module_name: str): - """Unregister a plugin module. MUST BE CALLED BEFORE INITIALIZATION.""" - if module_name not in self._plugins: - return - - del self._plugins[module_name] - def register_package(self, package_name: str) -> Sequence[ModuleType]: """Register all modules (sub-packages) under a given package name.""" try: diff --git a/aries_cloudagent/core/tests/test_plugin_registry.py b/aries_cloudagent/core/tests/test_plugin_registry.py index 2b16a644e4..7d4d86a9e4 100644 --- a/aries_cloudagent/core/tests/test_plugin_registry.py +++ b/aries_cloudagent/core/tests/test_plugin_registry.py @@ -15,7 +15,8 @@ class TestPluginRegistry(AsyncTestCase): def setUp(self): - self.registry = PluginRegistry() + self.blocked_module = "blocked_module" + self.registry = PluginRegistry(blocklist=[self.blocked_module]) self.context = InjectionContext(enforce_typing=False) self.proto_registry = async_mock.MagicMock( @@ -492,10 +493,8 @@ class MODULE: None, # message types None, # definition without versions attr ] - assert self.registry.register_plugin("dummy") == obj - assert "dummy" in self.registry._plugins.keys() - self.registry.unregister_plugin("dummy") - assert "dummy" not in self.registry._plugins.keys() + assert self.registry.register_plugin(self.blocked_module) == None + assert self.blocked_module not in self.registry._plugins.keys() async def test_register_definitions_malformed(self): class MODULE: From 223b60f2277adfc701b1aee6519f60fb56f2efc3 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Wed, 27 Apr 2022 11:20:39 -0700 Subject: [PATCH 221/872] Updates to tag and RC1 release Signed-off-by: Stephen Curran --- CHANGELOG.md | 14 ++++++++++++-- PUBLISHING.md | 20 +++++++++++--------- aries_cloudagent/version.py | 2 +- open-api/openapi.json | 2 +- 4 files changed, 25 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb2b01a765..65317eb1fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,8 +14,8 @@ by an organization, ideally with a controller starter kit to allow an approvals business flow. A lot of work has been put in for this release related to performance and load -testing, with updates being made to the key "shared component" ACA-Py -dependencies ([Aries Askar](https://github.com/bcgov/aries-askar), [Indy +testing, with significant updates being made to the key "shared component" +ACA-Py dependencies ([Aries Askar](https://github.com/bcgov/aries-askar), [Indy VDR](https://github.comyperledger/indy-vdr)) and [Indy Shared RS (including CredX)](https://github.com/hyperledger/indy-shared-rs). We now recommend using those components (by using Askar for the wallet type in the startup parameters) @@ -50,6 +50,7 @@ stuff needed for a growing codebase. - Auto-promote author did to public after endorsing [\#1607](https://github.com/hyperledger/aries-cloudagent-python/pull/1607) ([ianco](https://github.com/ianco)) - DID updates for endorser [\#1601](https://github.com/hyperledger/aries-cloudagent-python/pull/1601) ([ianco](https://github.com/ianco)) - Qualify did exch connection lookup by role [\#1670](https://github.com/hyperledger/aries-cloudagent-python/pull/1670) ([ianco](https://github.com/ianco)) + - Use provided connection_id if provided [\#1726](https://github.com/hyperledger/aries-cloudagent-python/pull/1726) ([ianco](https://github.com/ianco)) - Additions to the startup parameters, Admin API and Web Hooks - feat: accept taa using startup parameter --accept-taa [\#1643](https://github.com/hyperledger/aries-cloudagent-python/pull/1643) ([TimoGlastra](https://github.com/TimoGlastra)) @@ -76,8 +77,15 @@ stuff needed for a growing codebase. - Fixes and cleanups for issue-credential 1.0 [\#1619](https://github.com/hyperledger/aries-cloudagent-python/pull/1619) ([andrewwhitehead](https://github.com/andrewwhitehead)) - Fixes for v7.3.0 - Issue [\#1597](https://github.com/hyperledger/aries-cloudagent-python/issues/1597) [\#1711](https://github.com/hyperledger/aries-cloudagent-python/pull/1711) ([shaangill025](https://github.com/shaangill025)) - Fixes Issue 1 from [\#1597](https://github.com/hyperledger/aries-cloudagent-python/issues/1597): Tails file upload fails when a credDef is created and multi ledger support is enabled + +- Mediator updates and fixes + - feat: allow querying default mediator from base wallet [\#1729](https://github.com/hyperledger/aries-cloudagent-python/pull/1729) ([dbluhm](https://github.com/dbluhm)) + +- Multitenacy updates and fixes + - feat: create new JWT tokens and invalidate older for multitenancy [\#1725](https://github.com/hyperledger/aries-cloudagent-python/pull/1725) ([TimoGlastra](https://github.com/TimoGlastra)) - Dependencies and internal code updates/fixes + - Fix iteration over key list, update Askar to 0.2.5 [\#1740](https://github.com/hyperledger/aries-cloudagent-python/pull/1740) ([andrewwhitehead](https://github.com/andrewwhitehead)) - Fix: update IndyLedgerRequestsExecutor logic - multitenancy and basic base wallet type [\#1700](https://github.com/hyperledger/aries-cloudagent-python/pull/1700) ([shaangill025](https://github.com/shaangill025)) - Move database operations inside the session context [\#1633](https://github.com/hyperledger/aries-cloudagent-python/pull/1633) ([acuderman](https://github.com/acuderman)) - Upgrade ConfigArgParse to version 1.5.3 [\#1627](https://github.com/hyperledger/aries-cloudagent-python/pull/1627) ([WadeBarnes](https://github.com/WadeBarnes)) @@ -88,10 +96,12 @@ stuff needed for a growing codebase. - Add an integration test for mixed proof with a revocable cred and a n… [\#1672](https://github.com/hyperledger/aries-cloudagent-python/pull/1672) ([ianco](https://github.com/ianco)) - Documentation and Demo Updates + - Fixes logic for web hook formatter in Faber demo [\#1739](https://github.com/hyperledger/aries-cloudagent-python/pull/1739) ([amanji](https://github.com/amanji)) - Multitenancy Docs Update [\#1706](https://github.com/hyperledger/aries-cloudagent-python/pull/1706) ([MonolithicMonk](https://github.com/MonolithicMonk)) - [\#1674](https://github.com/hyperledger/aries-cloudagent-python/issue/1674) Add basic DOCKER\_ENV logging for run\_demo [\#1675](https://github.com/hyperledger/aries-cloudagent-python/pull/1675) ([tdiesler](https://github.com/tdiesler)) - Performance demo updates [\#1647](https://github.com/hyperledger/aries-cloudagent-python/pull/1647) ([ianco](https://github.com/ianco)) - docs: supported features attribution [\#1654](https://github.com/hyperledger/aries-cloudagent-python/pull/1654) ([TimoGlastra](https://github.com/TimoGlastra)) + - Documentation on existing language wrappers for aca-py [\#1738](https://github.com/hyperledger/aries-cloudagent-python/pull/1738) ([etschelp](https://github.com/etschelp)) - Code management and contributor/developer support updates - Pin markupsafe at version 2.0.1 [\#1642](https://github.com/hyperledger/aries-cloudagent-python/pull/1642) ([andrewwhitehead](https://github.com/andrewwhitehead)) diff --git a/PUBLISHING.md b/PUBLISHING.md index 625a7f5673..3fda774ed8 100644 --- a/PUBLISHING.md +++ b/PUBLISHING.md @@ -6,9 +6,11 @@ a major, minor or patch release, per [semver](https://semver.org/) rules. Once ready to do a release, create a local branch that includes the following updates: -1. Update the CHANGELOG.md to add the new release. If transitioning for a Release Candidate to the final release for the tag, do not create a new section -- just drop the "RC" designation. Check the date of the new release. +1. Create a PR branch from an updated `main` branch. -2. Include details of the merged PRs included in this release. General process to follow: +2. Update the CHANGELOG.md to add the new release. If transitioning for a Release Candidate to the final release for the tag, do not create a new section -- just drop the "RC" designation. Check the date of the new release. + +3. Include details of the merged PRs included in this release. General process to follow: - Gather the set of PRs since the last release and put them into a list. A good tool to use for this is the [github-changelog-generator](https://github.com/github-changelog-generator/github-changelog-generator). Steps: - Create a read only GitHub token for your account on this page: [https://github.com/settings/tokens](https://github.com/settings/tokens/new?description=GitHub%20Changelog%20Generator%20token) with a scope of `repo` / `public_repo`. @@ -17,16 +19,16 @@ Once ready to do a release, create a local branch that includes the following up - Organize the list into suitable categories, update (if necessary) the PR description and add notes to clarify the changes. See previous release entries to understand the style -- a format should help developers. - Add a narrative about the release above the PR that highlights what has gone into the release. -3. Update the ReadTheDocs in the `/docs` folder by following the instructions in the `docs/README.md` file. That will likely add a number of new and modified files to the PR. Eliminate all of the errors in the generation process, either by mocking external dependencies or by fixing ACA-Py code. If necessary, create an issue with the errors and assign it to the appropriate developer. Experience has demonstrated to use that documentation generation errors should be fixed in the code. +4. Update the ReadTheDocs in the `/docs` folder by following the instructions in the `docs/README.md` file. That will likely add a number of new and modified files to the PR. Eliminate all of the errors in the generation process, either by mocking external dependencies or by fixing ACA-Py code. If necessary, create an issue with the errors and assign it to the appropriate developer. Experience has demonstrated to use that documentation generation errors should be fixed in the code. -4. Update the version number listed in [aries_cloudagent/version.py](aries_cloudagent/version.py) and, prefixed with a "v" in [open-api/openapi.json](open-api/openapi.json) (e.g. "0.7.2" in the version.py file and "v0.7.2" in the openapi.json file). The incremented version number should adhere to the [Semantic Versioning Specification](https://semver.org/#semantic-versioning-specification-semver) based on the changes since the last published release. For Release Candidates, the form of the tag is "0.7.2-rc0". +5. Update the version number listed in [aries_cloudagent/version.py](aries_cloudagent/version.py) and, prefixed with a "v" in [open-api/openapi.json](open-api/openapi.json) (e.g. "0.7.2" in the version.py file and "v0.7.2" in the openapi.json file). The incremented version number should adhere to the [Semantic Versioning Specification](https://semver.org/#semantic-versioning-specification-semver) based on the changes since the last published release. For Release Candidates, the form of the tag is "0.7.2-rc0". -5. An extra search of the repo for the existing tag is recommended to see if there are any other instances of the tag in the repo. If any are found to be required (other than in CHANGELOG.md and the examples in this file, of course), finding a way to not need them is best, but if they are needed, please update this document to note where the tag can be found. +6. An extra search of the repo for the existing tag is recommended to see if there are any other instances of the tag in the repo. If any are found to be required (other than in CHANGELOG.md and the examples in this file, of course), finding a way to not need them is best, but if they are needed, please update this document to note where the tag can be found. -6. Double check all of these steps above, and then create a PR from the branch. If there are still further changes to be merged, mark the PR as "Draft", repeat **ALL** of the steps again, and then mark this PR as ready and then wait until it is merged. +7. Double check all of these steps above, and then submit a PR from the branch. If there are still further changes to be merged, mark the PR as "Draft", repeat **ALL** of the steps again, and then mark this PR as ready and then wait until it is merged. -7. Immediately after it is merged, create a new GitHub tag representing the version. The tag name and title of the release should be the same as the version in [aries_cloudagent/version.py](aries_cloudagent/version.py). Use the "Generate Release Notes" capability to get a sequential listing of the PRs in the release, to complement the manually curated Changelog. Verify on PyPi that the version is published. +8. Immediately after it is merged, create a new GitHub tag representing the version. The tag name and title of the release should be the same as the version in [aries_cloudagent/version.py](aries_cloudagent/version.py). Use the "Generate Release Notes" capability to get a sequential listing of the PRs in the release, to complement the manually curated Changelog. Verify on PyPi that the version is published. -8. Publish a new docker container on Docker Hub ([bcgovimages/aries-cloudagent](https://hub.docker.com/r/bcgovimages/aries-cloudagent/)) by following the README.md instructions to create a PR for the release in the repository [https://github.com/bcgov/aries-cloudagent-container](https://github.com/bcgov/aries-cloudagent-container). Appropriate permissions are required to publish the image. +9. Publish a new docker container on Docker Hub ([bcgovimages/aries-cloudagent](https://hub.docker.com/r/bcgovimages/aries-cloudagent/)) by following the README.md instructions to create a PR for the release in the repository [https://github.com/bcgov/aries-cloudagent-container](https://github.com/bcgov/aries-cloudagent-container). Appropriate permissions are required to publish the image. -9. Update the ACA-Py Read The Docs site by building the new "latest" (main branch) and activating and building the new release. Appropriate permissions are required to publish the new documentation version. +10. Update the ACA-Py Read The Docs site by building the new "latest" (main branch) and activating and building the new release. Appropriate permissions are required to publish the new documentation version. diff --git a/aries_cloudagent/version.py b/aries_cloudagent/version.py index 273fd7a0e6..ddabac341b 100644 --- a/aries_cloudagent/version.py +++ b/aries_cloudagent/version.py @@ -1,4 +1,4 @@ """Library version information.""" -__version__ = "0.7.4-rc0" +__version__ = "0.7.4-rc1" RECORD_TYPE_ACAPY_VERSION = "acapy_version" diff --git a/open-api/openapi.json b/open-api/openapi.json index 3f56197218..f6a0451648 100644 --- a/open-api/openapi.json +++ b/open-api/openapi.json @@ -1,7 +1,7 @@ { "swagger" : "2.0", "info" : { - "version" : "v0.7.4-rc0", + "version" : "v0.7.4-rc1", "title" : "Aries Cloud Agent" }, "tags" : [ { From 073684cda02da4721273350daeed0a71be935fc7 Mon Sep 17 00:00:00 2001 From: "arin.hlaj" Date: Thu, 28 Apr 2022 11:19:16 +0200 Subject: [PATCH 222/872] Added async with for mediator record delete Signed-off-by: arin.hlaj --- aries_cloudagent/protocols/coordinate_mediation/v1_0/routes.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/routes.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/routes.py index 6740dce76b..3435174712 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/routes.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/routes.py @@ -246,7 +246,8 @@ async def delete_mediation_request(request: web.BaseRequest): session, mediation_id ) result = mediation_record.serialize() - await mediation_record.delete_record(session) + async with context.profile.session() as session: + await mediation_record.delete_record(session) except StorageNotFoundError as err: raise web.HTTPNotFound(reason=err.roll_up) from err except (BaseModelError, StorageError) as err: From b6d45dbd5d589f70c72dd83b78e4de0f1610fecb Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Thu, 28 Apr 2022 09:50:41 -0600 Subject: [PATCH 223/872] fix: Resolve Revocation Notif Env Name Collision `--monitor-revocation-notification` and `--notify-revocation` have a name conflict with their corresponding environment variables. Now they don't have a conflict. Signed-off-by: Colton Wolkins (Indicio work address) --- aries_cloudagent/config/argparse.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index 4b91b22aad..352007516a 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -680,7 +680,7 @@ def add_arguments(self, parser: ArgumentParser): parser.add_argument( "--monitor-revocation-notification", action="store_true", - env_var="ACAPY_NOTIFY_REVOCATION", + env_var="ACAPY_MONITOR_REVOCATION_NOTIF", help=( "Specifies that aca-py will emit webhooks on notification of " "revocation received." From 7a545ee256c74d05e2ad79e5164a5cf8de5be169 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Thu, 28 Apr 2022 11:15:49 -0700 Subject: [PATCH 224/872] Fixing the intro paragraph and heading in the changelog of this 0.7.4RC1 Signed-off-by: Stephen Curran --- CHANGELOG.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65317eb1fb..08d18e6dfd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,11 @@ -# 0.7.4-RC0 +# 0.7.4-RC1 -This release is largely a release for internal fixes with a few minor -enhancements. There has been a lot of groups exercising ACA-Py and the updates -made in this release are a reflection of those updates. We have PRs that have -been contributed by 16 different people, which is very likely a record for a -single ACA-Py release. +The largest enhancement is in the area of the Hyperledger Indy endorser, +enabling an instance of ACA-Py to act as an Endorser for Indy authors needed +endorsing to write objects to an Indy ledger. We're hoping to see an +"aries-endorser-service" come from that work, an Endorser to be easily operated +by an organization, ideally with a controller starter kit to allow an approvals +business flow. The largest enhancement is in the area of the Hyperledger Indy endorser, enabling an instance of ACA-Py to act as an Endorser for Indy authors needed From b92c3a81986d4911cf1d62972ddb26bcc33da241 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Thu, 28 Apr 2022 11:54:19 -0700 Subject: [PATCH 225/872] 0.7.4-RC1 Changelog intro paragraph - fix copy/paste error Signed-off-by: Stephen Curran --- CHANGELOG.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08d18e6dfd..8f36553ef7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,10 @@ # 0.7.4-RC1 -The largest enhancement is in the area of the Hyperledger Indy endorser, -enabling an instance of ACA-Py to act as an Endorser for Indy authors needed -endorsing to write objects to an Indy ledger. We're hoping to see an -"aries-endorser-service" come from that work, an Endorser to be easily operated -by an organization, ideally with a controller starter kit to allow an approvals -business flow. +This release consists largely of internal fixes with a few minor enhancements. +There have been a lot of groups exercising ACA-Py and the updates made in this +release are a reflection of those efforts. We have PRs that have been +contributed by 17 different people, which is likely a record for a single ACA-Py +release. The largest enhancement is in the area of the Hyperledger Indy endorser, enabling an instance of ACA-Py to act as an Endorser for Indy authors needed From 3ab60bc9d2b521b46fed220b429aa9e1b8a62c31 Mon Sep 17 00:00:00 2001 From: shaangill025 Date: Mon, 2 May 2022 08:34:12 -0700 Subject: [PATCH 226/872] updates to -ot and -it validation Signed-off-by: shaangill025 --- aries_cloudagent/config/argparse.py | 5 +- .../config/tests/test_argparse.py | 18 +++++++ aries_cloudagent/core/conductor.py | 47 ------------------- 3 files changed, 22 insertions(+), 48 deletions(-) diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index cb25ebfd0d..05606648a3 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -1246,9 +1246,12 @@ def get_settings(self, args: Namespace): settings = {} if args.inbound_transports: settings["transport.inbound_configs"] = args.inbound_transports + else: + raise ArgsParseError("-it/--inbound-transport is required") if args.outbound_transports: settings["transport.outbound_configs"] = args.outbound_transports - + else: + raise ArgsParseError("-ot/--outbound-transport is required") settings["transport.enable_undelivered_queue"] = args.enable_undelivered_queue if args.label: diff --git a/aries_cloudagent/config/tests/test_argparse.py b/aries_cloudagent/config/tests/test_argparse.py index 0eeed5587f..08c47256dc 100644 --- a/aries_cloudagent/config/tests/test_argparse.py +++ b/aries_cloudagent/config/tests/test_argparse.py @@ -126,6 +126,24 @@ async def test_upgrade_config(self): == "./aries_cloudagent/config/tests/test-acapy-upgrade-config.yml" ) + async def test_outbound_is_required(self): + """Test that either -ot or -oq are required""" + parser = argparse.create_argument_parser() + group = argparse.TransportGroup() + group.add_arguments(parser) + + result = parser.parse_args( + [ + "--inbound-transport", + "http", + "0.0.0.0", + "80", + ] + ) + + with self.assertRaises(argparse.ArgsParseError): + settings = group.get_settings(result) + async def test_general_settings_file(self): """Test file argument parsing.""" diff --git a/aries_cloudagent/core/conductor.py b/aries_cloudagent/core/conductor.py index 99c6209934..a8ec2941f0 100644 --- a/aries_cloudagent/core/conductor.py +++ b/aries_cloudagent/core/conductor.py @@ -23,7 +23,6 @@ ledger_config, load_multiple_genesis_transactions_from_config, ) -from ..config.error import ArgsParseError from ..config.logging import LoggingConfigurator from ..config.wallet import wallet_config from ..core.profile import Profile @@ -106,52 +105,6 @@ async def setup(self): """Initialize the global request context.""" context = await self.context_builder.build_context() - ext_plugin = context.settings.get("external_plugins") - ext_plugin_config = context.settings.get("plugin_config") - in_transport_config_exception = False - if not context.settings.get("transport.inbound_configs"): - if ext_plugin and ext_plugin_config: - if ( - "redis_queue" in ext_plugin_config - and "inbound" in ext_plugin_config["redis_queue"] - ): - pass - else: - in_transport_config_exception = False - if ext_plugin and ext_plugin_config: - if ( - "kafka_queue" in ext_plugin_config - and "consumer-config" in ext_plugin_config["kafka_queue"] - ): - pass - else: - in_transport_config_exception = False - if in_transport_config_exception: - raise ArgsParseError( - "No --inbound-transport/-it and external transport config specified" - ) - out_transport_config_exception = False - if not context.settings.get("transport.outbound_configs"): - if ext_plugin and ext_plugin_config: - if ( - "redis_queue" in ext_plugin_config - and "outbound" in ext_plugin_config["redis_queue"] - ): - pass - else: - out_transport_config_exception = False - if ext_plugin and ext_plugin_config: - if ( - "kafka_queue" in ext_plugin_config - and "producer-config" in ext_plugin_config["kafka_queue"] - ): - pass - else: - out_transport_config_exception = False - if out_transport_config_exception: - raise ArgsParseError( - "No --outbound-transport/-ot and external transport config specified" - ) # Fetch genesis transactions if necessary if context.settings.get("ledger.ledger_config_list"): From e84b3f826db44377d8ed2190dbae44387e297819 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Tue, 3 May 2022 11:44:07 -0400 Subject: [PATCH 227/872] feat: askar profiles don't need finalizers Signed-off-by: Daniel Bluhm --- aries_cloudagent/askar/profile.py | 22 ++----------------- aries_cloudagent/askar/tests/test_profile.py | 11 ---------- aries_cloudagent/core/profile.py | 13 ----------- aries_cloudagent/indy/sdk/profile.py | 9 ++++++-- .../indy/sdk/tests/test_profile.py | 2 +- aries_cloudagent/multitenant/cache.py | 5 +++-- .../multitenant/tests/test_cache.py | 3 --- 7 files changed, 13 insertions(+), 52 deletions(-) diff --git a/aries_cloudagent/askar/profile.py b/aries_cloudagent/askar/profile.py index a03c7bbd3e..d992f845df 100644 --- a/aries_cloudagent/askar/profile.py +++ b/aries_cloudagent/askar/profile.py @@ -6,8 +6,8 @@ # import traceback -from typing import Any, Mapping, Optional -from weakref import finalize, ref +from typing import Any, Mapping +from weakref import ref from aries_askar import AskarError, Session, Store @@ -153,24 +153,6 @@ async def close(self): await self.opened.close() self.opened = None - def finalizer(self) -> Optional[finalize]: - """Return a finalizer for this profile. - - See docs for weakref.finalize for more details on behavior of finalizers. - """ - # Askar Profiles (not to be confused with AskarProfiles) don't need - # additional clean up - - if self.profile_id: - return None - - def _finalize(opened: Optional[AskarOpenStore]): - if opened: - LOGGER.debug("Profile finalizer called; closing wallet") - asyncio.get_event_loop().create_task(opened.close()) - - return finalize(self, _finalize, self.opened) - class AskarProfileSession(ProfileSession): """An active connection to the profile management backend.""" diff --git a/aries_cloudagent/askar/tests/test_profile.py b/aries_cloudagent/askar/tests/test_profile.py index cc484b109b..7855ae1188 100644 --- a/aries_cloudagent/askar/tests/test_profile.py +++ b/aries_cloudagent/askar/tests/test_profile.py @@ -83,14 +83,3 @@ async def test_profile_manager_store(): assert sessionProfile._opener == askar_profile_session askar_profile.store.session.assert_called_once_with(profile) - - -def test_finalizer(open_store, caplog): - def _smaller_scope(): - askar_profile = AskarProfile(open_store) - askar_profile.finalizer() - - with caplog.at_level(logging.DEBUG): - _smaller_scope() - - assert "finalizer called" in caplog.text diff --git a/aries_cloudagent/core/profile.py b/aries_cloudagent/core/profile.py index c8682aa222..e5de864d6e 100644 --- a/aries_cloudagent/core/profile.py +++ b/aries_cloudagent/core/profile.py @@ -4,7 +4,6 @@ from abc import ABC, abstractmethod from typing import Any, Mapping, Optional, Type -from weakref import finalize from .event_bus import EventBus, Event from ..config.base import InjectionError @@ -116,18 +115,6 @@ def inject_or( async def close(self): """Close the profile instance.""" - def finalizer(self) -> Optional[finalize]: - """Create and return a finalizer for the profile or None. - - None is returned if no special handling is required to close the profile. - - Finalizers enable automatic clean up of wallet profiles when all references to - the profile expire. - - See docs for weakref.finalize for more details on the behavior of finalizers. - """ - return None - async def remove(self): """Remove the profile.""" diff --git a/aries_cloudagent/indy/sdk/profile.py b/aries_cloudagent/indy/sdk/profile.py index 5034408938..c0d3d4adc2 100644 --- a/aries_cloudagent/indy/sdk/profile.py +++ b/aries_cloudagent/indy/sdk/profile.py @@ -31,13 +31,18 @@ class IndySdkProfile(Profile): BACKEND_NAME = "indy" - def __init__(self, opened: IndyOpenWallet, context: InjectionContext = None): + def __init__( + self, + opened: IndyOpenWallet, + context: InjectionContext = None, + ): """Create a new IndyProfile instance.""" super().__init__(context=context, name=opened.name, created=opened.created) self.opened = opened self.ledger_pool: IndySdkLedgerPool = None self.init_ledger_pool() self.bind_providers() + self._finalizer = self._make_finalizer() @property def name(self) -> str: @@ -117,7 +122,7 @@ async def close(self): await self.opened.close() self.opened = None - def finalizer(self) -> Optional[finalize]: + def _make_finalizer(self) -> finalize: """Return a finalizer for this profile. See docs for weakref.finalize for more details on behavior of finalizers. diff --git a/aries_cloudagent/indy/sdk/tests/test_profile.py b/aries_cloudagent/indy/sdk/tests/test_profile.py index 7372cd1fea..6bd97474e6 100644 --- a/aries_cloudagent/indy/sdk/tests/test_profile.py +++ b/aries_cloudagent/indy/sdk/tests/test_profile.py @@ -71,7 +71,7 @@ def test_read_only(open_wallet): def test_finalizer(open_wallet, caplog): def _smaller_scope(): profile = IndySdkProfile(open_wallet) - profile.finalizer() + assert profile with caplog.at_level(logging.DEBUG): _smaller_scope() diff --git a/aries_cloudagent/multitenant/cache.py b/aries_cloudagent/multitenant/cache.py index 7fb636843b..1fb3f37e3c 100644 --- a/aries_cloudagent/multitenant/cache.py +++ b/aries_cloudagent/multitenant/cache.py @@ -88,8 +88,9 @@ def put(self, key: str, value: Profile) -> None: value (Profile): the profile to set """ - # Close the profile when it falls out of scope - value.finalizer() + # Profiles are responsible for cleaning up after themselves when they + # fall out of scope. Previously the cache needed to create a finalizer. + # value.finalzer() # Keep track of currently opened profiles using weak references self.profiles[key] = value diff --git a/aries_cloudagent/multitenant/tests/test_cache.py b/aries_cloudagent/multitenant/tests/test_cache.py index 7acaaba318..ae7dbcc303 100644 --- a/aries_cloudagent/multitenant/tests/test_cache.py +++ b/aries_cloudagent/multitenant/tests/test_cache.py @@ -10,9 +10,6 @@ def session(self, context=None): def transaction(self, context=None): ... - def finalizer(self): - return None - def test_get_not_in_cache(): cache = ProfileCache(1) From 05f96d1b5eb487dfb874778d2dc2cef8242fe843 Mon Sep 17 00:00:00 2001 From: shaangill025 Date: Tue, 3 May 2022 08:58:10 -0700 Subject: [PATCH 228/872] add verification_result to PresAck msg Signed-off-by: shaangill025 --- .../protocols/present_proof/v1_0/manager.py | 6 ++- .../v1_0/messages/presentation_ack.py | 12 +++++- .../present_proof/v1_0/tests/test_manager.py | 42 ++++++++++++++++++- .../protocols/present_proof/v2_0/manager.py | 4 +- .../present_proof/v2_0/messages/pres_ack.py | 12 +++++- .../present_proof/v2_0/tests/test_manager.py | 38 ++++++++++++++++- 6 files changed, 104 insertions(+), 10 deletions(-) diff --git a/aries_cloudagent/protocols/present_proof/v1_0/manager.py b/aries_cloudagent/protocols/present_proof/v1_0/manager.py index 2a5245a417..b0a28320e2 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/manager.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/manager.py @@ -436,7 +436,9 @@ async def send_presentation_ack( responder = self._profile.inject_or(BaseResponder) if responder: - presentation_ack_message = PresentationAck() + presentation_ack_message = PresentationAck( + verification_result=presentation_exchange_record.verified + ) presentation_ack_message._thread = { "thid": presentation_exchange_record.thread_id } @@ -472,7 +474,7 @@ async def receive_presentation_ack( {"thread_id": message._thread_id}, {"connection_id": connection_record.connection_id}, ) - + presentation_exchange_record.verified = message._verification_result presentation_exchange_record.state = ( V10PresentationExchange.STATE_PRESENTATION_ACKED ) diff --git a/aries_cloudagent/protocols/present_proof/v1_0/messages/presentation_ack.py b/aries_cloudagent/protocols/present_proof/v1_0/messages/presentation_ack.py index 36f181d56e..98ad5d2ef3 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/messages/presentation_ack.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/messages/presentation_ack.py @@ -1,6 +1,6 @@ """Represents an explicit RFC 15 ack message, adopted into present-proof protocol.""" -from marshmallow import EXCLUDE +from marshmallow import EXCLUDE, fields, validate from ....notification.v1_0.messages.ack import V10Ack, V10AckSchema @@ -21,7 +21,7 @@ class Meta: message_type = PRESENTATION_ACK schema_class = "PresentationAckSchema" - def __init__(self, status: str = None, **kwargs): + def __init__(self, status: str = None, verification_result: str = None, **kwargs): """ Initialize an explicit ack message instance. @@ -30,6 +30,7 @@ def __init__(self, status: str = None, **kwargs): """ super().__init__(status, **kwargs) + self._verification_result = verification_result class PresentationAckSchema(V10AckSchema): @@ -40,3 +41,10 @@ class Meta: model_class = PresentationAck unknown = EXCLUDE + + verification_result = fields.Str( + required=False, + description="Whether presentation is verified: true or false", + example="true", + validate=validate.OneOf(["true", "false"]), + ) diff --git a/aries_cloudagent/protocols/present_proof/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/present_proof/v1_0/tests/test_manager.py index 548a97c382..4a787b0e78 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/tests/test_manager.py @@ -1264,13 +1264,31 @@ async def test_send_presentation_ack(self): messages = responder.messages assert len(messages) == 1 + exchange = V10PresentationExchange(verified="true") + + responder = MockResponder() + self.profile.context.injector.bind_instance(BaseResponder, responder) + + await self.manager.send_presentation_ack(exchange) + messages = responder.messages + assert len(messages) == 1 + + exchange = V10PresentationExchange(verified="false") + + responder = MockResponder() + self.profile.context.injector.bind_instance(BaseResponder, responder) + + await self.manager.send_presentation_ack(exchange) + messages = responder.messages + assert len(messages) == 1 + async def test_send_presentation_ack_no_responder(self): exchange = V10PresentationExchange() self.profile.context.injector.clear_binding(BaseResponder) await self.manager.send_presentation_ack(exchange) - async def test_receive_presentation_ack(self): + async def test_receive_presentation_ack_a(self): connection_record = async_mock.MagicMock(connection_id=CONN_ID) exchange_dummy = V10PresentationExchange() @@ -1291,6 +1309,28 @@ async def test_receive_presentation_ack(self): V10PresentationExchange.STATE_PRESENTATION_ACKED ) + async def test_receive_presentation_ack_b(self): + connection_record = async_mock.MagicMock(connection_id=CONN_ID) + + exchange_dummy = V10PresentationExchange() + message = async_mock.MagicMock(_verification_result="true") + + with async_mock.patch.object( + V10PresentationExchange, "save", autospec=True + ) as save_ex, async_mock.patch.object( + V10PresentationExchange, "retrieve_by_tag_filter", autospec=True + ) as retrieve_ex: + retrieve_ex.return_value = exchange_dummy + exchange_out = await self.manager.receive_presentation_ack( + message, connection_record + ) + save_ex.assert_called_once() + + assert exchange_out.state == ( + V10PresentationExchange.STATE_PRESENTATION_ACKED + ) + assert exchange_out.verified == "true" + async def test_receive_problem_report(self): connection_id = "connection-id" stored_exchange = V10PresentationExchange( diff --git a/aries_cloudagent/protocols/present_proof/v2_0/manager.py b/aries_cloudagent/protocols/present_proof/v2_0/manager.py index 94d5eaaed9..91b7542828 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/manager.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/manager.py @@ -395,7 +395,7 @@ async def send_pres_ack(self, pres_ex_record: V20PresExRecord): responder = self._profile.inject_or(BaseResponder) if responder: - pres_ack_message = V20PresAck() + pres_ack_message = V20PresAck(verification_result=pres_ex_record.verified) pres_ack_message._thread = {"thid": pres_ex_record.thread_id} pres_ack_message.assign_trace_decorator( self._profile.settings, pres_ex_record.trace @@ -425,7 +425,7 @@ async def receive_pres_ack(self, message: V20PresAck, conn_record: ConnRecord): {"thread_id": message._thread_id}, {"connection_id": conn_record.connection_id}, ) - + pres_ex_record.verified = message._verification_result pres_ex_record.state = V20PresExRecord.STATE_DONE await pres_ex_record.save(session, reason="receive v2.0 presentation ack") diff --git a/aries_cloudagent/protocols/present_proof/v2_0/messages/pres_ack.py b/aries_cloudagent/protocols/present_proof/v2_0/messages/pres_ack.py index d9032f8d6f..27fba5ef3d 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/messages/pres_ack.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/messages/pres_ack.py @@ -1,6 +1,6 @@ """Represents an explicit RFC 15 ack message, adopted into present-proof protocol.""" -from marshmallow import EXCLUDE +from marshmallow import EXCLUDE, fields, validate from ....notification.v1_0.messages.ack import V10Ack, V10AckSchema @@ -19,7 +19,7 @@ class Meta: message_type = PRES_20_ACK schema_class = "V20PresAckSchema" - def __init__(self, status: str = None, **kwargs): + def __init__(self, status: str = None, verification_result: str = None, **kwargs): """ Initialize an explicit ack message instance. @@ -28,6 +28,7 @@ def __init__(self, status: str = None, **kwargs): """ super().__init__(status, **kwargs) + self._verification_result = verification_result class V20PresAckSchema(V10AckSchema): @@ -38,3 +39,10 @@ class Meta: model_class = V20PresAck unknown = EXCLUDE + + verification_result = fields.Str( + required=False, + description="Whether presentation is verified: true or false", + example="true", + validate=validate.OneOf(["true", "false"]), + ) diff --git a/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py b/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py index ae6d924e02..55d22c8a66 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py @@ -1920,13 +1920,31 @@ async def test_send_pres_ack(self): messages = responder.messages assert len(messages) == 1 + px_rec = V20PresExRecord(verified="true") + + responder = MockResponder() + self.profile.context.injector.bind_instance(BaseResponder, responder) + + await self.manager.send_pres_ack(px_rec) + messages = responder.messages + assert len(messages) == 1 + + px_rec = V20PresExRecord(verified="false") + + responder = MockResponder() + self.profile.context.injector.bind_instance(BaseResponder, responder) + + await self.manager.send_pres_ack(px_rec) + messages = responder.messages + assert len(messages) == 1 + async def test_send_pres_ack_no_responder(self): px_rec = V20PresExRecord() self.profile.context.injector.clear_binding(BaseResponder) await self.manager.send_pres_ack(px_rec) - async def test_receive_pres_ack(self): + async def test_receive_pres_ack_a(self): conn_record = async_mock.MagicMock(connection_id=CONN_ID) px_rec_dummy = V20PresExRecord() @@ -1943,6 +1961,24 @@ async def test_receive_pres_ack(self): assert px_rec_out.state == V20PresExRecord.STATE_DONE + async def test_receive_pres_ack_b(self): + conn_record = async_mock.MagicMock(connection_id=CONN_ID) + + px_rec_dummy = V20PresExRecord() + message = async_mock.MagicMock(_verification_result="true") + + with async_mock.patch.object( + V20PresExRecord, "save", autospec=True + ) as save_ex, async_mock.patch.object( + V20PresExRecord, "retrieve_by_tag_filter", autospec=True + ) as retrieve_ex: + retrieve_ex.return_value = px_rec_dummy + px_rec_out = await self.manager.receive_pres_ack(message, conn_record) + save_ex.assert_called_once() + + assert px_rec_out.state == V20PresExRecord.STATE_DONE + assert px_rec_out.verified == "true" + async def test_receive_problem_report(self): connection_id = "connection-id" stored_exchange = V20PresExRecord( From 43edcd2d25b31c75faac88901e94f7502375c5f7 Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Tue, 3 May 2022 10:07:24 -0600 Subject: [PATCH 229/872] fix: Expand variable to match argument's full name Signed-off-by: Colton Wolkins (Indicio work address) --- aries_cloudagent/config/argparse.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index 352007516a..29ce270066 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -680,7 +680,7 @@ def add_arguments(self, parser: ArgumentParser): parser.add_argument( "--monitor-revocation-notification", action="store_true", - env_var="ACAPY_MONITOR_REVOCATION_NOTIF", + env_var="ACAPY_MONITOR_REVOCATION_NOTIFICATION", help=( "Specifies that aca-py will emit webhooks on notification of " "revocation received." From a214005d0cc71b7d5e450dc133300d102883a626 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Thu, 5 May 2022 11:37:19 -0700 Subject: [PATCH 230/872] Use default wallet type askar for alice/faber demo and bdd tests Signed-off-by: Ian Costanzo --- demo/README.md | 16 +++++++++++----- demo/runners/support/agent.py | 2 +- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/demo/README.md b/demo/README.md index 9791504592..15128bdcb9 100644 --- a/demo/README.md +++ b/demo/README.md @@ -25,7 +25,7 @@ There are several demos available for ACA-Py mostly (but not only) aimed at deve - [Multi-ledger](#multi-ledger) - [DID Exchange](#did-exchange) - [Endorser](#endorser) - - [Run Askar Backend](#run-askar-backend) + - [Run Indy-SDK Backend](#run-indy-sdk-backend) - [Learning about the Alice/Faber code](#learning-about-the-alicefaber-code) - [OpenAPI (Swagger) Demo](#openapi-swagger-demo) - [Performance Demo](#performance-demo) @@ -247,12 +247,12 @@ Note that you can't (currently) use the DID Exchange protocol to connect with an This is described in [Endorser.md](Endorser.md) -### Run Askar Backend +### Run Indy-SDK Backend -This runs using the askar libraries instead of indy-sdk: +This runs using the indy-sdk libraries instead of askar: ```bash -./run_demo faber --wallet-type askar +./run_demo faber --wallet-type indy ``` ### Mediation @@ -404,7 +404,7 @@ You can also run the demo against a postgres database using the following: (Obvs you need to be running a postgres database - the command to start postgres is in the yml file provided above.) -You can tweak the number of credentials issued using the `--count` and `--batch` parameters, and you can run against an Askar database using the `--wallet-type askar` option. +You can tweak the number of credentials issued using the `--count` and `--batch` parameters, and you can run against an Askar database using the `--wallet-type askar` option (or run using indy-sdk using `--wallet-type indy`). An example full set of options is: @@ -412,6 +412,12 @@ An example full set of options is: ./run_demo performance --arg-file demo/postgres-indy-args.yml -c 10000 -b 10 --wallet-type askar ``` +Or: + +```bash +./run_demo performance --arg-file demo/postgres-indy-args.yml -c 10000 -b 10 --wallet-type indy +``` + ## Coding Challenge: Adding ACME Now that you have a solid foundation in using ACA-Py, time for a coding challenge. In this challenge, we extend the Alice-Faber command line demo by adding in ACME Corp, a place where Alice wants to work. The demo adds: diff --git a/demo/runners/support/agent.py b/demo/runners/support/agent.py index abf3e7fd6e..fa769ec411 100644 --- a/demo/runners/support/agent.py +++ b/demo/runners/support/agent.py @@ -195,7 +195,7 @@ def __init__( else seed ) self.storage_type = params.get("storage_type") - self.wallet_type = params.get("wallet_type") or "indy" + self.wallet_type = params.get("wallet_type") or "askar" self.wallet_name = ( params.get("wallet_name") or self.ident.lower().replace(" ", "") + rand_name ) From 83a3681cc4f5e8dc83584d9927fe7a82de811e10 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Wed, 11 May 2022 09:35:45 -0400 Subject: [PATCH 231/872] feat: event and webhook on keylist update stored Signed-off-by: Daniel Bluhm --- aries_cloudagent/admin/server.py | 1 + aries_cloudagent/core/event_bus.py | 3 +- .../coordinate_mediation/v1_0/manager.py | 116 ++++++++++-------- .../v1_0/tests/test_mediation_manager.py | 39 ++++-- 4 files changed, 95 insertions(+), 64 deletions(-) diff --git a/aries_cloudagent/admin/server.py b/aries_cloudagent/admin/server.py index 0bbada1da6..70ec98c308 100644 --- a/aries_cloudagent/admin/server.py +++ b/aries_cloudagent/admin/server.py @@ -52,6 +52,7 @@ "acapy::actionmenu::received": "actionmenu", "acapy::actionmenu::get-active-menu": "get-active-menu", "acapy::actionmenu::perform-menu-action": "perform-menu-action", + "acapy::keylist::updated": "keylist", } diff --git a/aries_cloudagent/core/event_bus.py b/aries_cloudagent/core/event_bus.py index 180f4130dc..22d7c8f922 100644 --- a/aries_cloudagent/core/event_bus.py +++ b/aries_cloudagent/core/event_bus.py @@ -15,6 +15,7 @@ Optional, Pattern, TYPE_CHECKING, + Tuple, ) from functools import partial @@ -193,7 +194,7 @@ class MockEventBus(EventBus): def __init__(self): """Initialize MockEventBus.""" super().__init__() - self.events = [] + self.events: List[Tuple[Profile, Event]] = [] async def notify(self, profile: "Profile", event: Event): """Append the event to MockEventBus.events.""" diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py index 8fb2d2a449..6da7845a94 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py @@ -60,6 +60,7 @@ class MediationManager: SET_TO_DEFAULT_ON_GRANTED = "set_to_default_on_granted" METADATA_KEY = "mediation" METADATA_ID = "id" + KEYLIST_UPDATED_EVENT = "acapy::keylist::updated" def __init__(self, profile: Profile): """Initialize Mediation Manager. @@ -534,63 +535,74 @@ async def store_update_results( session: An active profile session """ - session = await self._profile.session() to_save: Sequence[RouteRecord] = [] to_remove: Sequence[RouteRecord] = [] - for updated in results: - if updated.result != KeylistUpdated.RESULT_SUCCESS: - # TODO better handle different results? - LOGGER.warning( - "Keylist update failure: %s(%s): %s", - updated.action, - updated.recipient_key, - updated.result, - ) - continue - if updated.action == KeylistUpdateRule.RULE_ADD: - # Multi-tenancy uses route record for internal relaying of wallets - # So the record could already exist. We update in that case - try: - record = await RouteRecord.retrieve_by_recipient_key( - session, updated.recipient_key - ) - record.connection_id = connection_id - record.role = RouteRecord.ROLE_CLIENT - except StorageNotFoundError: - record = RouteRecord( - role=RouteRecord.ROLE_CLIENT, - recipient_key=updated.recipient_key, - connection_id=connection_id, - ) - to_save.append(record) - elif updated.action == KeylistUpdateRule.RULE_REMOVE: - try: - records = await RouteRecord.query( - session, - { - "role": RouteRecord.ROLE_CLIENT, - "connection_id": connection_id, - "recipient_key": updated.recipient_key, - }, - ) - except StorageNotFoundError as err: - LOGGER.error( - "No route found while processing keylist update response: %s", - err, + + async with self._profile.session() as session: + for updated in results: + if updated.result != KeylistUpdated.RESULT_SUCCESS: + # TODO better handle different results? + LOGGER.warning( + "Keylist update failure: %s(%s): %s", + updated.action, + updated.recipient_key, + updated.result, ) - else: - if len(records) > 1: + continue + if updated.action == KeylistUpdateRule.RULE_ADD: + # Multi-tenancy uses route record for internal relaying of wallets + # So the record could already exist. We update in that case + try: + record = await RouteRecord.retrieve_by_recipient_key( + session, updated.recipient_key + ) + record.connection_id = connection_id + record.role = RouteRecord.ROLE_CLIENT + except StorageNotFoundError: + record = RouteRecord( + role=RouteRecord.ROLE_CLIENT, + recipient_key=updated.recipient_key, + connection_id=connection_id, + ) + to_save.append(record) + elif updated.action == KeylistUpdateRule.RULE_REMOVE: + try: + records = await RouteRecord.query( + session, + { + "role": RouteRecord.ROLE_CLIENT, + "connection_id": connection_id, + "recipient_key": updated.recipient_key, + }, + ) + except StorageNotFoundError as err: LOGGER.error( - f"Too many ({len(records)}) routes found " - "while processing keylist update response" + "No route found while processing keylist update response: %s", + err, ) - record = records[0] - to_remove.append(record) - - for record_for_saving in to_save: - await record_for_saving.save(session, reason="Route successfully added.") - for record_for_removal in to_remove: - await record_for_removal.delete_record(session) + else: + if len(records) > 1: + LOGGER.error( + f"Too many ({len(records)}) routes found " + "while processing keylist update response" + ) + record = records[0] + to_remove.append(record) + + for record_for_saving in to_save: + await record_for_saving.save( + session, reason="Route successfully added." + ) + for record_for_removal in to_remove: + await record_for_removal.delete_record(session) + + await self._profile.notify( + self.KEYLIST_UPDATED_EVENT, + { + "connection_id": connection_id, + "updated": [update.serialize() for update in results], + }, + ) async def get_my_keylist( self, connection_id: Optional[str] = None diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py index d2505ccfb9..3f834f589d 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py @@ -1,11 +1,14 @@ """Test MediationManager.""" import logging +from typing import AsyncIterable, Iterable import pytest from asynctest import mock as async_mock from .....core.profile import Profile, ProfileSession +from .....core.in_memory import InMemoryProfile +from .....core.event_bus import EventBus, MockEventBus from .....connections.models.conn_record import ConnRecord from .....messaging.request_context import RequestContext from .....storage.error import StorageNotFoundError @@ -36,29 +39,32 @@ @pytest.fixture -async def profile() -> Profile: +def profile() -> Iterable[Profile]: """Fixture for profile used in tests.""" # pylint: disable=W0621 - context = RequestContext.test_context() - context.message_receipt = MessageReceipt(sender_verkey=TEST_VERKEY) - context.connection_record = ConnRecord(connection_id=TEST_CONN_ID) - yield context.profile + yield InMemoryProfile.test_profile(bind={EventBus: MockEventBus()}) @pytest.fixture -async def session(profile) -> ProfileSession: # pylint: disable=W0621 +def mock_event_bus(profile: Profile): + yield profile.inject(EventBus) + + +@pytest.fixture +async def session(profile) -> AsyncIterable[ProfileSession]: # pylint: disable=W0621 """Fixture for profile session used in tests.""" - yield await profile.session() + async with profile.session() as session: + yield session @pytest.fixture -async def manager(profile) -> MediationManager: # pylint: disable=W0621 +def manager(profile) -> Iterable[MediationManager]: # pylint: disable=W0621 """Fixture for manager used in tests.""" yield MediationManager(profile) @pytest.fixture -def record() -> MediationRecord: +def record() -> Iterable[MediationRecord]: """Fixture for record used in tests.""" yield MediationRecord( state=MediationRecord.STATE_GRANTED, connection_id=TEST_CONN_ID @@ -71,7 +77,7 @@ class TestMediationManager: # pylint: disable=R0904,W0621 async def test_create_manager_no_profile(self): """test_create_manager_no_profile.""" with pytest.raises(MediationManagerError): - await MediationManager(None) + MediationManager(None) async def test_create_did(self, manager, session): """test_create_did.""" @@ -363,7 +369,12 @@ async def test_add_remove_key_mix(self, manager): assert update.updates[0].recipient_key == TEST_VERKEY assert update.updates[1].recipient_key == TEST_ROUTE_VERKEY - async def test_store_update_results(self, session, manager): + async def test_store_update_results( + self, + session: ProfileSession, + manager: MediationManager, + mock_event_bus: MockEventBus, + ): """test_store_update_results.""" await RouteRecord( role=RouteRecord.ROLE_CLIENT, @@ -383,6 +394,12 @@ async def test_store_update_results(self, session, manager): ), ] await manager.store_update_results(TEST_CONN_ID, results) + assert mock_event_bus.events + assert mock_event_bus.events[0][1].topic == manager.KEYLIST_UPDATED_EVENT + assert mock_event_bus.events[0][1].payload == { + "connection_id": TEST_CONN_ID, + "updated": [result.serialize() for result in results], + } routes = await RouteRecord.query(session) assert len(routes) == 1 From 2bf0eb790d206756f085daf2f157855c2fcb12bf Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Wed, 11 May 2022 09:59:02 -0700 Subject: [PATCH 232/872] 0.7.4-rc2 update, including some ReadTheDocs File updates Signed-off-by: Stephen Curran --- ...es_cloudagent.transport.outbound.queue.rst | 34 ------------------- .../aries_cloudagent.transport.outbound.rst | 8 ----- 2 files changed, 42 deletions(-) delete mode 100644 docs/generated/aries_cloudagent.transport.outbound.queue.rst diff --git a/docs/generated/aries_cloudagent.transport.outbound.queue.rst b/docs/generated/aries_cloudagent.transport.outbound.queue.rst deleted file mode 100644 index b23440d0dc..0000000000 --- a/docs/generated/aries_cloudagent.transport.outbound.queue.rst +++ /dev/null @@ -1,34 +0,0 @@ -aries\_cloudagent.transport.outbound.queue package -================================================== - -.. automodule:: aries_cloudagent.transport.outbound.queue - :members: - :undoc-members: - :show-inheritance: - -Submodules ----------- - -aries\_cloudagent.transport.outbound.queue.base module ------------------------------------------------------- - -.. automodule:: aries_cloudagent.transport.outbound.queue.base - :members: - :undoc-members: - :show-inheritance: - -aries\_cloudagent.transport.outbound.queue.loader module --------------------------------------------------------- - -.. automodule:: aries_cloudagent.transport.outbound.queue.loader - :members: - :undoc-members: - :show-inheritance: - -aries\_cloudagent.transport.outbound.queue.redis module -------------------------------------------------------- - -.. automodule:: aries_cloudagent.transport.outbound.queue.redis - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/generated/aries_cloudagent.transport.outbound.rst b/docs/generated/aries_cloudagent.transport.outbound.rst index eb749ee452..1fde7f0379 100644 --- a/docs/generated/aries_cloudagent.transport.outbound.rst +++ b/docs/generated/aries_cloudagent.transport.outbound.rst @@ -6,14 +6,6 @@ aries\_cloudagent.transport.outbound package :undoc-members: :show-inheritance: -Subpackages ------------ - -.. toctree:: - :maxdepth: 4 - - aries_cloudagent.transport.outbound.queue - Submodules ---------- From 82482f483aacddfce6741512f0abcd2c8c5d5cc0 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Wed, 11 May 2022 11:26:55 -0700 Subject: [PATCH 233/872] 0.7.4-rc2 update Signed-off-by: Stephen Curran --- CHANGELOG.md | 28 ++++++++++++++++++++++------ aries_cloudagent/version.py | 2 +- open-api/openapi.json | 2 +- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f36553ef7..b8b3812897 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,10 @@ -# 0.7.4-RC1 +# 0.7.4-RC2 -This release consists largely of internal fixes with a few minor enhancements. -There have been a lot of groups exercising ACA-Py and the updates made in this -release are a reflection of those efforts. We have PRs that have been -contributed by 17 different people, which is likely a record for a single ACA-Py -release. +The 0.7.4 release consists largely of internal fixes with a few minor +enhancements. There have been a lot of groups exercising ACA-Py and the updates +made in this release are a reflection of those efforts. We have PRs that have +been contributed by 17 different people, which is likely a record for a single +ACA-Py release. The largest enhancement is in the area of the Hyperledger Indy endorser, enabling an instance of ACA-Py to act as an Endorser for Indy authors needed @@ -27,6 +27,16 @@ the team at LISSI for creating the to make load testing so easy! And of course to the core ACA-Py team for addressing the findings. +The team has worked a lot on evolving the persistent queue (PQ) approach +available in ACA-Py. We have landed on a design whereby the ability to use +queues for inbound and outbound messages is within ACA-Py, with a default +in-memory implementation, and the implementations of external persistent queues +solutions is handled by referencing a plugin from a separate repository. There +will shortly be two concrete, out-of-the-box solutions available, one for Kafka +and one for Redis, and anyone else can implement their own PQ plugin as long as +it uses the same ACA-Py queuing interface. Look for the new PQ repos shortly +within Hyperledger Aries. + Several new ways to control ACA-Py configurations were added, including new startup parameters, Admin API parameters to control instances of protocols, and additional web hook notifications. @@ -60,6 +70,9 @@ stuff needed for a growing codebase. - Feature/undelivered events [\#1694](https://github.com/hyperledger/aries-cloudagent-python/pull/1694) ([mepeltier](https://github.com/mepeltier)) - Allow use of SEED when creating local wallet DID Issue-1682 Issue-1682 [\#1705](https://github.com/hyperledger/aries-cloudagent-python/pull/1705) ([DaevMithran](https://github.com/DaevMithran)) +- Persistent Queues + - Redis PQ Cleanup in preparation for enabling the uses of plugin PQ implementations \[Issue\#1659\] [\#1659](https://github.com/hyperledger/aries-cloudagent-python/pull/1690) ([shaangill025](https://github.com/shaangill025)) + - Issue Credential, Revocation, Present Proof updates/fixes - Fix: DIF proof proposal when creating bound presentation request \[Issue\#1687\] [\#1690](https://github.com/hyperledger/aries-cloudagent-python/pull/1690) ([shaangill025](https://github.com/shaangill025)) - Fix DIF PresExch and OOB request\_attach delete unused connection [\#1676](https://github.com/hyperledger/aries-cloudagent-python/pull/1676) ([shaangill025](https://github.com/shaangill025)) @@ -68,6 +81,7 @@ stuff needed for a growing codebase. - Fixes for credential details in issue-credential webhook responses [\#1668](https://github.com/hyperledger/aries-cloudagent-python/pull/1668) ([andrewwhitehead](https://github.com/andrewwhitehead)) - Fix: present-proof v2 send-proposal [issue\#1474](https://github.com/hyperledger/aries-cloudagent-python/issues/1474) [\#1667](https://github.com/hyperledger/aries-cloudagent-python/pull/1667) ([shaangill025](https://github.com/shaangill025)) - Fixes Issue 3b from [\#1597](https://github.com/hyperledger/aries-cloudagent-python/issues/1597): V2 Credential exchange ignores the auto-respond-credential-request + - fix: Resolve Revocation Notification environment variable name collision [\#1751](https://github.com/hyperledger/aries-cloudagent-python/pull/1751) ([frostyfrog](https://github.com/frostyfrog)) - fix: always notify if revocation notification record exists [\#1665](https://github.com/hyperledger/aries-cloudagent-python/pull/1665) ([TimoGlastra](https://github.com/TimoGlastra)) - Revert change to send\_credential\_ack return value [\#1660](https://github.com/hyperledger/aries-cloudagent-python/pull/1660) ([andrewwhitehead](https://github.com/andrewwhitehead)) - Fix usage of send\_credential\_ack [\#1653](https://github.com/hyperledger/aries-cloudagent-python/pull/1653) ([andrewwhitehead](https://github.com/andrewwhitehead)) @@ -80,6 +94,7 @@ stuff needed for a growing codebase. - Mediator updates and fixes - feat: allow querying default mediator from base wallet [\#1729](https://github.com/hyperledger/aries-cloudagent-python/pull/1729) ([dbluhm](https://github.com/dbluhm)) + - Added async with for mediator record delete [\#1749](https://github.com/hyperledger/aries-cloudagent-python/pull/1749) ([dejsenlitro](https://github.com/dejsenlitro)) - Multitenacy updates and fixes - feat: create new JWT tokens and invalidate older for multitenancy [\#1725](https://github.com/hyperledger/aries-cloudagent-python/pull/1725) ([TimoGlastra](https://github.com/TimoGlastra)) @@ -96,6 +111,7 @@ stuff needed for a growing codebase. - Add an integration test for mixed proof with a revocable cred and a n… [\#1672](https://github.com/hyperledger/aries-cloudagent-python/pull/1672) ([ianco](https://github.com/ianco)) - Documentation and Demo Updates + - Fetch from --genesis-url likely to fail in composed container [\#1746](https://github.com/hyperledger/aries-cloudagent-python/pull/1739) ([tdiesler](https://github.com/tdiesler)) - Fixes logic for web hook formatter in Faber demo [\#1739](https://github.com/hyperledger/aries-cloudagent-python/pull/1739) ([amanji](https://github.com/amanji)) - Multitenancy Docs Update [\#1706](https://github.com/hyperledger/aries-cloudagent-python/pull/1706) ([MonolithicMonk](https://github.com/MonolithicMonk)) - [\#1674](https://github.com/hyperledger/aries-cloudagent-python/issue/1674) Add basic DOCKER\_ENV logging for run\_demo [\#1675](https://github.com/hyperledger/aries-cloudagent-python/pull/1675) ([tdiesler](https://github.com/tdiesler)) diff --git a/aries_cloudagent/version.py b/aries_cloudagent/version.py index ddabac341b..e6a19d3978 100644 --- a/aries_cloudagent/version.py +++ b/aries_cloudagent/version.py @@ -1,4 +1,4 @@ """Library version information.""" -__version__ = "0.7.4-rc1" +__version__ = "0.7.4-rc2" RECORD_TYPE_ACAPY_VERSION = "acapy_version" diff --git a/open-api/openapi.json b/open-api/openapi.json index f6a0451648..2db2fad8c6 100644 --- a/open-api/openapi.json +++ b/open-api/openapi.json @@ -1,7 +1,7 @@ { "swagger" : "2.0", "info" : { - "version" : "v0.7.4-rc1", + "version" : "v0.7.4-rc2", "title" : "Aries Cloud Agent" }, "tags" : [ { From 40e40eb561fc50c0bdbcdb41fdeda7b0bd3ac1e4 Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Tue, 17 May 2022 08:59:52 -0600 Subject: [PATCH 234/872] fix: Replace hack within document loader After talking with Timo, the document loader uses a seperate thread due to the fact that PyLD is synchronous code and calls into the document loader (which requires asynchronous code). When swapping out the cache with a Redis based cache, new exceptions rose up due to this implementation. No matter what I tried, it seemed impossible to keep the separate thread/asyncio event loop due to the error `got Future attached to a different loop`. Switching to `nest_asyncio` over using a separate thread/event loop resolves the issues that I was observing. Signed-off-by: Colton Wolkins (Indicio work address) --- .../vc/ld_proofs/document_loader.py | 51 ++++++++++--------- requirements.txt | 1 + 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/aries_cloudagent/vc/ld_proofs/document_loader.py b/aries_cloudagent/vc/ld_proofs/document_loader.py index 2f4eab5e4e..3c35d7d985 100644 --- a/aries_cloudagent/vc/ld_proofs/document_loader.py +++ b/aries_cloudagent/vc/ld_proofs/document_loader.py @@ -14,6 +14,10 @@ from .error import LinkedDataProofException +import nest_asyncio + +nest_asyncio.apply() + class DocumentLoader: """JSON-LD document loader.""" @@ -32,6 +36,7 @@ def __init__(self, profile: Profile, cache_ttl: int = 300) -> None: self.requests_loader = requests.requests_document_loader() self.executor = concurrent.futures.ThreadPoolExecutor(max_workers=1) self.cache_ttl = cache_ttl + self._event_loop = asyncio.get_event_loop() async def _load_did_document(self, did: str, options: dict): # Resolver expects plain did without path, query, etc... @@ -59,14 +64,6 @@ def _load_http_document(self, url: str, options: dict): async def _load_async(self, url: str, options: dict): """Retrieve http(s) or did document.""" - cache_key = f"json_ld_document_resolver::{url}" - - # Try to get from cache - if self.cache: - document = await self.cache.get(cache_key) - if document: - return document - # Resolve DIDs using did resolver if url.startswith("did:"): document = await self._load_did_document(url, options) @@ -78,22 +75,9 @@ async def _load_async(self, url: str, options: dict): "'did:', 'http://' or 'https://'" ) - # Cache document, if cache is available - if self.cache: - await self.cache.set(cache_key, document, self.cache_ttl) - return document - def _load_sync(self, url: str, options: dict): - """Run document loader in event loop to make it async. - - NOTE: This should be called in a thread where an event loop is not already - running, such as a new thread. - """ - loop = asyncio.new_event_loop() - return loop.run_until_complete(self._load_async(url, options)) - - def load_document(self, url: str, options: dict): + async def load_document(self, url: str, options: dict): """Load JSON-LD document. Method signature conforms to PyLD document loader interface @@ -101,13 +85,30 @@ def load_document(self, url: str, options: dict): Document loading is processed in separate thread to deal with async to sync transformation. """ - future = self.executor.submit(self._load_sync, url, options) - return future.result() + cache_key = f"json_ld_document_resolver::{url}" + + # Try to get from cache + if self.cache: + document = await self.cache.get(cache_key) + if document: + return document + + document = await self._load_async(url, options) + + # Cache document, if cache is available + if self.cache: + await self.cache.set(cache_key, document, self.cache_ttl) + + return document def __call__(self, url: str, options: dict): """Load JSON-LD Document.""" - return self.load_document(url, options) + loop = self._event_loop + coroutine = self.load_document(url, options) + document = loop.run_until_complete(coroutine) + + return document DocumentLoaderMethod = Callable[[str, dict], dict] diff --git a/requirements.txt b/requirements.txt index 2429236466..3a7518e3b3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,6 +3,7 @@ aiohttp-apispec~=2.2.1 aiohttp-cors~=0.7.0 apispec~=3.3.0 async-timeout~=4.0.2 +nest_asyncio~=1.5.5 aioredis~=2.0.0 base58~=2.1.0 deepmerge~=0.3.0 From 211af2b4e5b8131d64b1b3855ef8e352e831b71f Mon Sep 17 00:00:00 2001 From: Akiff Manji Date: Tue, 17 May 2022 16:03:31 -0700 Subject: [PATCH 235/872] Adds transport_id variable assingment back to outbound enqueue method Signed-off-by: Akiff Manji --- aries_cloudagent/transport/outbound/manager.py | 1 + 1 file changed, 1 insertion(+) diff --git a/aries_cloudagent/transport/outbound/manager.py b/aries_cloudagent/transport/outbound/manager.py index 87d1fe0d94..ac2c54dd49 100644 --- a/aries_cloudagent/transport/outbound/manager.py +++ b/aries_cloudagent/transport/outbound/manager.py @@ -255,6 +255,7 @@ async def enqueue_message(self, profile: Profile, outbound: OutboundMessage): outbound: The outbound message to deliver """ targets = [outbound.target] if outbound.target else (outbound.target_list or []) + transport_id = None for target in targets: endpoint = target.endpoint try: From 75dbd89037c4e6da2d91ed5e71cd9df9c4033d87 Mon Sep 17 00:00:00 2001 From: Kim Ebert Date: Tue, 17 May 2022 19:18:00 -0600 Subject: [PATCH 236/872] fix: add a close statement to ensure session is closed on error Signed-off-by: Kim Ebert --- aries_cloudagent/askar/profile.py | 1 + 1 file changed, 1 insertion(+) diff --git a/aries_cloudagent/askar/profile.py b/aries_cloudagent/askar/profile.py index 4b72d20a57..a37f6082a6 100644 --- a/aries_cloudagent/askar/profile.py +++ b/aries_cloudagent/askar/profile.py @@ -213,6 +213,7 @@ async def _teardown(self, commit: bool = None): await self._handle.commit() except AskarError as err: raise ProfileError("Error committing transaction") from err + await self._handle.close() self._handle = None self._check_duration() From 044cb6b83312b536af59654536c6e508a4035070 Mon Sep 17 00:00:00 2001 From: Kim Ebert Date: Wed, 18 May 2022 14:14:23 -0600 Subject: [PATCH 237/872] feat: only close if we currently have a handle Signed-off-by: Kim Ebert --- aries_cloudagent/askar/profile.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/askar/profile.py b/aries_cloudagent/askar/profile.py index a37f6082a6..c20d436f01 100644 --- a/aries_cloudagent/askar/profile.py +++ b/aries_cloudagent/askar/profile.py @@ -213,7 +213,8 @@ async def _teardown(self, commit: bool = None): await self._handle.commit() except AskarError as err: raise ProfileError("Error committing transaction") from err - await self._handle.close() + if self._handle: + await self._handle.close() self._handle = None self._check_duration() From 0c60cc453f02dce5048d0d3578d93ed4ff366550 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Thu, 19 May 2022 14:48:28 -0700 Subject: [PATCH 238/872] Document impact of multi-ledger on TAA acceptance Signed-off-by: Ian Costanzo --- Multiledger.md | 20 +++++++++++ demo/docker/docker-compose.yml | 62 ++++++++++++++++++++++++++++++++++ demo/docker/ledgers.yaml | 16 +++++++++ 3 files changed, 98 insertions(+) create mode 100644 demo/docker/docker-compose.yml create mode 100644 demo/docker/ledgers.yaml diff --git a/Multiledger.md b/Multiledger.md index 30c692c878..5d575c9969 100644 --- a/Multiledger.md +++ b/Multiledger.md @@ -14,6 +14,7 @@ More background information including problem statement, design (algorithm) and - [Read Requests](#read-requests) - [For checking ledger in parallel](#for-checking-ledger-in-parallel) - [Write Requests](#write-requests) +- [A Special Warning for TAA Acceptance](#a-special-warning-for-taa-acceptance) - [Impact on other ACA-Py function](#impact-on-other-aca-py-function) ## Usage @@ -104,6 +105,25 @@ If multiple ledgers are configured then `IndyLedgerRequestsExecutor` service ext On startup, the first configured applicable ledger is assigned as the `write_ledger` [`BaseLedger`], the selection is dependant on the order (top-down) and whether it is `production` or `non_production`. For instance, considering this [example configuration](#example-config-file), ledger `bcorvinTest` will be set as `write_ledger` as it is the topmost `production` ledger. If no `production` ledgers are included in configuration then the topmost `non_production` ledger is selected. +## A Special Warning for TAA Acceptance + +When you run in multi-ledger mode, ACA-Py will use the `pool-name` (or `id`) specified in the ledger configuration file for each ledger. + +(When running in single-ledger mode, ACA-Py uses `default` as the ledger name.) + +If you are running against a ledger in `write` mode, and the ledger requires you to accept a Transaction Author Agreement (TAA), ACA-Py stores the TAA acceptance +status in the wallet in a non-secrets record, using the ledger's `pool_name` as a key. + +This means that if you are upgrading from single-ledger to multi-ledger mode, you will need to *either*: + +- set the `id` for your writable ledger to `default` (in your `ledgers.yaml` file) + +*or*: + +- re-accept the TAA once you restart your ACA-Py in multi-ledger mode + +Once you re-start ACA-Py, you can check the `GET /ledger/taa` endpoint to verify your TAA acceptance status. + ## Impact on other ACA-Py function There should be no impact/change in functionality to any ACA-Py protocols. diff --git a/demo/docker/docker-compose.yml b/demo/docker/docker-compose.yml new file mode 100644 index 0000000000..94c4061fa6 --- /dev/null +++ b/demo/docker/docker-compose.yml @@ -0,0 +1,62 @@ +# Sample docker-compose to start a local aca-py in multi-ledger mode +# To start aca-py and the postgres database, just run `docker-compose up` +# To shut down the services run `docker-compose rm` - this will retain the postgres database, so you can change aca-py startup parameters +# and restart the docker containers without losing your wallet data +# If you want to delete your wallet data just run `docker volume ls -q | xargs docker volume rm` +version: "3" +services: + vcr-agent: + image: bcgovimages/aries-cloudagent:py36-1.16-1_0.7.3 + ports: + - 8010:8010 + depends_on: + - wallet-db + entrypoint: /bin/bash + command: [ + "-c", + "sleep 5; \ + aca-py start \ + --auto-provision \ + --inbound-transport http '0.0.0.0' 8001 \ + --endpoint 'http://host.docker.internal:8001' \ + --outbound-transport http \ + --genesis-transactions-list 'ledgers.yaml' + --auto-accept-invites \ + --auto-accept-requests \ + --auto-ping-connection \ + --auto-respond-messages \ + --auto-respond-credential-proposal \ + --auto-respond-credential-offer \ + --auto-respond-credential-request \ + --auto-verify-presentation \ + --wallet-type 'indy' \ + --wallet-name 'acapy_agent_wallet' \ + --wallet-key 'key' \ + --wallet-storage-type 'postgres_storage' \ + --wallet-storage-config '{\"url\":\"wallet-db:5432\",\"max_connections\":5}' \ + --wallet-storage-creds '{\"account\":\"DB_USER\",\"password\":\"DB_PASSWORD\",\"admin_account\":\"postgres\",\"admin_password\":\"mysecretpassword\"}' \ + --admin '0.0.0.0' 8010 \ + --admin-insecure-mode \ + --label 'tester_agent' \ + --log-level 'info' ", + ] + volumes: + - ./ledgers.yaml:/home/indy/ledgers.yaml + +# note - if you want to start aca-py in single-ledger mode, replace the `--genesis-transactions-list` parameter above with: +# --genesis-url 'https://raw.githubusercontent.com/sovrin-foundation/sovrin/master/sovrin/pool_transactions_sandbox_genesis' \ + + wallet-db: + image: vcr-postgresql + environment: + - POSTGRESQL_USER=DB_USER + - POSTGRESQL_PASSWORD=DB_PASSWORD + - POSTGRESQL_DATABASE=DB_USER + - POSTGRESQL_ADMIN_PASSWORD=mysecretpassword + ports: + - 5433:5432 + volumes: + - wallet-db-data:/var/lib/pgsql/data + +volumes: + wallet-db-data: diff --git a/demo/docker/ledgers.yaml b/demo/docker/ledgers.yaml new file mode 100644 index 0000000000..baf174d35e --- /dev/null +++ b/demo/docker/ledgers.yaml @@ -0,0 +1,16 @@ +# the `id` is used as the `pool_name` in aca-py +# note that if you are upgrading from single- to multi-ledger, you need to *either*: +# - set the `id` of your `is_write: true` ledger to `default` (the `pool_name` used in single-ledger mode) +# *or*: +# - re-accept the TAA once you start aca-py in multi-ledger mode +# (the TAA acceptance is stored in a wallet record keyed on the `pool_name`) +- id: SOVRINSandbox + is_production: true + is_write: true + genesis_url: 'https://raw.githubusercontent.com/sovrin-foundation/sovrin/stable/sovrin/pool_transactions_sandbox_genesis' +- id: BCovrinTest + is_production: true + genesis_url: 'http://test.bcovrin.vonx.io/genesis' +- id: CANdyDev + is_production: true + genesis_url: 'https://raw.githubusercontent.com/ICCS-ISAC/dtrust-reconu/main/CANdy/dev/pool_transactions_genesis' From 88dda1e99f8974e28868846aba471cb08c8193c7 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Wed, 25 May 2022 16:21:19 -0400 Subject: [PATCH 239/872] feat: add update keylist for connection method and route Signed-off-by: Daniel Bluhm --- .../coordinate_mediation/v1_0/manager.py | 50 +++++++++++++++++-- .../coordinate_mediation/v1_0/routes.py | 41 ++++++++++++++- 2 files changed, 84 insertions(+), 7 deletions(-) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py index 8fb2d2a449..79c9f90d19 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py @@ -1,24 +1,23 @@ """Manager for Mediation coordination.""" import json import logging - from typing import Optional, Sequence, Tuple +from ....connections.models.conn_record import ConnRecord from ....core.error import BaseError from ....core.profile import Profile, ProfileSession +from ....messaging.responder import BaseResponder from ....storage.base import BaseStorage from ....storage.error import StorageNotFoundError from ....storage.record import StorageRecord -from ....wallet.key_type import KeyType -from ....wallet.did_method import DIDMethod from ....wallet.base import BaseWallet from ....wallet.did_info import DIDInfo - +from ....wallet.did_method import DIDMethod +from ....wallet.key_type import KeyType from ...routing.v1_0.manager import RoutingManager from ...routing.v1_0.models.route_record import RouteRecord from ...routing.v1_0.models.route_update import RouteUpdate from ...routing.v1_0.models.route_updated import RouteUpdated - from .messages.inner.keylist_key import KeylistKey from .messages.inner.keylist_query_paginate import KeylistQueryPaginate from .messages.inner.keylist_update_rule import KeylistUpdateRule @@ -485,6 +484,47 @@ async def prepare_keylist_query( ) return message + async def update_keylist_for_connection( + self, conn_record: ConnRecord, mediation_record: MediationRecord + ) -> Optional[KeylistUpdate]: + """Update the mediator with keys created for the connection. + + If the connection is in invitation received state, create the + connection keys and update. + """ + if conn_record.rfc23_state == ConnRecord.State.INVITATION.rfc23strict( + ConnRecord.Role.REQUESTER + ) or conn_record.rfc23_state == ConnRecord.State.REQUEST.rfc23strict( + ConnRecord.Role.RESPONDER + ): + if not conn_record.my_did: + async with self._profile.session() as session: + wallet = session.inject(BaseWallet) + # Create new DID for connection + my_info = await wallet.create_local_did( + DIDMethod.SOV, KeyType.ED25519 + ) + conn_record.my_did = my_info.did + await conn_record.save(session, reason="Connection my did created") + else: + async with self._profile.session() as session: + wallet = session.inject(BaseWallet) + my_info = await wallet.get_local_did(conn_record.my_did) + + keylist_update = await self.add_key(my_info.verkey) + if conn_record.rfc23_state == ConnRecord.State.REQUEST.rfc23strict( + ConnRecord.Role.RESPONDER + ): + keylist_update = await self.remove_key( + conn_record.invitation_key, keylist_update + ) + responder = self._profile.inject(BaseResponder) + await responder.send( + keylist_update, connection_id=mediation_record.connection_id + ) + return keylist_update + return None + async def add_key( self, recipient_key: str, message: Optional[KeylistUpdate] = None ) -> KeylistUpdate: diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/routes.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/routes.py index 3435174712..aed2df63ec 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/routes.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/routes.py @@ -495,7 +495,7 @@ async def set_default_mediator(request: web.BaseRequest): mediator_mgr = MediationManager(context.profile) await mediator_mgr.set_default_mediator_by_id(mediation_id=mediation_id) default_mediator = await mediator_mgr.get_default_mediator() - results = default_mediator.serialize() + results = default_mediator.serialize() if default_mediator else {} except (StorageError, BaseModelError) as err: raise web.HTTPBadRequest(reason=err.roll_up) from err return web.json_response(results, status=201) @@ -510,12 +510,49 @@ async def clear_default_mediator(request: web.BaseRequest): mediator_mgr = MediationManager(context.profile) default_mediator = await mediator_mgr.get_default_mediator() await mediator_mgr.clear_default_mediator() - results = default_mediator.serialize() + results = default_mediator.serialize() if default_mediator else {} except (StorageError, BaseModelError) as err: raise web.HTTPBadRequest(reason=err.roll_up) from err return web.json_response(results, status=201) +@docs(tags=["mediation"], summary="Update keylist for a connection") +@match_info_schema(ConnectionsConnIdMatchInfoSchema()) +@request_schema(MediationIdMatchInfoSchema(), 200) +@response_schema(KeylistUpdateSchema()) +async def update_keylist_for_connection(request: web.BaseRequest): + """Update keylist for a connection.""" + context: AdminRequestContext = request["context"] + body = await request.json() + mediation_id = body.get("mediation_id") + connection_id = request.match_info["conn_id"] + try: + mediation_mgr = MediationManager(context.profile) + mediation_id = mediation_id or await mediation_mgr.get_default_mediator() + if not mediation_id: + raise web.HTTPBadRequest( + reason="No mediation_id specified and no default mediator" + ) + + async with context.session() as session: + mediation_record = await MediationRecord.retrieve_by_id( + session, mediation_id + ) + connection_record = await ConnRecord.retrieve_by_id(session, connection_id) + + keylist_update = await mediation_mgr.update_keylist_for_connection( + connection_record, mediation_record + ) + + results = keylist_update.serialize() if keylist_update else {} + except StorageNotFoundError as err: + raise web.HTTPNotFound(reason=err.roll_up) from err + except (StorageError, BaseModelError) as err: + raise web.HTTPBadRequest(reason=err.roll_up) from err + + return web.json_response(results, status=201) + + async def register(app: web.Application): """Register routes.""" From 373786f0a53e18c986f9270d747c60d183eba67e Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Thu, 26 May 2022 09:04:03 -0600 Subject: [PATCH 240/872] feat: Add required notify_version API parameter to revocation Based upon discussion in #1734, a new required parameter for revocations has been added to the revocation API Signed-off-by: Colton Wolkins (Indicio work address) --- .../v1_0/models/rev_notification_record.py | 6 ++++++ .../v2_0/models/rev_notification_record.py | 6 ++++++ aries_cloudagent/revocation/manager.py | 4 ++++ aries_cloudagent/revocation/routes.py | 15 +++++++++++++++ 4 files changed, 31 insertions(+) diff --git a/aries_cloudagent/protocols/revocation_notification/v1_0/models/rev_notification_record.py b/aries_cloudagent/protocols/revocation_notification/v1_0/models/rev_notification_record.py index eac5bd2cee..4468915d6a 100644 --- a/aries_cloudagent/protocols/revocation_notification/v1_0/models/rev_notification_record.py +++ b/aries_cloudagent/protocols/revocation_notification/v1_0/models/rev_notification_record.py @@ -38,6 +38,7 @@ def __init__( connection_id: str = None, thread_id: str = None, comment: str = None, + version: str = None, **kwargs, ): """Construct record.""" @@ -47,6 +48,7 @@ def __init__( self.connection_id = connection_id self.thread_id = thread_id self.comment = comment + self.version = version @property def revocation_notification_id(self) -> Optional[str]: @@ -157,3 +159,7 @@ class Meta: description="Optional comment to include in revocation notification", required=False, ) + version = fields.Str( + description="Version of Revocation Notification to send out", + required=False, + ) diff --git a/aries_cloudagent/protocols/revocation_notification/v2_0/models/rev_notification_record.py b/aries_cloudagent/protocols/revocation_notification/v2_0/models/rev_notification_record.py index f1a913a727..e6db241bbd 100644 --- a/aries_cloudagent/protocols/revocation_notification/v2_0/models/rev_notification_record.py +++ b/aries_cloudagent/protocols/revocation_notification/v2_0/models/rev_notification_record.py @@ -38,6 +38,7 @@ def __init__( connection_id: str = None, thread_id: str = None, comment: str = None, + version: str = None, **kwargs, ): """Construct record.""" @@ -47,6 +48,7 @@ def __init__( self.connection_id = connection_id self.thread_id = thread_id self.comment = comment + self.version = version @property def revocation_notification_id(self) -> Optional[str]: @@ -158,3 +160,7 @@ class Meta: description="Optional comment to include in revocation notification", required=False, ) + version = fields.Str( + description="Version of Revocation Notification to send out", + required=False, + ) diff --git a/aries_cloudagent/revocation/manager.py b/aries_cloudagent/revocation/manager.py index 4bdf222655..4ee333620b 100644 --- a/aries_cloudagent/revocation/manager.py +++ b/aries_cloudagent/revocation/manager.py @@ -45,6 +45,7 @@ async def revoke_credential_by_cred_ex_id( cred_ex_id: str, publish: bool = False, notify: bool = False, + notify_version: str = None, thread_id: str = None, connection_id: str = None, comment: str = None, @@ -77,6 +78,7 @@ async def revoke_credential_by_cred_ex_id( cred_rev_id=rec.cred_rev_id, publish=publish, notify=notify, + notify_version=notify_version, thread_id=thread_id, connection_id=connection_id, comment=comment, @@ -88,6 +90,7 @@ async def revoke_credential( cred_rev_id: str, publish: bool = False, notify: bool = False, + notify_version: str = None, thread_id: str = None, connection_id: str = None, comment: str = None, @@ -121,6 +124,7 @@ async def revoke_credential( thread_id=thread_id, connection_id=connection_id, comment=comment, + version=notify_version, ) async with self._profile.session() as session: await rev_notify_rec.save(session, reason="New revocation notification") diff --git a/aries_cloudagent/revocation/routes.py b/aries_cloudagent/revocation/routes.py index ea2f83427a..39a4678d51 100644 --- a/aries_cloudagent/revocation/routes.py +++ b/aries_cloudagent/revocation/routes.py @@ -157,11 +157,16 @@ def validate_fields(self, data, **kwargs): notify = data.get("notify") connection_id = data.get("connection_id") + notify_version = data.get("notify_version") if notify and not connection_id: raise ValidationError( "Request must specify connection_id if notify is true" ) + if notify and not notify_version: + raise ValidationError( + "Request must specify notify_version if notify is true" + ) publish = fields.Boolean( description=( @@ -174,6 +179,11 @@ def validate_fields(self, data, **kwargs): description="Send a notification to the credential recipient", required=False, ) + notify_version = fields.String( + description="Specify which version of the revocation notification should be sent", + validate=validate.OneOf(["v1_0", "v2_0"]), + required=False, + ) connection_id = fields.Str( description=( "Connection ID to which the revocation notification will be sent; " @@ -377,9 +387,14 @@ async def revoke(request: web.BaseRequest): body["notify"] = body.get("notify", context.settings.get("revocation.notify")) notify = body.get("notify") connection_id = body.get("connection_id") + notify_version = body.get("notify_version") if notify and not connection_id: raise web.HTTPBadRequest(reason="connection_id must be set when notify is true") + if notify and not notify_version: + raise web.HTTPBadRequest( + reason="Request must specify notify_version if notify is true" + ) rev_manager = RevocationManager(context.profile) try: From 4cce47373f673b08372d2907647481ec0dd75df5 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Thu, 26 May 2022 09:22:04 -0700 Subject: [PATCH 241/872] Fix tails server upload multi-ledger mode Signed-off-by: Ian Costanzo --- aries_cloudagent/ledger/indy.py | 28 +++++++++++++++++++++ aries_cloudagent/tails/indy_tails_server.py | 16 +++++++++--- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/aries_cloudagent/ledger/indy.py b/aries_cloudagent/ledger/indy.py index 13f27abac2..96ac9e0e3a 100644 --- a/aries_cloudagent/ledger/indy.py +++ b/aries_cloudagent/ledger/indy.py @@ -5,6 +5,7 @@ import logging import tempfile from datetime import date, datetime +from io import StringIO from os import path from time import time from typing import Sequence, Tuple, Optional @@ -42,6 +43,17 @@ GENESIS_TRANSACTION_FILE = "indy_genesis_transactions.txt" +def _normalize_txns(txns: str) -> str: + """Normalize a set of genesis transactions.""" + lines = StringIO() + for line in txns.splitlines(): + line = line.strip() + if line: + lines.write(line) + lines.write("\n") + return lines.getvalue() + + class IndySdkLedgerPoolProvider(BaseProvider): """Indy ledger pool provider which keys off the selected pool name.""" @@ -107,12 +119,28 @@ def __init__( self.cache = cache self.cache_duration = cache_duration self.genesis_transactions = genesis_transactions + self.genesis_txns_cache = genesis_transactions self.handle = None self.name = name self.taa_cache = None self.read_only = read_only self.socks_proxy = socks_proxy + @property + def genesis_txns(self) -> str: + """Get the configured genesis transactions.""" + if not self.genesis_txns_cache: + try: + txn_path = path.join( + tempfile.gettempdir(), f"{self.name}_{GENESIS_TRANSACTION_FILE}" + ) + self.genesis_txns_cache = _normalize_txns(open(txn_path).read()) + except FileNotFoundError: + raise LedgerConfigError( + "Pool config '%s' not found", self.name + ) from None + return self.genesis_txns_cache + async def create_pool_config( self, genesis_transactions: str, recreate: bool = False ): diff --git a/aries_cloudagent/tails/indy_tails_server.py b/aries_cloudagent/tails/indy_tails_server.py index a7aee90a58..9f07970a42 100644 --- a/aries_cloudagent/tails/indy_tails_server.py +++ b/aries_cloudagent/tails/indy_tails_server.py @@ -1,6 +1,7 @@ """Indy tails server interface class.""" from typing import Tuple +import logging from ..config.injection_context import InjectionContext from ..ledger.multiple_ledger.base_manager import BaseMultipleLedgerManager @@ -10,6 +11,9 @@ from .error import TailsServerNotConfiguredError +LOGGER = logging.getLogger(__name__) + + class IndyTailsServer(BaseTailsServer): """Indy tails server interface.""" @@ -38,12 +42,16 @@ async def upload_tails_file( if not genesis_transactions: ledger_manager = context.injector.inject(BaseMultipleLedgerManager) write_ledgers = await ledger_manager.get_write_ledger() + LOGGER.debug(f"write_ledgers = {write_ledgers}") pool = write_ledgers[1].pool + LOGGER.debug(f"write_ledger pool = {pool}") + + genesis_transactions = pool.genesis_txns - try: - genesis_transactions = pool.genesis_transactions - except AttributeError: - genesis_transactions = pool.genesis_txns_cache + if not genesis_transactions: + raise TailsServerNotConfiguredError( + "no genesis_transactions for writable ledger" + ) if not tails_server_upload_url: raise TailsServerNotConfiguredError( From 223f460cbf992d7f3260fa7d551cae808e88b403 Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Thu, 26 May 2022 14:07:37 -0600 Subject: [PATCH 242/872] fix: Add version to Rev Notification Records Signed-off-by: Colton Wolkins (Indicio work address) --- .../v1_0/models/rev_notification_record.py | 3 +++ .../v1_0/models/tests/test_rev_notification_record.py | 2 ++ .../v2_0/models/rev_notification_record.py | 3 +++ .../v2_0/models/tests/test_rev_notification_record.py | 2 ++ demo/features/steps/0453-issue-credential.py | 1 + demo/features/steps/0586-sign-transaction.py | 1 + 6 files changed, 12 insertions(+) diff --git a/aries_cloudagent/protocols/revocation_notification/v1_0/models/rev_notification_record.py b/aries_cloudagent/protocols/revocation_notification/v1_0/models/rev_notification_record.py index 4468915d6a..3b43b233ff 100644 --- a/aries_cloudagent/protocols/revocation_notification/v1_0/models/rev_notification_record.py +++ b/aries_cloudagent/protocols/revocation_notification/v1_0/models/rev_notification_record.py @@ -27,6 +27,7 @@ class Meta: "rev_reg_id", "cred_rev_id", "connection_id", + "version", } def __init__( @@ -75,6 +76,7 @@ async def query_by_ids( rev_reg_id: the rev reg id by which to filter """ tag_filter = { + **{"version": "v1_0"}, **{"cred_rev_id": cred_rev_id for _ in [""] if cred_rev_id}, **{"rev_reg_id": rev_reg_id for _ in [""] if rev_reg_id}, } @@ -103,6 +105,7 @@ async def query_by_rev_reg_id( rev_reg_id: the rev reg id by which to filter """ tag_filter = { + **{"version": "v1_0"}, **{"rev_reg_id": rev_reg_id for _ in [""] if rev_reg_id}, } diff --git a/aries_cloudagent/protocols/revocation_notification/v1_0/models/tests/test_rev_notification_record.py b/aries_cloudagent/protocols/revocation_notification/v1_0/models/tests/test_rev_notification_record.py index c845f715ca..304ec37a90 100644 --- a/aries_cloudagent/protocols/revocation_notification/v1_0/models/tests/test_rev_notification_record.py +++ b/aries_cloudagent/protocols/revocation_notification/v1_0/models/tests/test_rev_notification_record.py @@ -21,6 +21,7 @@ def rec(): connection_id="mock_connection_id", thread_id="mock_thread_id", comment="mock_comment", + version="v1_0", ) @@ -50,6 +51,7 @@ async def test_storage(profile, rec): another = RevNotificationRecord( rev_reg_id="mock_rev_reg_id", cred_rev_id="mock_cred_rev_id", + version="v1_0", ) await another.save(session) await RevNotificationRecord.query_by_ids( diff --git a/aries_cloudagent/protocols/revocation_notification/v2_0/models/rev_notification_record.py b/aries_cloudagent/protocols/revocation_notification/v2_0/models/rev_notification_record.py index e6db241bbd..b91cc74967 100644 --- a/aries_cloudagent/protocols/revocation_notification/v2_0/models/rev_notification_record.py +++ b/aries_cloudagent/protocols/revocation_notification/v2_0/models/rev_notification_record.py @@ -27,6 +27,7 @@ class Meta: "rev_reg_id", "cred_rev_id", "connection_id", + "version", } def __init__( @@ -75,6 +76,7 @@ async def query_by_ids( rev_reg_id: the rev reg id by which to filter """ tag_filter = { + **{"version": "v2_0"}, **{"cred_rev_id": cred_rev_id for _ in [""] if cred_rev_id}, **{"rev_reg_id": rev_reg_id for _ in [""] if rev_reg_id}, } @@ -103,6 +105,7 @@ async def query_by_rev_reg_id( rev_reg_id: the rev reg id by which to filter """ tag_filter = { + **{"version": "v2_0"}, **{"rev_reg_id": rev_reg_id for _ in [""] if rev_reg_id}, } diff --git a/aries_cloudagent/protocols/revocation_notification/v2_0/models/tests/test_rev_notification_record.py b/aries_cloudagent/protocols/revocation_notification/v2_0/models/tests/test_rev_notification_record.py index e06be17091..e6bb64e5c7 100644 --- a/aries_cloudagent/protocols/revocation_notification/v2_0/models/tests/test_rev_notification_record.py +++ b/aries_cloudagent/protocols/revocation_notification/v2_0/models/tests/test_rev_notification_record.py @@ -21,6 +21,7 @@ def rec(): connection_id="mock_connection_id", thread_id="mock_thread_id", comment="mock_comment", + version="v2_0", ) @@ -50,6 +51,7 @@ async def test_storage(profile, rec): another = RevNotificationRecord( rev_reg_id="mock_rev_reg_id", cred_rev_id="mock_cred_rev_id", + version="v2_0", ) await another.save(session) await RevNotificationRecord.query_by_ids( diff --git a/demo/features/steps/0453-issue-credential.py b/demo/features/steps/0453-issue-credential.py index 2914489406..2c61d91520 100644 --- a/demo/features/steps/0453-issue-credential.py +++ b/demo/features/steps/0453-issue-credential.py @@ -98,6 +98,7 @@ def step_impl(context, holder): "rev_reg_id": cred_exchange["indy"]["rev_reg_id"], "cred_rev_id": cred_exchange["indy"]["cred_rev_id"], "publish": "Y", + "notify_version": "v1_0", "connection_id": cred_exchange["cred_ex_record"]["connection_id"], }, ) diff --git a/demo/features/steps/0586-sign-transaction.py b/demo/features/steps/0586-sign-transaction.py index 531d8a7009..5838ad147a 100644 --- a/demo/features/steps/0586-sign-transaction.py +++ b/demo/features/steps/0586-sign-transaction.py @@ -437,6 +437,7 @@ def step_impl(context, agent_name): "rev_reg_id": cred_exchange["indy"]["rev_reg_id"], "cred_rev_id": cred_exchange["indy"]["cred_rev_id"], "publish": False, + "notify_version": "v1_0", "connection_id": cred_exchange["cred_ex_record"]["connection_id"], }, ) From ac4fe653cb4776c8aba8f9c9a6b9b5913e420b01 Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Tue, 31 May 2022 11:04:23 -0600 Subject: [PATCH 243/872] feat: Add default value for notify_version to "v1_0" Signed-off-by: Colton Wolkins (Indicio work address) --- aries_cloudagent/revocation/routes.py | 4 ++-- demo/features/steps/0453-issue-credential.py | 1 - demo/features/steps/0586-sign-transaction.py | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/aries_cloudagent/revocation/routes.py b/aries_cloudagent/revocation/routes.py index 39a4678d51..0db51587c4 100644 --- a/aries_cloudagent/revocation/routes.py +++ b/aries_cloudagent/revocation/routes.py @@ -157,7 +157,7 @@ def validate_fields(self, data, **kwargs): notify = data.get("notify") connection_id = data.get("connection_id") - notify_version = data.get("notify_version") + notify_version = data.get("notify_version", "v1_0") if notify and not connection_id: raise ValidationError( @@ -387,7 +387,7 @@ async def revoke(request: web.BaseRequest): body["notify"] = body.get("notify", context.settings.get("revocation.notify")) notify = body.get("notify") connection_id = body.get("connection_id") - notify_version = body.get("notify_version") + notify_version = body.get("notify_version", "v1_0") if notify and not connection_id: raise web.HTTPBadRequest(reason="connection_id must be set when notify is true") diff --git a/demo/features/steps/0453-issue-credential.py b/demo/features/steps/0453-issue-credential.py index 2c61d91520..2914489406 100644 --- a/demo/features/steps/0453-issue-credential.py +++ b/demo/features/steps/0453-issue-credential.py @@ -98,7 +98,6 @@ def step_impl(context, holder): "rev_reg_id": cred_exchange["indy"]["rev_reg_id"], "cred_rev_id": cred_exchange["indy"]["cred_rev_id"], "publish": "Y", - "notify_version": "v1_0", "connection_id": cred_exchange["cred_ex_record"]["connection_id"], }, ) diff --git a/demo/features/steps/0586-sign-transaction.py b/demo/features/steps/0586-sign-transaction.py index 5838ad147a..531d8a7009 100644 --- a/demo/features/steps/0586-sign-transaction.py +++ b/demo/features/steps/0586-sign-transaction.py @@ -437,7 +437,6 @@ def step_impl(context, agent_name): "rev_reg_id": cred_exchange["indy"]["rev_reg_id"], "cred_rev_id": cred_exchange["indy"]["cred_rev_id"], "publish": False, - "notify_version": "v1_0", "connection_id": cred_exchange["cred_ex_record"]["connection_id"], }, ) From f49eaabfdc44546d9b9b04765c5905955d1eda6f Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Tue, 24 May 2022 16:24:43 -0700 Subject: [PATCH 244/872] Additional endpoints to get revocation details Signed-off-by: Ian Costanzo --- aries_cloudagent/revocation/indy.py | 37 +++++++++-- aries_cloudagent/revocation/routes.py | 89 ++++++++++++++++++++++++++- 2 files changed, 119 insertions(+), 7 deletions(-) diff --git a/aries_cloudagent/revocation/indy.py b/aries_cloudagent/revocation/indy.py index 669db1f31a..dea0351cb9 100644 --- a/aries_cloudagent/revocation/indy.py +++ b/aries_cloudagent/revocation/indy.py @@ -108,11 +108,41 @@ async def list_issuer_registries(self) -> Sequence["IssuerRevRegRecord"]: async with self._profile.session() as session: return await IssuerRevRegRecord.query(session) + async def get_issuer_rev_reg_delta( + self, rev_reg_id: str, fro: int = None, to: int = None + ) -> dict: + """ + Check ledger for revocation status for a given revocation registry. + + Args: + rev_reg_id: ID of the revocation registry + + """ + ledger = await self.get_ledger_for_registry(rev_reg_id) + async with ledger: + (rev_reg_delta, _) = await ledger.get_revoc_reg_delta( + rev_reg_id, + fro, + to, + ) + + return rev_reg_delta + async def get_ledger_registry(self, revoc_reg_id: str) -> "RevocationRegistry": """Get a revocation registry from the ledger, fetching as necessary.""" if revoc_reg_id in IndyRevocation.REV_REG_CACHE: return IndyRevocation.REV_REG_CACHE[revoc_reg_id] + ledger = await self.get_ledger_for_registry(revoc_reg_id) + + async with ledger: + rev_reg = RevocationRegistry.from_definition( + await ledger.get_revoc_reg_def(revoc_reg_id), True + ) + IndyRevocation.REV_REG_CACHE[revoc_reg_id] = rev_reg + return rev_reg + + async def get_ledger_for_registry(self, revoc_reg_id: str) -> "Ledger": multitenant_mgr = self._profile.inject_or(BaseMultitenantManager) if multitenant_mgr: ledger_exec_inst = IndyLedgerRequestsExecutor(self._profile) @@ -124,9 +154,4 @@ async def get_ledger_registry(self, revoc_reg_id: str) -> "RevocationRegistry": txn_record_type=GET_REVOC_REG_DEF, ) )[1] - async with ledger: - rev_reg = RevocationRegistry.from_definition( - await ledger.get_revoc_reg_def(revoc_reg_id), True - ) - IndyRevocation.REV_REG_CACHE[revoc_reg_id] = rev_reg - return rev_reg + return ledger diff --git a/aries_cloudagent/revocation/routes.py b/aries_cloudagent/revocation/routes.py index 0db51587c4..a6284055ff 100644 --- a/aries_cloudagent/revocation/routes.py +++ b/aries_cloudagent/revocation/routes.py @@ -259,6 +259,21 @@ class CredRevRecordResultSchema(OpenAPISchema): result = fields.Nested(IssuerCredRevRecordSchema()) +class CredRevRecordDetailsResultSchema(OpenAPISchema): + """Result schema for credential revocation record request.""" + + results = fields.List(fields.Nested(IssuerCredRevRecordSchema())) + + +class CredRevDeltaRecordResultSchema(OpenAPISchema): + """Result schema for revoc reg delta.""" + + result: fields.Dict( + required=True, + description="Credential revocation ids by revocation registry id", + ) + + class RevRegIssuedResultSchema(OpenAPISchema): """Result schema for revocation registry credentials issued request.""" @@ -588,7 +603,7 @@ async def get_rev_reg(request: web.BaseRequest): ) @match_info_schema(RevRegIdMatchInfoSchema()) @response_schema(RevRegIssuedResultSchema(), 200, description="") -async def get_rev_reg_issued(request: web.BaseRequest): +async def get_rev_reg_issued_count(request: web.BaseRequest): """ Request handler to get number of credentials issued against revocation registry. @@ -615,6 +630,68 @@ async def get_rev_reg_issued(request: web.BaseRequest): return web.json_response({"result": count}) +@docs( + tags=["revocation"], + summary="Get details of credentials issued against revocation registry", +) +@match_info_schema(RevRegIdMatchInfoSchema()) +@response_schema(CredRevRecordDetailsResultSchema(), 200, description="") +async def get_rev_reg_issued(request: web.BaseRequest): + """ + Request handler to get number of credentials issued against revocation registry. + + Args: + request: aiohttp request object + + Returns: + Number of credentials issued against revocation registry + + """ + context: AdminRequestContext = request["context"] + + rev_reg_id = request.match_info["rev_reg_id"] + + recs = [] + async with context.profile.session() as session: + try: + await IssuerRevRegRecord.retrieve_by_revoc_reg_id(session, rev_reg_id) + except StorageNotFoundError as err: + raise web.HTTPNotFound(reason=err.roll_up) from err + recs = await IssuerCredRevRecord.query_by_ids(session, rev_reg_id=rev_reg_id) + results = [] + for rec in recs: + results.append(rec.serialize()) + + return web.json_response(results) + + +@docs( + tags=["revocation"], + summary="Get details of revoked credentials from ledger", +) +@match_info_schema(RevRegIdMatchInfoSchema()) +@response_schema(CredRevDeltaRecordResultSchema(), 200, description="") +async def get_rev_reg_delta(request: web.BaseRequest): + """ + Request handler to get details of revoked credentials from ledger. + + Args: + request: aiohttp request object + + Returns: + Detailes of revoked credentials from ledger + + """ + context: AdminRequestContext = request["context"] + + rev_reg_id = request.match_info["rev_reg_id"] + + revoc = IndyRevocation(context.profile) + rev_reg_delta = await revoc.get_issuer_rev_reg_delta(rev_reg_id) + + return web.json_response({"result": rev_reg_delta}) + + @docs( tags=["revocation"], summary="Get credential revocation status", @@ -1334,9 +1411,19 @@ async def register(app: web.Application): ), web.get( "/revocation/registry/{rev_reg_id}/issued", + get_rev_reg_issued_count, + allow_head=False, + ), + web.get( + "/revocation/registry/{rev_reg_id}/issued/details", get_rev_reg_issued, allow_head=False, ), + web.get( + "/revocation/registry/{rev_reg_id}/issued/delta", + get_rev_reg_delta, + allow_head=False, + ), web.post("/revocation/create-registry", create_rev_reg), web.post("/revocation/registry/{rev_reg_id}/definition", send_rev_reg_def), web.post("/revocation/registry/{rev_reg_id}/entry", send_rev_reg_entry), From 7048ffbffaec8a328d662324e7e39679debcd13b Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Wed, 25 May 2022 10:51:05 -0700 Subject: [PATCH 245/872] Formatting and unit tests Signed-off-by: Ian Costanzo --- aries_cloudagent/revocation/indy.py | 4 +++- aries_cloudagent/revocation/routes.py | 5 ++--- aries_cloudagent/revocation/tests/test_routes.py | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/aries_cloudagent/revocation/indy.py b/aries_cloudagent/revocation/indy.py index dea0351cb9..7d09ece62f 100644 --- a/aries_cloudagent/revocation/indy.py +++ b/aries_cloudagent/revocation/indy.py @@ -3,6 +3,7 @@ from typing import Sequence from ..core.profile import Profile +from ..ledger.base import BaseLedger from ..ledger.multiple_ledger.ledger_requests_executor import ( GET_CRED_DEF, GET_REVOC_REG_DEF, @@ -142,7 +143,8 @@ async def get_ledger_registry(self, revoc_reg_id: str) -> "RevocationRegistry": IndyRevocation.REV_REG_CACHE[revoc_reg_id] = rev_reg return rev_reg - async def get_ledger_for_registry(self, revoc_reg_id: str) -> "Ledger": + async def get_ledger_for_registry(self, revoc_reg_id: str) -> "BaseLedger": + """Get the ledger for the given registry.""" multitenant_mgr = self._profile.inject_or(BaseMultitenantManager) if multitenant_mgr: ledger_exec_inst = IndyLedgerRequestsExecutor(self._profile) diff --git a/aries_cloudagent/revocation/routes.py b/aries_cloudagent/revocation/routes.py index a6284055ff..47028de01e 100644 --- a/aries_cloudagent/revocation/routes.py +++ b/aries_cloudagent/revocation/routes.py @@ -268,8 +268,7 @@ class CredRevRecordDetailsResultSchema(OpenAPISchema): class CredRevDeltaRecordResultSchema(OpenAPISchema): """Result schema for revoc reg delta.""" - result: fields.Dict( - required=True, + result = fields.Dict( description="Credential revocation ids by revocation registry id", ) @@ -689,7 +688,7 @@ async def get_rev_reg_delta(request: web.BaseRequest): revoc = IndyRevocation(context.profile) rev_reg_delta = await revoc.get_issuer_rev_reg_delta(rev_reg_id) - return web.json_response({"result": rev_reg_delta}) + return web.json_response({"result": rev_reg_delta.serialize()}) @docs( diff --git a/aries_cloudagent/revocation/tests/test_routes.py b/aries_cloudagent/revocation/tests/test_routes.py index ef3011edf2..f004d0c90b 100644 --- a/aries_cloudagent/revocation/tests/test_routes.py +++ b/aries_cloudagent/revocation/tests/test_routes.py @@ -368,7 +368,7 @@ async def test_get_rev_reg_issued(self): test_module.web, "json_response", async_mock.Mock() ) as mock_json_response: mock_query.return_value = return_value = [{"...": "..."}, {"...": "..."}] - result = await test_module.get_rev_reg_issued(self.request) + result = await test_module.get_rev_reg_issued_count(self.request) mock_json_response.assert_called_once_with({"result": 2}) assert result is mock_json_response.return_value From 20d753b83ab7787f60c3fd8d86737979761cba41 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Thu, 2 Jun 2022 15:24:52 -0400 Subject: [PATCH 246/872] fix: oddities and typos Signed-off-by: Daniel Bluhm --- aries_cloudagent/indy/sdk/profile.py | 15 +++++++-------- .../multitenant/askar_profile_manager.py | 4 ++-- aries_cloudagent/multitenant/base.py | 6 +++--- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/aries_cloudagent/indy/sdk/profile.py b/aries_cloudagent/indy/sdk/profile.py index c0d3d4adc2..d8151a5050 100644 --- a/aries_cloudagent/indy/sdk/profile.py +++ b/aries_cloudagent/indy/sdk/profile.py @@ -3,7 +3,7 @@ import asyncio import logging -from typing import Any, Mapping, Optional +from typing import Any, Mapping from weakref import finalize, ref from ...config.injection_context import InjectionContext @@ -42,7 +42,7 @@ def __init__( self.ledger_pool: IndySdkLedgerPool = None self.init_ledger_pool() self.bind_providers() - self._finalizer = self._make_finalizer() + self._finalizer = self._make_finalizer(opened) @property def name(self) -> str: @@ -122,18 +122,17 @@ async def close(self): await self.opened.close() self.opened = None - def _make_finalizer(self) -> finalize: + def _make_finalizer(self, opened: IndyOpenWallet) -> finalize: """Return a finalizer for this profile. See docs for weakref.finalize for more details on behavior of finalizers. """ - def _finalize(opened: Optional[IndyOpenWallet]): - if opened: - LOGGER.debug("Profile finalizer called; closing wallet") - asyncio.get_event_loop().create_task(opened.close()) + def _finalize(opened: IndyOpenWallet): + LOGGER.debug("Profile finalizer called; closing wallet") + asyncio.get_event_loop().create_task(opened.close()) - return finalize(self, _finalize, self.opened) + return finalize(self, _finalize, opened) async def remove(self): """Remove the profile associated with this instance.""" diff --git a/aries_cloudagent/multitenant/askar_profile_manager.py b/aries_cloudagent/multitenant/askar_profile_manager.py index d7d13b964c..83135cfe8e 100644 --- a/aries_cloudagent/multitenant/askar_profile_manager.py +++ b/aries_cloudagent/multitenant/askar_profile_manager.py @@ -14,7 +14,7 @@ class AskarProfileMultitenantManager(BaseMultitenantManager): """Class for handling askar profile multitenancy.""" - DEFAULT_MULTIENANT_WALLET_NAME = "multitenant_sub_wallet" + DEFAULT_MULTITENANT_WALLET_NAME = "multitenant_sub_wallet" def __init__(self, profile: Profile, multitenant_profile: AskarProfile = None): """Initialize askar profile multitenant Manager. @@ -62,7 +62,7 @@ async def get_wallet_profile( """ if not self._multitenant_profile: multitenant_wallet_name = base_context.settings.get( - "multitenant.wallet_name", self.DEFAULT_MULTIENANT_WALLET_NAME + "multitenant.wallet_name", self.DEFAULT_MULTITENANT_WALLET_NAME ) context = base_context.copy() sub_wallet_settings = { diff --git a/aries_cloudagent/multitenant/base.py b/aries_cloudagent/multitenant/base.py index b2c03afe65..6c1a0fb13e 100644 --- a/aries_cloudagent/multitenant/base.py +++ b/aries_cloudagent/multitenant/base.py @@ -2,7 +2,7 @@ from datetime import datetime import logging -from abc import abstractmethod, ABC, abstractproperty +from abc import abstractmethod, ABC import jwt from typing import Iterable, List, Optional, cast @@ -48,10 +48,10 @@ def __init__(self, profile: Profile): if not profile: raise MultitenantManagerError("Missing profile") - @abstractproperty + @property + @abstractmethod def open_profiles(self) -> Iterable[Profile]: """Return iterator over open profiles.""" - ... async def get_default_mediator(self) -> Optional[MediationRecord]: """Retrieve the default mediator used for subwallet routing. From da236fd34813171190c46f6c02e5ca7ad5cf98ac Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Thu, 2 Jun 2022 15:30:20 -0400 Subject: [PATCH 247/872] fix: add key_derivation_method help text back in Signed-off-by: Daniel Bluhm --- aries_cloudagent/config/argparse.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index a00c049f38..510d3b54c7 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -1626,8 +1626,9 @@ def add_arguments(self, parser: ArgumentParser): help=( "Specify multitenancy configuration in key=value pairs. " 'For example: "wallet_type=askar-profile wallet_name=askar-profile-name" ' - "Possible values: wallet_name, wallet_key, cache_size. " - '"wallet_name" is only used when "wallet_type" is "askar-profile"' + "Possible values: wallet_name, wallet_key, cache_size, " + 'key_derivation_method. "wallet_name" is only used when ' + '"wallet_type" is "askar-profile"' ), ) From 9575d070369cd97d2e06347e0be8aa2623e6faa2 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Thu, 2 Jun 2022 15:49:39 -0400 Subject: [PATCH 248/872] fix: missing async marks for pytest Signed-off-by: Daniel Bluhm --- aries_cloudagent/askar/tests/test_profile.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/aries_cloudagent/askar/tests/test_profile.py b/aries_cloudagent/askar/tests/test_profile.py index 7855ae1188..aef94fa481 100644 --- a/aries_cloudagent/askar/tests/test_profile.py +++ b/aries_cloudagent/askar/tests/test_profile.py @@ -15,6 +15,7 @@ def open_store(): yield mock.MagicMock() +@pytest.mark.asyncio async def test_init_success(open_store): askar_profile = AskarProfile( open_store, @@ -23,6 +24,7 @@ async def test_init_success(open_store): assert askar_profile.opened == open_store +@pytest.mark.asyncio async def test_remove_success(open_store): openStore = open_store context = InjectionContext() @@ -42,6 +44,7 @@ async def test_remove_success(open_store): openStore.store.remove_profile.assert_called_once_with(profile_id) +@pytest.mark.asyncio async def test_remove_profile_not_removed_if_wallet_type_not_askar_profile(open_store): openStore = open_store context = InjectionContext() From 19be853f5e60fb739d92447b178dc619ccbcad2c Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Fri, 3 Jun 2022 07:31:46 -0700 Subject: [PATCH 249/872] Integration tests and credx issuer udpates: Signed-off-by: Ian Costanzo --- aries_cloudagent/indy/credx/issuer.py | 9 + aries_cloudagent/ledger/routes.py | 21 ++- aries_cloudagent/ledger/tests/test_routes.py | 10 +- aries_cloudagent/revocation/recover.py | 113 ++++++++++++ aries_cloudagent/revocation/routes.py | 172 +++++++++++++++++- demo/INTEGRATION-TESTS.md | 29 ++- demo/docker/docker-compose.yml | 24 ++- demo/docker/ledgers.yaml | 9 +- demo/features/steps/0453-issue-credential.py | 117 ++++++++++++ demo/features/steps/0586-sign-transaction.py | 3 + .../steps/taa-txn-author-agreement.py | 89 +++++++++ .../taa-txn-author-acceptance.feature | 92 ++++++++++ demo/runners/agent_container.py | 12 ++ demo/runners/alice.py | 2 + demo/runners/faber.py | 2 + demo/runners/support/agent.py | 24 +++ 16 files changed, 709 insertions(+), 19 deletions(-) create mode 100644 aries_cloudagent/revocation/recover.py create mode 100644 demo/features/steps/taa-txn-author-agreement.py create mode 100644 demo/features/taa-txn-author-acceptance.feature diff --git a/aries_cloudagent/indy/credx/issuer.py b/aries_cloudagent/indy/credx/issuer.py index 9db5b61b48..e008010929 100644 --- a/aries_cloudagent/indy/credx/issuer.py +++ b/aries_cloudagent/indy/credx/issuer.py @@ -480,6 +480,15 @@ async def revoke_credentials( await txn.handle.replace( CATEGORY_REV_REG_INFO, revoc_reg_id, value_json=rev_info ) + for cred_rev_id in rev_crids: + issuer_cr_rec = await IssuerCredRevRecord.retrieve_by_ids( + txn, + revoc_reg_id, + str(cred_rev_id), + ) + await issuer_cr_rec.set_state( + txn, IssuerCredRevRecord.STATE_REVOKED + ) if not transaction: await txn.commit() except AskarError as err: diff --git a/aries_cloudagent/ledger/routes.py b/aries_cloudagent/ledger/routes.py index fc24386aaa..a5698f7fd2 100644 --- a/aries_cloudagent/ledger/routes.py +++ b/aries_cloudagent/ledger/routes.py @@ -1,6 +1,7 @@ """Ledger admin routes.""" import json +import logging from aiohttp import web from aiohttp_apispec import docs, querystring_schema, request_schema, response_schema @@ -53,6 +54,9 @@ from .util import notify_register_did_event +LOGGER = logging.getLogger(__name__) + + class LedgerModulesResultSchema(OpenAPISchema): """Schema for the modules endpoint.""" @@ -590,6 +594,7 @@ async def ledger_accept_taa(request: web.BaseRequest): raise web.HTTPForbidden(reason=reason) accept_input = await request.json() + LOGGER.info(">>> accepting TAA with: %s", accept_input) async with ledger: try: taa_info = await ledger.get_txn_author_agreement() @@ -597,13 +602,27 @@ async def ledger_accept_taa(request: web.BaseRequest): raise web.HTTPBadRequest( reason=f"Ledger {ledger.pool_name} TAA not available" ) + LOGGER.info(f"TAA on ledger: {taa_info}") + # this is a bit of a hack, but the "\ufeff" code is included in the + # ledger TAA and digest calculation, so it needs to be included in the + # TAA text that the user is accepting + # (if you copy the TAA text using swagger it won't include this character) + if taa_info["taa_record"]["text"].startswith("\ufeff"): + if not accept_input["text"].startswith("\ufeff"): + LOGGER.info( + ">>> pre-pending -endian character to TAA acceptance text" + ) + accept_input["text"] = "\ufeff" + accept_input["text"] taa_record = { "version": accept_input["version"], "text": accept_input["text"], "digest": ledger.taa_digest( - accept_input["version"], accept_input["text"] + accept_input["version"], + accept_input["text"], ), } + taa_record_digest = taa_record["digest"] + LOGGER.info(">>> accepting with digest: %s", taa_record_digest) await ledger.accept_txn_author_agreement( taa_record, accept_input["mechanism"] ) diff --git a/aries_cloudagent/ledger/tests/test_routes.py b/aries_cloudagent/ledger/tests/test_routes.py index aa682e2249..1310156e45 100644 --- a/aries_cloudagent/ledger/tests/test_routes.py +++ b/aries_cloudagent/ledger/tests/test_routes.py @@ -686,7 +686,10 @@ async def test_accept_taa(self): with async_mock.patch.object( test_module.web, "json_response", async_mock.Mock() ) as json_response: - self.ledger.get_txn_author_agreement.return_value = {"taa_required": True} + self.ledger.get_txn_author_agreement.return_value = { + "taa_record": {"text": "text"}, + "taa_required": True, + } result = await test_module.ledger_accept_taa(self.request) json_response.assert_called_once_with({}) self.ledger.accept_txn_author_agreement.assert_awaited_once_with( @@ -707,7 +710,10 @@ async def test_accept_taa_x(self): "mechanism": "mechanism", } ) - self.ledger.get_txn_author_agreement.return_value = {"taa_required": True} + self.ledger.get_txn_author_agreement.return_value = { + "taa_record": {"text": "text"}, + "taa_required": True, + } self.ledger.accept_txn_author_agreement.side_effect = test_module.StorageError() with self.assertRaises(test_module.web.HTTPBadRequest): await test_module.ledger_accept_taa(self.request) diff --git a/aries_cloudagent/revocation/recover.py b/aries_cloudagent/revocation/recover.py new file mode 100644 index 0000000000..71353d4224 --- /dev/null +++ b/aries_cloudagent/revocation/recover.py @@ -0,0 +1,113 @@ +"""Recover a revocation registry.""" + +import hashlib +import importlib +import logging +import tempfile +import time + +import aiohttp +import base58 + + +LOGGER = logging.getLogger(__name__) + + +""" +This module calculates a new ledger accumulator, based on the revocation status +on the ledger vs revocations recorded in the wallet. +The calculated transaction can be written to the ledger to get the ledger back +in sync with the wallet. +This function can be used if there were previous revocation errors (i.e. the +credential revocation was successfully written to the wallet but the ledger write +failed.) +""" + + +async def fetch_txns(genesis_txns, registry_id): + """Fetch tails file and revocation registry information.""" + + vdr_module = importlib.import_module("indy_vdr") + credx_module = importlib.import_module("indy_credx") + + pool = await vdr_module.open_pool(transactions=genesis_txns) + LOGGER.debug("Connected to pool") + + LOGGER.debug("Fetch registry: %s", registry_id) + fetch = vdr_module.ledger.build_get_revoc_reg_def_request(None, registry_id) + result = await pool.submit_request(fetch) + if not result["data"]: + LOGGER.error("Registry definition not found") + return + data = result["data"] + data["ver"] = "1.0" + defn = credx_module.RevocationRegistryDefinition.load(data) + LOGGER.debug("Tails URL: %s", defn.tails_location) + + async with aiohttp.ClientSession() as session: + data = await session.get(defn.tails_location) + tails_data = await data.read() + tails_hash = base58.b58encode(hashlib.sha256(tails_data).digest()).decode( + "utf-8" + ) + if tails_hash != defn.tails_hash: + LOGGER.debug("Tails hash mismatch: %s %s", tails_hash, defn.tails_hash) + return + else: + LOGGER.debug("Checked tails hash: %s", tails_hash) + tails_temp = tempfile.NamedTemporaryFile(delete=False) + tails_temp.write(tails_data) + tails_temp.close() + + to_timestamp = int(time.time()) + fetch = vdr_module.ledger.build_get_revoc_reg_delta_request( + None, registry_id, None, to_timestamp + ) + result = await pool.submit_request(fetch) + if not result["data"]: + LOGGER.error("Error fetching delta") + return None + + accum_to = result["data"]["value"]["accum_to"] + accum_to["ver"] = "1.0" + delta = credx_module.RevocationRegistryDelta.load(accum_to) + registry = credx_module.RevocationRegistry.load(accum_to) + LOGGER.debug("Ledger registry state: %s", registry.to_json()) + revoked = set(result["data"]["value"]["revoked"]) + LOGGER.debug("Ledger revoked indexes: %s", revoked) + + return defn, registry, delta, revoked, tails_temp + + +async def generate_ledger_rrrecovery_txn(genesis_txns, registry_id, set_revoked): + """Generate a new ledger accum entry, based on wallet vs ledger revocation state.""" + + new_delta = None + + ledger_data = await fetch_txns(genesis_txns, registry_id) + if not ledger_data: + return new_delta + defn, registry, delta, prev_revoked, tails_temp = ledger_data + + set_revoked = set(set_revoked) + mismatch = prev_revoked - set_revoked + if mismatch: + LOGGER.warn( + "Credential index(es) revoked on the ledger, but not in wallet: %s", + mismatch, + ) + + updates = set_revoked - prev_revoked + if not updates: + LOGGER.debug("No updates to perform") + else: + LOGGER.debug("New revoked indexes: %s", updates) + + LOGGER.debug("tails_temp: %s", tails_temp.name) + update_registry = registry.copy() + new_delta = update_registry.update(defn, [], updates, tails_temp.name) + + LOGGER.debug("New delta:") + LOGGER.debug(new_delta.to_json()) + + return new_delta diff --git a/aries_cloudagent/revocation/routes.py b/aries_cloudagent/revocation/routes.py index 47028de01e..2c9d57a3e9 100644 --- a/aries_cloudagent/revocation/routes.py +++ b/aries_cloudagent/revocation/routes.py @@ -22,6 +22,8 @@ from ..core.profile import Profile from ..indy.issuer import IndyIssuerError from ..indy.util import tails_path +from ..ledger.base import BaseLedger +from ..ledger.multiple_ledger.base_manager import BaseMultipleLedgerManager from ..ledger.error import LedgerError from ..messaging.credential_definitions.util import CRED_DEF_SENT_RECORD_TYPE from ..messaging.models.base import BaseModelError @@ -59,6 +61,7 @@ IssuerCredRevRecordSchema, ) from .models.issuer_rev_reg_record import IssuerRevRegRecord, IssuerRevRegRecordSchema +from .recover import generate_ledger_rrrecovery_txn from .util import ( REVOCATION_EVENT_PREFIX, REVOCATION_REG_EVENT, @@ -69,6 +72,7 @@ notify_revocation_tails_file_event, ) + LOGGER = logging.getLogger(__name__) @@ -265,11 +269,11 @@ class CredRevRecordDetailsResultSchema(OpenAPISchema): results = fields.List(fields.Nested(IssuerCredRevRecordSchema())) -class CredRevDeltaRecordResultSchema(OpenAPISchema): +class CredRevIndyRecordsResultSchema(OpenAPISchema): """Result schema for revoc reg delta.""" - result = fields.Dict( - description="Credential revocation ids by revocation registry id", + rev_reg_delta = fields.Dict( + description="Indy revocation registry delta", ) @@ -283,6 +287,29 @@ class RevRegIssuedResultSchema(OpenAPISchema): ) +class RevRegUpdateRequestMatchInfoSchema(OpenAPISchema): + """Path parameters and validators for request taking rev reg id.""" + + apply_ledger_update = fields.Bool( + description="Apply updated accumulator transaction to ledger", + required=True, + ) + + +class RevRegWalletUpdatedResultSchema(OpenAPISchema): + """Number of wallet revocation entries status updated.""" + + rev_reg_delta = fields.Dict( + description="Indy revocation registry delta", + ) + accum_calculated = fields.Dict( + description="Calculated accumulator for phantom revocations", + ) + accum_fixed = fields.Dict( + description="Applied ledger transaction to fix revocations", + ) + + class RevRegsCreatedSchema(OpenAPISchema): """Result schema for request for revocation registries created.""" @@ -637,7 +664,7 @@ async def get_rev_reg_issued_count(request: web.BaseRequest): @response_schema(CredRevRecordDetailsResultSchema(), 200, description="") async def get_rev_reg_issued(request: web.BaseRequest): """ - Request handler to get number of credentials issued against revocation registry. + Request handler to get credentials issued against revocation registry. Args: request: aiohttp request object @@ -669,8 +696,8 @@ async def get_rev_reg_issued(request: web.BaseRequest): summary="Get details of revoked credentials from ledger", ) @match_info_schema(RevRegIdMatchInfoSchema()) -@response_schema(CredRevDeltaRecordResultSchema(), 200, description="") -async def get_rev_reg_delta(request: web.BaseRequest): +@response_schema(CredRevIndyRecordsResultSchema(), 200, description="") +async def get_rev_reg_indy_recs(request: web.BaseRequest): """ Request handler to get details of revoked credentials from ledger. @@ -688,7 +715,130 @@ async def get_rev_reg_delta(request: web.BaseRequest): revoc = IndyRevocation(context.profile) rev_reg_delta = await revoc.get_issuer_rev_reg_delta(rev_reg_id) - return web.json_response({"result": rev_reg_delta.serialize()}) + return web.json_response( + { + "rev_reg_delta": rev_reg_delta, + } + ) + + +@docs( + tags=["revocation"], + summary="Fix revocation state in wallet and return number of updated entries", +) +@match_info_schema(RevRegIdMatchInfoSchema()) +@querystring_schema(RevRegUpdateRequestMatchInfoSchema()) +@response_schema(RevRegWalletUpdatedResultSchema(), 200, description="") +async def update_rev_reg_revoked_state(request: web.BaseRequest): + """ + Request handler to get number of credentials issued against revocation registry. + + Args: + request: aiohttp request object + + Returns: + Number of credentials updated in wallet + + """ + context: AdminRequestContext = request["context"] + + rev_reg_id = request.match_info["rev_reg_id"] + + apply_ledger_update_json = request.query.get("apply_ledger_update", "false") + LOGGER.debug(">>> apply_ledger_update_json = %s", apply_ledger_update_json) + apply_ledger_update = json.loads(request.query.get("apply_ledger_update", "false")) + + # get rev reg delta (revocations published to ledger) + revoc = IndyRevocation(context.profile) + rev_reg_delta = await revoc.get_issuer_rev_reg_delta(rev_reg_id) + + # get rev reg records from wallet (revocations and status) + recs = [] + rec_count = 0 + accum_count = 0 + recovery_txn = {} + applied_txn = {} + async with context.profile.session() as session: + try: + rev_reg_record = await IssuerRevRegRecord.retrieve_by_revoc_reg_id( + session, rev_reg_id + ) + except StorageNotFoundError as err: + raise web.HTTPNotFound(reason=err.roll_up) from err + recs = await IssuerCredRevRecord.query_by_ids(session, rev_reg_id=rev_reg_id) + + revoked_ids = [] + for rec in recs: + if rec.state == IssuerCredRevRecord.STATE_REVOKED: + revoked_ids.append(int(rec.cred_rev_id)) + if int(rec.cred_rev_id) not in rev_reg_delta["value"]["revoked"]: + # await rec.set_state(session, IssuerCredRevRecord.STATE_ISSUED) + rec_count += 1 + + LOGGER.debug(">>> fixed entry recs count = %s", rec_count) + LOGGER.debug( + ">>> rev_reg_record.revoc_reg_entry.value: %s", + rev_reg_record.revoc_reg_entry.value, + ) + LOGGER.debug('>>> rev_reg_delta.get("value"): %s', rev_reg_delta.get("value")) + + # if we had any revocation discrepencies, check the accumulator value + if rec_count > 0: + if ( + rev_reg_record.revoc_reg_entry.value and rev_reg_delta.get("value") + ) and not ( + rev_reg_record.revoc_reg_entry.value.accum + == rev_reg_delta["value"]["accum"] + ): + # rev_reg_record.revoc_reg_entry = rev_reg_delta["value"] + # await rev_reg_record.save(session) + accum_count += 1 + + genesis_transactions = context.settings.get("ledger.genesis_transactions") + if not genesis_transactions: + ledger_manager = context.injector.inject(BaseMultipleLedgerManager) + write_ledgers = await ledger_manager.get_write_ledger() + LOGGER.debug(f"write_ledgers = {write_ledgers}") + pool = write_ledgers[1].pool + LOGGER.debug(f"write_ledger pool = {pool}") + + genesis_transactions = pool.genesis_txns + + if not genesis_transactions: + raise web.HTTPInternalServerError( + reason="no genesis_transactions for writable ledger" + ) + + calculated_txn = await generate_ledger_rrrecovery_txn( + genesis_transactions, + rev_reg_id, + revoked_ids, + ) + recovery_txn = json.loads(calculated_txn.to_json()) + + LOGGER.debug(">>> apply_ledger_update = %s", apply_ledger_update) + if apply_ledger_update: + ledger = session.inject(BaseLedger) + if not ledger: + reason = "No ledger available" + if not session.context.settings.get_value("wallet.type"): + reason += ": missing wallet-type?" + raise web.HTTPInternalServerError(reason=reason) + + async with ledger: + ledger_response = await ledger.send_revoc_reg_entry( + rev_reg_id, "CL_ACCUM", recovery_txn + ) + + applied_txn = ledger_response["result"] + + return web.json_response( + { + "rev_reg_delta": rev_reg_delta, + "accum_calculated": recovery_txn, + "accum_fixed": applied_txn, + } + ) @docs( @@ -1419,8 +1569,8 @@ async def register(app: web.Application): allow_head=False, ), web.get( - "/revocation/registry/{rev_reg_id}/issued/delta", - get_rev_reg_delta, + "/revocation/registry/{rev_reg_id}/issued/indy_recs", + get_rev_reg_indy_recs, allow_head=False, ), web.post("/revocation/create-registry", create_rev_reg), @@ -1437,6 +1587,10 @@ async def register(app: web.Application): "/revocation/registry/{rev_reg_id}/set-state", set_rev_reg_state, ), + web.put( + "/revocation/registry/{rev_reg_id}/fix-revocation-entry-state", + update_rev_reg_revoked_state, + ), ] ) diff --git a/demo/INTEGRATION-TESTS.md b/demo/INTEGRATION-TESTS.md index 2d4726398a..e74008f29d 100644 --- a/demo/INTEGRATION-TESTS.md +++ b/demo/INTEGRATION-TESTS.md @@ -17,11 +17,13 @@ cd indy-tails-server/docker cd ../.. git clone https://github.com/hyperledger/aries-cloudagent-python cd aries-cloudagent-python/demo -./run_bdd +./run_bdd -t ~@taa_required ``` Note that an Indy ledger and tails server are both required (these can also be specified using environment variables). +Note also that some tests require a ledger with TAA enabled, how to run these tests will be described later. + By default the test suite runs using a default (SQLite) wallet, to run the tests using postgres run the following: ```bash @@ -33,7 +35,7 @@ ACAPY_ARG_FILE=postgres-indy-args.yml ./run_bdd To run the tests against the back-end `askar` libraries (as opposed to indy-sdk) run the following: ```bash -BDD_EXTRA_AGENT_ARGS="{\"wallet-type\":\"askar\"}" ./run_bdd +BDD_EXTRA_AGENT_ARGS="{\"wallet-type\":\"askar\"}" ./run_bdd -t ~@taa_required ``` (Note that `wallet-type` is currently the only extra argument supported.) @@ -44,6 +46,29 @@ You can run individual tests by specifying the tag(s): ./run_bdd -t @T001-AIP10-RFC0037 ``` +## Running Integration Tests which require TAA + +To run a local von-network with TAA enabled,run the following: + +```bash +git clone https://github.com/bcgov/von-network +cd von-network +./manage build +./manage start --taa-sample --logs +``` + +You can then run the TAA-enabled tests as follows: + +```bash +./run_bdd -t @taa_required +``` + +or: + +```bash +BDD_EXTRA_AGENT_ARGS="{\"wallet-type\":\"askar\"}" ./run_bdd -t @taa_required +``` + ## Aca-py Integration Tests vs Aries Agent Test Harness (AATH) Aca-py Behave tests are based on the interoperability tests that are implemented in the [Aries Agent Test Harness (AATH)](https://github.com/hyperledger/aries-agent-test-harness). Both use [Behave (Gherkin)](https://behave.readthedocs.io/en/stable/) to execute tests against a running aca-py agent (or in the case of AATH, against any compatible Aries agent), however the aca-py integration tests focus on aca-py specific features. diff --git a/demo/docker/docker-compose.yml b/demo/docker/docker-compose.yml index 94c4061fa6..ed2c993854 100644 --- a/demo/docker/docker-compose.yml +++ b/demo/docker/docker-compose.yml @@ -6,9 +6,12 @@ version: "3" services: vcr-agent: - image: bcgovimages/aries-cloudagent:py36-1.16-1_0.7.3 + build: + context: ../../ + dockerfile: docker/Dockerfile.run ports: - 8010:8010 + - 8001:8001 depends_on: - wallet-db entrypoint: /bin/bash @@ -17,10 +20,11 @@ services: "sleep 5; \ aca-py start \ --auto-provision \ + --seed '00000000o_faber_secondary_school' \ --inbound-transport http '0.0.0.0' 8001 \ --endpoint 'http://host.docker.internal:8001' \ --outbound-transport http \ - --genesis-transactions-list 'ledgers.yaml' + --genesis-url 'https://raw.githubusercontent.com/sovrin-foundation/sovrin/master/sovrin/pool_transactions_builder_genesis' \ --auto-accept-invites \ --auto-accept-requests \ --auto-ping-connection \ @@ -29,6 +33,9 @@ services: --auto-respond-credential-offer \ --auto-respond-credential-request \ --auto-verify-presentation \ + --tails-server-base-url 'https://tails-test.vonx.io' \ + --notify-revocation \ + --monitor-revocation-notification \ --wallet-type 'indy' \ --wallet-name 'acapy_agent_wallet' \ --wallet-key 'key' \ @@ -42,9 +49,13 @@ services: ] volumes: - ./ledgers.yaml:/home/indy/ledgers.yaml + networks: + - tails-network # note - if you want to start aca-py in single-ledger mode, replace the `--genesis-transactions-list` parameter above with: -# --genesis-url 'https://raw.githubusercontent.com/sovrin-foundation/sovrin/master/sovrin/pool_transactions_sandbox_genesis' \ +# --genesis-url 'https://raw.githubusercontent.com/sovrin-foundation/sovrin/master/sovrin/pool_transactions_builder_genesis' \ +# --genesis-url 'http://host.docker.internal:9000/genesis' \ +# --genesis-transactions-list 'ledgers.yaml' \ wallet-db: image: vcr-postgresql @@ -57,6 +68,13 @@ services: - 5433:5432 volumes: - wallet-db-data:/var/lib/pgsql/data + networks: + - tails-network volumes: wallet-db-data: + +networks: + tails-network: + external: + name: docker_tails-server diff --git a/demo/docker/ledgers.yaml b/demo/docker/ledgers.yaml index baf174d35e..da87ee6cf0 100644 --- a/demo/docker/ledgers.yaml +++ b/demo/docker/ledgers.yaml @@ -4,10 +4,15 @@ # *or*: # - re-accept the TAA once you start aca-py in multi-ledger mode # (the TAA acceptance is stored in a wallet record keyed on the `pool_name`) -- id: SOVRINSandbox +#- id: localhost +# is_production: true +# is_write: true +# genesis_url: 'http://host.docker.internal:9000/genesis' +# register a Sovrin dev DID here: https://selfserve.sovrin.org/ +- id: SOVRINDevelopment is_production: true is_write: true - genesis_url: 'https://raw.githubusercontent.com/sovrin-foundation/sovrin/stable/sovrin/pool_transactions_sandbox_genesis' + genesis_url: 'https://raw.githubusercontent.com/sovrin-foundation/sovrin/master/sovrin/pool_transactions_builder_genesis' - id: BCovrinTest is_production: true genesis_url: 'http://test.bcovrin.vonx.io/genesis' diff --git a/demo/features/steps/0453-issue-credential.py b/demo/features/steps/0453-issue-credential.py index 2914489406..e59f668b15 100644 --- a/demo/features/steps/0453-issue-credential.py +++ b/demo/features/steps/0453-issue-credential.py @@ -30,6 +30,7 @@ @given('"{issuer}" is ready to issue a credential for {schema_name}') +@then('"{issuer}" is ready to issue a credential for {schema_name}') def step_impl(context, issuer, schema_name): agent = context.active_agents[issuer] @@ -106,6 +107,122 @@ def step_impl(context, holder): async_sleep(3.0) +@given('"{holder}" successfully revoked the credential') +@when('"{holder}" successfully revoked the credential') +@then('"{holder}" successfully revoked the credential') +def step_impl(context, holder): + agent = context.active_agents[holder] + + # get the required revocation info from the last credential exchange + cred_exchange = context.cred_exchange + print("rev_reg_id:", cred_exchange["indy"]["rev_reg_id"]) + print("cred_rev_id:", cred_exchange["indy"]["cred_rev_id"]) + print("connection_id:", cred_exchange["cred_ex_record"]["connection_id"]) + + # check wallet status + wallet_revoked_creds = agent_container_GET( + agent["agent"], + "/revocation/registry/" + + cred_exchange["indy"]["rev_reg_id"] + + "/issued/details", + ) + print("wallet_revoked_creds:", wallet_revoked_creds) + matched = False + for rec in wallet_revoked_creds: + if rec["cred_rev_id"] == cred_exchange["indy"]["cred_rev_id"]: + matched = True + assert rec["state"] == "revoked" + assert matched + + # check ledger status + ledger_revoked_creds = agent_container_GET( + agent["agent"], + "/revocation/registry/" + + cred_exchange["indy"]["rev_reg_id"] + + "/issued/indy_recs", + ) + assert ( + int(cred_exchange["indy"]["cred_rev_id"]) + in ledger_revoked_creds["rev_reg_delta"]["value"]["revoked"] + ) + + +@given('"{holder}" attempts to revoke the credential') +@when('"{holder}" attempts to revoke the credential') +@then('"{holder}" attempts to revoke the credential') +def step_impl(context, holder): + agent = context.active_agents[holder] + + # get the required revocation info from the last credential exchange + cred_exchange = context.cred_exchange + + cred_exchange = agent_container_GET( + agent["agent"], "/issue-credential-2.0/records/" + cred_exchange["cred_ex_id"] + ) + context.cred_exchange = cred_exchange + print("rev_reg_id:", cred_exchange["indy"]["rev_reg_id"]) + print("cred_rev_id:", cred_exchange["indy"]["cred_rev_id"]) + print("connection_id:", cred_exchange["cred_ex_record"]["connection_id"]) + + # revoke the credential + try: + revoke_status = agent_container_POST( + agent["agent"], + "/revocation/revoke", + data={ + "rev_reg_id": cred_exchange["indy"]["rev_reg_id"], + "cred_rev_id": cred_exchange["indy"]["cred_rev_id"], + "publish": "Y", + "connection_id": cred_exchange["cred_ex_record"]["connection_id"], + }, + ) + except: + # ignore exceptions, we will check status later + pass + + # pause for a second + async_sleep(1.0) + + +@given('"{holder}" fails to publish the credential revocation') +@when('"{holder}" fails to publish the credential revocation') +@then('"{holder}" fails to publish the credential revocation') +def step_impl(context, holder): + agent = context.active_agents[holder] + + # get the required revocation info from the last credential exchange + cred_exchange = context.cred_exchange + print("rev_reg_id:", cred_exchange["indy"]["rev_reg_id"]) + print("cred_rev_id:", cred_exchange["indy"]["cred_rev_id"]) + print("connection_id:", cred_exchange["cred_ex_record"]["connection_id"]) + + # check wallet status + wallet_revoked_creds = agent_container_GET( + agent["agent"], + "/revocation/registry/" + + cred_exchange["indy"]["rev_reg_id"] + + "/issued/details", + ) + matched = False + for rec in wallet_revoked_creds: + if rec["cred_rev_id"] == cred_exchange["indy"]["cred_rev_id"]: + matched = True + assert rec["state"] == "revoked" + assert matched + + # check ledger status + ledger_revoked_creds = agent_container_GET( + agent["agent"], + "/revocation/registry/" + + cred_exchange["indy"]["rev_reg_id"] + + "/issued/indy_recs", + ) + assert ( + int(cred_exchange["indy"]["cred_rev_id"]) + not in ledger_revoked_creds["rev_reg_delta"]["value"]["revoked"] + ) + + @when('"{holder}" has the credential issued') @then('"{holder}" has the credential issued') def step_impl(context, holder): diff --git a/demo/features/steps/0586-sign-transaction.py b/demo/features/steps/0586-sign-transaction.py index 531d8a7009..5f3868f830 100644 --- a/demo/features/steps/0586-sign-transaction.py +++ b/demo/features/steps/0586-sign-transaction.py @@ -401,6 +401,9 @@ def step_impl(context, agent_name, schema_name): @when( '"{holder}" has an issued {schema_name} credential {credential_data} from "{issuer}"' ) +@then( + '"{holder}" has an issued {schema_name} credential {credential_data} from "{issuer}"' +) def step_impl(context, holder, schema_name, credential_data, issuer): context.execute_steps( ''' diff --git a/demo/features/steps/taa-txn-author-agreement.py b/demo/features/steps/taa-txn-author-agreement.py new file mode 100644 index 0000000000..6dc43cc22f --- /dev/null +++ b/demo/features/steps/taa-txn-author-agreement.py @@ -0,0 +1,89 @@ +from behave import given, when, then +import json +from time import sleep +import time + +from bdd_support.agent_backchannel_client import ( + agent_container_GET, + agent_container_POST, + agent_container_PUT, + async_sleep, +) + + +@given('"{issuer}" connects to a ledger that requires acceptance of the TAA') +def step_impl(context, issuer): + agent = context.active_agents[issuer] + + taa_info = agent_container_GET(agent["agent"], "/ledger/taa") + print("ledger taa_info:", taa_info) + assert taa_info["result"]["taa_required"] + + +@given('"{issuer}" accepts the TAA') +@when('"{issuer}" accepts the TAA') +@then('"{issuer}" accepts the TAA') +def step_impl(context, issuer): + agent = context.active_agents[issuer] + + taa_info = agent_container_GET(agent["agent"], "/ledger/taa") + print("ledger taa_info:", taa_info) + assert taa_info["result"]["taa_required"] + + taa_accept = { + "mechanism": list(taa_info["result"]["aml_record"]["aml"].keys())[0], + "version": taa_info["result"]["taa_record"]["version"], + "text": taa_info["result"]["taa_record"]["text"], + } + print("taa_acceptance:", taa_accept) + + taa_status = agent_container_POST( + agent["agent"], + "/ledger/taa/accept", + data=taa_accept, + ) + + +@given('"{issuer}" rejects the TAA') +@when('"{issuer}" rejects the TAA') +@then('"{issuer}" rejects the TAA') +def step_impl(context, issuer): + agent = context.active_agents[issuer] + + taa_info = agent_container_GET(agent["agent"], "/ledger/taa") + print("ledger taa_info:", taa_info) + assert taa_info["result"]["taa_required"] + + # reject by "accepting" with the wrong text (this can override a prior acceptance) + taa_accept = { + "mechanism": list(taa_info["result"]["aml_record"]["aml"].keys())[0], + "version": taa_info["result"]["taa_record"]["version"], + "text": "Unacceptable text", + } + print("taa_rejectance:", taa_accept) + + taa_status = agent_container_POST( + agent["agent"], + "/ledger/taa/accept", + data=taa_accept, + ) + + +@when('"{issuer}" posts a revocation correction to the ledger') +@then('"{issuer}" posts a revocation correction to the ledger') +def step_impl(context, issuer): + agent = context.active_agents[issuer] + + # get the required revocation info from the last credential exchange + cred_exchange = context.cred_exchange + + # post a correcting leger entry + ledger_status = agent_container_PUT( + agent["agent"], + "/revocation/registry/" + + cred_exchange["indy"]["rev_reg_id"] + + "/fix-revocation-entry-state", + params={ + "apply_ledger_update": "true", + }, + ) diff --git a/demo/features/taa-txn-author-acceptance.feature b/demo/features/taa-txn-author-acceptance.feature new file mode 100644 index 0000000000..bcaaa3dc32 --- /dev/null +++ b/demo/features/taa-txn-author-acceptance.feature @@ -0,0 +1,92 @@ +Feature: TAA Transaction Author Agreement related tests + +# Note that these tests require a ledger with TAA enabled +# you can run von-network as `./manage start --taa-sample --logs` + + @T001-TAA @taa_required + Scenario Outline: accept the ledger TAA and write to the ledger + Given we have "1" agents + | name | role | capabilities | + | Acme | issuer | | + And "Acme" connects to a ledger that requires acceptance of the TAA + When "Acme" accepts the TAA + Then "Acme" is ready to issue a credential for + + Examples: + | Acme_capabilities | Schema_name | + | --taa-accept | driverslicense | + | --taa-accept --multitenant | driverslicense | + | --taa-accept --revocation | driverslicense | + + @T001a-TAA @taa_required + Scenario Outline: accept the ledger TAA and write to the ledger via endorser + Given we have "2" agents + | name | role | capabilities | + | Acme | endorser | | + | Bob | author | | + And "Acme" connects to a ledger that requires acceptance of the TAA + And "Bob" connects to a ledger that requires acceptance of the TAA + And "Acme" and "Bob" have an existing connection + When "Acme" accepts the TAA + And "Bob" accepts the TAA + And "Acme" has a DID with role "ENDORSER" + And "Acme" connection has job role "TRANSACTION_ENDORSER" + And "Bob" connection has job role "TRANSACTION_AUTHOR" + And "Bob" connection sets endorser info + And "Bob" has a DID with role "AUTHOR" + And "Bob" authors a schema transaction with + And "Bob" requests endorsement for the transaction + And "Acme" endorses the transaction + Then "Bob" can write the transaction to the ledger + And "Bob" has written the schema to the ledger + + Examples: + | Acme_capabilities | Bob_capabilities | Schema_name | + | --taa-accept | --taa-accept | driverslicense | + | --taa-accept --multitenant | --taa-accept --multitenant | driverslicense | + + @T002-TAA @taa_required + Scenario Outline: Revoke credential using a ledger with TAA required + Given we have "2" agents + | name | role | capabilities | + | Faber | verifier | | + | Bob | prover | | + And "Faber" connects to a ledger that requires acceptance of the TAA + And "Faber" accepts the TAA + And "Faber" and "Bob" have an existing connection + And "Bob" has an issued credential from "" + And "Faber" revokes the credential + And "Faber" successfully revoked the credential + When "Faber" sends a request for proof presentation to "Bob" + Then "Faber" has the proof verification fail + + Examples: + | issuer | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Proof_request | + | Faber | --taa-accept --revocation --public-did | | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | + + @T003-TAA @taa_required + Scenario Outline: Fail to publish revoked credential using a ledger with TAA required, and fix the ledger + Given we have "2" agents + | name | role | capabilities | + | Faber | verifier | | + | Bob | prover | | + And "Faber" connects to a ledger that requires acceptance of the TAA + And "Faber" accepts the TAA + And "Faber" and "Bob" have an existing connection + And "Bob" has an issued credential from "" + And "Faber" revokes the credential + And "Faber" successfully revoked the credential + When "Faber" rejects the TAA + And "Bob" has an issued credential from "" + And "Faber" attempts to revoke the credential + And "Faber" fails to publish the credential revocation + Then "Faber" accepts the TAA + And "Faber" posts a revocation correction to the ledger + And "Faber" successfully revoked the credential + And "Bob" has an issued credential from "" + And "Faber" revokes the credential + And "Faber" successfully revoked the credential + + Examples: + | issuer | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Proof_request | + | Faber | --taa-accept --revocation --public-did | | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | diff --git a/demo/runners/agent_container.py b/demo/runners/agent_container.py index e683ca2850..164360e0e7 100644 --- a/demo/runners/agent_container.py +++ b/demo/runners/agent_container.py @@ -628,6 +628,7 @@ def __init__( arg_file: str = None, endorser_role: str = None, reuse_connections: bool = False, + taa_accept: bool = False, ): # configuration parameters self.genesis_txns = genesis_txns @@ -660,6 +661,7 @@ def __init__( # local agent(s) self.agent = None self.mediator_agent = None + self.taa_accept = taa_accept async def initialize( self, @@ -746,11 +748,15 @@ async def initialize( webhook_port=None, mediator_agent=self.mediator_agent, endorser_agent=self.endorser_agent, + taa_accept=self.taa_accept, ) elif self.mediation: # we need to pre-connect the agent to its mediator if not await connect_wallet_to_mediator(self.agent, self.mediator_agent): raise Exception("Mediation setup FAILED :-(") + else: + if self.taa_accept: + await self.agent.taa_accept() if self.public_did and self.cred_type == CRED_FORMAT_JSON_LD: # create did of appropriate type @@ -1179,6 +1185,11 @@ def arg_parser(ident: str = None, port: int = 8020): metavar="", help="Specify a file containing additional aca-py parameters", ) + parser.add_argument( + "--taa-accept", + action="store_true", + help="Accept the ledger's TAA, if required", + ) return parser @@ -1279,6 +1290,7 @@ async def create_agent_with_args(args, ident: str = None): aip=aip, endorser_role=args.endorser_role, reuse_connections=reuse_connections, + taa_accept=args.taa_accept, ) return agent diff --git a/demo/runners/alice.py b/demo/runners/alice.py index 3a63177c14..8bea579d29 100644 --- a/demo/runners/alice.py +++ b/demo/runners/alice.py @@ -169,11 +169,13 @@ async def main(args): target_wallet_name, webhook_port=alice_agent.agent.get_new_webhook_port(), mediator_agent=alice_agent.mediator_agent, + taa_accept=alice_agent.taa_accept, ) else: await alice_agent.agent.register_or_switch_wallet( target_wallet_name, mediator_agent=alice_agent.mediator_agent, + taa_accept=alice_agent.taa_accept, ) elif option == "3": diff --git a/demo/runners/faber.py b/demo/runners/faber.py index 78a3ad552b..8f89a23384 100644 --- a/demo/runners/faber.py +++ b/demo/runners/faber.py @@ -476,6 +476,7 @@ async def main(args): public_did=True, mediator_agent=faber_agent.mediator_agent, endorser_agent=faber_agent.endorser_agent, + taa_accept=faber_agent.taa_accept, ) else: created = await faber_agent.agent.register_or_switch_wallet( @@ -484,6 +485,7 @@ async def main(args): mediator_agent=faber_agent.mediator_agent, endorser_agent=faber_agent.endorser_agent, cred_type=faber_agent.cred_type, + taa_accept=faber_agent.taa_accept, ) # create a schema and cred def for the new wallet # TODO check first in case we are switching between existing wallets diff --git a/demo/runners/support/agent.py b/demo/runners/support/agent.py index abf3e7fd6e..74db9995af 100644 --- a/demo/runners/support/agent.py +++ b/demo/runners/support/agent.py @@ -520,6 +520,7 @@ async def register_or_switch_wallet( mediator_agent=None, cred_type: str = CRED_FORMAT_INDY, endorser_agent=None, + taa_accept=False, ): if webhook_port is not None: await self.listen_webhooks(webhook_port) @@ -535,6 +536,9 @@ async def register_or_switch_wallet( self.managed_wallet_params["wallet_id"] = wallet_params["id"] self.managed_wallet_params["token"] = wallet_params["token"] + if taa_accept: + await self.taa_accept() + self.log(f"Switching to AGENCY wallet {target_wallet_name}") return False @@ -554,6 +558,9 @@ async def register_or_switch_wallet( self.managed_wallet_params["wallet_id"] = wallet_params["id"] self.managed_wallet_params["token"] = wallet_params["token"] + if taa_accept: + await self.taa_accept() + self.log(f"Switching to EXISTING wallet {target_wallet_name}") return False @@ -578,6 +585,9 @@ async def register_or_switch_wallet( if not await connect_wallet_to_endorser(self, endorser_agent): raise Exception("Endorser setup FAILED :-(") + if taa_accept: + await self.taa_accept() + if public_did: if cred_type == CRED_FORMAT_INDY: # assign public did @@ -812,6 +822,20 @@ async def handle_revocation_registry(self, message): reg_id = message.get("revoc_reg_id", "(undetermined)") self.log(f"Revocation registry: {reg_id} state: {message['state']}") + async def taa_accept(self): + taa_info = await self.admin_GET("/ledger/taa") + if taa_info["result"]["taa_required"]: + taa_accept = { + "mechanism": list(taa_info["result"]["aml_record"]["aml"].keys())[0], + "version": taa_info["result"]["taa_record"]["version"], + "text": taa_info["result"]["taa_record"]["text"], + } + self.log(f"Accepting TAA with: {taa_accept}") + await self.admin_POST( + "/ledger/taa/accept", + data=taa_accept, + ) + async def admin_request( self, method, path, data=None, text=False, params=None, headers=None ) -> ClientResponse: From f190b0a2980818eee93040fe18a595f2398d0efa Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Fri, 3 Jun 2022 10:55:04 -0700 Subject: [PATCH 250/872] Code cleanup from PR review Signed-off-by: Ian Costanzo --- aries_cloudagent/ledger/routes.py | 2 +- aries_cloudagent/revocation/recover.py | 22 ++++++++++++++-------- aries_cloudagent/revocation/routes.py | 2 +- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/aries_cloudagent/ledger/routes.py b/aries_cloudagent/ledger/routes.py index a5698f7fd2..504dd3d3cc 100644 --- a/aries_cloudagent/ledger/routes.py +++ b/aries_cloudagent/ledger/routes.py @@ -602,7 +602,7 @@ async def ledger_accept_taa(request: web.BaseRequest): raise web.HTTPBadRequest( reason=f"Ledger {ledger.pool_name} TAA not available" ) - LOGGER.info(f"TAA on ledger: {taa_info}") + LOGGER.info("TAA on ledger: ", taa_info) # this is a bit of a hack, but the "\ufeff" code is included in the # ledger TAA and digest calculation, so it needs to be included in the # TAA text that the user is accepting diff --git a/aries_cloudagent/revocation/recover.py b/aries_cloudagent/revocation/recover.py index 71353d4224..0c1d2c96f0 100644 --- a/aries_cloudagent/revocation/recover.py +++ b/aries_cloudagent/revocation/recover.py @@ -24,11 +24,18 @@ """ +class RevocRecoveryException(Exception): + """Raise exception generating the recovery transaction.""" + + async def fetch_txns(genesis_txns, registry_id): """Fetch tails file and revocation registry information.""" - vdr_module = importlib.import_module("indy_vdr") - credx_module = importlib.import_module("indy_credx") + try: + vdr_module = importlib.import_module("indy_vdr") + credx_module = importlib.import_module("indy_credx") + except Exception as e: + raise RevocRecoveryException(f"Failed to import library {e}") pool = await vdr_module.open_pool(transactions=genesis_txns) LOGGER.debug("Connected to pool") @@ -37,8 +44,7 @@ async def fetch_txns(genesis_txns, registry_id): fetch = vdr_module.ledger.build_get_revoc_reg_def_request(None, registry_id) result = await pool.submit_request(fetch) if not result["data"]: - LOGGER.error("Registry definition not found") - return + raise RevocRecoveryException(f"Registry definition not found for {registry_id}") data = result["data"] data["ver"] = "1.0" defn = credx_module.RevocationRegistryDefinition.load(data) @@ -51,8 +57,9 @@ async def fetch_txns(genesis_txns, registry_id): "utf-8" ) if tails_hash != defn.tails_hash: - LOGGER.debug("Tails hash mismatch: %s %s", tails_hash, defn.tails_hash) - return + raise RevocRecoveryException( + f"Tails hash mismatch {tails_hash} {defn.tails_hash}" + ) else: LOGGER.debug("Checked tails hash: %s", tails_hash) tails_temp = tempfile.NamedTemporaryFile(delete=False) @@ -65,8 +72,7 @@ async def fetch_txns(genesis_txns, registry_id): ) result = await pool.submit_request(fetch) if not result["data"]: - LOGGER.error("Error fetching delta") - return None + raise RevocRecoveryException(f"Error fetching delta fromledger") accum_to = result["data"]["value"]["accum_to"] accum_to["ver"] = "1.0" diff --git a/aries_cloudagent/revocation/routes.py b/aries_cloudagent/revocation/routes.py index 2c9d57a3e9..10bd6cb336 100644 --- a/aries_cloudagent/revocation/routes.py +++ b/aries_cloudagent/revocation/routes.py @@ -818,7 +818,7 @@ async def update_rev_reg_revoked_state(request: web.BaseRequest): LOGGER.debug(">>> apply_ledger_update = %s", apply_ledger_update) if apply_ledger_update: - ledger = session.inject(BaseLedger) + ledger = session.inject_or(BaseLedger) if not ledger: reason = "No ledger available" if not session.context.settings.get_value("wallet.type"): From 42bcb838c0b1901c0d3b4539860a1f88139a0054 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Fri, 3 Jun 2022 11:28:47 -0700 Subject: [PATCH 251/872] Formatting fixes Signed-off-by: Ian Costanzo --- aries_cloudagent/revocation/recover.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aries_cloudagent/revocation/recover.py b/aries_cloudagent/revocation/recover.py index 0c1d2c96f0..49adcbc5ed 100644 --- a/aries_cloudagent/revocation/recover.py +++ b/aries_cloudagent/revocation/recover.py @@ -72,7 +72,7 @@ async def fetch_txns(genesis_txns, registry_id): ) result = await pool.submit_request(fetch) if not result["data"]: - raise RevocRecoveryException(f"Error fetching delta fromledger") + raise RevocRecoveryException("Error fetching delta from ledger") accum_to = result["data"]["value"]["accum_to"] accum_to["ver"] = "1.0" From d460885691eaea81156711298014276389316330 Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Tue, 7 Jun 2022 11:58:18 -0600 Subject: [PATCH 252/872] feat: Add filter param to connection list for invitations As mentioned in #1789, there is no way to currently filter for connections/invited created via the OOB handler. This commit adds a filter query param to filter off the invitation message ID. Signed-off-by: Colton Wolkins (Indicio work address) --- aries_cloudagent/protocols/connections/v1_0/routes.py | 6 ++++++ .../protocols/connections/v1_0/tests/test_routes.py | 2 ++ 2 files changed, 8 insertions(+) diff --git a/aries_cloudagent/protocols/connections/v1_0/routes.py b/aries_cloudagent/protocols/connections/v1_0/routes.py index b35a6b9067..6c25b07bad 100644 --- a/aries_cloudagent/protocols/connections/v1_0/routes.py +++ b/aries_cloudagent/protocols/connections/v1_0/routes.py @@ -205,6 +205,11 @@ class ConnectionsListQueryStringSchema(OpenAPISchema): ), example=ConnRecord.Protocol.RFC_0160.aries_protocol, ) + invitation_msg_id = fields.UUID( + description="Identifier of the associated Invintation Mesage", + required=False, + example=UUIDFour.EXAMPLE, + ) class CreateInvitationQueryStringSchema(OpenAPISchema): @@ -336,6 +341,7 @@ async def connections_list(request: web.BaseRequest): "request_id", "invitation_key", "their_public_did", + "invitation_msg_id", ): if param_name in request.query and request.query[param_name] != "": tag_filter[param_name] = request.query[param_name] diff --git a/aries_cloudagent/protocols/connections/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/connections/v1_0/tests/test_routes.py index b769191c25..5886e5aed9 100644 --- a/aries_cloudagent/protocols/connections/v1_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/connections/v1_0/tests/test_routes.py @@ -33,6 +33,7 @@ async def test_connections_list(self): "connection_protocol": ConnRecord.Protocol.RFC_0160.aries_protocol, "invitation_key": "some-invitation-key", "their_public_did": "a_public_did", + "invitation_msg_id": "dummy_msg", } STATE_COMPLETED = ConnRecord.State.COMPLETED @@ -94,6 +95,7 @@ async def test_connections_list(self): "invitation_id": "dummy", "invitation_key": "some-invitation-key", "their_public_did": "a_public_did", + "invitation_msg_id": "dummy_msg", }, post_filter_positive={ "their_role": [v for v in ConnRecord.Role.REQUESTER.value], From 8a349205e2bc483d8f0e029000bf3fea15283b21 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Wed, 8 Jun 2022 09:16:56 -0400 Subject: [PATCH 253/872] refactor: move mediation record retrieval util Signed-off-by: Daniel Bluhm --- aries_cloudagent/connections/util.py | 36 ------------------- .../protocols/connections/v1_0/manager.py | 31 ++++++++-------- .../coordinate_mediation/v1_0/manager.py | 26 ++++++++++++++ 3 files changed, 42 insertions(+), 51 deletions(-) delete mode 100644 aries_cloudagent/connections/util.py diff --git a/aries_cloudagent/connections/util.py b/aries_cloudagent/connections/util.py deleted file mode 100644 index e688643f9d..0000000000 --- a/aries_cloudagent/connections/util.py +++ /dev/null @@ -1,36 +0,0 @@ -"""Class for providing base utilities for Mediator support.""" - -from ..protocols.coordinate_mediation.v1_0.manager import MediationManager -from ..protocols.coordinate_mediation.v1_0.models.mediation_record import ( - MediationRecord, -) -from ..core.profile import Profile - -from .base_manager import BaseConnectionManagerError - - -async def mediation_record_if_id( - profile: Profile, mediation_id: str = None, or_default: bool = False -): - """Validate mediation and return record. - - If mediation_id is not None, - validate mediation record state and return record - else, return None - """ - mediation_record = None - if mediation_id: - async with profile.session() as session: - mediation_record = await MediationRecord.retrieve_by_id( - session, mediation_id - ) - elif or_default: - mediation_record = await MediationManager(profile).get_default_mediator() - - if mediation_record: - if mediation_record.state != MediationRecord.STATE_GRANTED: - raise BaseConnectionManagerError( - "Mediation is not granted for mediation identified by " - f"{mediation_record.mediation_id}" - ) - return mediation_record diff --git a/aries_cloudagent/protocols/connections/v1_0/manager.py b/aries_cloudagent/protocols/connections/v1_0/manager.py index e833327f56..6405682aad 100644 --- a/aries_cloudagent/protocols/connections/v1_0/manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/manager.py @@ -285,7 +285,6 @@ async def receive_invitation( auto_accept: bool = None, alias: str = None, mediation_id: str = None, - mediation_record: MediationRecord = None, ) -> ConnRecord: """ Create a new connection record to track a received invitation. @@ -379,22 +378,9 @@ async def create_request( """ keylist_updates = None - - # Mediation Record can still be None after this operation if no - # mediation id passed and no default - mediation_record = await mediation_record_if_id( - self.profile, - mediation_id, - or_default=True, - ) - + my_info = None multitenant_mgr = self.profile.inject_or(BaseMultitenantManager) wallet_id = self.profile.settings.get("wallet.id") - base_mediation_record = None - - if multitenant_mgr and wallet_id: - base_mediation_record = await multitenant_mgr.get_default_mediator() - my_info = None if connection.my_did: async with self.profile.session() as session: @@ -406,6 +392,7 @@ async def create_request( # Create new DID for connection my_info = await wallet.create_local_did(DIDMethod.SOV, KeyType.ED25519) connection.my_did = my_info.did + mediation_mgr = MediationManager(self.profile) keylist_updates = await mediation_mgr.add_key( my_info.verkey, keylist_updates @@ -425,6 +412,20 @@ async def create_request( my_endpoints.append(default_endpoint) my_endpoints.extend(self.profile.settings.get("additional_endpoints", [])) + # Retrieve MediationRecords for constructing DID Document + mediation_mgr = MediationManager(self.profile) + + # Mediation Record can still be None after this operation if no + # mediation id passed and no default + mediation_record = await mediation_mgr.mediation_record_if_id( + mediation_id, + or_default=True, + ) + + base_mediation_record = None + if multitenant_mgr and wallet_id: + base_mediation_record = await multitenant_mgr.get_default_mediator() + did_doc = await self.create_did_document( my_info, connection.inbound_connection_id, diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py index 79c9f90d19..7d310667ce 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py @@ -650,4 +650,30 @@ async def get_my_keylist( async with self._profile.session() as session: return await RouteRecord.query(session, tag_filter) + async def mediation_record_if_id( + self, mediation_id: Optional[str] = None, or_default: bool = False + ): + """Validate mediation and return record. + + If mediation_id is not None, + validate mediation record state and return record + else, return None + """ + mediation_record = None + if mediation_id: + async with self._profile.session() as session: + mediation_record = await MediationRecord.retrieve_by_id( + session, mediation_id + ) + elif or_default: + mediation_record = await MediationManager(profile).get_default_mediator() + + if mediation_record: + if mediation_record.state != MediationRecord.STATE_GRANTED: + raise BaseConnectionManagerError( + "Mediation is not granted for mediation identified by " + f"{mediation_record.mediation_id}" + ) + return mediation_record + # }}} From 0d588bb1191910a8416bb9d2ba4aa8cb068c9905 Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Wed, 8 Jun 2022 09:55:02 -0600 Subject: [PATCH 254/872] chore: Fix typo in string Signed-off-by: Colton Wolkins (Indicio work address) --- aries_cloudagent/protocols/connections/v1_0/routes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aries_cloudagent/protocols/connections/v1_0/routes.py b/aries_cloudagent/protocols/connections/v1_0/routes.py index 6c25b07bad..41d7306b26 100644 --- a/aries_cloudagent/protocols/connections/v1_0/routes.py +++ b/aries_cloudagent/protocols/connections/v1_0/routes.py @@ -206,7 +206,7 @@ class ConnectionsListQueryStringSchema(OpenAPISchema): example=ConnRecord.Protocol.RFC_0160.aries_protocol, ) invitation_msg_id = fields.UUID( - description="Identifier of the associated Invintation Mesage", + description="Identifier of the associated Invitation Mesage", required=False, example=UUIDFour.EXAMPLE, ) From 36e38372516c109b46c730a4e162e2cb7fa9c0cd Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Wed, 8 Jun 2022 21:57:11 -0400 Subject: [PATCH 255/872] feat: add route managers Signed-off-by: Daniel Bluhm --- aries_cloudagent/multitenant/route_manager.py | 100 +++++++++ .../v1_0/route_manager.py | 199 ++++++++++++++++++ 2 files changed, 299 insertions(+) create mode 100644 aries_cloudagent/multitenant/route_manager.py create mode 100644 aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py diff --git a/aries_cloudagent/multitenant/route_manager.py b/aries_cloudagent/multitenant/route_manager.py new file mode 100644 index 0000000000..25af722ad5 --- /dev/null +++ b/aries_cloudagent/multitenant/route_manager.py @@ -0,0 +1,100 @@ +"""Multitenancy route manager.""" + + +import logging +from typing import List, Optional, Tuple +from aries_cloudagent.core.profile import Profile +from aries_cloudagent.messaging.responder import BaseResponder + +from aries_cloudagent.protocols.coordinate_mediation.v1_0.manager import ( + MediationManager, +) +from aries_cloudagent.protocols.routing.v1_0.manager import RoutingManager +from aries_cloudagent.protocols.routing.v1_0.models.route_record import RouteRecord +from aries_cloudagent.storage.error import StorageNotFoundError + +from ..protocols.coordinate_mediation.v1_0.models.mediation_record import ( + MediationRecord, +) +from ..protocols.coordinate_mediation.v1_0.route_manager import RouteManager + + +LOGGER = logging.getLogger(__name__) + + +class MultitenantRouteManager(RouteManager): + """Multitenancy route manager.""" + + def __init__(self, root_profile: Profile, sub_profile: Profile, wallet_id: str): + self.root_profile = root_profile + self.wallet_id = wallet_id + super().__init__(sub_profile) + + @property + def sub_profile(self) -> Profile: + return self.profile + + async def get_base_wallet_mediator(self) -> Optional[MediationRecord]: + return await MediationManager(self.root_profile).get_default_mediator() + + async def _route_for_key( + self, + recipient_key: str, + mediation_record: Optional[MediationRecord] = None, + *, + skip_if_exists: bool = False, + replace_key: Optional[str] = None, + ): + LOGGER.info( + f"Add route record for recipient {recipient_key} to wallet {self.wallet_id}" + ) + routing_mgr = RoutingManager(self.root_profile) + mediation_mgr = MediationManager(self.root_profile) + # Passed in mediation_record is ignored altogether. + # Only the base mediator needs updates. + mediation_record = await self.get_base_wallet_mediator() + + if skip_if_exists: + try: + async with self.root_profile.session() as session: + await RouteRecord.retrieve_by_recipient_key(session, recipient_key) + + # If no error is thrown, it means there is already a record + return None + except (StorageNotFoundError): + pass + + await routing_mgr.create_route_record( + recipient_key=recipient_key, internal_wallet_id=self.wallet_id + ) + + # External mediation + keylist_updates = None + if mediation_record: + keylist_updates = await mediation_mgr.add_key(recipient_key) + if replace_key: + keylist_updates = await mediation_mgr.remove_key(replace_key) + + responder = self.root_profile.inject(BaseResponder) + await responder.send( + keylist_updates, connection_id=mediation_record.connection_id + ) + + return keylist_updates + + async def routing_info( + self, my_endpoint: str, mediation_record: Optional[MediationRecord] = None + ) -> Tuple[List[str], str]: + routing_keys = [] + + base_mediation_record = await self.get_base_wallet_mediator() + + if base_mediation_record: + routing_keys = base_mediation_record.routing_keys + my_endpoint = base_mediation_record.endpoint + + if mediation_record: + routing_keys = [*routing_keys, *mediation_record.routing_keys] + my_endpoint = mediation_record.endpoint + + return routing_keys, my_endpoint diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py new file mode 100644 index 0000000000..480401e7f1 --- /dev/null +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py @@ -0,0 +1,199 @@ +"""Route manager. + +Set up routing for newly formed connections. +""" + + +from abc import ABC, abstractmethod +from typing import List, Optional, Tuple + +from aries_cloudagent.protocols.routing.v1_0.models.route_record import RouteRecord +from aries_cloudagent.storage.error import StorageNotFoundError + +from ....connections.models.conn_record import ConnRecord +from ....core.profile import Profile +from ....messaging.responder import BaseResponder +from ....wallet.base import BaseWallet +from ....wallet.did_info import DIDInfo +from ....wallet.did_method import DIDMethod +from ....wallet.key_type import KeyType +from .manager import MediationManager +from .models.mediation_record import MediationRecord + + +class RouteManagerError(Exception): + """Raised on error from route manager.""" + + +class RouteManager(ABC): + """Base Route Manager.""" + + def __init__(self, profile: Profile): + self.profile = profile + + async def get_or_create_my_did(self, conn_record: ConnRecord) -> DIDInfo: + if not conn_record.my_did: + async with self.profile.session() as session: + wallet = session.inject(BaseWallet) + # Create new DID for connection + my_info = await wallet.create_local_did(DIDMethod.SOV, KeyType.ED25519) + conn_record.my_did = my_info.did + await conn_record.save(session, reason="Connection my did created") + else: + async with self.profile.session() as session: + wallet = session.inject(BaseWallet) + my_info = await wallet.get_local_did(conn_record.my_did) + + return my_info + + async def mediation_record_if_id( + self, mediation_id: Optional[str] = None, or_default: bool = False + ): + """Validate mediation and return record. + + If mediation_id is not None, + validate mediation record state and return record + else, return None + """ + mediation_record = None + if mediation_id: + async with self.profile.session() as session: + mediation_record = await MediationRecord.retrieve_by_id( + session, mediation_id + ) + elif or_default: + mediation_record = await MediationManager( + self.profile + ).get_default_mediator() + + if mediation_record: + if mediation_record.state != MediationRecord.STATE_GRANTED: + raise RouteManagerError( + "Mediation is not granted for mediation identified by " + f"{mediation_record.mediation_id}" + ) + return mediation_record + + @abstractmethod + async def _route_for_key( + self, + recipient_key: str, + mediation_record: Optional[MediationRecord] = None, + *, + skip_if_exists: bool = False, + replace_key: Optional[str] = None, + ): + """Route a key.""" + + async def route_connection_as_invitee( + self, + conn_record: ConnRecord, + mediation_record: Optional[MediationRecord] = None, + ): + """Set up routing for a new connection when we are the invitee.""" + my_info = await self.get_or_create_my_did(conn_record) + return await self._route_for_key( + my_info.verkey, mediation_record, skip_if_exists=True + ) + + async def route_connection_as_inviter( + self, + conn_record: ConnRecord, + mediation_record: Optional[MediationRecord] = None, + ): + """Set up routing for a new connection when we are the inviter.""" + my_info = await self.get_or_create_my_did(conn_record) + return await self._route_for_key( + my_info.verkey, + mediation_record, + replace_key=conn_record.invitation_key, + skip_if_exists=True, + ) + + async def route_invitation( + self, + conn_record: ConnRecord, + mediation_record: Optional[MediationRecord] = None, + ): + """Set up routing for receiving a response to an invitation.""" + if mediation_record: + # Save that this invitation was created with mediation + async with self.profile.session() as session: + await conn_record.metadata_set( + session, + MediationManager.METADATA_KEY, + {MediationManager.METADATA_ID: mediation_record.mediation_id}, + ) + + if conn_record.invitation_key: + return await self._route_for_key( + conn_record.invitation_key, mediation_record, skip_if_exists=True + ) + + raise ValueError("Expected connection to have invitation_key") + + async def route_public_did(self, verkey: str): + """Establish routing for a public DID.""" + return await self._route_for_key(verkey, skip_if_exists=True) + + async def route_static( + self, + conn_record: ConnRecord, + mediation_record: Optional[MediationRecord] = None, + ): + my_info = await self.get_or_create_my_did(conn_record) + return await self._route_for_key( + my_info.verkey, mediation_record, skip_if_exists=True + ) + + @abstractmethod + async def routing_info( + self, + my_endpoint: str, + mediation_record: Optional[MediationRecord] = None, + ) -> Tuple[List[str], str]: + """Retrieve routing keys.""" + + +class CoordinateMediationV1RouteManager(RouteManager): + """Manage routes using Coordinate Mediation protocol.""" + + async def _route_for_key( + self, + recipient_key: str, + mediation_record: Optional[MediationRecord] = None, + *, + skip_if_exists: bool = False, + replace_key: Optional[str] = None, + ): + if not mediation_record: + return None + + if skip_if_exists: + try: + async with self.profile.session() as session: + await RouteRecord.retrieve_by_recipient_key(session, recipient_key) + + return None + except StorageNotFoundError: + pass + + # Keylist update is idempotent, skip_if_exists ignored + mediation_mgr = MediationManager(self.profile) + keylist_update = await mediation_mgr.add_key(recipient_key) + if replace_key: + keylist_update = await mediation_mgr.remove_key(replace_key) + + responder = self.profile.inject(BaseResponder) + await responder.send( + keylist_update, connection_id=mediation_record.connection_id + ) + return keylist_update + + async def routing_info( + self, my_endpoint: str, mediation_record: Optional[MediationRecord] = None + ) -> Tuple[List[str], str]: + if mediation_record: + return mediation_record.routing_keys, mediation_record.endpoint + + return [], my_endpoint From 3d56a0eca5f17cfa4ccc33263a83cc744adf52d6 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Wed, 8 Jun 2022 22:01:22 -0400 Subject: [PATCH 256/872] feat: integrate route manager in base classes Signed-off-by: Daniel Bluhm --- aries_cloudagent/connections/base_manager.py | 16 +++++++++ aries_cloudagent/multitenant/base.py | 36 ++++++++++---------- 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/aries_cloudagent/connections/base_manager.py b/aries_cloudagent/connections/base_manager.py index bd5e281694..9a1df8e393 100644 --- a/aries_cloudagent/connections/base_manager.py +++ b/aries_cloudagent/connections/base_manager.py @@ -18,12 +18,17 @@ from ..core.error import BaseError from ..core.profile import Profile from ..did.did_key import DIDKey +from ..multitenant.base import BaseMultitenantManager from ..protocols.connections.v1_0.messages.connection_invitation import ( ConnectionInvitation, ) from ..protocols.coordinate_mediation.v1_0.models.mediation_record import ( MediationRecord, ) +from ..protocols.coordinate_mediation.v1_0.route_manager import ( + CoordinateMediationV1RouteManager, + RouteManager, +) from ..resolver.base import ResolverError from ..resolver.did_resolver import DIDResolver from ..storage.base import BaseStorage @@ -57,6 +62,17 @@ def __init__(self, profile: Profile): self._logger = logging.getLogger(__name__) self._profile = profile + multitenant_mgr = profile.inject_or(BaseMultitenantManager) + wallet_id = profile.settings.get_str("wallet.id") + if multitenant_mgr and wallet_id: + self._route_manager: RouteManager = multitenant_mgr.get_route_manager( + profile, wallet_id + ) + else: + self._route_manager: RouteManager = CoordinateMediationV1RouteManager( + profile + ) + async def create_did_document( self, did_info: DIDInfo, diff --git a/aries_cloudagent/multitenant/base.py b/aries_cloudagent/multitenant/base.py index 18fe63a9c5..a500c88007 100644 --- a/aries_cloudagent/multitenant/base.py +++ b/aries_cloudagent/multitenant/base.py @@ -1,31 +1,28 @@ """Manager for multitenancy.""" +from abc import abstractmethod from datetime import datetime import logging -from abc import abstractmethod +from typing import List, Optional, cast import jwt -from typing import List, Optional, cast -from ..core.profile import ( - Profile, - ProfileSession, -) -from ..messaging.responder import BaseResponder from ..config.injection_context import InjectionContext -from ..wallet.models.wallet_record import WalletRecord -from ..wallet.base import BaseWallet from ..core.error import BaseError -from ..protocols.routing.v1_0.manager import RouteNotFoundError, RoutingManager -from ..protocols.routing.v1_0.models.route_record import RouteRecord -from ..transport.wire_format import BaseWireFormat -from ..storage.base import BaseStorage -from ..storage.error import StorageNotFoundError +from ..core.profile import Profile, ProfileSession +from ..messaging.responder import BaseResponder +from ..multitenant.route_manager import MultitenantRouteManager from ..protocols.coordinate_mediation.v1_0.manager import ( MediationManager, MediationRecord, ) - +from ..protocols.routing.v1_0.manager import RouteNotFoundError, RoutingManager +from ..protocols.routing.v1_0.models.route_record import RouteRecord +from ..storage.base import BaseStorage +from ..storage.error import StorageNotFoundError +from ..transport.wire_format import BaseWireFormat +from ..wallet.base import BaseWallet +from ..wallet.models.wallet_record import WalletRecord from .error import WalletKeyMissingError LOGGER = logging.getLogger(__name__) @@ -201,9 +198,9 @@ async def create_wallet( public_did_info = await wallet.get_public_did() if public_did_info: - await self.add_key( - wallet_record.wallet_id, public_did_info.verkey, skip_if_exists=True - ) + await self.get_route_manager( + profile, wallet_record.wallet_id + ).route_public_did(public_did_info.verkey) except Exception: await wallet_record.delete_record(session) raise @@ -294,6 +291,9 @@ async def remove_wallet_profile(self, profile: Profile): """ + def get_route_manager(self, sub_profile: Profile, wallet_id: str): + return MultitenantRouteManager(self._profile, sub_profile, wallet_id) + async def add_key( self, wallet_id: str, recipient_key: str, *, skip_if_exists: bool = False ): From efa8b1f932d541d5a4458e85ba8002c5c4cc9e59 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Wed, 8 Jun 2022 22:06:36 -0400 Subject: [PATCH 257/872] refactor: connection manager uses routing manager Signed-off-by: Daniel Bluhm --- .../handlers/connection_request_handler.py | 1 - .../protocols/connections/v1_0/manager.py | 190 +++++------------- 2 files changed, 47 insertions(+), 144 deletions(-) diff --git a/aries_cloudagent/protocols/connections/v1_0/handlers/connection_request_handler.py b/aries_cloudagent/protocols/connections/v1_0/handlers/connection_request_handler.py index 662e0bfbee..82eb79cf81 100644 --- a/aries_cloudagent/protocols/connections/v1_0/handlers/connection_request_handler.py +++ b/aries_cloudagent/protocols/connections/v1_0/handlers/connection_request_handler.py @@ -38,7 +38,6 @@ async def handle(self, context: RequestContext, responder: BaseResponder): connection = await mgr.receive_request( context.message, context.message_receipt, - mediation_id=mediation_id, ) if connection.accept == ConnRecord.ACCEPT_AUTO: diff --git a/aries_cloudagent/protocols/connections/v1_0/manager.py b/aries_cloudagent/protocols/connections/v1_0/manager.py index 6405682aad..cf6301101f 100644 --- a/aries_cloudagent/protocols/connections/v1_0/manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/manager.py @@ -2,14 +2,13 @@ import logging -from typing import Coroutine, Sequence, Tuple +from typing import Coroutine, Sequence, Tuple, cast from ....cache.base import BaseCache from ....config.base import InjectionError from ....connections.base_manager import BaseConnectionManager from ....connections.models.conn_record import ConnRecord from ....connections.models.connection_target import ConnectionTarget -from ....connections.util import mediation_record_if_id from ....core.error import BaseError from ....core.profile import Profile from ....messaging.responder import BaseResponder @@ -26,7 +25,6 @@ from ...routing.v1_0.manager import RoutingManager from ...coordinate_mediation.v1_0.manager import MediationManager -from ...coordinate_mediation.v1_0.models.mediation_record import MediationRecord from ...discovery.v2_0.manager import V20DiscoveryMgr from .message_types import ARIES_PROTOCOL as CONN_PROTO @@ -124,18 +122,12 @@ async def create_invitation( """ # Mediation Record can still be None after this operation if no # mediation id passed and no default - mediation_record = await mediation_record_if_id( - self.profile, + mediation_record = await self._route_manager.mediation_record_if_id( mediation_id, or_default=True, ) - keylist_updates = None image_url = self.profile.context.settings.get("image_url") - # Multitenancy setup - multitenant_mgr = self.profile.inject_or(BaseMultitenantManager) - wallet_id = self.profile.settings.get("wallet.id") - if not my_label: my_label = self.profile.settings.get("default_label") @@ -168,10 +160,7 @@ async def create_invitation( # Add mapping for multitenant relaying. # Mediation of public keys is not supported yet - if multitenant_mgr and wallet_id: - await multitenant_mgr.add_key( - wallet_id, public_did.verkey, skip_if_exists=True - ) + await self._route_manager.route_public_did(public_did.verkey) return None, invitation @@ -192,13 +181,6 @@ async def create_invitation( ) invitation_key = invitation_signing_key.verkey recipient_keys = [invitation_key] - mediation_mgr = MediationManager(self.profile) - keylist_updates = await mediation_mgr.add_key( - invitation_key, keylist_updates - ) - - if multitenant_mgr and wallet_id: - await multitenant_mgr.add_key(wallet_id, invitation_key) accept = ( ConnRecord.ACCEPT_AUTO @@ -225,39 +207,11 @@ async def create_invitation( async with self.profile.session() as session: await connection.save(session, reason="Created new invitation") - routing_keys = [] - my_endpoint = my_endpoint or self.profile.settings.get("default_endpoint") - - # The base wallet can act as a mediator for all tenants - if multitenant_mgr and wallet_id: - base_mediation_record = await multitenant_mgr.get_default_mediator() - - if base_mediation_record: - routing_keys = base_mediation_record.routing_keys - my_endpoint = base_mediation_record.endpoint - - # If we use a mediator for the base wallet we don't - # need to register the key at the subwallet mediator - # because it only needs to know the key of the base mediator - # sub wallet mediator -> base wallet mediator -> agent - keylist_updates = None - if mediation_record: - routing_keys = [*routing_keys, *mediation_record.routing_keys] - my_endpoint = mediation_record.endpoint - - # Save that this invitation was created with mediation - async with self.profile.session() as session: - await connection.metadata_set( - session, - MediationManager.METADATA_KEY, - {MediationManager.METADATA_ID: mediation_record.mediation_id}, - ) - - if keylist_updates: - responder = self.profile.inject_or(BaseResponder) - await responder.send( - keylist_updates, connection_id=mediation_record.connection_id - ) + await self._route_manager.route_invitation(connection, mediation_record) + routing_keys, my_endpoint = await self._route_manager.routing_info( + 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 @@ -377,11 +331,18 @@ async def create_request( """ - keylist_updates = None - my_info = None + mediation_record = await self._route_manager.mediation_record_if_id( + mediation_id, + or_default=True, + ) + multitenant_mgr = self.profile.inject_or(BaseMultitenantManager) wallet_id = self.profile.settings.get("wallet.id") + base_mediation_record = None + if multitenant_mgr and wallet_id: + base_mediation_record = await multitenant_mgr.get_default_mediator() + if connection.my_did: async with self.profile.session() as session: wallet = session.inject(BaseWallet) @@ -393,14 +354,10 @@ async def create_request( my_info = await wallet.create_local_did(DIDMethod.SOV, KeyType.ED25519) connection.my_did = my_info.did - mediation_mgr = MediationManager(self.profile) - keylist_updates = await mediation_mgr.add_key( - my_info.verkey, keylist_updates - ) - - # Add mapping for multitenant relay - if multitenant_mgr and wallet_id: - await multitenant_mgr.add_key(wallet_id, my_info.verkey) + # Idempotent; if routing has already been set up, no action taken + await self._route_manager.route_connection_as_invitee( + connection, mediation_record + ) # Create connection request message if my_endpoint: @@ -412,19 +369,8 @@ async def create_request( my_endpoints.append(default_endpoint) my_endpoints.extend(self.profile.settings.get("additional_endpoints", [])) - # Retrieve MediationRecords for constructing DID Document - mediation_mgr = MediationManager(self.profile) - # Mediation Record can still be None after this operation if no # mediation id passed and no default - mediation_record = await mediation_mgr.mediation_record_if_id( - mediation_id, - or_default=True, - ) - - base_mediation_record = None - if multitenant_mgr and wallet_id: - base_mediation_record = await multitenant_mgr.get_default_mediator() did_doc = await self.create_did_document( my_info, @@ -451,21 +397,12 @@ async def create_request( async with self.profile.session() as session: await connection.save(session, reason="Created connection request") - # Notify mediator of keylist changes - if keylist_updates and mediation_record: - # send a update keylist message with new recipient keys. - responder = self.profile.inject_or(BaseResponder) - await responder.send( - keylist_updates, connection_id=mediation_record.connection_id - ) - return request async def receive_request( self, request: ConnectionRequest, receipt: MessageReceipt, - mediation_id: str = None, ) -> ConnRecord: """ Receive and store a connection request. @@ -484,16 +421,10 @@ async def receive_request( settings=self.profile.settings, ) - mediation_mgr = MediationManager(self.profile) - keylist_updates = None connection = None connection_key = None my_info = None - # Multitenancy setup - multitenant_mgr = self.profile.inject_or(BaseMultitenantManager) - wallet_id = self.profile.settings.get("wallet.id") - # Determine what key will need to sign the response if receipt.recipient_did_public: async with self.profile.session() as session: @@ -533,9 +464,6 @@ async def receive_request( my_info = await wallet.create_local_did( DIDMethod.SOV, KeyType.ED25519 ) - keylist_updates = await mediation_mgr.add_key( - my_info.verkey, keylist_updates - ) new_connection = ConnRecord( invitation_key=connection_key, @@ -563,14 +491,6 @@ async def receive_request( connection = new_connection - # Add mapping for multitenant relay - if multitenant_mgr and wallet_id: - await multitenant_mgr.add_key(wallet_id, my_info.verkey) - else: - # remove key from mediator keylist - keylist_updates = await mediation_mgr.remove_key( - connection_key, keylist_updates - ) conn_did_doc = request.connection.did_doc if not conn_did_doc: raise ConnectionManagerError( @@ -597,14 +517,7 @@ async def receive_request( async with self.profile.session() as session: wallet = session.inject(BaseWallet) my_info = await wallet.create_local_did(DIDMethod.SOV, KeyType.ED25519) - # send update-keylist message with new recipient keys - keylist_updates = await mediation_mgr.add_key( - my_info.verkey, keylist_updates - ) - # Add mapping for multitenant relay - if multitenant_mgr and wallet_id: - await multitenant_mgr.add_key(wallet_id, my_info.verkey) async with self.profile.session() as session: connection = await ConnRecord.retrieve_by_invitation_msg_id( session=session, @@ -634,14 +547,6 @@ async def receive_request( # Attach the connection request so it can be found and responded to await connection.attach_request(session, request) - # Send keylist updates to mediator - mediation_record = await mediation_record_if_id(self.profile, mediation_id) - if keylist_updates and mediation_record: - responder = self.profile.inject_or(BaseResponder) - await responder.send( - keylist_updates, connection_id=mediation_record.connection_id - ) - return connection async def create_response( @@ -668,14 +573,15 @@ async def create_response( settings=self.profile.settings, ) - keylist_updates = None - mediation_record = await mediation_record_if_id(self.profile, mediation_id) + mediation_record = await self._route_manager.mediation_record_if_id( + mediation_id + ) # Multitenancy setup multitenant_mgr = self.profile.inject_or(BaseMultitenantManager) wallet_id = self.profile.settings.get("wallet.id") - base_mediation_record = None + base_mediation_record = None if multitenant_mgr and wallet_id: base_mediation_record = await multitenant_mgr.get_default_mediator() @@ -689,6 +595,7 @@ async def create_response( async with self.profile.session() as session: request = await connection.retrieve_request(session) + if connection.my_did: async with self.profile.session() as session: wallet = session.inject(BaseWallet) @@ -698,13 +605,11 @@ async def create_response( wallet = session.inject(BaseWallet) my_info = await wallet.create_local_did(DIDMethod.SOV, KeyType.ED25519) connection.my_did = my_info.did - mediation_mgr = MediationManager(self.profile) - keylist_updates = await mediation_mgr.add_key( - my_info.verkey, keylist_updates - ) - # Add mapping for multitenant relay - if multitenant_mgr and wallet_id: - await multitenant_mgr.add_key(wallet_id, my_info.verkey) + + # Idempotent; if routing has already been set up, no action taken + await self._route_manager.route_connection_as_inviter( + connection, mediation_record + ) # Create connection response message if my_endpoint: @@ -746,13 +651,6 @@ async def create_response( log_params={"response": response}, ) - # Update mediator if necessary - if keylist_updates and mediation_record: - responder = self.profile.inject_or(BaseResponder) - await responder.send( - keylist_updates, connection_id=mediation_record.connection_id - ) - # TODO It's possible the mediation request sent here might arrive # before the connection response. This would result in an error condition # difficult to accomodate for without modifying handlers for trust ping @@ -901,6 +799,7 @@ async def create_static_connection( their_endpoint: str = None, their_label: str = None, alias: str = None, + mediation_id: str = None, ) -> Tuple[DIDInfo, DIDInfo, ConnRecord]: """ Register a new static connection (for use by the test suite). @@ -918,11 +817,6 @@ async def create_static_connection( Tuple: my DIDInfo, their DIDInfo, new `ConnRecord` instance """ - # Multitenancy setup - multitenant_mgr = self.profile.inject_or(BaseMultitenantManager) - wallet_id = self.profile.settings.get("wallet.id") - base_mediation_record = None - async with self.profile.session() as session: wallet = session.inject(BaseWallet) # seed and DID optional @@ -962,20 +856,30 @@ async def create_static_connection( connection_id=connection.connection_id ) - # Add mapping for multitenant relaying / mediation + # Routing + mediation_record = await self._route_manager.mediation_record_if_id( + mediation_id, or_default=True + ) + + multitenant_mgr = self.profile.inject_or(BaseMultitenantManager) + wallet_id = self.profile.settings.get("wallet.id") + + base_mediation_record = None if multitenant_mgr and wallet_id: base_mediation_record = await multitenant_mgr.get_default_mediator() - await multitenant_mgr.add_key(wallet_id, my_info.verkey) + + await self._route_manager.route_static(connection, mediation_record) # Synthesize their DID doc did_doc = await self.create_did_document( their_info, None, [their_endpoint or ""], - mediation_records=[base_mediation_record] - if base_mediation_record - else None, + mediation_records=list( + filter(None, [base_mediation_record, mediation_record]) + ), ) + await self.store_did_document(did_doc) return my_info, their_info, connection From 2d3dfbc183e2e3c0c7c2789b3483016070706182 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Wed, 8 Jun 2022 22:15:54 -0400 Subject: [PATCH 258/872] refactor: route_connection, utils on route manager Signed-off-by: Daniel Bluhm --- .../coordinate_mediation/v1_0/manager.py | 70 +------------------ .../v1_0/route_manager.py | 17 +++++ 2 files changed, 18 insertions(+), 69 deletions(-) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py index 7d310667ce..31b9921644 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py @@ -3,10 +3,9 @@ import logging from typing import Optional, Sequence, Tuple -from ....connections.models.conn_record import ConnRecord + from ....core.error import BaseError from ....core.profile import Profile, ProfileSession -from ....messaging.responder import BaseResponder from ....storage.base import BaseStorage from ....storage.error import StorageNotFoundError from ....storage.record import StorageRecord @@ -484,47 +483,6 @@ async def prepare_keylist_query( ) return message - async def update_keylist_for_connection( - self, conn_record: ConnRecord, mediation_record: MediationRecord - ) -> Optional[KeylistUpdate]: - """Update the mediator with keys created for the connection. - - If the connection is in invitation received state, create the - connection keys and update. - """ - if conn_record.rfc23_state == ConnRecord.State.INVITATION.rfc23strict( - ConnRecord.Role.REQUESTER - ) or conn_record.rfc23_state == ConnRecord.State.REQUEST.rfc23strict( - ConnRecord.Role.RESPONDER - ): - if not conn_record.my_did: - async with self._profile.session() as session: - wallet = session.inject(BaseWallet) - # Create new DID for connection - my_info = await wallet.create_local_did( - DIDMethod.SOV, KeyType.ED25519 - ) - conn_record.my_did = my_info.did - await conn_record.save(session, reason="Connection my did created") - else: - async with self._profile.session() as session: - wallet = session.inject(BaseWallet) - my_info = await wallet.get_local_did(conn_record.my_did) - - keylist_update = await self.add_key(my_info.verkey) - if conn_record.rfc23_state == ConnRecord.State.REQUEST.rfc23strict( - ConnRecord.Role.RESPONDER - ): - keylist_update = await self.remove_key( - conn_record.invitation_key, keylist_update - ) - responder = self._profile.inject(BaseResponder) - await responder.send( - keylist_update, connection_id=mediation_record.connection_id - ) - return keylist_update - return None - async def add_key( self, recipient_key: str, message: Optional[KeylistUpdate] = None ) -> KeylistUpdate: @@ -650,30 +608,4 @@ async def get_my_keylist( async with self._profile.session() as session: return await RouteRecord.query(session, tag_filter) - async def mediation_record_if_id( - self, mediation_id: Optional[str] = None, or_default: bool = False - ): - """Validate mediation and return record. - - If mediation_id is not None, - validate mediation record state and return record - else, return None - """ - mediation_record = None - if mediation_id: - async with self._profile.session() as session: - mediation_record = await MediationRecord.retrieve_by_id( - session, mediation_id - ) - elif or_default: - mediation_record = await MediationManager(profile).get_default_mediator() - - if mediation_record: - if mediation_record.state != MediationRecord.STATE_GRANTED: - raise BaseConnectionManagerError( - "Mediation is not granted for mediation identified by " - f"{mediation_record.mediation_id}" - ) - return mediation_record - # }}} diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py index 480401e7f1..e26405f1fc 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py @@ -110,6 +110,23 @@ async def route_connection_as_inviter( skip_if_exists=True, ) + async def route_connection( + self, + conn_record: ConnRecord, + mediation_record: Optional[MediationRecord] = None, + ): + if conn_record.rfc23_state == ConnRecord.State.INVITATION.rfc23strict( + ConnRecord.Role.REQUESTER + ): + return await self.route_connection_as_invitee(conn_record, mediation_record) + + if conn_record.rfc23_state == ConnRecord.State.REQUEST.rfc23strict( + ConnRecord.Role.RESPONDER + ): + return await self.route_connection_as_inviter(conn_record, mediation_record) + + return None + async def route_invitation( self, conn_record: ConnRecord, From 2ca17d150ec997beec89e75fc36a4cbe9521ab15 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Wed, 8 Jun 2022 22:41:36 -0400 Subject: [PATCH 259/872] fix: retrieve previously used mediator when possible Signed-off-by: Daniel Bluhm --- .../protocols/connections/v1_0/manager.py | 19 ++++----- .../v1_0/route_manager.py | 40 ++++++++++++++++--- .../coordinate_mediation/v1_0/routes.py | 31 ++++++++------ 3 files changed, 61 insertions(+), 29 deletions(-) diff --git a/aries_cloudagent/protocols/connections/v1_0/manager.py b/aries_cloudagent/protocols/connections/v1_0/manager.py index cf6301101f..3f4e71c0b7 100644 --- a/aries_cloudagent/protocols/connections/v1_0/manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/manager.py @@ -1,7 +1,6 @@ """Classes to manage connections.""" import logging - from typing import Coroutine, Sequence, Tuple, cast from ....cache.base import BaseCache @@ -16,17 +15,15 @@ from ....storage.error import StorageError, StorageNotFoundError from ....transport.inbound.receipt import MessageReceipt from ....wallet.base import BaseWallet -from ....wallet.did_info import DIDInfo from ....wallet.crypto import create_keypair, seed_to_did -from ....wallet.key_type import KeyType +from ....wallet.did_info import DIDInfo from ....wallet.did_method import DIDMethod from ....wallet.error import WalletNotFoundError +from ....wallet.key_type import KeyType from ....wallet.util import bytes_to_b58 -from ...routing.v1_0.manager import RoutingManager from ...coordinate_mediation.v1_0.manager import MediationManager - from ...discovery.v2_0.manager import V20DiscoveryMgr - +from ...routing.v1_0.manager import RoutingManager from .message_types import ARIES_PROTOCOL as CONN_PROTO from .messages.connection_invitation import ConnectionInvitation from .messages.connection_request import ConnectionRequest @@ -331,7 +328,8 @@ async def create_request( """ - mediation_record = await self._route_manager.mediation_record_if_id( + mediation_record = await self._route_manager.mediation_record_for_connection( + connection, mediation_id, or_default=True, ) @@ -369,9 +367,6 @@ async def create_request( my_endpoints.append(default_endpoint) my_endpoints.extend(self.profile.settings.get("additional_endpoints", [])) - # Mediation Record can still be None after this operation if no - # mediation id passed and no default - did_doc = await self.create_did_document( my_info, connection.inbound_connection_id, @@ -573,8 +568,8 @@ async def create_response( settings=self.profile.settings, ) - mediation_record = await self._route_manager.mediation_record_if_id( - mediation_id + mediation_record = await self._route_manager.mediation_record_for_connection( + connection, mediation_id ) # Multitenancy setup diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py index e26405f1fc..f189a2a6f6 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py @@ -46,6 +46,40 @@ async def get_or_create_my_did(self, conn_record: ConnRecord) -> DIDInfo: return my_info + def _validate_mediation_state(self, mediation_record: MediationRecord): + if mediation_record.state != MediationRecord.STATE_GRANTED: + raise RouteManagerError( + "Mediation is not granted for mediation identified by " + f"{mediation_record.mediation_id}" + ) + + async def mediation_record_for_connection( + self, + conn_record: ConnRecord, + mediation_id: Optional[str] = None, + or_default: bool = False, + ): + """Validate mediation and return record. + + If mediation_id is not None, + validate mediation record state and return record + else, return None + """ + mediation_record = None + async with self.profile.session() as session: + try: + mediation_record = await MediationRecord.retrieve_by_connection_id( + session, conn_record.connection_id + ) + except StorageNotFoundError: + pass + + if mediation_record: + self._validate_mediation_state(mediation_record) + return mediation_record + + return await self.mediation_record_if_id(mediation_id, or_default) + async def mediation_record_if_id( self, mediation_id: Optional[str] = None, or_default: bool = False ): @@ -67,11 +101,7 @@ async def mediation_record_if_id( ).get_default_mediator() if mediation_record: - if mediation_record.state != MediationRecord.STATE_GRANTED: - raise RouteManagerError( - "Mediation is not granted for mediation identified by " - f"{mediation_record.mediation_id}" - ) + self._validate_mediation_state(mediation_record) return mediation_record @abstractmethod diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/routes.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/routes.py index aed2df63ec..55a19f07c5 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/routes.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/routes.py @@ -15,11 +15,10 @@ from ....messaging.models.base import BaseModelError from ....messaging.models.openapi import OpenAPISchema from ....messaging.valid import UUIDFour +from ....multitenant.base import BaseMultitenantManager from ....storage.error import StorageError, StorageNotFoundError - from ...connections.v1_0.routes import ConnectionsConnIdMatchInfoSchema from ...routing.v1_0.models.route_record import RouteRecord, RouteRecordSchema - from .manager import MediationManager, MediationManagerError from .message_types import SPEC_URI from .messages.inner.keylist_update_rule import ( @@ -31,6 +30,7 @@ from .messages.mediate_deny import MediationDenySchema from .messages.mediate_grant import MediationGrantSchema from .models.mediation_record import MediationRecord, MediationRecordSchema +from .route_manager import CoordinateMediationV1RouteManager CONNECTION_ID_SCHEMA = fields.UUID( @@ -527,20 +527,27 @@ async def update_keylist_for_connection(request: web.BaseRequest): mediation_id = body.get("mediation_id") connection_id = request.match_info["conn_id"] try: - mediation_mgr = MediationManager(context.profile) - mediation_id = mediation_id or await mediation_mgr.get_default_mediator() - if not mediation_id: - raise web.HTTPBadRequest( - reason="No mediation_id specified and no default mediator" + multitenant_mgr = context.inject_or(BaseMultitenantManager) + wallet_id = context.settings.get_str("wallet.id") + if multitenant_mgr and wallet_id: + routing_manager = multitenant_mgr.get_route_manager( + context.profile, wallet_id ) + else: + routing_manager = CoordinateMediationV1RouteManager(context.profile) async with context.session() as session: - mediation_record = await MediationRecord.retrieve_by_id( - session, mediation_id - ) connection_record = await ConnRecord.retrieve_by_id(session, connection_id) + mediation_record = await routing_manager.mediation_record_for_connection( + connection_record, mediation_id, or_default=True + ) + + if not mediation_record: + raise web.HTTPBadRequest( + reason="No mediation_id specified and no default mediator" + ) - keylist_update = await mediation_mgr.update_keylist_for_connection( + keylist_update = await routing_manager.route_connection( connection_record, mediation_record ) @@ -550,7 +557,7 @@ async def update_keylist_for_connection(request: web.BaseRequest): except (StorageError, BaseModelError) as err: raise web.HTTPBadRequest(reason=err.roll_up) from err - return web.json_response(results, status=201) + return web.json_response(results, status=200) async def register(app: web.Application): From b116c4ae76139551a6f938e92da9b4f93ad174b2 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Wed, 8 Jun 2022 22:49:27 -0400 Subject: [PATCH 260/872] fix: broken imports Signed-off-by: Daniel Bluhm --- .../protocols/coordinate_mediation/v1_0/routes.py | 1 + .../protocols/didexchange/v1_0/manager.py | 12 +++++++----- .../protocols/out_of_band/v1_0/manager.py | 6 ++---- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/routes.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/routes.py index 55a19f07c5..1c84e74ec3 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/routes.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/routes.py @@ -519,6 +519,7 @@ async def clear_default_mediator(request: web.BaseRequest): @docs(tags=["mediation"], summary="Update keylist for a connection") @match_info_schema(ConnectionsConnIdMatchInfoSchema()) @request_schema(MediationIdMatchInfoSchema(), 200) +# TODO Fix this response so that it adequately represents Optionals @response_schema(KeylistUpdateSchema()) async def update_keylist_for_connection(request: web.BaseRequest): """Update keylist for a connection.""" diff --git a/aries_cloudagent/protocols/didexchange/v1_0/manager.py b/aries_cloudagent/protocols/didexchange/v1_0/manager.py index fc13a67c6e..158fb748e2 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/manager.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/manager.py @@ -9,7 +9,6 @@ from ....connections.models.conn_record import ConnRecord from ....connections.models.diddoc import DIDDoc from ....connections.base_manager import BaseConnectionManager -from ....connections.util import mediation_record_if_id from ....core.error import BaseError from ....core.profile import Profile from ....messaging.decorators.attach_decorator import AttachDecorator @@ -255,8 +254,7 @@ async def create_request( # Mediation Support mediation_mgr = MediationManager(self.profile) keylist_updates = None - mediation_record = await mediation_record_if_id( - self.profile, + mediation_record = await self._route_manager.mediation_record_if_id( mediation_id, or_default=True, ) @@ -552,7 +550,9 @@ async def receive_request( await conn_rec.attach_request(session, request) # Send keylist updates to mediator - mediation_record = await mediation_record_if_id(self.profile, mediation_id) + mediation_record = await self._route_manager.mediation_record_if_id( + mediation_id + ) if keylist_updates and mediation_record: responder = self.profile.inject(BaseResponder) await responder.send( @@ -588,7 +588,9 @@ async def create_response( mediation_mgr = MediationManager(self.profile) keylist_updates = None - mediation_record = await mediation_record_if_id(self.profile, mediation_id) + mediation_record = await self._route_manager.mediation_record_if_id( + mediation_id + ) base_mediation_record = None # Multitenancy setup diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py index ec157bf1b6..332473df78 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py @@ -8,7 +8,6 @@ from ....connections.base_manager import BaseConnectionManager from ....connections.models.conn_record import ConnRecord -from ....connections.util import mediation_record_if_id from ....core.error import BaseError from ....core.profile import Profile from ....did.did_key import DIDKey @@ -125,8 +124,7 @@ async def create_invitation( """ mediation_mgr = MediationManager(self.profile) - mediation_record = await mediation_record_if_id( - self.profile, + mediation_record = await self._route_manager.mediation_record_if_id( mediation_id, or_default=True, ) @@ -404,7 +402,7 @@ async def receive_invitation( """ if mediation_id: try: - await mediation_record_if_id(self.profile, mediation_id) + await self._route_manager.mediation_record_if_id(mediation_id) except StorageNotFoundError: mediation_id = None From f2bd49ece96d3a63c4249c0c31ca369e310a02c9 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Wed, 8 Jun 2022 22:53:20 -0400 Subject: [PATCH 261/872] fix: status code on response Signed-off-by: Daniel Bluhm --- .../protocols/coordinate_mediation/v1_0/routes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/routes.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/routes.py index 1c84e74ec3..06056a126b 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/routes.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/routes.py @@ -518,9 +518,9 @@ async def clear_default_mediator(request: web.BaseRequest): @docs(tags=["mediation"], summary="Update keylist for a connection") @match_info_schema(ConnectionsConnIdMatchInfoSchema()) -@request_schema(MediationIdMatchInfoSchema(), 200) +@request_schema(MediationIdMatchInfoSchema()) # TODO Fix this response so that it adequately represents Optionals -@response_schema(KeylistUpdateSchema()) +@response_schema(KeylistUpdateSchema(), 200) async def update_keylist_for_connection(request: web.BaseRequest): """Update keylist for a connection.""" context: AdminRequestContext = request["context"] From b6b67845e28ba54204a73d0e201eae2712ac0708 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Wed, 8 Jun 2022 23:07:09 -0400 Subject: [PATCH 262/872] fix: mediation id from metadata Signed-off-by: Daniel Bluhm --- .../coordinate_mediation/v1_0/route_manager.py | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py index f189a2a6f6..47f9075724 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py @@ -65,18 +65,13 @@ async def mediation_record_for_connection( validate mediation record state and return record else, return None """ - mediation_record = None async with self.profile.session() as session: - try: - mediation_record = await MediationRecord.retrieve_by_connection_id( - session, conn_record.connection_id - ) - except StorageNotFoundError: - pass - - if mediation_record: - self._validate_mediation_state(mediation_record) - return mediation_record + mediation_metadata = await conn_record.metadata_get( + session, MediationManager.METADATA_KEY, {} + ) + mediation_id = ( + mediation_metadata.get(MediationManager.METADATA_ID) or mediation_id + ) return await self.mediation_record_if_id(mediation_id, or_default) From 82893f60e5f712a2ea0c065c9942bb5b5bc5246d Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Wed, 8 Jun 2022 23:28:11 -0400 Subject: [PATCH 263/872] feat: register update-keylist route Signed-off-by: Daniel Bluhm --- aries_cloudagent/protocols/coordinate_mediation/v1_0/routes.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/routes.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/routes.py index 06056a126b..3ebfc87f1c 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/routes.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/routes.py @@ -593,6 +593,9 @@ async def register(app: web.Application): ), web.put("/mediation/{mediation_id}/default-mediator", set_default_mediator), web.delete("/mediation/default-mediator", clear_default_mediator), + web.post( + "/mediation/update-keylist/{conn_id}", update_keylist_for_connection + ), ] ) From fcb70c694c0a82cc1779863461ba4ee25828d35c Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Thu, 9 Jun 2022 11:12:10 -0400 Subject: [PATCH 264/872] fix: state mismatches, overwriting updates Signed-off-by: Daniel Bluhm --- aries_cloudagent/multitenant/route_manager.py | 4 +- .../protocols/connections/v1_0/manager.py | 4 ++ .../v1_0/route_manager.py | 45 ++++++++++++++----- 3 files changed, 40 insertions(+), 13 deletions(-) diff --git a/aries_cloudagent/multitenant/route_manager.py b/aries_cloudagent/multitenant/route_manager.py index 25af722ad5..fb9d96de36 100644 --- a/aries_cloudagent/multitenant/route_manager.py +++ b/aries_cloudagent/multitenant/route_manager.py @@ -73,7 +73,9 @@ async def _route_for_key( if mediation_record: keylist_updates = await mediation_mgr.add_key(recipient_key) if replace_key: - keylist_updates = await mediation_mgr.remove_key(replace_key) + keylist_updates = await mediation_mgr.remove_key( + replace_key, keylist_updates + ) responder = self.root_profile.inject(BaseResponder) await responder.send( diff --git a/aries_cloudagent/protocols/connections/v1_0/manager.py b/aries_cloudagent/protocols/connections/v1_0/manager.py index 3f4e71c0b7..c8bc663b9d 100644 --- a/aries_cloudagent/protocols/connections/v1_0/manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/manager.py @@ -294,6 +294,10 @@ async def receive_invitation( # Save the invitation for later processing await connection.attach_invitation(session, invitation) + await self._route_manager.save_mediator_for_connection( + connection, mediation_id=mediation_id + ) + if connection.accept == ConnRecord.ACCEPT_AUTO: request = await self.create_request(connection, mediation_id=mediation_id) responder = self.profile.inject_or(BaseResponder) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py index 47f9075724..adbcd9c18d 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py @@ -5,6 +5,7 @@ from abc import ABC, abstractmethod +import logging from typing import List, Optional, Tuple from aries_cloudagent.protocols.routing.v1_0.models.route_record import RouteRecord @@ -21,6 +22,9 @@ from .models.mediation_record import MediationRecord +LOGGER = logging.getLogger(__name__) + + class RouteManagerError(Exception): """Raised on error from route manager.""" @@ -73,7 +77,10 @@ async def mediation_record_for_connection( mediation_metadata.get(MediationManager.METADATA_ID) or mediation_id ) - return await self.mediation_record_if_id(mediation_id, or_default) + mediation_record = await self.mediation_record_if_id(mediation_id, or_default) + if mediation_record: + await self.save_mediator_for_connection(conn_record, mediation_record) + return mediation_record async def mediation_record_if_id( self, mediation_id: Optional[str] = None, or_default: bool = False @@ -116,6 +123,7 @@ async def route_connection_as_invitee( mediation_record: Optional[MediationRecord] = None, ): """Set up routing for a new connection when we are the invitee.""" + LOGGER.debug("Routing connection as invitee") my_info = await self.get_or_create_my_did(conn_record) return await self._route_for_key( my_info.verkey, mediation_record, skip_if_exists=True @@ -127,6 +135,7 @@ async def route_connection_as_inviter( mediation_record: Optional[MediationRecord] = None, ): """Set up routing for a new connection when we are the inviter.""" + LOGGER.debug("Routing connection as inviter") my_info = await self.get_or_create_my_did(conn_record) return await self._route_for_key( my_info.verkey, @@ -141,12 +150,12 @@ async def route_connection( mediation_record: Optional[MediationRecord] = None, ): if conn_record.rfc23_state == ConnRecord.State.INVITATION.rfc23strict( - ConnRecord.Role.REQUESTER + ConnRecord.Role.RESPONDER ): return await self.route_connection_as_invitee(conn_record, mediation_record) if conn_record.rfc23_state == ConnRecord.State.REQUEST.rfc23strict( - ConnRecord.Role.RESPONDER + ConnRecord.Role.REQUESTER ): return await self.route_connection_as_inviter(conn_record, mediation_record) @@ -158,14 +167,7 @@ async def route_invitation( mediation_record: Optional[MediationRecord] = None, ): """Set up routing for receiving a response to an invitation.""" - if mediation_record: - # Save that this invitation was created with mediation - async with self.profile.session() as session: - await conn_record.metadata_set( - session, - MediationManager.METADATA_KEY, - {MediationManager.METADATA_ID: mediation_record.mediation_id}, - ) + await self.save_mediator_for_connection(conn_record, mediation_record) if conn_record.invitation_key: return await self._route_for_key( @@ -188,6 +190,25 @@ async def route_static( my_info.verkey, mediation_record, skip_if_exists=True ) + async def save_mediator_for_connection( + self, + conn_record: ConnRecord, + mediation_record: Optional[MediationRecord] = None, + mediation_id: Optional[str] = None, + ): + async with self.profile.session() as session: + if mediation_id: + mediation_record = await MediationRecord.retrieve_by_id( + session, mediation_id + ) + + if mediation_record: + await conn_record.metadata_set( + session, + MediationManager.METADATA_KEY, + {MediationManager.METADATA_ID: mediation_record.mediation_id}, + ) + @abstractmethod async def routing_info( self, @@ -224,7 +245,7 @@ async def _route_for_key( mediation_mgr = MediationManager(self.profile) keylist_update = await mediation_mgr.add_key(recipient_key) if replace_key: - keylist_update = await mediation_mgr.remove_key(replace_key) + keylist_update = await mediation_mgr.remove_key(replace_key, keylist_update) responder = self.profile.inject(BaseResponder) await responder.send( From 998cb4910bb7fa835158fd2458a6b3947c2f9ea6 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Thu, 9 Jun 2022 11:49:53 -0400 Subject: [PATCH 265/872] feat: out of band manager use route manager Signed-off-by: Daniel Bluhm --- .../protocols/out_of_band/v1_0/manager.py | 60 ++----------------- 1 file changed, 6 insertions(+), 54 deletions(-) diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py index 332473df78..dea80bf851 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py @@ -15,14 +15,12 @@ from ....indy.models.xform import indy_proof_req_preview2indy_requested_creds from ....messaging.decorators.attach_decorator import AttachDecorator from ....messaging.responder import BaseResponder -from ....multitenant.base import BaseMultitenantManager from ....storage.error import StorageNotFoundError from ....transport.inbound.receipt import MessageReceipt from ....wallet.base import BaseWallet from ....wallet.util import b64_to_bytes from ....wallet.key_type import KeyType -from ...coordinate_mediation.v1_0.manager import MediationManager from ...connections.v1_0.manager import ConnectionManager from ...connections.v1_0.messages.connection_invitation import ConnectionInvitation from ...didcomm_prefix import DIDCommPrefix @@ -123,12 +121,10 @@ async def create_invitation( Invitation record """ - mediation_mgr = MediationManager(self.profile) mediation_record = await self._route_manager.mediation_record_if_id( mediation_id, or_default=True, ) - keylist_updates = None if not (hs_protos or attachments): raise OutOfBandManagerError( @@ -136,10 +132,6 @@ async def create_invitation( "request attachments, or both" ) - # Multitenancy setup - multitenant_mgr = self.profile.inject_or(BaseMultitenantManager) - wallet_id = self.profile.settings.get("wallet.id") - accept = bool( auto_accept or ( @@ -236,9 +228,6 @@ async def create_invitation( requests_attach=message_attachments, services=[f"did:sov:{public_did.did}"], ) - keylist_updates = await mediation_mgr.add_key( - public_did.verkey, keylist_updates - ) endpoint, *_ = await self.resolve_invitation(public_did.did) invi_url = invi_msg.to_url(endpoint) @@ -257,11 +246,6 @@ async def create_invitation( await conn_rec.save(session, reason="Created new invitation") await conn_rec.attach_invitation(session, invi_msg) - if multitenant_mgr and wallet_id: # add mapping for multitenant relay - await multitenant_mgr.add_key( - wallet_id, public_did.verkey, skip_if_exists=True - ) - else: invitation_mode = ( ConnRecord.INVITATION_MODE_MULTI @@ -277,12 +261,7 @@ async def create_invitation( async with self.profile.session() as session: wallet = session.inject(BaseWallet) connection_key = await wallet.create_signing_key(KeyType.ED25519) - keylist_updates = await mediation_mgr.add_key( - connection_key.verkey, keylist_updates - ) - # Add mapping for multitenant relay - if multitenant_mgr and wallet_id: - await multitenant_mgr.add_key(wallet_id, connection_key.verkey) + # Initializing InvitationMessage here to include # invitation_msg_id in webhook poyload invi_msg = InvitationMessage() @@ -301,38 +280,9 @@ async def create_invitation( async with self.profile.session() as session: await conn_rec.save(session, reason="Created new connection") - routing_keys = [] - # The base wallet can act as a mediator for all tenants - if multitenant_mgr and wallet_id: - base_mediation_record = await multitenant_mgr.get_default_mediator() - - if base_mediation_record: - routing_keys = base_mediation_record.routing_keys - my_endpoint = base_mediation_record.endpoint - - # If we use a mediator for the base wallet we don't - # need to register the key at the subwallet mediator - # because it only needs to know the key of the base mediator - # sub wallet mediator -> base wallet mediator -> agent - keylist_updates = None - if mediation_record: - routing_keys = [*routing_keys, *mediation_record.routing_keys] - my_endpoint = mediation_record.endpoint - - # Save that this invitation was created with mediation - - async with self.profile.session() as session: - await conn_rec.metadata_set( - session, - MediationManager.METADATA_KEY, - {MediationManager.METADATA_ID: mediation_record.mediation_id}, - ) - - if keylist_updates: - responder = self.profile.inject_or(BaseResponder) - await responder.send( - keylist_updates, connection_id=mediation_record.connection_id - ) + routing_keys, my_endpoint = await self._route_manager.routing_info( + my_endpoint, mediation_record + ) routing_keys = [ key if len(key.split(":")) == 3 @@ -371,6 +321,8 @@ async def create_invitation( for key, value in metadata.items(): await conn_rec.metadata_set(session, key, value) + await self._route_manager.route_invitation(conn_rec, mediation_record) + return InvitationRecord( # for return via admin API, not storage state=InvitationRecord.STATE_INITIAL, invi_msg_id=invi_msg._id, From 4a3313e4ea1948894084e94e208eb099bc29c6eb Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Thu, 9 Jun 2022 11:59:23 -0400 Subject: [PATCH 266/872] feat: didexchange manager use route manager Signed-off-by: Daniel Bluhm --- .../protocols/didexchange/v1_0/manager.py | 97 +++++-------------- 1 file changed, 25 insertions(+), 72 deletions(-) diff --git a/aries_cloudagent/protocols/didexchange/v1_0/manager.py b/aries_cloudagent/protocols/didexchange/v1_0/manager.py index 158fb748e2..d36472c0dd 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/manager.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/manager.py @@ -152,6 +152,11 @@ async def receive_invitation( conn_rec.invitation_key = did_document.verification_method[ 0 ].public_key_base58 + + await self._route_manager.save_mediator_for_connection( + conn_rec, mediation_id=mediation_id + ) + if conn_rec.accept == ConnRecord.ACCEPT_AUTO: request = await self.create_request(conn_rec, mediation_id=mediation_id) responder = self.profile.inject_or(BaseResponder) @@ -252,17 +257,17 @@ async def create_request( """ # Mediation Support - mediation_mgr = MediationManager(self.profile) - keylist_updates = None - mediation_record = await self._route_manager.mediation_record_if_id( + mediation_record = await self._route_manager.mediation_record_for_connection( + conn_rec, mediation_id, or_default=True, ) - base_mediation_record = None # Multitenancy setup multitenant_mgr = self.profile.inject_or(BaseMultitenantManager) wallet_id = self.profile.settings.get("wallet.id") + + base_mediation_record = None if multitenant_mgr and wallet_id: base_mediation_record = await multitenant_mgr.get_default_mediator() @@ -281,12 +286,11 @@ async def create_request( key_type=KeyType.ED25519, ) conn_rec.my_did = my_info.did - keylist_updates = await mediation_mgr.add_key( - my_info.verkey, keylist_updates - ) - # Add mapping for multitenant relay - if multitenant_mgr and wallet_id: - await multitenant_mgr.add_key(wallet_id, my_info.verkey) + + # Idempotent; if routing has already been set up, no action taken + await self._route_manager.route_connection_as_invitee( + conn_rec, mediation_record + ) # Create connection request message if my_endpoint: @@ -297,6 +301,7 @@ async def create_request( if default_endpoint: my_endpoints.append(default_endpoint) my_endpoints.extend(self.profile.settings.get("additional_endpoints", [])) + did_doc = await self.create_did_document( my_info, conn_rec.inbound_connection_id, @@ -329,12 +334,6 @@ async def create_request( async with self.profile.session() as session: await conn_rec.save(session, reason="Created connection request") - # Notify Mediator - if keylist_updates and mediation_record: - responder = self.profile.inject_or(BaseResponder) - await responder.send( - keylist_updates, connection_id=mediation_record.connection_id - ) return request async def receive_request( @@ -345,7 +344,6 @@ async def receive_request( my_endpoint: str = None, alias: str = None, auto_accept_implicit: bool = None, - mediation_id: str = None, ) -> ConnRecord: """ Receive and store a connection request. @@ -357,8 +355,6 @@ async def receive_request( my_endpoint: My endpoint alias: Alias for the connection auto_accept: Auto-accept request against implicit invitation - mediation_id: The record id for mediation that contains routing_keys and - service endpoint Returns: The new or updated `ConnRecord` instance @@ -369,16 +365,10 @@ async def receive_request( settings=self.profile.settings, ) - mediation_mgr = MediationManager(self.profile) - keylist_updates = None conn_rec = None connection_key = None my_info = None - # Multitenancy setup - multitenant_mgr = self.profile.inject_or(BaseMultitenantManager) - wallet_id = self.profile.settings.get("wallet.id") - # Determine what key will need to sign the response if recipient_verkey: # peer DID connection_key = recipient_verkey @@ -428,9 +418,6 @@ async def receive_request( method=DIDMethod.SOV, key_type=KeyType.ED25519, ) - keylist_updates = await mediation_mgr.add_key( - my_info.verkey, keylist_updates - ) new_conn_rec = ConnRecord( invitation_key=connection_key, @@ -458,14 +445,6 @@ async def receive_request( conn_rec = new_conn_rec - # Add mapping for multitenant relay - if multitenant_mgr and wallet_id: - await multitenant_mgr.add_key(wallet_id, my_info.verkey) - else: - keylist_updates = await mediation_mgr.remove_key( - connection_key, keylist_updates - ) - # request DID doc describes requester DID if not (request.did_doc_attach and request.did_doc_attach.data): raise DIDXManagerError( @@ -509,14 +488,6 @@ async def receive_request( key_type=KeyType.ED25519, ) - keylist_updates = await mediation_mgr.add_key( - my_info.verkey, keylist_updates - ) - - # Add mapping for multitenant relay - if multitenant_mgr and wallet_id: - await multitenant_mgr.add_key(wallet_id, my_info.verkey) - auto_accept = bool( auto_accept_implicit or ( @@ -549,16 +520,6 @@ async def receive_request( # Attach the connection request so it can be found and responded to await conn_rec.attach_request(session, request) - # Send keylist updates to mediator - mediation_record = await self._route_manager.mediation_record_if_id( - mediation_id - ) - if keylist_updates and mediation_record: - responder = self.profile.inject(BaseResponder) - await responder.send( - keylist_updates, connection_id=mediation_record.connection_id - ) - return conn_rec async def create_response( @@ -586,16 +547,15 @@ async def create_response( settings=self.profile.settings, ) - mediation_mgr = MediationManager(self.profile) - keylist_updates = None - mediation_record = await self._route_manager.mediation_record_if_id( - mediation_id + mediation_record = await self._route_manager.mediation_record_for_connection( + conn_rec, mediation_id ) - base_mediation_record = None # Multitenancy setup multitenant_mgr = self.profile.inject_or(BaseMultitenantManager) wallet_id = self.profile.settings.get("wallet.id") + + base_mediation_record = None if multitenant_mgr and wallet_id: base_mediation_record = await multitenant_mgr.get_default_mediator() @@ -618,12 +578,11 @@ async def create_response( key_type=KeyType.ED25519, ) conn_rec.my_did = my_info.did - keylist_updates = await mediation_mgr.add_key( - my_info.verkey, keylist_updates - ) - # Add mapping for multitenant relay - if multitenant_mgr and wallet_id: - await multitenant_mgr.add_key(wallet_id, my_info.verkey) + + # Idempotent; if routing has already been set up, no action taken + await self._route_manager.route_connection_as_inviter( + conn_rec, mediation_record + ) # Create connection response message if my_endpoint: @@ -634,6 +593,7 @@ async def create_response( if default_endpoint: my_endpoints.append(default_endpoint) my_endpoints.extend(self.profile.settings.get("additional_endpoints", [])) + did_doc = await self.create_did_document( my_info, conn_rec.inbound_connection_id, @@ -660,13 +620,6 @@ async def create_response( log_params={"response": response}, ) - # Update Mediator if necessary - if keylist_updates and mediation_record: - responder = self.profile.inject_or(BaseResponder) - await responder.send( - keylist_updates, connection_id=mediation_record.connection_id - ) - async with self.profile.session() as session: send_mediation_request = await conn_rec.metadata_get( session, MediationManager.SEND_REQ_AFTER_CONNECTION From c645446d642689d49e3301839c00d26b0a72f128 Mon Sep 17 00:00:00 2001 From: shaangill025 Date: Fri, 10 Jun 2022 07:43:45 -0700 Subject: [PATCH 267/872] impl check in add_non_secrets_records function Signed-off-by: shaangill025 --- .../messaging/credential_definitions/routes.py | 18 +++++++++++++----- aries_cloudagent/messaging/schemas/routes.py | 14 ++++++++++++-- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/aries_cloudagent/messaging/credential_definitions/routes.py b/aries_cloudagent/messaging/credential_definitions/routes.py index 82ffa09fab..81e60b62c8 100644 --- a/aries_cloudagent/messaging/credential_definitions/routes.py +++ b/aries_cloudagent/messaging/credential_definitions/routes.py @@ -554,12 +554,20 @@ async def add_cred_def_non_secrets_record( "cred_def_id": credential_definition_id, "epoch": str(int(time())), } - record = StorageRecord( - CRED_DEF_SENT_RECORD_TYPE, credential_definition_id, cred_def_tags - ) async with profile.session() as session: - storage = session.inject(BaseStorage) - await storage.add_record(record) + found = await storage.find_all_records( + type_filter=CRED_DEF_SENT_RECORD_TYPE, + tag_query={ + "cred_def_id": credential_definition_id, + "schema_id": schema_id, + }, + ) + if len(found) == 0: + record = StorageRecord( + CRED_DEF_SENT_RECORD_TYPE, credential_definition_id, cred_def_tags + ) + storage = session.inject(BaseStorage) + await storage.add_record(record) async def register(app: web.Application): diff --git a/aries_cloudagent/messaging/schemas/routes.py b/aries_cloudagent/messaging/schemas/routes.py index aa3f48b0fa..78973289fe 100644 --- a/aries_cloudagent/messaging/schemas/routes.py +++ b/aries_cloudagent/messaging/schemas/routes.py @@ -484,10 +484,20 @@ async def add_schema_non_secrets_record(profile: Profile, schema_id: str): "schema_version": schema_id_parts[-1], "epoch": str(int(time())), } - record = StorageRecord(SCHEMA_SENT_RECORD_TYPE, schema_id, schema_tags) + tag_query = { + "schema_name": schema_id_parts[-2], + "schema_version": schema_id_parts[-1], + } async with profile.session() as session: storage = session.inject(BaseStorage) - await storage.add_record(record) + found = await storage.find_all_records( + type_filter=SCHEMA_SENT_RECORD_TYPE, + tag_query=tag_query, + ) + if len(found) == 0: + record = StorageRecord(SCHEMA_SENT_RECORD_TYPE, schema_id, schema_tags) + storage = session.inject(BaseStorage) + await storage.add_record(record) async def register(app: web.Application): From 458903e9fcf5822685d2ccc0a3eb334bda19987d Mon Sep 17 00:00:00 2001 From: shaangill025 Date: Fri, 10 Jun 2022 07:58:19 -0700 Subject: [PATCH 268/872] update - make indy_vdr and indy consistent Signed-off-by: shaangill025 --- aries_cloudagent/ledger/indy_vdr.py | 31 ------------------- .../credential_definitions/routes.py | 18 +++-------- aries_cloudagent/messaging/schemas/routes.py | 14 ++------- 3 files changed, 7 insertions(+), 56 deletions(-) diff --git a/aries_cloudagent/ledger/indy_vdr.py b/aries_cloudagent/ledger/indy_vdr.py index 5c5e3fe689..02ed5d8818 100644 --- a/aries_cloudagent/ledger/indy_vdr.py +++ b/aries_cloudagent/ledger/indy_vdr.py @@ -464,19 +464,6 @@ async def create_and_send_schema( else: raise - schema_id_parts = schema_id.split(":") - schema_tags = { - "schema_id": schema_id, - "schema_issuer_did": public_info.did, - "schema_name": schema_id_parts[-2], - "schema_version": schema_id_parts[-1], - "epoch": str(int(time())), - } - record = StorageRecord(SCHEMA_SENT_RECORD_TYPE, schema_id, schema_tags) - async with self.profile.session() as session: - storage = session.inject(BaseStorage) - await storage.add_record(record) - return schema_id, schema_def async def check_existing_schema( @@ -715,24 +702,6 @@ async def create_and_send_credential_definition( if not write_ledger: return (credential_definition_id, {"signed_txn": resp}, novel) - # Add non-secrets record - schema_id_parts = schema_id.split(":") - cred_def_tags = { - "schema_id": schema_id, - "schema_issuer_did": schema_id_parts[0], - "schema_name": schema_id_parts[-2], - "schema_version": schema_id_parts[-1], - "issuer_did": public_info.did, - "cred_def_id": credential_definition_id, - "epoch": str(int(time())), - } - record = StorageRecord( - CRED_DEF_SENT_RECORD_TYPE, credential_definition_id, cred_def_tags - ) - async with self.profile.session() as session: - storage = session.inject(BaseStorage) - await storage.add_record(record) - return (credential_definition_id, json.loads(credential_definition_json), novel) async def get_credential_definition(self, credential_definition_id: str) -> dict: diff --git a/aries_cloudagent/messaging/credential_definitions/routes.py b/aries_cloudagent/messaging/credential_definitions/routes.py index 81e60b62c8..82ffa09fab 100644 --- a/aries_cloudagent/messaging/credential_definitions/routes.py +++ b/aries_cloudagent/messaging/credential_definitions/routes.py @@ -554,20 +554,12 @@ async def add_cred_def_non_secrets_record( "cred_def_id": credential_definition_id, "epoch": str(int(time())), } + record = StorageRecord( + CRED_DEF_SENT_RECORD_TYPE, credential_definition_id, cred_def_tags + ) async with profile.session() as session: - found = await storage.find_all_records( - type_filter=CRED_DEF_SENT_RECORD_TYPE, - tag_query={ - "cred_def_id": credential_definition_id, - "schema_id": schema_id, - }, - ) - if len(found) == 0: - record = StorageRecord( - CRED_DEF_SENT_RECORD_TYPE, credential_definition_id, cred_def_tags - ) - storage = session.inject(BaseStorage) - await storage.add_record(record) + storage = session.inject(BaseStorage) + await storage.add_record(record) async def register(app: web.Application): diff --git a/aries_cloudagent/messaging/schemas/routes.py b/aries_cloudagent/messaging/schemas/routes.py index 78973289fe..aa3f48b0fa 100644 --- a/aries_cloudagent/messaging/schemas/routes.py +++ b/aries_cloudagent/messaging/schemas/routes.py @@ -484,20 +484,10 @@ async def add_schema_non_secrets_record(profile: Profile, schema_id: str): "schema_version": schema_id_parts[-1], "epoch": str(int(time())), } - tag_query = { - "schema_name": schema_id_parts[-2], - "schema_version": schema_id_parts[-1], - } + record = StorageRecord(SCHEMA_SENT_RECORD_TYPE, schema_id, schema_tags) async with profile.session() as session: storage = session.inject(BaseStorage) - found = await storage.find_all_records( - type_filter=SCHEMA_SENT_RECORD_TYPE, - tag_query=tag_query, - ) - if len(found) == 0: - record = StorageRecord(SCHEMA_SENT_RECORD_TYPE, schema_id, schema_tags) - storage = session.inject(BaseStorage) - await storage.add_record(record) + await storage.add_record(record) async def register(app: web.Application): From e301f5e77e5071d0e5236e2564cbbc1b569e3ceb Mon Sep 17 00:00:00 2001 From: shaangill025 Date: Fri, 10 Jun 2022 08:44:34 -0700 Subject: [PATCH 269/872] formatting and test fix Signed-off-by: shaangill025 --- aries_cloudagent/ledger/indy_vdr.py | 2 -- aries_cloudagent/storage/tests/test_indy_storage.py | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/aries_cloudagent/ledger/indy_vdr.py b/aries_cloudagent/ledger/indy_vdr.py index 02ed5d8818..3043f16cc0 100644 --- a/aries_cloudagent/ledger/indy_vdr.py +++ b/aries_cloudagent/ledger/indy_vdr.py @@ -19,8 +19,6 @@ from ..cache.base import BaseCache from ..core.profile import Profile from ..indy.issuer import IndyIssuer, IndyIssuerError, DEFAULT_CRED_DEF_TAG -from ..messaging.credential_definitions.util import CRED_DEF_SENT_RECORD_TYPE -from ..messaging.schemas.util import SCHEMA_SENT_RECORD_TYPE from ..storage.base import BaseStorage, StorageRecord from ..utils import sentinel from ..utils.env import storage_path diff --git a/aries_cloudagent/storage/tests/test_indy_storage.py b/aries_cloudagent/storage/tests/test_indy_storage.py index cca1cb8ed0..1bb9a17b84 100644 --- a/aries_cloudagent/storage/tests/test_indy_storage.py +++ b/aries_cloudagent/storage/tests/test_indy_storage.py @@ -354,7 +354,7 @@ async def test_storage_del_close(self): while not mock_indy_close_search.await_count and c < 10: await asyncio.sleep(0.1) c += 1 - mock_indy_close_search.assert_awaited_once_with(1) + mock_indy_close_search.assert_awaited_with(1) with async_mock.patch.object( # error on close indy.non_secrets, "open_wallet_search", async_mock.CoroutineMock() From b15a2efa5e3d378d18ce2ea01ea4ff7fbc7c0de3 Mon Sep 17 00:00:00 2001 From: shaangill025 Date: Fri, 10 Jun 2022 11:02:46 -0700 Subject: [PATCH 270/872] porting changes over from PR#1792 Signed-off-by: shaangill025 --- aries_cloudagent/transport/inbound/base.py | 7 +++++++ aries_cloudagent/transport/inbound/http.py | 2 -- aries_cloudagent/transport/inbound/ws.py | 2 -- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/aries_cloudagent/transport/inbound/base.py b/aries_cloudagent/transport/inbound/base.py index 648514a6be..66f23179cf 100644 --- a/aries_cloudagent/transport/inbound/base.py +++ b/aries_cloudagent/transport/inbound/base.py @@ -19,6 +19,7 @@ def __init__( create_session: Callable, *, max_message_size: int = 0, + is_external: bool = False, wire_format: BaseWireFormat = None, root_profile: Profile = None, ): @@ -35,6 +36,7 @@ def __init__( self._scheme = scheme self.wire_format: BaseWireFormat = wire_format self.root_profile: Profile = root_profile + self._is_external = is_external @property def max_message_size(self): @@ -46,6 +48,11 @@ def scheme(self): """Accessor for this transport's scheme.""" return self._scheme + @property + def is_external(self): + """Accessor for this transport's is_external.""" + return self._is_external + def create_session( self, *, diff --git a/aries_cloudagent/transport/inbound/http.py b/aries_cloudagent/transport/inbound/http.py index d5cbda3e8c..8179023379 100644 --- a/aries_cloudagent/transport/inbound/http.py +++ b/aries_cloudagent/transport/inbound/http.py @@ -15,8 +15,6 @@ class HttpTransport(BaseInboundTransport): """Http Transport class.""" - is_external = False - def __init__(self, host: str, port: int, create_session, **kwargs) -> None: """ Initialize an inbound HTTP transport instance. diff --git a/aries_cloudagent/transport/inbound/ws.py b/aries_cloudagent/transport/inbound/ws.py index 96739ad273..0b74a46990 100644 --- a/aries_cloudagent/transport/inbound/ws.py +++ b/aries_cloudagent/transport/inbound/ws.py @@ -16,8 +16,6 @@ class WsTransport(BaseInboundTransport): """Websockets Transport class.""" - is_external = False - def __init__(self, host: str, port: int, create_session, **kwargs) -> None: """ Initialize an inbound WebSocket transport instance. From e837b6521c46c91e5df61232c322845e71a1ecaa Mon Sep 17 00:00:00 2001 From: shaangill025 Date: Fri, 10 Jun 2022 19:52:39 -0700 Subject: [PATCH 271/872] update print_banner Signed-off-by: shaangill025 --- aries_cloudagent/config/logging.py | 58 ++++++++++++++++-------------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/aries_cloudagent/config/logging.py b/aries_cloudagent/config/logging.py index e33d7cc550..4bb03248b8 100644 --- a/aries_cloudagent/config/logging.py +++ b/aries_cloudagent/config/logging.py @@ -113,47 +113,53 @@ def print_banner( # Inbound transports banner.print_subtitle("Inbound Transports") - banner.print_spacer() - banner.print_list( - [ - f"{transport.scheme}://{transport.host}:{transport.port}" - for transport in inbound_transports.values() - ] - ) - banner.print_spacer() - - external_in_transports = set().union( - *( - transport - for transport in inbound_transports.values() - if transport.is_external - ) - ) + internal_in_transports = [ + f"{transport.scheme}://{transport.host}:{transport.port}" + for transport in inbound_transports.values() + if not transport.is_external + ] + if internal_in_transports: + banner.print_spacer() + banner.print_list(internal_in_transports) + banner.print_spacer() + external_in_transports = [ + f"{transport.scheme}://{transport.host}:{transport.port}" + for transport in inbound_transports.values() + if transport.is_external + ] if external_in_transports: banner.print_spacer() - banner.print_list([f"{external_in_transports}"]) + banner.print_subtitle(" External Plugin") + banner.print_spacer() + banner.print_list(external_in_transports) banner.print_spacer() # Outbound transports - schemes = set().union( - *(transport.schemes for transport in outbound_transports.values()) + banner.print_subtitle("Outbound Transports") + internal_schemes = set().union( + *( + transport.schemes + for transport in outbound_transports.values() + if not transport.is_external + ) ) - if schemes: - banner.print_subtitle("Outbound Transports") + if internal_schemes: banner.print_spacer() - banner.print_list([f"{scheme}" for scheme in sorted(schemes)]) + banner.print_list([f"{scheme}" for scheme in sorted(internal_schemes)]) banner.print_spacer() - external_out_transports = set().union( + external_schemes = set().union( *( - transport + transport.schemes for transport in outbound_transports.values() if transport.is_external ) ) - if external_out_transports: + if external_schemes: + banner.print_spacer() + banner.print_subtitle(" External Plugin") banner.print_spacer() - banner.print_list([f"{external_out_transports}"]) + banner.print_list([f"{scheme}" for scheme in sorted(external_schemes)]) banner.print_spacer() # DID info From 7b1b69c791695154f409f9bd60a65ba73a38a196 Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Mon, 13 Jun 2022 14:51:11 -0700 Subject: [PATCH 272/872] adjust revocation registry update procedure to shorten transactions Signed-off-by: Andrew Whitehead --- aries_cloudagent/indy/credx/issuer.py | 182 ++++++++++-------- aries_cloudagent/indy/issuer.py | 2 - aries_cloudagent/indy/sdk/issuer.py | 18 +- aries_cloudagent/revocation/manager.py | 113 ++++++----- .../revocation/tests/test_manager.py | 66 +++++-- 5 files changed, 221 insertions(+), 160 deletions(-) diff --git a/aries_cloudagent/indy/credx/issuer.py b/aries_cloudagent/indy/credx/issuer.py index e008010929..b0108fedbc 100644 --- a/aries_cloudagent/indy/credx/issuer.py +++ b/aries_cloudagent/indy/credx/issuer.py @@ -20,7 +20,6 @@ ) from ...askar.profile import AskarProfile -from ...core.profile import ProfileSession from ..issuer import ( IndyIssuer, @@ -384,7 +383,6 @@ async def revoke_credentials( revoc_reg_id: str, tails_file_path: str, cred_revoc_ids: Sequence[str], - transaction: ProfileSession = None, ) -> Tuple[str, Sequence[str]]: """ Revoke a set of credentials in a revocation registry. @@ -399,66 +397,84 @@ async def revoke_credentials( """ - txn = transaction if transaction else await self._profile.transaction() - try: - rev_reg_def = await txn.handle.fetch(CATEGORY_REV_REG_DEF, revoc_reg_id) - rev_reg = await txn.handle.fetch( - CATEGORY_REV_REG, revoc_reg_id, for_update=True - ) - rev_reg_info = await txn.handle.fetch( - CATEGORY_REV_REG_INFO, revoc_reg_id, for_update=True - ) - if not rev_reg_def: - raise IndyIssuerError("Revocation registry definition not found") - if not rev_reg: - raise IndyIssuerError("Revocation registry not found") - if not rev_reg_info: - raise IndyIssuerError("Revocation registry metadata not found") - except AskarError as err: - raise IndyIssuerError("Error retrieving revocation registry") from err + delta = None + failed_crids = set() + max_attempt = 5 + attempt = 0 - try: - rev_reg_def = RevocationRegistryDefinition.load(rev_reg_def.raw_value) - except CredxError as err: - raise IndyIssuerError( - "Error loading revocation registry definition" - ) from err - - rev_crids = [] - failed_crids = [] - max_cred_num = rev_reg_def.max_cred_num - rev_info = rev_reg_info.value_json - used_ids = set(rev_info.get("used_ids") or []) - - for rev_id in cred_revoc_ids: - rev_id = int(rev_id) - if rev_id < 1 or rev_id > max_cred_num: - LOGGER.error( - "Skipping requested credential revocation" - "on rev reg id %s, cred rev id=%s not in range", - revoc_reg_id, - rev_id, - ) - elif rev_id > rev_info["curr_id"]: - LOGGER.warn( - "Skipping requested credential revocation" - "on rev reg id %s, cred rev id=%s not yet issued", - revoc_reg_id, - rev_id, - ) - elif rev_id in used_ids: - LOGGER.warn( - "Skipping requested credential revocation" - "on rev reg id %s, cred rev id=%s already revoked", - revoc_reg_id, - rev_id, - ) - else: - rev_crids.append(rev_id) + while True: + attempt += 1 + if attempt >= max_attempt: + raise IndyIssuerError("Repeated conflict attempting to update registry") + try: + async with self._profile.session() as session: + rev_reg_def = await session.handle.fetch( + CATEGORY_REV_REG_DEF, revoc_reg_id + ) + rev_reg = await session.handle.fetch(CATEGORY_REV_REG, revoc_reg_id) + rev_reg_info = await session.handle.fetch( + CATEGORY_REV_REG_INFO, revoc_reg_id + ) + if not rev_reg_def: + raise IndyIssuerError("Revocation registry definition not found") + if not rev_reg: + raise IndyIssuerError("Revocation registry not found") + if not rev_reg_info: + raise IndyIssuerError("Revocation registry metadata not found") + except AskarError as err: + raise IndyIssuerError("Error retrieving revocation registry") from err + + try: + rev_reg_def = RevocationRegistryDefinition.load(rev_reg_def.raw_value) + except CredxError as err: + raise IndyIssuerError( + "Error loading revocation registry definition" + ) from err + + rev_crids = set() + failed_crids = set() + max_cred_num = rev_reg_def.max_cred_num + rev_info = rev_reg_info.value_json + used_ids = set(rev_info.get("used_ids") or []) + + for rev_id in cred_revoc_ids: + rev_id = int(rev_id) + if rev_id < 1 or rev_id > max_cred_num: + LOGGER.error( + "Skipping requested credential revocation" + "on rev reg id %s, cred rev id=%s not in range", + revoc_reg_id, + rev_id, + ) + failed_crids.add(rev_id) + elif rev_id > rev_info["curr_id"]: + LOGGER.warn( + "Skipping requested credential revocation" + "on rev reg id %s, cred rev id=%s not yet issued", + revoc_reg_id, + rev_id, + ) + failed_crids.add(rev_id) + elif rev_id in used_ids: + LOGGER.warn( + "Skipping requested credential revocation" + "on rev reg id %s, cred rev id=%s already revoked", + revoc_reg_id, + rev_id, + ) + failed_crids.add(rev_id) + else: + rev_crids.add(rev_id) + + if not rev_crids: + break - if rev_crids: try: rev_reg = RevocationRegistry.load(rev_reg.raw_value) + except CredxError as err: + raise IndyIssuerError("Error loading revocation registry") from err + + try: delta = await asyncio.get_event_loop().run_in_executor( None, lambda: rev_reg.update( @@ -472,31 +488,41 @@ async def revoke_credentials( raise IndyIssuerError("Error updating revocation registry") from err try: - await txn.handle.replace( - CATEGORY_REV_REG, revoc_reg_id, rev_reg.to_json_buffer() - ) - used_ids.update(rev_crids) - rev_info["used_ids"] = list(used_ids) - await txn.handle.replace( - CATEGORY_REV_REG_INFO, revoc_reg_id, value_json=rev_info - ) - for cred_rev_id in rev_crids: - issuer_cr_rec = await IssuerCredRevRecord.retrieve_by_ids( - txn, - revoc_reg_id, - str(cred_rev_id), + async with self._profile.transaction() as txn: + rev_reg_upd = await txn.handle.fetch( + CATEGORY_REV_REG, revoc_reg_id, for_update=True + ) + rev_info_upd = await txn.handle.fetch( + CATEGORY_REV_REG_INFO, revoc_reg_id, for_update=True ) - await issuer_cr_rec.set_state( - txn, IssuerCredRevRecord.STATE_REVOKED + if not rev_reg_upd or not rev_reg_info: + LOGGER.warn( + "Revocation registry missing, skipping update: {}", + revoc_reg_id, + ) + delta = None + break + rev_info_upd = rev_info_upd.value_json + if rev_info_upd != rev_info: + # handle concurrent update to the registry by retrying + continue + await txn.handle.replace( + CATEGORY_REV_REG, revoc_reg_id, rev_reg.to_json_buffer() + ) + used_ids.update(rev_crids) + rev_info_upd["used_ids"] = sorted(used_ids) + await txn.handle.replace( + CATEGORY_REV_REG_INFO, revoc_reg_id, value_json=rev_info_upd ) - if not transaction: await txn.commit() except AskarError as err: raise IndyIssuerError("Error saving revocation registry") from err - else: - delta = None + break - return (delta and delta.to_json(), failed_crids) + return ( + delta and delta.to_json(), + [str(rev_id) for rev_id in sorted(failed_crids)], + ) async def merge_revocation_registry_deltas( self, fro_delta: str, to_delta: str @@ -567,7 +593,7 @@ async def create_and_store_revocation_registry( rev_reg_def, rev_reg_def_private, rev_reg, - rev_reg_delta, + _rev_reg_delta, ) = await asyncio.get_event_loop().run_in_executor( None, lambda: RevocationRegistryDefinition.create( diff --git a/aries_cloudagent/indy/issuer.py b/aries_cloudagent/indy/issuer.py index 05c538cb43..3503626f72 100644 --- a/aries_cloudagent/indy/issuer.py +++ b/aries_cloudagent/indy/issuer.py @@ -4,7 +4,6 @@ from typing import Sequence, Tuple from ..core.error import BaseError -from ..core.profile import ProfileSession DEFAULT_CRED_DEF_TAG = "default" @@ -150,7 +149,6 @@ async def revoke_credentials( revoc_reg_id: str, tails_file_path: str, cred_rev_ids: Sequence[str], - transaction: ProfileSession = None, ) -> Tuple[str, Sequence[str]]: """ Revoke a set of credentials in a revocation registry. diff --git a/aries_cloudagent/indy/sdk/issuer.py b/aries_cloudagent/indy/sdk/issuer.py index 2298143e61..8fe7d23777 100644 --- a/aries_cloudagent/indy/sdk/issuer.py +++ b/aries_cloudagent/indy/sdk/issuer.py @@ -8,7 +8,6 @@ import indy.blob_storage from indy.error import AnoncredsRevocationRegistryFullError, IndyError, ErrorCode -from ...core.profile import ProfileSession from ...indy.sdk.profile import IndySdkProfile from ...messaging.util import encode from ...revocation.models.issuer_cred_rev_record import IssuerCredRevRecord @@ -267,7 +266,6 @@ async def revoke_credentials( rev_reg_id: str, tails_file_path: str, cred_rev_ids: Sequence[str], - transaction: ProfileSession = None, ) -> Tuple[str, Sequence[str]]: """ Revoke a set of credentials in a revocation registry. @@ -281,7 +279,7 @@ async def revoke_credentials( Tuple with the combined revocation delta, list of cred rev ids not revoked """ - failed_crids = [] + failed_crids = set() tails_reader_handle = await create_tails_reader(tails_file_path) result_json = None @@ -290,22 +288,12 @@ async def revoke_credentials( "Exception when revoking credential", IndyIssuerError ): try: - session = await self.profile.session() delta_json = await indy.anoncreds.issuer_revoke_credential( self.profile.wallet.handle, tails_reader_handle, rev_reg_id, cred_rev_id, ) - issuer_cr_rec = await IssuerCredRevRecord.retrieve_by_ids( - session, - rev_reg_id, - cred_rev_id, - ) - await issuer_cr_rec.set_state( - session, IssuerCredRevRecord.STATE_REVOKED - ) - except IndyError as err: if err.error_code == ErrorCode.AnoncredsInvalidUserRevocId: LOGGER.error( @@ -323,7 +311,7 @@ async def revoke_credentials( err, "Revocation error", IndyIssuerError ).roll_up ) - failed_crids.append(cred_rev_id) + failed_crids.add(int(cred_rev_id)) continue except StorageError as err: LOGGER.warning( @@ -344,7 +332,7 @@ async def revoke_credentials( else: result_json = delta_json - return (result_json, failed_crids) + return (result_json, [str(rev_id) for rev_id in sorted(failed_crids)]) async def merge_revocation_registry_deltas( self, fro_delta: str, to_delta: str diff --git a/aries_cloudagent/revocation/manager.py b/aries_cloudagent/revocation/manager.py index 4ee333620b..85729ac9f2 100644 --- a/aries_cloudagent/revocation/manager.py +++ b/aries_cloudagent/revocation/manager.py @@ -113,7 +113,7 @@ async def revoke_credential( issuer_rr_rec = await revoc.get_issuer_rev_reg_record(rev_reg_id) if not issuer_rr_rec: raise RevocationManagerError( - f"No revocation registry record found for id {rev_reg_id}" + f"No revocation registry record found for id: {rev_reg_id}" ) if notify: @@ -137,19 +137,25 @@ async def revoke_credential( (delta_json, _) = await issuer.revoke_credentials( issuer_rr_rec.revoc_reg_id, issuer_rr_rec.tails_local_path, crids ) - if delta_json: - issuer_rr_rec.revoc_reg_entry = json.loads(delta_json) - await issuer_rr_rec.send_entry(self._profile) - async with self._profile.session() as session: - await issuer_rr_rec.clear_pending(session) - await self.set_cred_revoked_state(rev_reg_id, [cred_rev_id]) - await notify_revocation_published_event( - self._profile, rev_reg_id, [cred_rev_id] + async with self._profile.transaction() as txn: + issuer_rr_upd = await IssuerRevRegRecord.retrieve_by_id( + txn, issuer_rr_rec.record_id, for_update=True ) + if delta_json: + issuer_rr_upd.revoc_reg_entry = json.loads(delta_json) + await issuer_rr_upd.clear_pending(txn, crids) + await txn.commit() + if delta_json: + await issuer_rr_upd.send_entry(self._profile) + await self.set_cred_revoked_state(rev_reg_id, [cred_rev_id]) + await notify_revocation_published_event( + self._profile, rev_reg_id, [cred_rev_id] + ) else: - async with self._profile.session() as session: - await issuer_rr_rec.mark_pending(session, cred_rev_id) + async with self._profile.transaction() as txn: + await issuer_rr_rec.mark_pending(txn, cred_rev_id) + await txn.commit() async def publish_pending_revocations( self, @@ -181,37 +187,42 @@ async def publish_pending_revocations( result = {} issuer = self._profile.inject(IndyIssuer) - async with self._profile.transaction() as txn: - issuer_rr_recs = await IssuerRevRegRecord.query_by_pending(txn) - for issuer_rr_rec in issuer_rr_recs: - rrid = issuer_rr_rec.revoc_reg_id - crids = [] - if not rrid2crid: - crids = issuer_rr_rec.pending_pub - elif rrid in rrid2crid: - crids = [ - crid - for crid in issuer_rr_rec.pending_pub - if crid in (rrid2crid[rrid] or []) or not rrid2crid[rrid] - ] - if crids: - # FIXME - must use the same transaction - (delta_json, failed_crids) = await issuer.revoke_credentials( - issuer_rr_rec.revoc_reg_id, - issuer_rr_rec.tails_local_path, - crids, - transaction=txn, + async with self._profile.session() as session: + issuer_rr_recs = await IssuerRevRegRecord.query_by_pending(session) + + for issuer_rr_rec in issuer_rr_recs: + rrid = issuer_rr_rec.revoc_reg_id + if rrid2crid: + if rrid not in rrid2crid: + continue + limit_crids = rrid2crid[rrid] + else: + limit_crids = () + crids = set(issuer_rr_rec.pending_pub or ()) + if limit_crids: + crids = crids.intersection(limit_crids) + if crids: + (delta_json, failed_crids) = await issuer.revoke_credentials( + issuer_rr_rec.revoc_reg_id, + issuer_rr_rec.tails_local_path, + crids, + ) + async with self._profile.transaction() as txn: + issuer_rr_upd = await IssuerRevRegRecord.retrieve_by_id( + txn, issuer_rr_rec.record_id, for_update=True ) - issuer_rr_rec.revoc_reg_entry = json.loads(delta_json) - await issuer_rr_rec.send_entry(self._profile) - published = [crid for crid in crids if crid not in failed_crids] - result[issuer_rr_rec.revoc_reg_id] = published - await issuer_rr_rec.clear_pending(txn, published) + if delta_json: + issuer_rr_upd.revoc_reg_entry = json.loads(delta_json) + await issuer_rr_upd.clear_pending(txn, crids) await txn.commit() - await self.set_cred_revoked_state(issuer_rr_rec.revoc_reg_id, crids) - await notify_revocation_published_event( - self._profile, issuer_rr_rec.revoc_reg_id, crids - ) + if delta_json: + await issuer_rr_upd.send_entry(self._profile) + published = sorted(crid for crid in crids if crid not in failed_crids) + result[issuer_rr_rec.revoc_reg_id] = published + await self.set_cred_revoked_state(issuer_rr_rec.revoc_reg_id, crids) + await notify_revocation_published_event( + self._profile, issuer_rr_rec.revoc_reg_id, crids + ) return result @@ -246,6 +257,7 @@ async def clear_pending_revocations( """ result = {} + notify = [] async with self._profile.transaction() as txn: issuer_rr_recs = await IssuerRevRegRecord.query_by_pending(txn) @@ -254,9 +266,12 @@ async def clear_pending_revocations( await issuer_rr_rec.clear_pending(txn, (purge or {}).get(rrid)) if issuer_rr_rec.pending_pub: result[rrid] = issuer_rr_rec.pending_pub - await notify_pending_cleared_event(self._profile, rrid) + notify.append(rrid) await txn.commit() + for rrid in notify: + await notify_pending_cleared_event(self._profile, rrid) + return result async def set_cred_revoked_state( @@ -274,31 +289,31 @@ async def set_cred_revoked_state( """ for cred_rev_id in cred_rev_ids: - async with self._profile.session() as session: + async with self._profile.transaction() as txn: try: rev_rec = await IssuerCredRevRecord.retrieve_by_ids( - session, rev_reg_id, cred_rev_id + txn, rev_reg_id, cred_rev_id ) try: cred_ex_record = await V10CredentialExchange.retrieve_by_id( - session, rev_rec.cred_ex_id + txn, rev_rec.cred_ex_id, for_update=True ) cred_ex_record.state = ( V10CredentialExchange.STATE_CREDENTIAL_REVOKED ) - await cred_ex_record.save(session, reason="revoke credential") + await cred_ex_record.save(txn, reason="revoke credential") + await txn.commit() except StorageNotFoundError: try: cred_ex_record = await V20CredExRecord.retrieve_by_id( - session, rev_rec.cred_ex_id + txn, rev_rec.cred_ex_id, for_update=True ) cred_ex_record.state = ( V20CredExRecord.STATE_CREDENTIAL_REVOKED ) - await cred_ex_record.save( - session, reason="revoke credential" - ) + await cred_ex_record.save(txn, reason="revoke credential") + await txn.commit() except StorageNotFoundError: pass diff --git a/aries_cloudagent/revocation/tests/test_manager.py b/aries_cloudagent/revocation/tests/test_manager.py index 72163e2830..470107b6ab 100644 --- a/aries_cloudagent/revocation/tests/test_manager.py +++ b/aries_cloudagent/revocation/tests/test_manager.py @@ -2,6 +2,7 @@ from asynctest import mock as async_mock from asynctest import TestCase as AsyncTestCase +from more_itertools import side_effect from ...core.in_memory import InMemoryProfile from ...indy.issuer import IndyIssuer @@ -32,22 +33,27 @@ async def setUp(self): async def test_revoke_credential_publish(self): CRED_EX_ID = "dummy-cxid" CRED_REV_ID = "1" + mock_issuer_rev_reg_record = async_mock.MagicMock( + revoc_reg_id=REV_REG_ID, + tails_local_path=TAILS_LOCAL, + send_entry=async_mock.CoroutineMock(), + clear_pending=async_mock.CoroutineMock(), + ) + with async_mock.patch.object( test_module.IssuerCredRevRecord, "retrieve_by_cred_ex_id", async_mock.CoroutineMock(), ) as mock_retrieve, async_mock.patch.object( test_module, "IndyRevocation", autospec=True - ) as revoc: + ) as revoc, async_mock.patch.object( + test_module.IssuerRevRegRecord, + "retrieve_by_id", + async_mock.CoroutineMock(return_value=mock_issuer_rev_reg_record), + ): mock_retrieve.return_value = async_mock.MagicMock( rev_reg_id="dummy-rr-id", cred_rev_id=CRED_REV_ID ) - mock_issuer_rev_reg_record = async_mock.MagicMock( - revoc_reg_id=REV_REG_ID, - tails_local_path=TAILS_LOCAL, - send_entry=async_mock.CoroutineMock(), - clear_pending=async_mock.CoroutineMock(), - ) mock_rev_reg = async_mock.MagicMock( get_or_fetch_local_tails_path=async_mock.CoroutineMock() ) @@ -80,7 +86,6 @@ async def test_revoke_credential_publish(self): async def test_revoke_cred_by_cxid_not_found(self): CRED_EX_ID = "dummy-cxid" - CRED_REV_ID = "1" with async_mock.patch.object( test_module.IssuerCredRevRecord, @@ -120,16 +125,25 @@ async def test_revoke_credential_no_rev_reg_rec(self): async def test_revoke_credential_pend(self): CRED_REV_ID = "1" + mock_issuer_rev_reg_record = async_mock.MagicMock( + mark_pending=async_mock.CoroutineMock() + ) + with async_mock.patch.object( test_module, "IndyRevocation", autospec=True ) as revoc, async_mock.patch.object( self.profile, "session", async_mock.MagicMock(return_value=self.profile.session()), - ) as session: - mock_issuer_rev_reg_record = async_mock.MagicMock( - mark_pending=async_mock.CoroutineMock() - ) + ) as session, async_mock.patch.object( + self.profile, + "transaction", + async_mock.MagicMock(return_value=session.return_value), + ) as session, async_mock.patch.object( + test_module.IssuerRevRegRecord, + "retrieve_by_id", + async_mock.CoroutineMock(return_value=mock_issuer_rev_reg_record), + ): revoc.return_value.get_issuer_rev_reg_record = async_mock.CoroutineMock( return_value=mock_issuer_rev_reg_record ) @@ -142,7 +156,7 @@ async def test_revoke_credential_pend(self): session.return_value, CRED_REV_ID ) - async def test_publish_pending_revocations(self): + async def test_publish_pending_revocations_basic(self): deltas = [ { "ver": "1.0", @@ -169,7 +183,11 @@ async def test_publish_pending_revocations(self): test_module.IssuerRevRegRecord, "query_by_pending", async_mock.CoroutineMock(return_value=[mock_issuer_rev_reg_record]), - ) as record_query: + ), async_mock.patch.object( + test_module.IssuerRevRegRecord, + "retrieve_by_id", + async_mock.CoroutineMock(return_value=mock_issuer_rev_reg_record), + ): issuer = async_mock.MagicMock(IndyIssuer, autospec=True) issuer.merge_revocation_registry_deltas = async_mock.CoroutineMock( side_effect=deltas @@ -202,6 +220,7 @@ async def test_publish_pending_revocations_1_rev_reg_all(self): mock_issuer_rev_reg_records = [ async_mock.MagicMock( + record_id=0, revoc_reg_id=REV_REG_ID, tails_local_path=TAILS_LOCAL, pending_pub=["1", "2"], @@ -209,6 +228,7 @@ async def test_publish_pending_revocations_1_rev_reg_all(self): clear_pending=async_mock.CoroutineMock(), ), async_mock.MagicMock( + record_id=1, revoc_reg_id=f"{TEST_DID}:4:{CRED_DEF_ID}:CL_ACCUM:tag2", tails_local_path=TAILS_LOCAL, pending_pub=["9", "99"], @@ -220,7 +240,13 @@ async def test_publish_pending_revocations_1_rev_reg_all(self): test_module.IssuerRevRegRecord, "query_by_pending", async_mock.CoroutineMock(return_value=mock_issuer_rev_reg_records), - ) as record: + ), async_mock.patch.object( + test_module.IssuerRevRegRecord, + "retrieve_by_id", + async_mock.CoroutineMock( + side_effect=lambda _, id, **args: mock_issuer_rev_reg_records[id] + ), + ): issuer = async_mock.MagicMock(IndyIssuer, autospec=True) issuer.merge_revocation_registry_deltas = async_mock.CoroutineMock( side_effect=deltas @@ -254,6 +280,7 @@ async def test_publish_pending_revocations_1_rev_reg_some(self): mock_issuer_rev_reg_records = [ async_mock.MagicMock( + record_id=0, revoc_reg_id=REV_REG_ID, tails_local_path=TAILS_LOCAL, pending_pub=["1", "2"], @@ -261,6 +288,7 @@ async def test_publish_pending_revocations_1_rev_reg_some(self): clear_pending=async_mock.CoroutineMock(), ), async_mock.MagicMock( + record_id=1, revoc_reg_id=f"{TEST_DID}:4:{CRED_DEF_ID}:CL_ACCUM:tag2", tails_local_path=TAILS_LOCAL, pending_pub=["9", "99"], @@ -272,7 +300,13 @@ async def test_publish_pending_revocations_1_rev_reg_some(self): test_module.IssuerRevRegRecord, "query_by_pending", async_mock.CoroutineMock(return_value=mock_issuer_rev_reg_records), - ) as record: + ), async_mock.patch.object( + test_module.IssuerRevRegRecord, + "retrieve_by_id", + async_mock.CoroutineMock( + side_effect=lambda _, id, **args: mock_issuer_rev_reg_records[id] + ), + ): issuer = async_mock.MagicMock(IndyIssuer, autospec=True) issuer.merge_revocation_registry_deltas = async_mock.CoroutineMock( side_effect=deltas From dd7d074e5bedfa5d03f3d17c9589c872031cc3f7 Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Tue, 14 Jun 2022 17:10:50 -0700 Subject: [PATCH 273/872] remove session usage, redundant error handling Signed-off-by: Andrew Whitehead --- aries_cloudagent/revocation/routes.py | 73 ++++++++++++--------------- 1 file changed, 33 insertions(+), 40 deletions(-) diff --git a/aries_cloudagent/revocation/routes.py b/aries_cloudagent/revocation/routes.py index 10bd6cb336..f22ac1c0fd 100644 --- a/aries_cloudagent/revocation/routes.py +++ b/aries_cloudagent/revocation/routes.py @@ -1337,16 +1337,13 @@ async def on_revocation_registry_event(profile: Profile, event: Event): profile, f"{tails_base_url}/{registry_record.revoc_reg_id}", ) - async with profile.session() as session: - rev_reg_resp = await registry_record.send_def( - session.profile, - write_ledger=write_ledger, - endorser_did=endorser_did, - ) - except RevocationError as e: - raise RevocationError(e.message) from e - except RevocationNotSupportedError as e: - raise RevocationNotSupportedError(reason=e.message) from e + rev_reg_resp = await registry_record.send_def( + profile, + write_ledger=write_ledger, + endorser_did=endorser_did, + ) + except RevocationError: + raise if not create_transaction_for_endorser: meta_data = event.payload @@ -1384,19 +1381,18 @@ async def on_revocation_registry_event(profile: Profile, event: Event): except (StorageError, TransactionManagerError) as err: raise TransactionManagerError(reason=err.roll_up) from err - async with profile.session() as session: - responder = session.inject_or(BaseResponder) - if responder: - await responder.send( - revo_transaction_request, - connection_id=connection.connection_id, - ) - else: - LOGGER.warning( - "Configuration has no BaseResponder: cannot update " - "revocation on cred def %s", - cred_def_id, - ) + responder = profile.inject_or(BaseResponder) + if responder: + await responder.send( + revo_transaction_request, + connection_id=connection.connection_id, + ) + else: + LOGGER.warning( + "Configuration has no BaseResponder: cannot update " + "revocation on cred def %s", + cred_def_id, + ) async def on_revocation_entry_event(profile: Profile, event: Event): @@ -1429,10 +1425,8 @@ async def on_revocation_entry_event(profile: Profile, event: Event): write_ledger=write_ledger, endorser_did=endorser_did, ) - except RevocationError as e: - raise RevocationError(e.message) from e - except RevocationNotSupportedError as e: - raise RevocationError(e.message) from e + except RevocationError: + raise if not create_transaction_for_endorser: meta_data = event.payload @@ -1468,19 +1462,18 @@ async def on_revocation_entry_event(profile: Profile, event: Event): except (StorageError, TransactionManagerError) as err: raise RevocationError(err.roll_up) from err - async with profile.session() as session: - responder = session.inject_or(BaseResponder) - if responder: - await responder.send( - revo_transaction_request, - connection_id=connection.connection_id, - ) - else: - LOGGER.warning( - "Configuration has no BaseResponder: cannot update " - "revocation on cred def %s", - event.payload["endorser"]["cred_def_id"], - ) + responder = profile.inject_or(BaseResponder) + if responder: + await responder.send( + revo_transaction_request, + connection_id=connection.connection_id, + ) + else: + LOGGER.warning( + "Configuration has no BaseResponder: cannot update " + "revocation on cred def %s", + event.payload["endorser"]["cred_def_id"], + ) async def on_revocation_tails_file_event(profile: Profile, event: Event): From bac22739151c2f0a9e23e90c064db4f8cab0c434 Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Wed, 15 Jun 2022 08:25:29 -0600 Subject: [PATCH 274/872] feat: Allow using outbound transports from plugins Signed-off-by: Colton Wolkins (Indicio work address) --- aries_cloudagent/transport/outbound/manager.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/aries_cloudagent/transport/outbound/manager.py b/aries_cloudagent/transport/outbound/manager.py index ac2c54dd49..950c2514b8 100644 --- a/aries_cloudagent/transport/outbound/manager.py +++ b/aries_cloudagent/transport/outbound/manager.py @@ -100,12 +100,12 @@ async def setup(self): for outbound_transport in outbound_transports: self.register(outbound_transport) - def register(self, module: str) -> str: + def register(self, module_name: str) -> str: """ Register a new outbound transport by module path. Args: - module: Module name to register + module_name: Module name to register Raises: OutboundTransportRegistrationError: If the imported class cannot @@ -117,13 +117,19 @@ def register(self, module: str) -> str: """ try: + if "." in module_name: + package, module = module_name.split(".", 1) + else: + package = MODULE_BASE_PATH + module = module_name + imported_class = ClassLoader.load_subclass_of( - BaseOutboundTransport, module, MODULE_BASE_PATH + BaseInboundTransport, module, package ) - except (ModuleLoadError, ClassNotFoundError): - raise OutboundTransportRegistrationError( + except (ModuleLoadError, ClassNotFoundError) as e: + raise InboundTransportRegistrationError( f"Outbound transport module {module} could not be resolved." - ) + ) from e return self.register_class(imported_class) From 9ee7b2a99b340601b0d5fbae16c83efcb27c877a Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Tue, 14 Jun 2022 17:39:25 -0700 Subject: [PATCH 275/872] fix put_file when the server returns a redirect Signed-off-by: Andrew Whitehead --- aries_cloudagent/utils/http.py | 72 ++++++++++++++++++----- aries_cloudagent/utils/tests/test_http.py | 69 ++++++++++++++++++---- 2 files changed, 115 insertions(+), 26 deletions(-) diff --git a/aries_cloudagent/utils/http.py b/aries_cloudagent/utils/http.py index 8e780338c3..d17fe234f2 100644 --- a/aries_cloudagent/utils/http.py +++ b/aries_cloudagent/utils/http.py @@ -1,8 +1,16 @@ """HTTP utility methods.""" import asyncio - -from aiohttp import BaseConnector, ClientError, ClientResponse, ClientSession +import logging +import urllib.parse + +from aiohttp import ( + BaseConnector, + ClientError, + ClientResponse, + ClientSession, + FormData, +) from aiohttp.web import HTTPConflict from ..core.error import BaseError @@ -10,6 +18,9 @@ from .repeat import RepeatSequence +LOGGER = logging.getLogger(__name__) + + class FetchError(BaseError): """Error raised when an HTTP fetch fails.""" @@ -147,7 +158,6 @@ async def put_file( """ (data_key, file_path) = [k for k in file_data.items()][0] - data = {**extra_data} limit = max_attempts if retry else 1 if not session: @@ -158,17 +168,51 @@ async def put_file( async for attempt in RepeatSequence(limit, interval, backoff): try: async with attempt.timeout(request_timeout): - with open(file_path, "rb") as f: - data[data_key] = f - response: ClientResponse = await session.put(url, data=data) - if (response.status < 200 or response.status >= 300) and ( - response.status != HTTPConflict.status_code - ): - raise ClientError( - f"Bad response from server: {response.status}, " - f"{response.reason}" - ) + formdata = FormData() + try: + fp = open(file_path, "rb") + except OSError as e: + raise PutError("Error opening file for upload") from e + if extra_data: + for k, v in extra_data.items(): + formdata.add_field(k, v) + formdata.add_field( + data_key, fp, content_type="application/octet-stream" + ) + response: ClientResponse = await session.put( + url, data=formdata, allow_redirects=False + ) + if ( + # redirect codes + response.status in (301, 302, 303, 307, 308) + and not attempt.final + ): + # NOTE: a redirect counts as another upload attempt + to_url = response.headers.get("Location") + if not to_url: + raise PutError("Redirect missing target URL") + try: + parsed_to = urllib.parse.urlsplit(to_url) + parsed_from = urllib.parse.urlsplit(url) + except ValueError: + raise PutError("Invalid redirect URL") + if parsed_to.hostname != parsed_from.hostname: + raise PutError("Redirect denied: hostname mismatch") + url = to_url + LOGGER.info("Upload redirect: %s", to_url) + elif (response.status < 200 or response.status >= 300) and ( + response.status != HTTPConflict.status_code + ): + raise ClientError( + f"Bad response from server: {response.status}, " + f"{response.reason}" + ) + else: return await (response.json() if json else response.text()) except (ClientError, asyncio.TimeoutError) as e: + if isinstance(e, ClientError): + LOGGER.warning("Upload error: %s", e) + else: + LOGGER.warning("Upload error: request timed out") if attempt.final: - raise PutError("Exceeded maximum put attempts") from e + raise PutError("Exceeded maximum upload attempts") from e diff --git a/aries_cloudagent/utils/tests/test_http.py b/aries_cloudagent/utils/tests/test_http.py index e760769958..6e2ff84adb 100644 --- a/aries_cloudagent/utils/tests/test_http.py +++ b/aries_cloudagent/utils/tests/test_http.py @@ -1,14 +1,33 @@ +import os +import tempfile + from aiohttp import web -from aiohttp.test_utils import AioHTTPTestCase, unittest_run_loop -from asynctest import mock as async_mock, mock_open +from aiohttp.test_utils import AioHTTPTestCase from ..http import fetch, fetch_stream, FetchError, put_file, PutError +class TempFile: + def __init__(self): + self.name = None + + def __enter__(self): + file = tempfile.NamedTemporaryFile(delete=False) + file.write(b"test") + file.close() + self.name = file.name + return self.name + + def __exit__(self, *args): + if self.name: + os.unlink(self.name) + + class TestTransportUtils(AioHTTPTestCase): async def setUpAsync(self): self.fail_calls = 0 self.succeed_calls = 0 + self.redirects = 0 await super().setUpAsync() async def get_application(self): @@ -19,12 +38,15 @@ async def get_application(self): web.get("/succeed", self.succeed_route), web.put("/fail", self.fail_route), web.put("/succeed", self.succeed_route), + web.put("/redirect", self.redirect_route), ] ) return app async def fail_route(self, request): self.fail_calls += 1 + # avoid aiohttp test server issue: https://github.com/aio-libs/aiohttp/issues/3968 + await request.read() raise web.HTTPForbidden() async def succeed_route(self, request): @@ -32,6 +54,14 @@ async def succeed_route(self, request): ret = web.json_response([True]) return ret + async def redirect_route(self, request): + if self.redirects > 0: + self.redirects -= 1 + # avoid aiohttp test server issue: https://github.com/aio-libs/aiohttp/issues/3968 + await request.read() + raise web.HTTPRedirection(f"http://localhost:{self.server.port}/success") + return await self.succeed_route(request) + async def test_fetch_stream(self): server_addr = f"http://localhost:{self.server.port}" stream = await fetch_stream( @@ -84,40 +114,55 @@ async def test_fetch_fail(self): ) assert self.fail_calls == 2 - async def test_put_file(self): + async def test_put_file_with_session(self): server_addr = f"http://localhost:{self.server.port}" - with async_mock.patch("builtins.open", mock_open(read_data="data")): + with TempFile() as tails: result = await put_file( f"{server_addr}/succeed", - {"tails": "/tmp/dummy/path"}, + {"tails": tails}, {"genesis": "..."}, session=self.client.session, json=True, ) - assert result == [1] + assert result == [True] assert self.succeed_calls == 1 async def test_put_file_default_client(self): server_addr = f"http://localhost:{self.server.port}" - with async_mock.patch("builtins.open", mock_open(read_data="data")): + with TempFile() as tails: result = await put_file( f"{server_addr}/succeed", - {"tails": "/tmp/dummy/path"}, + {"tails": tails}, {"genesis": "..."}, json=True, ) - assert result == [1] + assert result == [True] assert self.succeed_calls == 1 async def test_put_file_fail(self): server_addr = f"http://localhost:{self.server.port}" - with async_mock.patch("builtins.open", mock_open(read_data="data")): + with TempFile() as tails: with self.assertRaises(PutError): - result = await put_file( + _ = await put_file( f"{server_addr}/fail", - {"tails": "/tmp/dummy/path"}, + {"tails": tails}, {"genesis": "..."}, max_attempts=2, json=True, ) assert self.fail_calls == 2 + + async def test_put_file_redirect(self): + server_addr = f"http://localhost:{self.server.port}" + self.redirects = 1 + with TempFile() as tails: + result = await put_file( + f"{server_addr}/redirect", + {"tails": tails}, + {"genesis": "..."}, + max_attempts=2, + json=True, + ) + assert result == [True] + assert self.succeed_calls == 1 + assert self.redirects == 0 From ec776d630247c8d8dc3841aa4b4d4d9881c2de6a Mon Sep 17 00:00:00 2001 From: shaangill025 Date: Wed, 15 Jun 2022 10:40:07 -0700 Subject: [PATCH 276/872] present-proof v1 proposal fix Signed-off-by: shaangill025 --- .../v1_0/handlers/presentation_request_handler.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_request_handler.py b/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_request_handler.py index cbd01a8cec..1736d22843 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_request_handler.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_request_handler.py @@ -80,6 +80,10 @@ async def handle(self, context: RequestContext, responder: BaseResponder): "connection_id": connection_id, }, ) # holder initiated via proposal + presentation_exchange_record.presentation_request = indy_proof_request + presentation_exchange_record.presentation_request_dict = ( + context.message.serialize() + ) except StorageNotFoundError: # verifier sent this request free of any proposal presentation_exchange_record = V10PresentationExchange( connection_id=connection_id, @@ -94,7 +98,6 @@ async def handle(self, context: RequestContext, responder: BaseResponder): trace=(context.message._trace is not None), ) - presentation_exchange_record.presentation_request = indy_proof_request presentation_exchange_record = await presentation_manager.receive_request( presentation_exchange_record ) # mgr only saves record: on exception, saving state null is hopeless From 6de005ff42fbd7c4d838bb3fdae8e8161ffbe3ac Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Wed, 15 Jun 2022 20:07:49 -0600 Subject: [PATCH 277/872] fix: Fix copy/paste error from inbound to outbound queues Signed-off-by: Colton Wolkins (Indicio work address) --- aries_cloudagent/transport/outbound/manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aries_cloudagent/transport/outbound/manager.py b/aries_cloudagent/transport/outbound/manager.py index 950c2514b8..8506bfb094 100644 --- a/aries_cloudagent/transport/outbound/manager.py +++ b/aries_cloudagent/transport/outbound/manager.py @@ -124,7 +124,7 @@ def register(self, module_name: str) -> str: module = module_name imported_class = ClassLoader.load_subclass_of( - BaseInboundTransport, module, package + BaseOutboundTransport, module, package ) except (ModuleLoadError, ClassNotFoundError) as e: raise InboundTransportRegistrationError( From 4987bec01802c4d1a437e5894042d9a49a0384c3 Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Thu, 16 Jun 2022 06:40:20 -0600 Subject: [PATCH 278/872] fix: Typo on error type Signed-off-by: Colton Wolkins (Indicio work address) --- aries_cloudagent/transport/outbound/manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aries_cloudagent/transport/outbound/manager.py b/aries_cloudagent/transport/outbound/manager.py index 8506bfb094..094f537f32 100644 --- a/aries_cloudagent/transport/outbound/manager.py +++ b/aries_cloudagent/transport/outbound/manager.py @@ -127,7 +127,7 @@ def register(self, module_name: str) -> str: BaseOutboundTransport, module, package ) except (ModuleLoadError, ClassNotFoundError) as e: - raise InboundTransportRegistrationError( + raise OutboundTransportRegistrationError( f"Outbound transport module {module} could not be resolved." ) from e From 3c12f319503a33e5837f621e4801c33ed24d1f10 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Thu, 16 Jun 2022 10:44:18 -0300 Subject: [PATCH 279/872] Fix missing webhook handler Signed-off-by: Ian Costanzo --- demo/runners/agent_container.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/demo/runners/agent_container.py b/demo/runners/agent_container.py index 164360e0e7..7c16d81d7d 100644 --- a/demo/runners/agent_container.py +++ b/demo/runners/agent_container.py @@ -99,6 +99,10 @@ async def handle_oob_invitation(self, message): print("handle_oob_invitation()") pass + async def handle_out_of_band(self, message): + print("handle_out_of_band()") + pass + async def handle_connection_reuse(self, message): # we are reusing an existing connection, set our status to the existing connection if not self._connection_ready.done(): From a072ffed91efad74f1a3fe207658bb75924298c7 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Thu, 16 Jun 2022 09:47:19 -0700 Subject: [PATCH 280/872] Changelog, version and ReadTheDocs updates for 0.7.4-rc3 release Signed-off-by: Stephen Curran --- 0.7.4-rc3.md | 99 +++++++++++++++++++ CHANGELOG.md | 80 ++++++++++----- aries_cloudagent/version.py | 2 +- docs/conf.py | 1 + docs/generated/aries_cloudagent.core.rst | 8 ++ .../aries_cloudagent.messaging.decorators.rst | 8 ++ ...gent.protocols.out_of_band.v1_0.models.rst | 8 ++ ...gent.protocols.revocation_notification.rst | 1 + ....revocation_notification.v2_0.handlers.rst | 18 ++++ ....revocation_notification.v2_0.messages.rst | 18 ++++ ...ls.revocation_notification.v2_0.models.rst | 18 ++++ ...protocols.revocation_notification.v2_0.rst | 36 +++++++ .../generated/aries_cloudagent.revocation.rst | 8 ++ open-api/openapi.json | 2 +- 14 files changed, 279 insertions(+), 28 deletions(-) create mode 100644 0.7.4-rc3.md create mode 100644 docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.handlers.rst create mode 100644 docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.messages.rst create mode 100644 docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.models.rst create mode 100644 docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.rst diff --git a/0.7.4-rc3.md b/0.7.4-rc3.md new file mode 100644 index 0000000000..367992ca7c --- /dev/null +++ b/0.7.4-rc3.md @@ -0,0 +1,99 @@ +# Changelog + +## [0.7.4-rc3](https://github.com/hyperledger/aries-cloudagent-python/tree/0.7.4-rc3) (2022-06-15) + +[Full Changelog](https://github.com/hyperledger/aries-cloudagent-python/compare/0.7.4-rc2...0.7.4-rc3) + +**Closed issues:** + +- AATH ACA-Py - ACA-Py DID Exchange tests failing [\#1803](https://github.com/hyperledger/aries-cloudagent-python/issues/1803) +- duplicated events and wallet credentials when using /issue-credential-2.0/send with askar wallet [\#1801](https://github.com/hyperledger/aries-cloudagent-python/issues/1801) +- Verified: true - For revoked Credentials - Aries agent image: bcgovimages/aries-cloudagent:py36-1.16-1\_0.7.3 [\#1799](https://github.com/hyperledger/aries-cloudagent-python/issues/1799) +- How to turn off auto-accept-requests and auto-accept-invites? [\#1796](https://github.com/hyperledger/aries-cloudagent-python/issues/1796) +- Which mobile agent framework is recommended to work with aca-py as the mediator agent? [\#1794](https://github.com/hyperledger/aries-cloudagent-python/issues/1794) +- Inbound Transport now has an undocumented, required attribute is\_external [\#1791](https://github.com/hyperledger/aries-cloudagent-python/issues/1791) +- Issues with aries-acapy-plugin-toolbox when updating to 0.7.4 [\#1790](https://github.com/hyperledger/aries-cloudagent-python/issues/1790) +- Unable to filter for invitation from OOB invitations in multi-tenant environment [\#1789](https://github.com/hyperledger/aries-cloudagent-python/issues/1789) +- The QR code type of Acme [\#1788](https://github.com/hyperledger/aries-cloudagent-python/issues/1788) +- Duplicated Schema and Definition Ids [\#1786](https://github.com/hyperledger/aries-cloudagent-python/issues/1786) +- aries\_cloudagent.transport.outbound.manager ERROR \>\>\> Error when posting to: http://0.0.0.0:8020; [\#1781](https://github.com/hyperledger/aries-cloudagent-python/issues/1781) +- Pool timeout Error when running aca-py [\#1780](https://github.com/hyperledger/aries-cloudagent-python/issues/1780) +- Is selective disclosure implemented yet? [\#1779](https://github.com/hyperledger/aries-cloudagent-python/issues/1779) +- How to view the history of connection requests? [\#1775](https://github.com/hyperledger/aries-cloudagent-python/issues/1775) +- Cannot verify SSL certificates behind proxy [\#1773](https://github.com/hyperledger/aries-cloudagent-python/issues/1773) +- The type of QR code [\#1768](https://github.com/hyperledger/aries-cloudagent-python/issues/1768) +- ACA-Py demo bug on ./run\_demo faber: Exception: Timed out waiting for agent process to start \(status=None\). Admin URL: http://host.docker.internal:8021/status [\#1766](https://github.com/hyperledger/aries-cloudagent-python/issues/1766) +- ACA-Py demo bug Play With Docker: waiting for connection \(faber\) persists even after starting Alice [\#1765](https://github.com/hyperledger/aries-cloudagent-python/issues/1765) +- OOB invitation: only works with auto\_respond\_presentation\_request configured? [\#1764](https://github.com/hyperledger/aries-cloudagent-python/issues/1764) +- Fetch from --genesis-url likely to fail in composed container setup [\#1745](https://github.com/hyperledger/aries-cloudagent-python/issues/1745) +- Creating a /multitenancy/wallet may ignore wallet\_dispatch\_type [\#1742](https://github.com/hyperledger/aries-cloudagent-python/issues/1742) +- Support receiving messages of any minor version of any protocol [\#1736](https://github.com/hyperledger/aries-cloudagent-python/issues/1736) +- Case sensitive role parameter with /ledger/register-nym [\#1731](https://github.com/hyperledger/aries-cloudagent-python/issues/1731) +- ledger read operations with multiledger enabled causes timeouts and failure [\#1728](https://github.com/hyperledger/aries-cloudagent-python/issues/1728) +- Issue receiving credentials with 0.7.4rc0 of aca-py [\#1724](https://github.com/hyperledger/aries-cloudagent-python/issues/1724) +- revocation notification webhooks should use `revocation_notification` instead of `revocation-notification` as the topic [\#1713](https://github.com/hyperledger/aries-cloudagent-python/issues/1713) +- Make connection id optional if revoking with notification and cred\_ex\_id is present [\#1712](https://github.com/hyperledger/aries-cloudagent-python/issues/1712) +- GET /schemas/created returns duplicates [\#1707](https://github.com/hyperledger/aries-cloudagent-python/issues/1707) +- Set Public DID error with Multitenancy and Author/Endorser [\#1703](https://github.com/hyperledger/aries-cloudagent-python/issues/1703) +- Adding 'auto\_verify' flag to '/present-proof/send-request' API [\#1698](https://github.com/hyperledger/aries-cloudagent-python/issues/1698) +- Custom agent [\#1693](https://github.com/hyperledger/aries-cloudagent-python/issues/1693) +- Trouble Running Faber Demo On Apple Silicon [\#1689](https://github.com/hyperledger/aries-cloudagent-python/issues/1689) +- Error: "Revocation registry is full" when bulk running bulk issuances. [\#1684](https://github.com/hyperledger/aries-cloudagent-python/issues/1684) +- AATH acapy-main and aries-vcx RFC0025 tests have been failing since the March 2 credential exchange updates [\#1679](https://github.com/hyperledger/aries-cloudagent-python/issues/1679) +- Add basic DOCKER\_ENV logging for run\_demo [\#1674](https://github.com/hyperledger/aries-cloudagent-python/issues/1674) +- Credential Revocations: Unrecoverable state after failed credential revocation due to ledger connection issues [\#1669](https://github.com/hyperledger/aries-cloudagent-python/issues/1669) +- Could not store issued credential in mobile wallet [\#1664](https://github.com/hyperledger/aries-cloudagent-python/issues/1664) +- Able to recreate the same schema [\#1661](https://github.com/hyperledger/aries-cloudagent-python/issues/1661) +- Invalid proof presentation when requesting revocable and non-revocable credentials [\#1651](https://github.com/hyperledger/aries-cloudagent-python/issues/1651) +- Askar-profile wallet creation error causes wallet to be only registered in the relay database [\#1649](https://github.com/hyperledger/aries-cloudagent-python/issues/1649) +- Running Docker Scripts on Windows [\#1644](https://github.com/hyperledger/aries-cloudagent-python/issues/1644) +- Unable to receive connectionless out-of-band invitation [\#1636](https://github.com/hyperledger/aries-cloudagent-python/issues/1636) +- RecipientKey is not a raw Ed25519VerificationKey2018 key [\#1634](https://github.com/hyperledger/aries-cloudagent-python/issues/1634) +- Supporting the OOB Transition RFC documentation [\#1625](https://github.com/hyperledger/aries-cloudagent-python/issues/1625) +- Send Presentation Error [\#1598](https://github.com/hyperledger/aries-cloudagent-python/issues/1598) +- Inconsistent behavior when issuing multiple credentials in sequential. Some of the requests/issuances are failing with error "400 Revocation registry metadata not found". [\#1586](https://github.com/hyperledger/aries-cloudagent-python/issues/1586) +- Can I re-use a Peer DID in another connection? [\#1582](https://github.com/hyperledger/aries-cloudagent-python/issues/1582) +- JSON-ld contexts are not handled correctly [\#1568](https://github.com/hyperledger/aries-cloudagent-python/issues/1568) +- What does the "start-introduction" command do? [\#1559](https://github.com/hyperledger/aries-cloudagent-python/issues/1559) +- Error Creating Wallet indy.error.CommonIOError [\#1555](https://github.com/hyperledger/aries-cloudagent-python/issues/1555) +- DatabasePerWalletStrategy + Postgres Storage fails to Recreate Wallet [\#1547](https://github.com/hyperledger/aries-cloudagent-python/issues/1547) +- Support ARM based docker images [\#1546](https://github.com/hyperledger/aries-cloudagent-python/issues/1546) +- Delete wallet only DIDs [\#1520](https://github.com/hyperledger/aries-cloudagent-python/issues/1520) +- Create an implementation and documentation for addressing the "in-memory queue" issue in ACA-Py [\#1513](https://github.com/hyperledger/aries-cloudagent-python/issues/1513) +- Schema/Credential Definition Error - Multi Tenancy Enabled [\#1505](https://github.com/hyperledger/aries-cloudagent-python/issues/1505) +- Update outbound queue docs [\#1502](https://github.com/hyperledger/aries-cloudagent-python/issues/1502) +- Inconsistency in proof presentation in v 0.7.1 [\#1499](https://github.com/hyperledger/aries-cloudagent-python/issues/1499) +- Storing a Credential with a negative numeric string value causes Marshmallow.exceptions.ValidationError [\#1462](https://github.com/hyperledger/aries-cloudagent-python/issues/1462) +- Access profile on plugin initialization [\#1452](https://github.com/hyperledger/aries-cloudagent-python/issues/1452) +- Add endorser support to updating DID attributes [\#1447](https://github.com/hyperledger/aries-cloudagent-python/issues/1447) +- Presentation exchange XXXXX in done state \(must be request-received\) [\#1444](https://github.com/hyperledger/aries-cloudagent-python/issues/1444) +- ACA-PY answers requests with wrong response [\#1441](https://github.com/hyperledger/aries-cloudagent-python/issues/1441) +- Aca-py not respecting NO\_PROXY variables [\#1403](https://github.com/hyperledger/aries-cloudagent-python/issues/1403) +- Keyword conflict with /transactions/create-request, request body schema name Date [\#1319](https://github.com/hyperledger/aries-cloudagent-python/issues/1319) +- How to issue an image as part of a credential [\#1310](https://github.com/hyperledger/aries-cloudagent-python/issues/1310) +- Minor Bug: Endorse-Transaction endpoint mismatch [\#1309](https://github.com/hyperledger/aries-cloudagent-python/issues/1309) +- \[Proposal/Question\] Verify correctness of own Verifiable Credentials, using the Signature and Raw Data [\#1239](https://github.com/hyperledger/aries-cloudagent-python/issues/1239) +- Return id of the new ledger object on endorsed transaction write [\#1159](https://github.com/hyperledger/aries-cloudagent-python/issues/1159) +- Register-Nym: Status Code: 422: Unprocessable Entity \(missing data\) [\#1037](https://github.com/hyperledger/aries-cloudagent-python/issues/1037) +- Missing TAA Accepting Mechanism in Faber Agent for Demo Repos [\#855](https://github.com/hyperledger/aries-cloudagent-python/issues/855) + +**Merged pull requests:** + +- Fix put\_file when the server returns a redirect [\#1808](https://github.com/hyperledger/aries-cloudagent-python/pull/1808) ([andrewwhitehead](https://github.com/andrewwhitehead)) +- Adjust revocation registry update procedure to shorten transactions [\#1804](https://github.com/hyperledger/aries-cloudagent-python/pull/1804) ([andrewwhitehead](https://github.com/andrewwhitehead)) +- Fix: Inbound Transport is\_external attribute [\#1802](https://github.com/hyperledger/aries-cloudagent-python/pull/1802) ([shaangill025](https://github.com/shaangill025)) +- Fix: Duplicated schema and cred\_def - Askar and Postgres [\#1800](https://github.com/hyperledger/aries-cloudagent-python/pull/1800) ([shaangill025](https://github.com/shaangill025)) +- feat: Add filter param to connection list for invitations [\#1797](https://github.com/hyperledger/aries-cloudagent-python/pull/1797) ([frostyfrog](https://github.com/frostyfrog)) +- Fix tails server upload multi-ledger mode [\#1785](https://github.com/hyperledger/aries-cloudagent-python/pull/1785) ([ianco](https://github.com/ianco)) +- Additional endpoints to get revocation details and fix "published" status [\#1783](https://github.com/hyperledger/aries-cloudagent-python/pull/1783) ([ianco](https://github.com/ianco)) +- Document impact of multi-ledger on TAA acceptance [\#1778](https://github.com/hyperledger/aries-cloudagent-python/pull/1778) ([ianco](https://github.com/ianco)) +- fix: add a close statement to ensure session is closed on error [\#1777](https://github.com/hyperledger/aries-cloudagent-python/pull/1777) ([reflectivedevelopment](https://github.com/reflectivedevelopment)) +- Adds `transport_id` variable assignment back to outbound enqueue method [\#1776](https://github.com/hyperledger/aries-cloudagent-python/pull/1776) ([amanji](https://github.com/amanji)) +- Replace async workaround within document loader [\#1774](https://github.com/hyperledger/aries-cloudagent-python/pull/1774) ([frostyfrog](https://github.com/frostyfrog)) +- Feature: Add the ability to deny specific plugins from loading [\#1737](https://github.com/hyperledger/aries-cloudagent-python/pull/1737) ([frostyfrog](https://github.com/frostyfrog)) +- Feat/revocation notification v2 [\#1734](https://github.com/hyperledger/aries-cloudagent-python/pull/1734) ([frostyfrog](https://github.com/frostyfrog)) +- feat: support connectionless exchange [\#1710](https://github.com/hyperledger/aries-cloudagent-python/pull/1710) ([TimoGlastra](https://github.com/TimoGlastra)) + + + +\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)* diff --git a/CHANGELOG.md b/CHANGELOG.md index b8b3812897..ee85921b12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ -# 0.7.4-RC2 +# 0.7.4-RC3 -The 0.7.4 release consists largely of internal fixes with a few minor +The 0.7.4 release consists largely of internal fixes to ACA-Py, big increases in +performance resulting from the now recommended use of [Aries +Askar](https://github.com/bcgov/aries-askar) instead of the Indy-SDK, plus a few enhancements. There have been a lot of groups exercising ACA-Py and the updates made in this release are a reflection of those efforts. We have PRs that have been contributed by 17 different people, which is likely a record for a single @@ -13,16 +15,26 @@ endorsing to write objects to an Indy ledger. We're hoping to see an by an organization, ideally with a controller starter kit to allow an approvals business flow. +A focus towards the end of the 0.7.4 development and release cycle was on the +handling of AnonCreds revocation in ACA-Py. Most important, a production issue +was uncovered where by an ACA-Py issuer's local Revocation Registry data could +get out of sync with what was published on an Indy ledger, resulting in an +inability to publish new RevRegEntry transactions -- making new revocations +impossible. As a result, we have added some new endpoints to enable an update to +the RevReg storage such that RevRegEntry transactions can again be published to +the ledger. Other changes were added related to revocation in general +and in the handling of tails files in particular. + A lot of work has been put in for this release related to performance and load testing, with significant updates being made to the key "shared component" ACA-Py dependencies ([Aries Askar](https://github.com/bcgov/aries-askar), [Indy VDR](https://github.comyperledger/indy-vdr)) and [Indy Shared RS (including CredX)](https://github.com/hyperledger/indy-shared-rs). We now recommend using -those components (by using Askar for the wallet type in the startup parameters) -for new ACA-Py deployments. A wallet migration tool from indy-sdk storage to -Askar storage is still needed before migrating existing deployment to Askar. A -big thanks to those creating/reporting on stress test scenarios, and especially -the team at LISSI for creating the +those components (by using `--wallet-type askar` in the ACA-Py startup +parameters) for new ACA-Py deployments. A wallet migration tool from indy-sdk +storage to Askar storage is still needed before migrating existing deployment to +Askar. A big thanks to those creating/reporting on stress test scenarios, and +especially the team at LISSI for creating the [aries-cloudagent-loadgenerator](https://github.com/lissi-id/aries-cloudagent-loadgenerator) to make load testing so easy! And of course to the core ACA-Py team for addressing the findings. @@ -42,20 +54,20 @@ startup parameters, Admin API parameters to control instances of protocols, and additional web hook notifications. A number of fixes were made to the Credential Exchange protocols, both for V1 -and V2, and for both AnonCreds and W3C format VCs. Nothing new and no changes in -the APIs. +and V2, and for both AnonCreds and W3C format VCs. Nothing new was added and +there no changes in the APIs. As well there were a number of internal fixes, dependency updates, documentation and demo changes, developer tools and release management updates. All the usual stuff needed for a growing codebase. -## April 7, 2022 +## June 16, 2022 - Hyperledger Indy Endorser related updates: - Fix order of operations connecting faber to endorser [\#1716](https://github.com/hyperledger/aries-cloudagent-python/pull/1716) ([ianco](https://github.com/ianco)) - Endorser support for updating DID endpoints on ledger [\#1696](https://github.com/hyperledger/aries-cloudagent-python/pull/1696) ([frostyfrog](https://github.com/frostyfrog)) - Add "sent" key to both Schema and Cred Defs when using Endorsers [\#1663](https://github.com/hyperledger/aries-cloudagent-python/pull/1663) ([frostyfrog](https://github.com/frostyfrog)) - - Add cred\_def\_id to metadata when using an Endorser [\#1655](https://github.com/hyperledger/aries-cloudagent-python/pull/1655) ([frostyfrog](https://github.com/frostyfrog)) + - Add cred_def_id to metadata when using an Endorser [\#1655](https://github.com/hyperledger/aries-cloudagent-python/pull/1655) ([frostyfrog](https://github.com/frostyfrog)) - Update Endorser documentation [\#1646](https://github.com/hyperledger/aries-cloudagent-python/pull/1646) ([chumbert](https://github.com/chumbert)) - Auto-promote author did to public after endorsing [\#1607](https://github.com/hyperledger/aries-cloudagent-python/pull/1607) ([ianco](https://github.com/ianco)) - DID updates for endorser [\#1601](https://github.com/hyperledger/aries-cloudagent-python/pull/1601) ([ianco](https://github.com/ianco)) @@ -64,33 +76,45 @@ stuff needed for a growing codebase. - Additions to the startup parameters, Admin API and Web Hooks - feat: accept taa using startup parameter --accept-taa [\#1643](https://github.com/hyperledger/aries-cloudagent-python/pull/1643) ([TimoGlastra](https://github.com/TimoGlastra)) - - Add auto\_verify flag in present-proof protocol [\#1702](https://github.com/hyperledger/aries-cloudagent-python/pull/1702) ([DaevMithran](https://github.com/DaevMithran)) - - feat: query connections by their\_public\_did [\#1637](https://github.com/hyperledger/aries-cloudagent-python/pull/1637) ([TimoGlastra](https://github.com/TimoGlastra)) + - Add auto_verify flag in present-proof protocol [\#1702](https://github.com/hyperledger/aries-cloudagent-python/pull/1702) ([DaevMithran](https://github.com/DaevMithran)) + - feat: query connections by their_public_did [\#1637](https://github.com/hyperledger/aries-cloudagent-python/pull/1637) ([TimoGlastra](https://github.com/TimoGlastra)) - feat: enable webhook events for mediation records [\#1614](https://github.com/hyperledger/aries-cloudagent-python/pull/1614) ([TimoGlastra](https://github.com/TimoGlastra)) - Feature/undelivered events [\#1694](https://github.com/hyperledger/aries-cloudagent-python/pull/1694) ([mepeltier](https://github.com/mepeltier)) - Allow use of SEED when creating local wallet DID Issue-1682 Issue-1682 [\#1705](https://github.com/hyperledger/aries-cloudagent-python/pull/1705) ([DaevMithran](https://github.com/DaevMithran)) + - Feature: Add the ability to deny specific plugins from loading [\#1737](https://github.com/hyperledger/aries-cloudagent-python/pull/1737) ([frostyfrog](https://github.com/frostyfrog)) + - feat: Add filter param to connection list for invitations [\#1797](https://github.com/hyperledger/aries-cloudagent-python/pull/1797) ([frostyfrog](https://github.com/frostyfrog)) + - Fix missing webhook handler [\#1816](https://github.com/hyperledger/aries-cloudagent-python/pull/1816) ([ianco](https://github.com/ianco)) - Persistent Queues - Redis PQ Cleanup in preparation for enabling the uses of plugin PQ implementations \[Issue\#1659\] [\#1659](https://github.com/hyperledger/aries-cloudagent-python/pull/1690) ([shaangill025](https://github.com/shaangill025)) -- Issue Credential, Revocation, Present Proof updates/fixes +- Credential Revocation and Tails File Handling + - Additional endpoints to get revocation details and fix "published" status [\#1783](https://github.com/hyperledger/aries-cloudagent-python/pull/1783) ([ianco](https://github.com/ianco)) + - Fix put_file when the server returns a redirect [\#1808](https://github.com/hyperledger/aries-cloudagent-python/pull/1808) ([andrewwhitehead](https://github.com/andrewwhitehead)) + - Adjust revocation registry update procedure to shorten transactions [\#1804](https://github.com/hyperledger/aries-cloudagent-python/pull/1804) ([andrewwhitehead](https://github.com/andrewwhitehead)) + - fix: Resolve Revocation Notification environment variable name collision [\#1751](https://github.com/hyperledger/aries-cloudagent-python/pull/1751) ([frostyfrog](https://github.com/frostyfrog)) + - fix: always notify if revocation notification record exists [\#1665](https://github.com/hyperledger/aries-cloudagent-python/pull/1665) ([TimoGlastra](https://github.com/TimoGlastra)) + - Fix for AnonCreds non-revoc proof with no timestamp [\#1628](https://github.com/hyperledger/aries-cloudagent-python/pull/1628) ([ianco](https://github.com/ianco)) + - Fixes for v7.3.0 - Issue [\#1597](https://github.com/hyperledger/aries-cloudagent-python/issues/1597) [\#1711](https://github.com/hyperledger/aries-cloudagent-python/pull/1711) ([shaangill025](https://github.com/shaangill025)) + - Fixes Issue 1 from [\#1597](https://github.com/hyperledger/aries-cloudagent-python/issues/1597): Tails file upload fails when a credDef is created and multi ledger support is enabled + - Fix tails server upload multi-ledger mode [\#1785](https://github.com/hyperledger/aries-cloudagent-python/pull/1785) ([ianco](https://github.com/ianco)) + - Feat/revocation notification v2 [\#1734](https://github.com/hyperledger/aries-cloudagent-python/pull/1734) ([frostyfrog](https://github.com/frostyfrog)) + +- Issue Credential, Present Proof updates/fixes + - feat: support connectionless exchange [\#1710](https://github.com/hyperledger/aries-cloudagent-python/pull/1710) ([TimoGlastra](https://github.com/TimoGlastra)) - Fix: DIF proof proposal when creating bound presentation request \[Issue\#1687\] [\#1690](https://github.com/hyperledger/aries-cloudagent-python/pull/1690) ([shaangill025](https://github.com/shaangill025)) - - Fix DIF PresExch and OOB request\_attach delete unused connection [\#1676](https://github.com/hyperledger/aries-cloudagent-python/pull/1676) ([shaangill025](https://github.com/shaangill025)) + - Fix DIF PresExch and OOB request_attach delete unused connection [\#1676](https://github.com/hyperledger/aries-cloudagent-python/pull/1676) ([shaangill025](https://github.com/shaangill025)) - Fix DIFPresFormatHandler returning invalid V20PresExRecord on presentation verification [\#1645](https://github.com/hyperledger/aries-cloudagent-python/pull/1645) ([rmnre](https://github.com/rmnre)) - Update aries-askar patch version to at least 0.2.4 as 0.2.3 does not include backward compatibility [\#1603](https://github.com/hyperledger/aries-cloudagent-python/pull/1603) ([acuderman](https://github.com/acuderman)) - Fixes for credential details in issue-credential webhook responses [\#1668](https://github.com/hyperledger/aries-cloudagent-python/pull/1668) ([andrewwhitehead](https://github.com/andrewwhitehead)) - Fix: present-proof v2 send-proposal [issue\#1474](https://github.com/hyperledger/aries-cloudagent-python/issues/1474) [\#1667](https://github.com/hyperledger/aries-cloudagent-python/pull/1667) ([shaangill025](https://github.com/shaangill025)) - Fixes Issue 3b from [\#1597](https://github.com/hyperledger/aries-cloudagent-python/issues/1597): V2 Credential exchange ignores the auto-respond-credential-request - - fix: Resolve Revocation Notification environment variable name collision [\#1751](https://github.com/hyperledger/aries-cloudagent-python/pull/1751) ([frostyfrog](https://github.com/frostyfrog)) - - fix: always notify if revocation notification record exists [\#1665](https://github.com/hyperledger/aries-cloudagent-python/pull/1665) ([TimoGlastra](https://github.com/TimoGlastra)) - - Revert change to send\_credential\_ack return value [\#1660](https://github.com/hyperledger/aries-cloudagent-python/pull/1660) ([andrewwhitehead](https://github.com/andrewwhitehead)) - - Fix usage of send\_credential\_ack [\#1653](https://github.com/hyperledger/aries-cloudagent-python/pull/1653) ([andrewwhitehead](https://github.com/andrewwhitehead)) + - Revert change to send_credential_ack return value [\#1660](https://github.com/hyperledger/aries-cloudagent-python/pull/1660) ([andrewwhitehead](https://github.com/andrewwhitehead)) + - Fix usage of send_credential_ack [\#1653](https://github.com/hyperledger/aries-cloudagent-python/pull/1653) ([andrewwhitehead](https://github.com/andrewwhitehead)) - Replace blank credential/presentation exchange states with abandoned state [\#1605](https://github.com/hyperledger/aries-cloudagent-python/pull/1605) ([andrewwhitehead](https://github.com/andrewwhitehead)) - Fixes Issue 4 from [\#1597](https://github.com/hyperledger/aries-cloudagent-python/issues/1597): Wallet type askar has issues when receiving V1 credentials - - Fix for AnonCreds non-revoc proof with no timestamp [\#1628](https://github.com/hyperledger/aries-cloudagent-python/pull/1628) ([ianco](https://github.com/ianco)) - Fixes and cleanups for issue-credential 1.0 [\#1619](https://github.com/hyperledger/aries-cloudagent-python/pull/1619) ([andrewwhitehead](https://github.com/andrewwhitehead)) - - Fixes for v7.3.0 - Issue [\#1597](https://github.com/hyperledger/aries-cloudagent-python/issues/1597) [\#1711](https://github.com/hyperledger/aries-cloudagent-python/pull/1711) ([shaangill025](https://github.com/shaangill025)) - - Fixes Issue 1 from [\#1597](https://github.com/hyperledger/aries-cloudagent-python/issues/1597): Tails file upload fails when a credDef is created and multi ledger support is enabled + - Fix: Duplicated schema and cred_def - Askar and Postgres [\#1800](https://github.com/hyperledger/aries-cloudagent-python/pull/1800) ([shaangill025](https://github.com/shaangill025)) - Mediator updates and fixes - feat: allow querying default mediator from base wallet [\#1729](https://github.com/hyperledger/aries-cloudagent-python/pull/1729) ([dbluhm](https://github.com/dbluhm)) @@ -109,28 +133,32 @@ stuff needed for a growing codebase. - Fix auto connection response not being properly mediated [\#1638](https://github.com/hyperledger/aries-cloudagent-python/pull/1638) ([dbluhm](https://github.com/dbluhm)) - platform target in run tests. [\#1697](https://github.com/hyperledger/aries-cloudagent-python/pull/1697) ([burdettadam](https://github.com/burdettadam)) - Add an integration test for mixed proof with a revocable cred and a n… [\#1672](https://github.com/hyperledger/aries-cloudagent-python/pull/1672) ([ianco](https://github.com/ianco)) + - Fix: Inbound Transport is_external attribute [\#1802](https://github.com/hyperledger/aries-cloudagent-python/pull/1802) ([shaangill025](https://github.com/shaangill025)) + - fix: add a close statement to ensure session is closed on error [\#1777](https://github.com/hyperledger/aries-cloudagent-python/pull/1777) ([reflectivedevelopment](https://github.com/reflectivedevelopment)) + - Adds `transport_id` variable assignment back to outbound enqueue method [\#1776](https://github.com/hyperledger/aries-cloudagent-python/pull/1776) ([amanji](https://github.com/amanji)) + - Replace async workaround within document loader [\#1774](https://github.com/hyperledger/aries-cloudagent-python/pull/1774) ([frostyfrog](https://github.com/frostyfrog)) - Documentation and Demo Updates - Fetch from --genesis-url likely to fail in composed container [\#1746](https://github.com/hyperledger/aries-cloudagent-python/pull/1739) ([tdiesler](https://github.com/tdiesler)) - Fixes logic for web hook formatter in Faber demo [\#1739](https://github.com/hyperledger/aries-cloudagent-python/pull/1739) ([amanji](https://github.com/amanji)) - Multitenancy Docs Update [\#1706](https://github.com/hyperledger/aries-cloudagent-python/pull/1706) ([MonolithicMonk](https://github.com/MonolithicMonk)) - - [\#1674](https://github.com/hyperledger/aries-cloudagent-python/issue/1674) Add basic DOCKER\_ENV logging for run\_demo [\#1675](https://github.com/hyperledger/aries-cloudagent-python/pull/1675) ([tdiesler](https://github.com/tdiesler)) + - [\#1674](https://github.com/hyperledger/aries-cloudagent-python/issue/1674) Add basic DOCKER_ENV logging for run_demo [\#1675](https://github.com/hyperledger/aries-cloudagent-python/pull/1675) ([tdiesler](https://github.com/tdiesler)) - Performance demo updates [\#1647](https://github.com/hyperledger/aries-cloudagent-python/pull/1647) ([ianco](https://github.com/ianco)) - docs: supported features attribution [\#1654](https://github.com/hyperledger/aries-cloudagent-python/pull/1654) ([TimoGlastra](https://github.com/TimoGlastra)) - Documentation on existing language wrappers for aca-py [\#1738](https://github.com/hyperledger/aries-cloudagent-python/pull/1738) ([etschelp](https://github.com/etschelp)) - + - Document impact of multi-ledger on TAA acceptance [\#1778](https://github.com/hyperledger/aries-cloudagent-python/pull/1778) ([ianco](https://github.com/ianco)) + - Code management and contributor/developer support updates - Pin markupsafe at version 2.0.1 [\#1642](https://github.com/hyperledger/aries-cloudagent-python/pull/1642) ([andrewwhitehead](https://github.com/andrewwhitehead)) - style: format with stable black release [\#1615](https://github.com/hyperledger/aries-cloudagent-python/pull/1615) ([TimoGlastra](https://github.com/TimoGlastra)) - Remove references to play with von [\#1688](https://github.com/hyperledger/aries-cloudagent-python/pull/1688) ([ianco](https://github.com/ianco)) - Add pre-commit as optional developer tool [\#1671](https://github.com/hyperledger/aries-cloudagent-python/pull/1671) ([dbluhm](https://github.com/dbluhm)) - - run\_docker start - pass environment variables [\#1715](https://github.com/hyperledger/aries-cloudagent-python/pull/1715) ([shaangill025](https://github.com/shaangill025)) + - run_docker start - pass environment variables [\#1715](https://github.com/hyperledger/aries-cloudagent-python/pull/1715) ([shaangill025](https://github.com/shaangill025)) - Release management-related updates - Added missed new module -- upgrade -- to the RTD generated docs [\#1593](https://github.com/hyperledger/aries-cloudagent-python/pull/1593) ([swcurran](https://github.com/swcurran)) - Doh....update the date in the Changelog for 0.7.3 [\#1592](https://github.com/hyperledger/aries-cloudagent-python/pull/1592) ([swcurran](https://github.com/swcurran)) - # 0.7.3 ## January 10, 2022 diff --git a/aries_cloudagent/version.py b/aries_cloudagent/version.py index e6a19d3978..199857eac7 100644 --- a/aries_cloudagent/version.py +++ b/aries_cloudagent/version.py @@ -1,4 +1,4 @@ """Library version information.""" -__version__ = "0.7.4-rc2" +__version__ = "0.7.4-rc3" RECORD_TYPE_ACAPY_VERSION = "acapy_version" diff --git a/docs/conf.py b/docs/conf.py index af761e29b2..d3c2d570da 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -46,6 +46,7 @@ "unflatten", "qrcode", "rlp", + "nest_asyncio", ] # "aries_cloudagent.tests.test_conductor", diff --git a/docs/generated/aries_cloudagent.core.rst b/docs/generated/aries_cloudagent.core.rst index b708e02226..f304ae0aa0 100644 --- a/docs/generated/aries_cloudagent.core.rst +++ b/docs/generated/aries_cloudagent.core.rst @@ -57,6 +57,14 @@ aries\_cloudagent.core.goal\_code\_registry module :undoc-members: :show-inheritance: +aries\_cloudagent.core.oob\_processor module +-------------------------------------------- + +.. automodule:: aries_cloudagent.core.oob_processor + :members: + :undoc-members: + :show-inheritance: + aries\_cloudagent.core.plugin\_registry module ---------------------------------------------- diff --git a/docs/generated/aries_cloudagent.messaging.decorators.rst b/docs/generated/aries_cloudagent.messaging.decorators.rst index 3bf19b2edd..64fb269fb1 100644 --- a/docs/generated/aries_cloudagent.messaging.decorators.rst +++ b/docs/generated/aries_cloudagent.messaging.decorators.rst @@ -49,6 +49,14 @@ aries\_cloudagent.messaging.decorators.please\_ack\_decorator module :undoc-members: :show-inheritance: +aries\_cloudagent.messaging.decorators.service\_decorator module +---------------------------------------------------------------- + +.. automodule:: aries_cloudagent.messaging.decorators.service_decorator + :members: + :undoc-members: + :show-inheritance: + aries\_cloudagent.messaging.decorators.signature\_decorator module ------------------------------------------------------------------ diff --git a/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.models.rst b/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.models.rst index 260dbd201f..b1b7bfd33c 100644 --- a/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.models.rst +++ b/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.models.rst @@ -16,3 +16,11 @@ aries\_cloudagent.protocols.out\_of\_band.v1\_0.models.invitation module :members: :undoc-members: :show-inheritance: + +aries\_cloudagent.protocols.out\_of\_band.v1\_0.models.oob\_record module +------------------------------------------------------------------------- + +.. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.models.oob_record + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.revocation_notification.rst b/docs/generated/aries_cloudagent.protocols.revocation_notification.rst index 013c0319ac..c88d26085d 100644 --- a/docs/generated/aries_cloudagent.protocols.revocation_notification.rst +++ b/docs/generated/aries_cloudagent.protocols.revocation_notification.rst @@ -13,6 +13,7 @@ Subpackages :maxdepth: 4 aries_cloudagent.protocols.revocation_notification.v1_0 + aries_cloudagent.protocols.revocation_notification.v2_0 Submodules ---------- diff --git a/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.handlers.rst new file mode 100644 index 0000000000..1fa7a93885 --- /dev/null +++ b/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.handlers.rst @@ -0,0 +1,18 @@ +aries\_cloudagent.protocols.revocation\_notification.v2\_0.handlers package +=========================================================================== + +.. automodule:: aries_cloudagent.protocols.revocation_notification.v2_0.handlers + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +aries\_cloudagent.protocols.revocation\_notification.v2\_0.handlers.revoke\_handler module +------------------------------------------------------------------------------------------ + +.. automodule:: aries_cloudagent.protocols.revocation_notification.v2_0.handlers.revoke_handler + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.messages.rst b/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.messages.rst new file mode 100644 index 0000000000..db465ff9e3 --- /dev/null +++ b/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.messages.rst @@ -0,0 +1,18 @@ +aries\_cloudagent.protocols.revocation\_notification.v2\_0.messages package +=========================================================================== + +.. automodule:: aries_cloudagent.protocols.revocation_notification.v2_0.messages + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +aries\_cloudagent.protocols.revocation\_notification.v2\_0.messages.revoke module +--------------------------------------------------------------------------------- + +.. automodule:: aries_cloudagent.protocols.revocation_notification.v2_0.messages.revoke + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.models.rst b/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.models.rst new file mode 100644 index 0000000000..cffa7e42ae --- /dev/null +++ b/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.models.rst @@ -0,0 +1,18 @@ +aries\_cloudagent.protocols.revocation\_notification.v2\_0.models package +========================================================================= + +.. automodule:: aries_cloudagent.protocols.revocation_notification.v2_0.models + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +aries\_cloudagent.protocols.revocation\_notification.v2\_0.models.rev\_notification\_record module +-------------------------------------------------------------------------------------------------- + +.. automodule:: aries_cloudagent.protocols.revocation_notification.v2_0.models.rev_notification_record + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.rst b/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.rst new file mode 100644 index 0000000000..8247c6853a --- /dev/null +++ b/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.rst @@ -0,0 +1,36 @@ +aries\_cloudagent.protocols.revocation\_notification.v2\_0 package +================================================================== + +.. automodule:: aries_cloudagent.protocols.revocation_notification.v2_0 + :members: + :undoc-members: + :show-inheritance: + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + aries_cloudagent.protocols.revocation_notification.v2_0.handlers + aries_cloudagent.protocols.revocation_notification.v2_0.messages + aries_cloudagent.protocols.revocation_notification.v2_0.models + +Submodules +---------- + +aries\_cloudagent.protocols.revocation\_notification.v2\_0.message\_types module +-------------------------------------------------------------------------------- + +.. automodule:: aries_cloudagent.protocols.revocation_notification.v2_0.message_types + :members: + :undoc-members: + :show-inheritance: + +aries\_cloudagent.protocols.revocation\_notification.v2\_0.routes module +------------------------------------------------------------------------ + +.. automodule:: aries_cloudagent.protocols.revocation_notification.v2_0.routes + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.revocation.rst b/docs/generated/aries_cloudagent.revocation.rst index 6c7c6957a5..7263263c71 100644 --- a/docs/generated/aries_cloudagent.revocation.rst +++ b/docs/generated/aries_cloudagent.revocation.rst @@ -41,6 +41,14 @@ aries\_cloudagent.revocation.manager module :undoc-members: :show-inheritance: +aries\_cloudagent.revocation.recover module +------------------------------------------- + +.. automodule:: aries_cloudagent.revocation.recover + :members: + :undoc-members: + :show-inheritance: + aries\_cloudagent.revocation.routes module ------------------------------------------ diff --git a/open-api/openapi.json b/open-api/openapi.json index 2db2fad8c6..4548dd1e29 100644 --- a/open-api/openapi.json +++ b/open-api/openapi.json @@ -1,7 +1,7 @@ { "swagger" : "2.0", "info" : { - "version" : "v0.7.4-rc2", + "version" : "v0.7.4-rc3", "title" : "Aries Cloud Agent" }, "tags" : [ { From 1f85bad90a730d8c921fa97efddb7ea6741beb5f Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Thu, 16 Jun 2022 10:41:53 -0700 Subject: [PATCH 281/872] Remove temp generated file Signed-off-by: Stephen Curran --- 0.7.4-rc3.md | 99 ---------------------------------------------------- 1 file changed, 99 deletions(-) delete mode 100644 0.7.4-rc3.md diff --git a/0.7.4-rc3.md b/0.7.4-rc3.md deleted file mode 100644 index 367992ca7c..0000000000 --- a/0.7.4-rc3.md +++ /dev/null @@ -1,99 +0,0 @@ -# Changelog - -## [0.7.4-rc3](https://github.com/hyperledger/aries-cloudagent-python/tree/0.7.4-rc3) (2022-06-15) - -[Full Changelog](https://github.com/hyperledger/aries-cloudagent-python/compare/0.7.4-rc2...0.7.4-rc3) - -**Closed issues:** - -- AATH ACA-Py - ACA-Py DID Exchange tests failing [\#1803](https://github.com/hyperledger/aries-cloudagent-python/issues/1803) -- duplicated events and wallet credentials when using /issue-credential-2.0/send with askar wallet [\#1801](https://github.com/hyperledger/aries-cloudagent-python/issues/1801) -- Verified: true - For revoked Credentials - Aries agent image: bcgovimages/aries-cloudagent:py36-1.16-1\_0.7.3 [\#1799](https://github.com/hyperledger/aries-cloudagent-python/issues/1799) -- How to turn off auto-accept-requests and auto-accept-invites? [\#1796](https://github.com/hyperledger/aries-cloudagent-python/issues/1796) -- Which mobile agent framework is recommended to work with aca-py as the mediator agent? [\#1794](https://github.com/hyperledger/aries-cloudagent-python/issues/1794) -- Inbound Transport now has an undocumented, required attribute is\_external [\#1791](https://github.com/hyperledger/aries-cloudagent-python/issues/1791) -- Issues with aries-acapy-plugin-toolbox when updating to 0.7.4 [\#1790](https://github.com/hyperledger/aries-cloudagent-python/issues/1790) -- Unable to filter for invitation from OOB invitations in multi-tenant environment [\#1789](https://github.com/hyperledger/aries-cloudagent-python/issues/1789) -- The QR code type of Acme [\#1788](https://github.com/hyperledger/aries-cloudagent-python/issues/1788) -- Duplicated Schema and Definition Ids [\#1786](https://github.com/hyperledger/aries-cloudagent-python/issues/1786) -- aries\_cloudagent.transport.outbound.manager ERROR \>\>\> Error when posting to: http://0.0.0.0:8020; [\#1781](https://github.com/hyperledger/aries-cloudagent-python/issues/1781) -- Pool timeout Error when running aca-py [\#1780](https://github.com/hyperledger/aries-cloudagent-python/issues/1780) -- Is selective disclosure implemented yet? [\#1779](https://github.com/hyperledger/aries-cloudagent-python/issues/1779) -- How to view the history of connection requests? [\#1775](https://github.com/hyperledger/aries-cloudagent-python/issues/1775) -- Cannot verify SSL certificates behind proxy [\#1773](https://github.com/hyperledger/aries-cloudagent-python/issues/1773) -- The type of QR code [\#1768](https://github.com/hyperledger/aries-cloudagent-python/issues/1768) -- ACA-Py demo bug on ./run\_demo faber: Exception: Timed out waiting for agent process to start \(status=None\). Admin URL: http://host.docker.internal:8021/status [\#1766](https://github.com/hyperledger/aries-cloudagent-python/issues/1766) -- ACA-Py demo bug Play With Docker: waiting for connection \(faber\) persists even after starting Alice [\#1765](https://github.com/hyperledger/aries-cloudagent-python/issues/1765) -- OOB invitation: only works with auto\_respond\_presentation\_request configured? [\#1764](https://github.com/hyperledger/aries-cloudagent-python/issues/1764) -- Fetch from --genesis-url likely to fail in composed container setup [\#1745](https://github.com/hyperledger/aries-cloudagent-python/issues/1745) -- Creating a /multitenancy/wallet may ignore wallet\_dispatch\_type [\#1742](https://github.com/hyperledger/aries-cloudagent-python/issues/1742) -- Support receiving messages of any minor version of any protocol [\#1736](https://github.com/hyperledger/aries-cloudagent-python/issues/1736) -- Case sensitive role parameter with /ledger/register-nym [\#1731](https://github.com/hyperledger/aries-cloudagent-python/issues/1731) -- ledger read operations with multiledger enabled causes timeouts and failure [\#1728](https://github.com/hyperledger/aries-cloudagent-python/issues/1728) -- Issue receiving credentials with 0.7.4rc0 of aca-py [\#1724](https://github.com/hyperledger/aries-cloudagent-python/issues/1724) -- revocation notification webhooks should use `revocation_notification` instead of `revocation-notification` as the topic [\#1713](https://github.com/hyperledger/aries-cloudagent-python/issues/1713) -- Make connection id optional if revoking with notification and cred\_ex\_id is present [\#1712](https://github.com/hyperledger/aries-cloudagent-python/issues/1712) -- GET /schemas/created returns duplicates [\#1707](https://github.com/hyperledger/aries-cloudagent-python/issues/1707) -- Set Public DID error with Multitenancy and Author/Endorser [\#1703](https://github.com/hyperledger/aries-cloudagent-python/issues/1703) -- Adding 'auto\_verify' flag to '/present-proof/send-request' API [\#1698](https://github.com/hyperledger/aries-cloudagent-python/issues/1698) -- Custom agent [\#1693](https://github.com/hyperledger/aries-cloudagent-python/issues/1693) -- Trouble Running Faber Demo On Apple Silicon [\#1689](https://github.com/hyperledger/aries-cloudagent-python/issues/1689) -- Error: "Revocation registry is full" when bulk running bulk issuances. [\#1684](https://github.com/hyperledger/aries-cloudagent-python/issues/1684) -- AATH acapy-main and aries-vcx RFC0025 tests have been failing since the March 2 credential exchange updates [\#1679](https://github.com/hyperledger/aries-cloudagent-python/issues/1679) -- Add basic DOCKER\_ENV logging for run\_demo [\#1674](https://github.com/hyperledger/aries-cloudagent-python/issues/1674) -- Credential Revocations: Unrecoverable state after failed credential revocation due to ledger connection issues [\#1669](https://github.com/hyperledger/aries-cloudagent-python/issues/1669) -- Could not store issued credential in mobile wallet [\#1664](https://github.com/hyperledger/aries-cloudagent-python/issues/1664) -- Able to recreate the same schema [\#1661](https://github.com/hyperledger/aries-cloudagent-python/issues/1661) -- Invalid proof presentation when requesting revocable and non-revocable credentials [\#1651](https://github.com/hyperledger/aries-cloudagent-python/issues/1651) -- Askar-profile wallet creation error causes wallet to be only registered in the relay database [\#1649](https://github.com/hyperledger/aries-cloudagent-python/issues/1649) -- Running Docker Scripts on Windows [\#1644](https://github.com/hyperledger/aries-cloudagent-python/issues/1644) -- Unable to receive connectionless out-of-band invitation [\#1636](https://github.com/hyperledger/aries-cloudagent-python/issues/1636) -- RecipientKey is not a raw Ed25519VerificationKey2018 key [\#1634](https://github.com/hyperledger/aries-cloudagent-python/issues/1634) -- Supporting the OOB Transition RFC documentation [\#1625](https://github.com/hyperledger/aries-cloudagent-python/issues/1625) -- Send Presentation Error [\#1598](https://github.com/hyperledger/aries-cloudagent-python/issues/1598) -- Inconsistent behavior when issuing multiple credentials in sequential. Some of the requests/issuances are failing with error "400 Revocation registry metadata not found". [\#1586](https://github.com/hyperledger/aries-cloudagent-python/issues/1586) -- Can I re-use a Peer DID in another connection? [\#1582](https://github.com/hyperledger/aries-cloudagent-python/issues/1582) -- JSON-ld contexts are not handled correctly [\#1568](https://github.com/hyperledger/aries-cloudagent-python/issues/1568) -- What does the "start-introduction" command do? [\#1559](https://github.com/hyperledger/aries-cloudagent-python/issues/1559) -- Error Creating Wallet indy.error.CommonIOError [\#1555](https://github.com/hyperledger/aries-cloudagent-python/issues/1555) -- DatabasePerWalletStrategy + Postgres Storage fails to Recreate Wallet [\#1547](https://github.com/hyperledger/aries-cloudagent-python/issues/1547) -- Support ARM based docker images [\#1546](https://github.com/hyperledger/aries-cloudagent-python/issues/1546) -- Delete wallet only DIDs [\#1520](https://github.com/hyperledger/aries-cloudagent-python/issues/1520) -- Create an implementation and documentation for addressing the "in-memory queue" issue in ACA-Py [\#1513](https://github.com/hyperledger/aries-cloudagent-python/issues/1513) -- Schema/Credential Definition Error - Multi Tenancy Enabled [\#1505](https://github.com/hyperledger/aries-cloudagent-python/issues/1505) -- Update outbound queue docs [\#1502](https://github.com/hyperledger/aries-cloudagent-python/issues/1502) -- Inconsistency in proof presentation in v 0.7.1 [\#1499](https://github.com/hyperledger/aries-cloudagent-python/issues/1499) -- Storing a Credential with a negative numeric string value causes Marshmallow.exceptions.ValidationError [\#1462](https://github.com/hyperledger/aries-cloudagent-python/issues/1462) -- Access profile on plugin initialization [\#1452](https://github.com/hyperledger/aries-cloudagent-python/issues/1452) -- Add endorser support to updating DID attributes [\#1447](https://github.com/hyperledger/aries-cloudagent-python/issues/1447) -- Presentation exchange XXXXX in done state \(must be request-received\) [\#1444](https://github.com/hyperledger/aries-cloudagent-python/issues/1444) -- ACA-PY answers requests with wrong response [\#1441](https://github.com/hyperledger/aries-cloudagent-python/issues/1441) -- Aca-py not respecting NO\_PROXY variables [\#1403](https://github.com/hyperledger/aries-cloudagent-python/issues/1403) -- Keyword conflict with /transactions/create-request, request body schema name Date [\#1319](https://github.com/hyperledger/aries-cloudagent-python/issues/1319) -- How to issue an image as part of a credential [\#1310](https://github.com/hyperledger/aries-cloudagent-python/issues/1310) -- Minor Bug: Endorse-Transaction endpoint mismatch [\#1309](https://github.com/hyperledger/aries-cloudagent-python/issues/1309) -- \[Proposal/Question\] Verify correctness of own Verifiable Credentials, using the Signature and Raw Data [\#1239](https://github.com/hyperledger/aries-cloudagent-python/issues/1239) -- Return id of the new ledger object on endorsed transaction write [\#1159](https://github.com/hyperledger/aries-cloudagent-python/issues/1159) -- Register-Nym: Status Code: 422: Unprocessable Entity \(missing data\) [\#1037](https://github.com/hyperledger/aries-cloudagent-python/issues/1037) -- Missing TAA Accepting Mechanism in Faber Agent for Demo Repos [\#855](https://github.com/hyperledger/aries-cloudagent-python/issues/855) - -**Merged pull requests:** - -- Fix put\_file when the server returns a redirect [\#1808](https://github.com/hyperledger/aries-cloudagent-python/pull/1808) ([andrewwhitehead](https://github.com/andrewwhitehead)) -- Adjust revocation registry update procedure to shorten transactions [\#1804](https://github.com/hyperledger/aries-cloudagent-python/pull/1804) ([andrewwhitehead](https://github.com/andrewwhitehead)) -- Fix: Inbound Transport is\_external attribute [\#1802](https://github.com/hyperledger/aries-cloudagent-python/pull/1802) ([shaangill025](https://github.com/shaangill025)) -- Fix: Duplicated schema and cred\_def - Askar and Postgres [\#1800](https://github.com/hyperledger/aries-cloudagent-python/pull/1800) ([shaangill025](https://github.com/shaangill025)) -- feat: Add filter param to connection list for invitations [\#1797](https://github.com/hyperledger/aries-cloudagent-python/pull/1797) ([frostyfrog](https://github.com/frostyfrog)) -- Fix tails server upload multi-ledger mode [\#1785](https://github.com/hyperledger/aries-cloudagent-python/pull/1785) ([ianco](https://github.com/ianco)) -- Additional endpoints to get revocation details and fix "published" status [\#1783](https://github.com/hyperledger/aries-cloudagent-python/pull/1783) ([ianco](https://github.com/ianco)) -- Document impact of multi-ledger on TAA acceptance [\#1778](https://github.com/hyperledger/aries-cloudagent-python/pull/1778) ([ianco](https://github.com/ianco)) -- fix: add a close statement to ensure session is closed on error [\#1777](https://github.com/hyperledger/aries-cloudagent-python/pull/1777) ([reflectivedevelopment](https://github.com/reflectivedevelopment)) -- Adds `transport_id` variable assignment back to outbound enqueue method [\#1776](https://github.com/hyperledger/aries-cloudagent-python/pull/1776) ([amanji](https://github.com/amanji)) -- Replace async workaround within document loader [\#1774](https://github.com/hyperledger/aries-cloudagent-python/pull/1774) ([frostyfrog](https://github.com/frostyfrog)) -- Feature: Add the ability to deny specific plugins from loading [\#1737](https://github.com/hyperledger/aries-cloudagent-python/pull/1737) ([frostyfrog](https://github.com/frostyfrog)) -- Feat/revocation notification v2 [\#1734](https://github.com/hyperledger/aries-cloudagent-python/pull/1734) ([frostyfrog](https://github.com/frostyfrog)) -- feat: support connectionless exchange [\#1710](https://github.com/hyperledger/aries-cloudagent-python/pull/1710) ([TimoGlastra](https://github.com/TimoGlastra)) - - - -\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)* From 6ba9ae2480d265f358719b3ad2f5183c782d9ae1 Mon Sep 17 00:00:00 2001 From: shaangill025 Date: Fri, 17 Jun 2022 07:06:52 -0700 Subject: [PATCH 282/872] receive pres - update exception raising condition Signed-off-by: shaangill025 --- .../present_proof/v2_0/formats/indy/handler.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/aries_cloudagent/protocols/present_proof/v2_0/formats/indy/handler.py b/aries_cloudagent/protocols/present_proof/v2_0/formats/indy/handler.py index 5313dde87d..54ef915af0 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/formats/indy/handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/formats/indy/handler.py @@ -199,7 +199,10 @@ def _check_proof_vs_proposal(): f"attr::{name}::value": proof_value, } - if not any(r.items() <= criteria.items() for r in req_restrictions): + if ( + not any(r.items() <= criteria.items() for r in req_restrictions) + and len(req_restrictions) != 0 + ): raise V20PresFormatHandlerError( f"Presented attribute {reft} does not satisfy proof request " f"restrictions {req_restrictions}" @@ -234,7 +237,10 @@ def _check_proof_vs_proposal(): }, } - if not any(r.items() <= criteria.items() for r in req_restrictions): + if ( + not any(r.items() <= criteria.items() for r in req_restrictions) + and len(req_restrictions) != 0 + ): raise V20PresFormatHandlerError( f"Presented attr group {reft} does not satisfy proof request " f"restrictions {req_restrictions}" @@ -287,7 +293,10 @@ def _check_proof_vs_proposal(): "issuer_did": cred_def_id.split(":")[-5], } - if not any(r.items() <= criteria.items() for r in req_restrictions): + if ( + not any(r.items() <= criteria.items() for r in req_restrictions) + and len(req_restrictions) != 0 + ): raise V20PresFormatHandlerError( f"Presented predicate {reft} does not satisfy proof request " f"restrictions {req_restrictions}" From 6b3d396d9475f9beb8cfad43397629ef8db68efb Mon Sep 17 00:00:00 2001 From: lineko <36633510+lineko@users.noreply.github.com> Date: Sun, 19 Jun 2022 19:57:41 +0200 Subject: [PATCH 283/872] Update POST /present-proof/send-request to POST /present-proof-2.0/send-request I got error messages when following the instructions and tried the updated version, which worked. Signed-off-by: lineko <36633510+lineko@users.noreply.github.com> --- demo/AriesOpenAPIDemo.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/AriesOpenAPIDemo.md b/demo/AriesOpenAPIDemo.md index 7e3709a032..e392cbde49 100644 --- a/demo/AriesOpenAPIDemo.md +++ b/demo/AriesOpenAPIDemo.md @@ -601,7 +601,7 @@ Alice now has her Faber credential. Let’s have the Faber agent send a request ### Faber sends a Proof Request -From the Faber browser tab, get ready to execute the **`POST /present-proof/send-request`** endpoint. After hitting `Try it Now`, erase the data in the block labelled "Edit Value Model", replacing it with the text below. Once that is done, replace in the JSON each instance of `cred_def_id` (there are four instances) and `connection_id` with the values found using the same techniques we've used earlier in this tutorial. Both can be found by scrolling back a little in the Faber terminal, or you can execute API endpoints we've already covered. You can also change the value of the `comment` item to whatever you want. +From the Faber browser tab, get ready to execute the **`POST /present-proof-2.0/send-request`** endpoint. After hitting `Try it Now`, erase the data in the block labelled "Edit Value Model", replacing it with the text below. Once that is done, replace in the JSON each instance of `cred_def_id` (there are four instances) and `connection_id` with the values found using the same techniques we've used earlier in this tutorial. Both can be found by scrolling back a little in the Faber terminal, or you can execute API endpoints we've already covered. You can also change the value of the `comment` item to whatever you want. ```jsonc { From 213d2fa07aba49e6d67ce580921e9786e8a526eb Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Mon, 20 Jun 2022 15:15:38 -0700 Subject: [PATCH 284/872] fix IssuerCredRevRecord state update on revocation publish; add unit test Signed-off-by: Andrew Whitehead --- aries_cloudagent/indy/sdk/issuer.py | 2 +- .../indy/sdk/tests/test_issuer.py | 21 +++-- aries_cloudagent/revocation/manager.py | 62 +++++++------ .../models/issuer_cred_rev_record.py | 7 +- .../revocation/tests/test_manager.py | 93 ++++++++++++++----- 5 files changed, 125 insertions(+), 60 deletions(-) diff --git a/aries_cloudagent/indy/sdk/issuer.py b/aries_cloudagent/indy/sdk/issuer.py index 8fe7d23777..72f569f2ee 100644 --- a/aries_cloudagent/indy/sdk/issuer.py +++ b/aries_cloudagent/indy/sdk/issuer.py @@ -283,7 +283,7 @@ async def revoke_credentials( tails_reader_handle = await create_tails_reader(tails_file_path) result_json = None - for cred_rev_id in cred_rev_ids: + for cred_rev_id in set(cred_rev_ids): with IndyErrorHandler( "Exception when revoking credential", IndyIssuerError ): diff --git a/aries_cloudagent/indy/sdk/tests/test_issuer.py b/aries_cloudagent/indy/sdk/tests/test_issuer.py index 5981233abc..0378044314 100644 --- a/aries_cloudagent/indy/sdk/tests/test_issuer.py +++ b/aries_cloudagent/indy/sdk/tests/test_issuer.py @@ -311,17 +311,20 @@ async def test_create_revoke_credentials_x( values = json.loads(call_values) assert "attr1" in values - mock_indy_revoke_credential.side_effect = [ - json.dumps(TEST_RR_DELTA), - IndyError( - error_code=ErrorCode.AnoncredsInvalidUserRevocId, - error_details={"message": "already revoked"}, - ), - IndyError( + def mock_revoke(_h, _t, _r, cred_rev_id): + if cred_rev_id == "42": + return json.dumps(TEST_RR_DELTA) + if cred_rev_id == "54": + raise IndyError( + error_code=ErrorCode.AnoncredsInvalidUserRevocId, + error_details={"message": "already revoked"}, + ) + raise IndyError( error_code=ErrorCode.UnknownCryptoTypeError, error_details={"message": "truly an outlier"}, - ), - ] + ) + + mock_indy_revoke_credential.side_effect = mock_revoke mock_indy_merge_rr_deltas.return_value = json.dumps(TEST_RR_DELTA) (result, failed) = await self.issuer.revoke_credentials( REV_REG_ID, tails_file_path="dummy", cred_rev_ids=test_cred_rev_ids diff --git a/aries_cloudagent/revocation/manager.py b/aries_cloudagent/revocation/manager.py index 85729ac9f2..4057d7a799 100644 --- a/aries_cloudagent/revocation/manager.py +++ b/aries_cloudagent/revocation/manager.py @@ -133,7 +133,7 @@ async def revoke_credential( rev_reg = await revoc.get_ledger_registry(rev_reg_id) await rev_reg.get_or_fetch_local_tails_path() # pick up pending revocations on input revocation registry - crids = list(set(issuer_rr_rec.pending_pub + [cred_rev_id])) + crids = (issuer_rr_rec.pending_pub or []) + [cred_rev_id] (delta_json, _) = await issuer.revoke_credentials( issuer_rr_rec.revoc_reg_id, issuer_rr_rec.tails_local_path, crids ) @@ -145,9 +145,9 @@ async def revoke_credential( issuer_rr_upd.revoc_reg_entry = json.loads(delta_json) await issuer_rr_upd.clear_pending(txn, crids) await txn.commit() + await self.set_cred_revoked_state(rev_reg_id, crids) if delta_json: await issuer_rr_upd.send_entry(self._profile) - await self.set_cred_revoked_state(rev_reg_id, [cred_rev_id]) await notify_revocation_published_event( self._profile, rev_reg_id, [cred_rev_id] ) @@ -215,11 +215,11 @@ async def publish_pending_revocations( issuer_rr_upd.revoc_reg_entry = json.loads(delta_json) await issuer_rr_upd.clear_pending(txn, crids) await txn.commit() + await self.set_cred_revoked_state(issuer_rr_rec.revoc_reg_id, crids) if delta_json: await issuer_rr_upd.send_entry(self._profile) published = sorted(crid for crid in crids if crid not in failed_crids) result[issuer_rr_rec.revoc_reg_id] = published - await self.set_cred_revoked_state(issuer_rr_rec.revoc_reg_id, crids) await notify_revocation_published_event( self._profile, issuer_rr_rec.revoc_reg_id, crids ) @@ -289,34 +289,40 @@ async def set_cred_revoked_state( """ for cred_rev_id in cred_rev_ids: + cred_ex_id = None + + try: + async with self._profile.transaction() as txn: + rev_rec = await IssuerCredRevRecord.retrieve_by_ids( + txn, rev_reg_id, cred_rev_id, for_update=True + ) + cred_ex_id = rev_rec.cred_ex_id + rev_rec.state = IssuerCredRevRecord.STATE_REVOKED + await rev_rec.save(txn, reason="revoke credential") + await txn.commit() + except StorageNotFoundError: + continue + async with self._profile.transaction() as txn: try: - rev_rec = await IssuerCredRevRecord.retrieve_by_ids( - txn, rev_reg_id, cred_rev_id + cred_ex_record = await V10CredentialExchange.retrieve_by_id( + txn, cred_ex_id, for_update=True + ) + cred_ex_record.state = ( + V10CredentialExchange.STATE_CREDENTIAL_REVOKED ) - try: - cred_ex_record = await V10CredentialExchange.retrieve_by_id( - txn, rev_rec.cred_ex_id, for_update=True - ) - cred_ex_record.state = ( - V10CredentialExchange.STATE_CREDENTIAL_REVOKED - ) - await cred_ex_record.save(txn, reason="revoke credential") - await txn.commit() - - except StorageNotFoundError: - try: - cred_ex_record = await V20CredExRecord.retrieve_by_id( - txn, rev_rec.cred_ex_id, for_update=True - ) - cred_ex_record.state = ( - V20CredExRecord.STATE_CREDENTIAL_REVOKED - ) - await cred_ex_record.save(txn, reason="revoke credential") - await txn.commit() - - except StorageNotFoundError: - pass + await cred_ex_record.save(txn, reason="revoke credential") + await txn.commit() + continue # skip 2.0 record check + except StorageNotFoundError: + pass + try: + cred_ex_record = await V20CredExRecord.retrieve_by_id( + txn, cred_ex_id, for_update=True + ) + cred_ex_record.state = V20CredExRecord.STATE_CREDENTIAL_REVOKED + await cred_ex_record.save(txn, reason="revoke credential") + await txn.commit() except StorageNotFoundError: pass diff --git a/aries_cloudagent/revocation/models/issuer_cred_rev_record.py b/aries_cloudagent/revocation/models/issuer_cred_rev_record.py index cb87c070b9..5ab8d3ba09 100644 --- a/aries_cloudagent/revocation/models/issuer_cred_rev_record.py +++ b/aries_cloudagent/revocation/models/issuer_cred_rev_record.py @@ -90,10 +90,15 @@ async def retrieve_by_ids( session: ProfileSession, rev_reg_id: str, cred_rev_id: str, + *, + for_update: bool = False, ) -> "IssuerCredRevRecord": """Retrieve an issuer cred rev record by rev reg id and cred rev id.""" return await cls.retrieve_by_tag_filter( - session, {"rev_reg_id": rev_reg_id}, {"cred_rev_id": cred_rev_id} + session, + {"rev_reg_id": rev_reg_id}, + {"cred_rev_id": cred_rev_id}, + for_update=for_update, ) @classmethod diff --git a/aries_cloudagent/revocation/tests/test_manager.py b/aries_cloudagent/revocation/tests/test_manager.py index 470107b6ab..113c4850d1 100644 --- a/aries_cloudagent/revocation/tests/test_manager.py +++ b/aries_cloudagent/revocation/tests/test_manager.py @@ -4,6 +4,10 @@ from asynctest import TestCase as AsyncTestCase from more_itertools import side_effect +from aries_cloudagent.revocation.models.issuer_cred_rev_record import ( + IssuerCredRevRecord, +) + from ...core.in_memory import InMemoryProfile from ...indy.issuer import IndyIssuer from ...protocols.issue_credential.v1_0.models.credential_exchange import ( @@ -38,7 +42,25 @@ async def test_revoke_credential_publish(self): tails_local_path=TAILS_LOCAL, send_entry=async_mock.CoroutineMock(), clear_pending=async_mock.CoroutineMock(), + pending_pub=["2"], ) + issuer = async_mock.MagicMock(IndyIssuer, autospec=True) + issuer.revoke_credentials = async_mock.CoroutineMock( + return_value=( + json.dumps( + { + "ver": "1.0", + "value": { + "prevAccum": "1 ...", + "accum": "21 ...", + "issued": [1], + }, + } + ), + [], + ) + ) + self.profile.context.injector.bind_instance(IndyIssuer, issuer) with async_mock.patch.object( test_module.IssuerCredRevRecord, @@ -64,26 +86,14 @@ async def test_revoke_credential_publish(self): return_value=mock_rev_reg ) - issuer = async_mock.MagicMock(IndyIssuer, autospec=True) - issuer.revoke_credentials = async_mock.CoroutineMock( - return_value=( - json.dumps( - { - "ver": "1.0", - "value": { - "prevAccum": "1 ...", - "accum": "21 ...", - "issued": [1], - }, - } - ), - [], - ) - ) - self.profile.context.injector.bind_instance(IndyIssuer, issuer) - await self.manager.revoke_credential_by_cred_ex_id(CRED_EX_ID, publish=True) + issuer.revoke_credentials.assert_awaited_once_with( + mock_issuer_rev_reg_record.revoc_reg_id, + mock_issuer_rev_reg_record.tails_local_path, + ["2", "1"], + ) + async def test_revoke_cred_by_cxid_not_found(self): CRED_EX_ID = "dummy-cxid" @@ -128,6 +138,8 @@ async def test_revoke_credential_pend(self): mock_issuer_rev_reg_record = async_mock.MagicMock( mark_pending=async_mock.CoroutineMock() ) + issuer = async_mock.MagicMock(IndyIssuer, autospec=True) + self.profile.context.injector.bind_instance(IndyIssuer, issuer) with async_mock.patch.object( test_module, "IndyRevocation", autospec=True @@ -148,14 +160,13 @@ async def test_revoke_credential_pend(self): return_value=mock_issuer_rev_reg_record ) - issuer = async_mock.MagicMock(IndyIssuer, autospec=True) - self.profile.context.injector.bind_instance(IndyIssuer, issuer) - await self.manager.revoke_credential(REV_REG_ID, CRED_REV_ID, False) mock_issuer_rev_reg_record.mark_pending.assert_called_once_with( session.return_value, CRED_REV_ID ) + issuer.revoke_credentials.assert_not_awaited() + async def test_publish_pending_revocations_basic(self): deltas = [ { @@ -415,3 +426,43 @@ async def test_retrieve_records(self): ) assert ret_ex.connection_id == str(index) assert ret_ex.thread_id == str(1000 + index) + + async def test_set_revoked_state(self): + CRED_REV_ID = "1" + + async with self.profile.session() as session: + exchange_record = V10CredentialExchange( + connection_id="mark-revoked-cid", + thread_id="mark-revoked-tid", + initiator=V10CredentialExchange.INITIATOR_SELF, + revoc_reg_id=REV_REG_ID, + revocation_id=CRED_REV_ID, + role=V10CredentialExchange.ROLE_ISSUER, + state=V10CredentialExchange.STATE_ISSUED, + ) + await exchange_record.save(session) + + crev_record = IssuerCredRevRecord( + cred_ex_id=exchange_record.credential_exchange_id, + cred_def_id=CRED_DEF_ID, + rev_reg_id=REV_REG_ID, + cred_rev_id=CRED_REV_ID, + state=IssuerCredRevRecord.STATE_ISSUED, + ) + await crev_record.save(session) + + await self.manager.set_cred_revoked_state(REV_REG_ID, [CRED_REV_ID]) + + async with self.profile.session() as session: + check_exchange_record = await V10CredentialExchange.retrieve_by_id( + session, exchange_record.credential_exchange_id + ) + assert ( + check_exchange_record.state + == V10CredentialExchange.STATE_CREDENTIAL_REVOKED + ) + + check_crev_record = await IssuerCredRevRecord.retrieve_by_id( + session, crev_record.record_id + ) + assert check_crev_record.state == IssuerCredRevRecord.STATE_REVOKED From 5e32ee67c82903cef1f9805b278feae81382875a Mon Sep 17 00:00:00 2001 From: shaangill025 Date: Tue, 21 Jun 2022 08:50:09 -0700 Subject: [PATCH 285/872] unit tests for revealed attr and predicates Signed-off-by: shaangill025 --- .../present_proof/v2_0/tests/test_manager.py | 158 ++++++++++++++++++ 1 file changed, 158 insertions(+) diff --git a/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py b/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py index 65fba1264e..18d020e115 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py @@ -1400,6 +1400,164 @@ async def test_receive_pres_receive_pred_value_mismatch_punt_to_indy(self): save_ex.assert_called_once() assert px_rec_out.state == (V20PresExRecord.STATE_PRESENTATION_RECEIVED) + async def test_receive_pres_indy_no_predicate_restrictions(self): + connection_record = async_mock.MagicMock(connection_id=CONN_ID) + indy_proof_req = { + "name": PROOF_REQ_NAME, + "version": PROOF_REQ_VERSION, + "nonce": PROOF_REQ_NONCE, + "requested_attributes": { + "0_player_uuid": { + "name": "player", + "restrictions": [{"cred_def_id": CD_ID}], + "non_revoked": {"from": NOW, "to": NOW}, + }, + "0_screencapture_uuid": { + "name": "screenCapture", + "restrictions": [{"cred_def_id": CD_ID}], + "non_revoked": {"from": NOW, "to": NOW}, + }, + }, + "requested_predicates": { + "0_highscore_GE_uuid": { + "name": "highScore", + "p_type": ">=", + "p_value": 1000000, + "restrictions": [], + "non_revoked": {"from": NOW, "to": NOW}, + } + }, + } + pres_request = V20PresRequest( + formats=[ + V20PresFormat( + attach_id="indy", + format_=ATTACHMENT_FORMAT[PRES_20_REQUEST][ + V20PresFormat.Format.INDY.api + ], + ) + ], + request_presentations_attach=[ + AttachDecorator.data_base64(indy_proof_req, ident="indy") + ], + ) + pres = V20Pres( + formats=[ + V20PresFormat( + attach_id="indy", + format_=ATTACHMENT_FORMAT[PRES_20][V20PresFormat.Format.INDY.api], + ) + ], + presentations_attach=[ + AttachDecorator.data_base64(INDY_PROOF, ident="indy") + ], + ) + pres.assign_thread_id("thread-id") + + px_rec_dummy = V20PresExRecord( + pres_request=pres_request.serialize(), + ) + + # cover by_format property + by_format = px_rec_dummy.by_format + + assert by_format.get("pres_request").get("indy") == indy_proof_req + + with async_mock.patch.object( + V20PresExRecord, "save", autospec=True + ) as save_ex, async_mock.patch.object( + V20PresExRecord, "retrieve_by_tag_filter", autospec=True + ) as retrieve_ex, async_mock.patch.object( + self.profile, + "session", + async_mock.MagicMock(return_value=self.profile.session()), + ) as session: + retrieve_ex.side_effect = [px_rec_dummy] + px_rec_out = await self.manager.receive_pres(pres, connection_record, None) + retrieve_ex.assert_called_once_with( + session.return_value, + {"thread_id": "thread-id"}, + {"role": V20PresExRecord.ROLE_VERIFIER, "connection_id": CONN_ID}, + ) + save_ex.assert_called_once() + assert px_rec_out.state == (V20PresExRecord.STATE_PRESENTATION_RECEIVED) + + async def test_receive_pres_indy_no_attr_restrictions(self): + connection_record = async_mock.MagicMock(connection_id=CONN_ID) + indy_proof_req = { + "name": PROOF_REQ_NAME, + "version": PROOF_REQ_VERSION, + "nonce": PROOF_REQ_NONCE, + "requested_attributes": { + "0_player_uuid": { + "name": "player", + "restrictions": [], + "non_revoked": {"from": NOW, "to": NOW}, + } + }, + "requested_predicates": {}, + } + proof = deepcopy(INDY_PROOF) + proof["requested_proof"]["revealed_attrs"] = { + "0_player_uuid": { + "sub_proof_index": 0, + "raw": "Richie Knucklez", + "encoded": "516439982", + } + } + proof["requested_proof"]["predicates"] = {} + pres_request = V20PresRequest( + formats=[ + V20PresFormat( + attach_id="indy", + format_=ATTACHMENT_FORMAT[PRES_20_REQUEST][ + V20PresFormat.Format.INDY.api + ], + ) + ], + request_presentations_attach=[ + AttachDecorator.data_base64(indy_proof_req, ident="indy") + ], + ) + pres = V20Pres( + formats=[ + V20PresFormat( + attach_id="indy", + format_=ATTACHMENT_FORMAT[PRES_20][V20PresFormat.Format.INDY.api], + ) + ], + presentations_attach=[AttachDecorator.data_base64(proof, ident="indy")], + ) + pres.assign_thread_id("thread-id") + + px_rec_dummy = V20PresExRecord( + pres_request=pres_request.serialize(), + ) + + # cover by_format property + by_format = px_rec_dummy.by_format + + assert by_format.get("pres_request").get("indy") == indy_proof_req + + with async_mock.patch.object( + V20PresExRecord, "save", autospec=True + ) as save_ex, async_mock.patch.object( + V20PresExRecord, "retrieve_by_tag_filter", autospec=True + ) as retrieve_ex, async_mock.patch.object( + self.profile, + "session", + async_mock.MagicMock(return_value=self.profile.session()), + ) as session: + retrieve_ex.side_effect = [px_rec_dummy] + px_rec_out = await self.manager.receive_pres(pres, connection_record, None) + retrieve_ex.assert_called_once_with( + session.return_value, + {"thread_id": "thread-id"}, + {"role": V20PresExRecord.ROLE_VERIFIER, "connection_id": CONN_ID}, + ) + save_ex.assert_called_once() + assert px_rec_out.state == (V20PresExRecord.STATE_PRESENTATION_RECEIVED) + async def test_receive_pres_bait_and_switch_attr_name(self): connection_record = async_mock.MagicMock(connection_id=CONN_ID) indy_proof_req = deepcopy(INDY_PROOF_REQ_NAME) From 540b9578460746bd66c1210d5a1b16920128ac85 Mon Sep 17 00:00:00 2001 From: shaangill025 Date: Tue, 21 Jun 2022 09:58:27 -0700 Subject: [PATCH 286/872] set wallet-type to askar Signed-off-by: shaangill025 --- demo/alice-local.sh | 2 +- demo/docker/docker-compose.yml | 2 +- demo/faber-local.sh | 2 +- demo/local-indy-args.yaml | 2 +- demo/runners/support/utils.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/demo/alice-local.sh b/demo/alice-local.sh index d24e50945e..babc85b491 100755 --- a/demo/alice-local.sh +++ b/demo/alice-local.sh @@ -11,7 +11,7 @@ PYTHONPATH=.. ../bin/aca-py start \ --outbound-transport http \ --admin 0.0.0.0 8031 \ --admin-insecure-mode \ - --wallet-type indy \ + --wallet-type askar \ --wallet-name alice.agent420695 \ --wallet-key alice.agent420695 \ --preserve-exchange-records \ diff --git a/demo/docker/docker-compose.yml b/demo/docker/docker-compose.yml index ed2c993854..bcec777fd0 100644 --- a/demo/docker/docker-compose.yml +++ b/demo/docker/docker-compose.yml @@ -36,7 +36,7 @@ services: --tails-server-base-url 'https://tails-test.vonx.io' \ --notify-revocation \ --monitor-revocation-notification \ - --wallet-type 'indy' \ + --wallet-type 'askar' \ --wallet-name 'acapy_agent_wallet' \ --wallet-key 'key' \ --wallet-storage-type 'postgres_storage' \ diff --git a/demo/faber-local.sh b/demo/faber-local.sh index fcedc69bbf..7471c31b67 100755 --- a/demo/faber-local.sh +++ b/demo/faber-local.sh @@ -11,7 +11,7 @@ PYTHONPATH=.. ../bin/aca-py start \ --outbound-transport http \ --admin 0.0.0.0 8021 \ --admin-insecure-mode \ - --wallet-type indy \ + --wallet-type askar \ --wallet-name faber.agent916333 \ --wallet-key faber.agent916333 \ --preserve-exchange-records \ diff --git a/demo/local-indy-args.yaml b/demo/local-indy-args.yaml index 204b04e3ea..60b0e4a91f 100644 --- a/demo/local-indy-args.yaml +++ b/demo/local-indy-args.yaml @@ -25,7 +25,7 @@ auto-ping-connection: true # curl -d '{"seed":"my_seed_000000000000000000000000", "role":"TRUST_ANCHOR", "alias":"My Agent"}' -X POST http://localhost:9000/register # note that the env var name is configured in argparse.py # seed = comes from ACAPY_WALLET_SEED -wallet-type: indy +wallet-type: askar wallet-name: testwallet # wallet-key = comes from ACAPY_WALLET_KEY # run a local postgres (docker) like: diff --git a/demo/runners/support/utils.py b/demo/runners/support/utils.py index 5f908a883e..63982d4437 100644 --- a/demo/runners/support/utils.py +++ b/demo/runners/support/utils.py @@ -235,7 +235,7 @@ def progress(*args, **kwargs): def check_requires(args): - wtype = args.wallet_type or "indy" + wtype = args.wallet_type or "askar" if wtype == "indy": try: From 338851c3a1e18be418ab604f0a8b054670a29b09 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Thu, 16 Jun 2022 11:55:55 -0700 Subject: [PATCH 287/872] Add troubleshooting document, include initial examples - ledger connection, out-of-sync RevReg Signed-off-by: Stephen Curran --- DevReadMe.md | 1 + README.md | 13 ++++++ Troubleshooting.md | 100 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+) create mode 100644 Troubleshooting.md diff --git a/DevReadMe.md b/DevReadMe.md index 53e8511fcb..e37f1c624a 100644 --- a/DevReadMe.md +++ b/DevReadMe.md @@ -18,6 +18,7 @@ See the [README](README.md) for details about this repository and information ab - [Developing](#developing) - [Prerequisites](#prerequisites) - [Running Locally](#running-locally) + - [Logging](#logging) - [Running Tests](#running-tests) - [Running Aries Agent Test Harness Tests](#running-aries-agent-test-harness-tests) - [Development Workflow](#development-workflow) diff --git a/README.md b/README.md index 39d61eb421..936847fbc5 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,19 @@ An ACA-Py instance puts together an OpenAPI-documented REST interface based on t Technical note: the administrative API exposed by the agent for the controller to use must be protected with an API key (using the --admin-api-key command line arg) or deliberately left unsecured using the --admin-insecure-mode command line arg. The latter should not be used other than in development if the API is not otherwise secured. +## Troubleshooting + +There are a number of resources for getting help with ACA-Py and troubleshooting +any problems you might run into. The [Troubleshooting](Troubleshooting.md) +document contains some guidance about issues that have been experienced in the +past. Feel free to submit PRs to supplement the troubleshooting document! +Searching the [ACA-Py GitHub +issues](https://github.com/hyperledger/aries-cloudagent-python/issues) will +often uncover challenges that others have experienced, often with answers to +solving those challenges. As well, there is the "aries-cloudagent-python" +channel on the Hyperledger Discord chat server ([invitation +here](https://discord.gg/hyperledger)). + ## Credit The initial implementation of ACA-Py was developed by the Government of British Columbia’s Digital Trust Team in Canada. To learn more about what’s happening with decentralized identity and digital trust in British Columbia, a new website will be launching and the link will be made available here. diff --git a/Troubleshooting.md b/Troubleshooting.md new file mode 100644 index 0000000000..776b2d63fa --- /dev/null +++ b/Troubleshooting.md @@ -0,0 +1,100 @@ +# Troubleshooting Aries Cloud Agent Python + +## Table of Contents + +- [Unable to Connect to Ledger](#unable-to-connect-to-ledger) + - [Local ledger running?](#local-ledger-running) + - [Any Firewalls](#any-firewalls) +- [Damaged, Unpublishable Revocation Registry](#damaged-unpublishable-revocation-registry) + +## Unable to Connect to Ledger + +The most common issue hit by first time users is getting an error on startup "unable to connect to ledger". Here are a list of things to check when you see that error. + +### Local ledger running? + +Unless you specify via startup parameters or environment variables that you are using a public Hyperledger Indy ledger, ACA-Py assumes that you are running a local ledger -- an instance of [von-network](https://github.com/bcgov/von-network). +If that is the cause -- have you started your local ledger, and did it startup properly. Things to check: + +- Any errors in the startup of von-network? +- Is the von-network webserver (usually at `https:/localhost:9000`) accessible? If so, can you click on and see the Genesis File? +- Do you even need a local ledger? If not, you can use a public sandbox ledger, + such as the [Dev Greenlight ledger](), likely by just prefacing your ACA-Py + command with `LEDGER_URL=http://dev.greenlight.bcovrin.vonx.io`. For example, + when running the Alice-Faber demo in the [demo](demo) folder, you can run (for + example), the Faber agent using the command: + `LEDGER_URL=http://dev.greenlight.bcovrin.vonx.io ./run_demo faber` + +### Any Firewalls + +Do you have any firewalls in play that might be blocking the ports that are used by the ledger, notably 9701-9708? To access a ledger +the ACA-Py instance must be able to get to those ports of the ledger, regardless if the ledger is local or remote. + +## Damaged, Unpublishable Revocation Registry + +We have discovered that in the ACA-Py AnonCreds implementation, it is possible +to get into a state where the publishing of updates to a Revocation Registry +(RevReg) is impossible. This can happen where ACA-Py starts to publish an update +to the RevReg, but the write transaction to the Hyperledger Indy ledger fails +for some reason. When a credential revocation is published, aca-py (via indy-sdk +or askar/credx) updates the revocation state in the wallet as well as on the +ledger. The revocation state is dependant on whatever the previous revocation +state is/was, so if the ledger and wallet are mis-matched the publish will fail. +(Andrew/s PR # 1804 (merged) should mitigate but probably won't completely +eliminate this from happening). + +For example, in case we've seen, the write RevRegEntry transaction failed at the +ledger because there was a problem with accepting the TAA (Transaction Author +Agreement). Once the error occurred, the RevReg state held by the ACA-Py agent, +and the RevReg state on the ledger were different. Even after the ability to +write to the ledger was restored, the RevReg could still not be published +because of the differences in the RevReg state. Such a situation can now be +corrected, as follows: + +To address this issue, some new endpoints were added to ACA-Py in Release 0.7.4, +as follows: + +- GET /revocation/registry//issued - counts of the number of issued/revoked + within a registry +- GET /revocation/registry//issued/details - details of all credentials + issued/revoked within a registry +- GET /revocation/registry//issued/indy_recs - calculated rev_reg_delta from + the ledger + - This is used to compare ledger revoked vs wallet revoked credentials, which + is essentially the state of the RevReg on the ledger and in ACA-Py. Where + there is a difference, we have an error. +- PUT /revocation/registry//fix-revocation-entry-state - publish an update + to the RevReg state on the ledger to bring it into alignment with what is in + the ACA-Py instance. + - There is a boolean parameter (`apply_ledger_update`) to control whether the + ledger entry actually gets published so, if you are so inclined, you can + call the endpoint to see what the transaction would be, before you actually + try to do a ledger update. This will return: + - `rev_reg_delta` - same as the ".../indy_recs" endpoint + - `accum_calculated` - transaction to write to ledger + - `accum_fixed` - If `apply_ledger_update`, the transaction actually written + to the ledger + +Note that there is (currently) a backlog item to prevent the wallet and ledger +from getting out of sync (e.g. don't update the ACA-Py RevReg state if the +ledger write fails), but even after that change is made, having this ability +will be retained for use if needed. + +We originally ran into this due to the TAA acceptance getting lost when +switching to multi-ledger (as described +[here](https://github.com/hyperledger/aries-cloudagent-python/blob/main/Multiledger.md#a-special-warning-for-taa-acceptance). +Note that this is one reason how this "out of sync" scenario can occur, but +there may be others. + +We add an integration test that demonstrates/tests this issue [here](https://github.com/hyperledger/aries-cloudagent-python/blob/main/demo/features/taa-txn-author-acceptance.feature#L67). + +To run the scenario either manually or using the integration tests, you can do the following: + +- Start von-network in TAA mode: + - `./manage start --taa-sample --logs` +- Start the tails server as usual: + - `./manage start --logs` +- To run the scenario manually, start faber and let the agent know it needs to TAA-accept before doing any ledger writes: + - `./run_demo faber --revocation --taa-accept`, and then you can run through all the transactions using the Swagger page. +- To run the scenario via an integration test, run: + - `./run_bdd -t @taa_required` From b69cc56e90b900b49cc8f719b8956a47a1a81e80 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Thu, 16 Jun 2022 12:07:33 -0700 Subject: [PATCH 288/872] Add an intro to the troubleshooting document. Signed-off-by: Stephen Curran --- Troubleshooting.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Troubleshooting.md b/Troubleshooting.md index 776b2d63fa..3a526473c0 100644 --- a/Troubleshooting.md +++ b/Troubleshooting.md @@ -1,5 +1,14 @@ # Troubleshooting Aries Cloud Agent Python +This document contains some troubleshooting information that contributors to the +community think may be helpful. Most of the content here assumes the reader has +gotten started with ACA-Py and has arrived here because of an issue that came up +in their use of ACA-Py. + +Contributions (via pull request) to this document are welcome. Topics added here +will mostly come from reported issues that contributors think would be helpful +to the larger community. + ## Table of Contents - [Unable to Connect to Ledger](#unable-to-connect-to-ledger) From 115b2e75a99a19a18e914b0b071181386731b93a Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Tue, 21 Jun 2022 10:05:51 -0700 Subject: [PATCH 289/872] Updated endpoints containing id to be backtick quoted Signed-off-by: Stephen Curran --- Troubleshooting.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Troubleshooting.md b/Troubleshooting.md index 3a526473c0..15660e6b45 100644 --- a/Troubleshooting.md +++ b/Troubleshooting.md @@ -63,16 +63,16 @@ corrected, as follows: To address this issue, some new endpoints were added to ACA-Py in Release 0.7.4, as follows: -- GET /revocation/registry//issued - counts of the number of issued/revoked +- GET `/revocation/registry//issued` - counts of the number of issued/revoked within a registry -- GET /revocation/registry//issued/details - details of all credentials +- GET `/revocation/registry//issued/details` - details of all credentials issued/revoked within a registry -- GET /revocation/registry//issued/indy_recs - calculated rev_reg_delta from +- GET `/revocation/registry//issued/indy_recs` - calculated rev_reg_delta from the ledger - This is used to compare ledger revoked vs wallet revoked credentials, which is essentially the state of the RevReg on the ledger and in ACA-Py. Where there is a difference, we have an error. -- PUT /revocation/registry//fix-revocation-entry-state - publish an update +- PUT `/revocation/registry//fix-revocation-entry-state` - publish an update to the RevReg state on the ledger to bring it into alignment with what is in the ACA-Py instance. - There is a boolean parameter (`apply_ledger_update`) to control whether the From 435606092c93a25e6bf9f68e85cb98726215d78f Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Tue, 21 Jun 2022 08:57:41 -0700 Subject: [PATCH 290/872] update pyjwt to 2.4 Signed-off-by: Andrew Whitehead --- aries_cloudagent/multitenant/base.py | 2 +- aries_cloudagent/multitenant/tests/test_base.py | 16 ++++++++-------- requirements.txt | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/aries_cloudagent/multitenant/base.py b/aries_cloudagent/multitenant/base.py index 18fe63a9c5..77decdbb80 100644 --- a/aries_cloudagent/multitenant/base.py +++ b/aries_cloudagent/multitenant/base.py @@ -366,7 +366,7 @@ async def create_auth_token( jwt_payload["wallet_key"] = wallet_key - token = jwt.encode(jwt_payload, jwt_secret, algorithm="HS256").decode() + token = jwt.encode(jwt_payload, jwt_secret, algorithm="HS256") # Store iat for verification later on wallet_record.jwt_iat = iat diff --git a/aries_cloudagent/multitenant/tests/test_base.py b/aries_cloudagent/multitenant/tests/test_base.py index 5d858dd6f9..f20605e9cb 100644 --- a/aries_cloudagent/multitenant/tests/test_base.py +++ b/aries_cloudagent/multitenant/tests/test_base.py @@ -430,7 +430,7 @@ async def test_create_auth_token_managed(self): expected_token = jwt.encode( {"wallet_id": wallet_record.wallet_id, "iat": iat}, "very_secret_jwt" - ).decode() + ) with async_mock.patch.object(test_module, "datetime") as mock_datetime: mock_datetime.utcnow.return_value = utc_now @@ -459,7 +459,7 @@ async def test_create_auth_token_unmanaged(self): "wallet_key": "test_key", }, "very_secret_jwt", - ).decode() + ) with async_mock.patch.object(test_module, "datetime") as mock_datetime: mock_datetime.utcnow.return_value = utc_now @@ -471,7 +471,7 @@ async def test_create_auth_token_unmanaged(self): async def test_get_profile_for_token_invalid_token_raises(self): self.profile.settings["multitenant.jwt_secret"] = "very_secret_jwt" - token = jwt.encode({"wallet_id": "test"}, "some_random_key").decode() + token = jwt.encode({"wallet_id": "test"}, "some_random_key") with self.assertRaises(jwt.InvalidTokenError): await self.manager.get_profile_for_token(self.profile.context, token) @@ -486,7 +486,7 @@ async def test_get_profile_for_token_wallet_key_missing_raises(self): await wallet_record.save(session) token = jwt.encode( {"wallet_id": wallet_record.wallet_id}, "very_secret_jwt", algorithm="HS256" - ).decode() + ) with self.assertRaises(WalletKeyMissingError): await self.manager.get_profile_for_token(self.profile.context, token) @@ -503,7 +503,7 @@ async def test_get_profile_for_token_managed_wallet_no_iat(self): token = jwt.encode( {"wallet_id": wallet_record.wallet_id}, "very_secret_jwt", algorithm="HS256" - ).decode() + ) with async_mock.patch.object( BaseMultitenantManager, "get_wallet_profile" @@ -540,7 +540,7 @@ async def test_get_profile_for_token_managed_wallet_iat(self): {"wallet_id": wallet_record.wallet_id, "iat": iat}, "very_secret_jwt", algorithm="HS256", - ).decode() + ) with async_mock.patch.object( BaseMultitenantManager, "get_wallet_profile" @@ -578,7 +578,7 @@ async def test_get_profile_for_token_managed_wallet_x_iat_no_match(self): {"wallet_id": wallet_record.wallet_id, "iat": 200}, "very_secret_jwt", algorithm="HS256", - ).decode() + ) with async_mock.patch.object( BaseMultitenantManager, "get_wallet_profile" @@ -614,7 +614,7 @@ async def test_get_profile_for_token_unmanaged_wallet(self): {"wallet_id": wallet_record.wallet_id, "wallet_key": "wallet_key"}, "very_secret_jwt", algorithm="HS256", - ).decode() + ) with async_mock.patch.object( BaseMultitenantManager, "get_wallet_profile" diff --git a/requirements.txt b/requirements.txt index 3a7518e3b3..76ed1bf719 100644 --- a/requirements.txt +++ b/requirements.txt @@ -19,7 +19,7 @@ packaging~=20.4 pyld~=2.0.3 pyyaml~=5.4.0 ConfigArgParse~=1.5.3 -pyjwt~=1.7.1 +pyjwt~=2.4.0 pydid~=0.3.3 jsonpath_ng==1.5.2 pytz~=2021.1 From ccf6f400af69a60842f16f6e6928ca803d82c0b7 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Tue, 21 Jun 2022 15:01:29 -0700 Subject: [PATCH 291/872] Update changelog and version for 0.7.4-rc4 Signed-off-by: Stephen Curran --- CHANGELOG.md | 47 ++++++++++++++++++++++++++++++------- aries_cloudagent/version.py | 2 +- open-api/openapi.json | 2 +- 3 files changed, 40 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee85921b12..adb90847dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,21 @@ -# 0.7.4-RC3 +# 0.7.4-RC4 + +## June 21, 2022 + +**NOTE:** 0.7.4-rc4 contains a fix for a revocation-related issue introduced in +0.7.4-rc3. We recommend updating to 0.7.4-rc4 immediately if you have been using +0.7.4-rc3. + +- See issue: [\#1823](https://github.com/hyperledger/aries-cloudagent-python/issues/1823) +- See Pull Request: [\#1827](https://github.com/hyperledger/aries-cloudagent-python/pull/1827) The 0.7.4 release consists largely of internal fixes to ACA-Py, big increases in performance resulting from the now recommended use of [Aries -Askar](https://github.com/bcgov/aries-askar) instead of the Indy-SDK, plus a few -enhancements. There have been a lot of groups exercising ACA-Py and the updates -made in this release are a reflection of those efforts. We have PRs that have -been contributed by 17 different people, which is likely a record for a single -ACA-Py release. +Askar](https://github.com/bcgov/aries-askar) instead of the Indy-SDK, tools for +dealing with revocation-related issues, plus a number of enhancements. There have been +a lot of groups exercising ACA-Py and the updates made in this release are a +reflection of those efforts. We have PRs that have been contributed by 20 +different people, which is likely a record for a single ACA-Py release. The largest enhancement is in the area of the Hyperledger Indy endorser, enabling an instance of ACA-Py to act as an Endorser for Indy authors needed @@ -61,8 +70,6 @@ As well there were a number of internal fixes, dependency updates, documentation and demo changes, developer tools and release management updates. All the usual stuff needed for a growing codebase. -## June 16, 2022 - - Hyperledger Indy Endorser related updates: - Fix order of operations connecting faber to endorser [\#1716](https://github.com/hyperledger/aries-cloudagent-python/pull/1716) ([ianco](https://github.com/ianco)) - Endorser support for updating DID endpoints on ledger [\#1696](https://github.com/hyperledger/aries-cloudagent-python/pull/1696) ([frostyfrog](https://github.com/frostyfrog)) @@ -90,6 +97,7 @@ stuff needed for a growing codebase. - Credential Revocation and Tails File Handling - Additional endpoints to get revocation details and fix "published" status [\#1783](https://github.com/hyperledger/aries-cloudagent-python/pull/1783) ([ianco](https://github.com/ianco)) + - Fix IssuerCredRevRecord state update on revocation publish [\#1827](https://github.com/hyperledger/aries-cloudagent-python/pull/1827) ([andrewwhitehead](https://github.com/andrewwhitehead)) - Fix put_file when the server returns a redirect [\#1808](https://github.com/hyperledger/aries-cloudagent-python/pull/1808) ([andrewwhitehead](https://github.com/andrewwhitehead)) - Adjust revocation registry update procedure to shorten transactions [\#1804](https://github.com/hyperledger/aries-cloudagent-python/pull/1804) ([andrewwhitehead](https://github.com/andrewwhitehead)) - fix: Resolve Revocation Notification environment variable name collision [\#1751](https://github.com/hyperledger/aries-cloudagent-python/pull/1751) ([frostyfrog](https://github.com/frostyfrog)) @@ -101,6 +109,9 @@ stuff needed for a growing codebase. - Feat/revocation notification v2 [\#1734](https://github.com/hyperledger/aries-cloudagent-python/pull/1734) ([frostyfrog](https://github.com/frostyfrog)) - Issue Credential, Present Proof updates/fixes + - Fix: Present Proof v2 - check_proof_vs_proposal update to support proof request with restrictions [\#1820](https://github.com/hyperledger/aries-cloudagent-python/pull/1820) ([shaangill025](https://github.com/shaangill025)) + - Fix: present-proof v1 send-proposal flow [\#1811](https://github.com/hyperledger/aries-cloudagent-python/pull/1811) ([shaangill025](https://github.com/shaangill025)) + - Prover - verification outcome from presentation ack message [\#1757](https://github.com/hyperledger/aries-cloudagent-python/pull/1757) ([shaangill025](https://github.com/shaangill025)) - feat: support connectionless exchange [\#1710](https://github.com/hyperledger/aries-cloudagent-python/pull/1710) ([TimoGlastra](https://github.com/TimoGlastra)) - Fix: DIF proof proposal when creating bound presentation request \[Issue\#1687\] [\#1690](https://github.com/hyperledger/aries-cloudagent-python/pull/1690) ([shaangill025](https://github.com/shaangill025)) - Fix DIF PresExch and OOB request_attach delete unused connection [\#1676](https://github.com/hyperledger/aries-cloudagent-python/pull/1676) ([shaangill025](https://github.com/shaangill025)) @@ -124,6 +135,8 @@ stuff needed for a growing codebase. - feat: create new JWT tokens and invalidate older for multitenancy [\#1725](https://github.com/hyperledger/aries-cloudagent-python/pull/1725) ([TimoGlastra](https://github.com/TimoGlastra)) - Dependencies and internal code updates/fixes + - Update pyjwt to 2.4 [\#1829](https://github.com/hyperledger/aries-cloudagent-python/pull/1829) ([andrewwhitehead](https://github.com/andrewwhitehead)) + - Fix external Outbound Transport loading code [\#1812](https://github.com/hyperledger/aries-cloudagent-python/pull/1812) ([frostyfrog](https://github.com/frostyfrog)) - Fix iteration over key list, update Askar to 0.2.5 [\#1740](https://github.com/hyperledger/aries-cloudagent-python/pull/1740) ([andrewwhitehead](https://github.com/andrewwhitehead)) - Fix: update IndyLedgerRequestsExecutor logic - multitenancy and basic base wallet type [\#1700](https://github.com/hyperledger/aries-cloudagent-python/pull/1700) ([shaangill025](https://github.com/shaangill025)) - Move database operations inside the session context [\#1633](https://github.com/hyperledger/aries-cloudagent-python/pull/1633) ([acuderman](https://github.com/acuderman)) @@ -139,6 +152,15 @@ stuff needed for a growing codebase. - Replace async workaround within document loader [\#1774](https://github.com/hyperledger/aries-cloudagent-python/pull/1774) ([frostyfrog](https://github.com/frostyfrog)) - Documentation and Demo Updates + - Changelog, version and ReadTheDocs updates for 0.7.4-rc3 release [\#1817](https://github.com/hyperledger/aries-cloudagent-python/pull/1817) ([swcurran](https://github.com/swcurran)) + - 0.7.4-rc2 update [\#1771](https://github.com/hyperledger/aries-cloudagent-python/pull/1771) ([swcurran](https://github.com/swcurran)) + - Some ReadTheDocs File updates [\#1770](https://github.com/hyperledger/aries-cloudagent-python/pull/1770) ([swcurran](https://github.com/swcurran)) + - 0.7.4-RC1 Changelog intro paragraph - fix copy/paste error [\#1753](https://github.com/hyperledger/aries-cloudagent-python/pull/1753) ([swcurran](https://github.com/swcurran)) + - Fixing the intro paragraph and heading in the changelog of this 0.7.4RC1 [\#1752](https://github.com/hyperledger/aries-cloudagent-python/pull/1752) ([swcurran](https://github.com/swcurran)) + - Updates to Changelog for 0.7.4. RC1 release [\#1747](https://github.com/hyperledger/aries-cloudagent-python/pull/1747) ([swcurran](https://github.com/swcurran)) + - Prep for adding the 0.7.4-rc0 tag [\#1722](https://github.com/hyperledger/aries-cloudagent-python/pull/1722) ([swcurran](https://github.com/swcurran)) + - Add troubleshooting document, include initial examples - ledger connection, out-of-sync RevReg [\#1818](https://github.com/hyperledger/aries-cloudagent-python/pull/1818) ([swcurran](https://github.com/swcurran)) + - Update POST /present-proof/send-request to POST /present-proof-2.0/send-request [\#1824](https://github.com/hyperledger/aries-cloudagent-python/pull/1824) ([lineko](https://github.com/lineko)) - Fetch from --genesis-url likely to fail in composed container [\#1746](https://github.com/hyperledger/aries-cloudagent-python/pull/1739) ([tdiesler](https://github.com/tdiesler)) - Fixes logic for web hook formatter in Faber demo [\#1739](https://github.com/hyperledger/aries-cloudagent-python/pull/1739) ([amanji](https://github.com/amanji)) - Multitenancy Docs Update [\#1706](https://github.com/hyperledger/aries-cloudagent-python/pull/1706) ([MonolithicMonk](https://github.com/MonolithicMonk)) @@ -155,7 +177,14 @@ stuff needed for a growing codebase. - Add pre-commit as optional developer tool [\#1671](https://github.com/hyperledger/aries-cloudagent-python/pull/1671) ([dbluhm](https://github.com/dbluhm)) - run_docker start - pass environment variables [\#1715](https://github.com/hyperledger/aries-cloudagent-python/pull/1715) ([shaangill025](https://github.com/shaangill025)) -- Release management-related updates +- Release management pull requests + - Changelog, version and ReadTheDocs updates for 0.7.4-rc3 release [\#1817](https://github.com/hyperledger/aries-cloudagent-python/pull/1817) ([swcurran](https://github.com/swcurran)) + - 0.7.4-rc2 update [\#1771](https://github.com/hyperledger/aries-cloudagent-python/pull/1771) ([swcurran](https://github.com/swcurran)) + - Some ReadTheDocs File updates [\#1770](https://github.com/hyperledger/aries-cloudagent-python/pull/1770) ([swcurran](https://github.com/swcurran)) + - 0.7.4-RC1 Changelog intro paragraph - fix copy/paste error [\#1753](https://github.com/hyperledger/aries-cloudagent-python/pull/1753) ([swcurran](https://github.com/swcurran)) + - Fixing the intro paragraph and heading in the changelog of this 0.7.4RC1 [\#1752](https://github.com/hyperledger/aries-cloudagent-python/pull/1752) ([swcurran](https://github.com/swcurran)) + - Updates to Changelog for 0.7.4. RC1 release [\#1747](https://github.com/hyperledger/aries-cloudagent-python/pull/1747) ([swcurran](https://github.com/swcurran)) + - Prep for adding the 0.7.4-rc0 tag [\#1722](https://github.com/hyperledger/aries-cloudagent-python/pull/1722) ([swcurran](https://github.com/swcurran)) - Added missed new module -- upgrade -- to the RTD generated docs [\#1593](https://github.com/hyperledger/aries-cloudagent-python/pull/1593) ([swcurran](https://github.com/swcurran)) - Doh....update the date in the Changelog for 0.7.3 [\#1592](https://github.com/hyperledger/aries-cloudagent-python/pull/1592) ([swcurran](https://github.com/swcurran)) diff --git a/aries_cloudagent/version.py b/aries_cloudagent/version.py index 199857eac7..35c49c1091 100644 --- a/aries_cloudagent/version.py +++ b/aries_cloudagent/version.py @@ -1,4 +1,4 @@ """Library version information.""" -__version__ = "0.7.4-rc3" +__version__ = "0.7.4-rc4" RECORD_TYPE_ACAPY_VERSION = "acapy_version" diff --git a/open-api/openapi.json b/open-api/openapi.json index 4548dd1e29..5988f2e0ef 100644 --- a/open-api/openapi.json +++ b/open-api/openapi.json @@ -1,7 +1,7 @@ { "swagger" : "2.0", "info" : { - "version" : "v0.7.4-rc3", + "version" : "v0.7.4-rc4", "title" : "Aries Cloud Agent" }, "tags" : [ { From cebaed6822e2024fed652fcebaf700eb125ea8a0 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Tue, 21 Jun 2022 15:03:12 -0700 Subject: [PATCH 292/872] Add rc4 PR to changelog Signed-off-by: Stephen Curran --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index adb90847dc..747243e778 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -178,6 +178,7 @@ stuff needed for a growing codebase. - run_docker start - pass environment variables [\#1715](https://github.com/hyperledger/aries-cloudagent-python/pull/1715) ([shaangill025](https://github.com/shaangill025)) - Release management pull requests + - Update changelog and version for 0.7.4-rc4 [\#1830](https://github.com/hyperledger/aries-cloudagent-python/pull/1830) ([swcurran](https://github.com/swcurran)) - Changelog, version and ReadTheDocs updates for 0.7.4-rc3 release [\#1817](https://github.com/hyperledger/aries-cloudagent-python/pull/1817) ([swcurran](https://github.com/swcurran)) - 0.7.4-rc2 update [\#1771](https://github.com/hyperledger/aries-cloudagent-python/pull/1771) ([swcurran](https://github.com/swcurran)) - Some ReadTheDocs File updates [\#1770](https://github.com/hyperledger/aries-cloudagent-python/pull/1770) ([swcurran](https://github.com/swcurran)) From 4cfd012d7b2adfe8e6df18ef8ba1515f9cffa038 Mon Sep 17 00:00:00 2001 From: Ry Jones Date: Tue, 21 Jun 2022 14:36:24 -0700 Subject: [PATCH 293/872] Create pip-audit.yml Signed-off-by: Ry Jones --- .github/workflows/pip-audit.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .github/workflows/pip-audit.yml diff --git a/.github/workflows/pip-audit.yml b/.github/workflows/pip-audit.yml new file mode 100644 index 0000000000..4d81158b21 --- /dev/null +++ b/.github/workflows/pip-audit.yml @@ -0,0 +1,16 @@ +name: pip-audit + +on: + workflow_dispatch: + +permissions: + contents: read + +jobs: + selftest: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: install + run: python -m pip install . + - uses: trailofbits/gh-action-pip-audit@v0.0.4 From e180aa0431f1418511add34256d30ceed36fad82 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Wed, 22 Jun 2022 11:44:09 -0400 Subject: [PATCH 294/872] feat: include thread id in keylist update recv webhooks Signed-off-by: Daniel Bluhm --- .../v1_0/handlers/keylist_update_handler.py | 1 + .../keylist_update_response_handler.py | 3 ++ .../test_keylist_update_response_handler.py | 7 ++- .../coordinate_mediation/v1_0/manager.py | 7 ++- .../v1_0/tests/test_mediation_manager.py | 51 ++++++++++++------- 5 files changed, 49 insertions(+), 20 deletions(-) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/keylist_update_handler.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/keylist_update_handler.py index 8e8f8922a5..20c63a5e15 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/keylist_update_handler.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/keylist_update_handler.py @@ -32,6 +32,7 @@ async def handle(self, context: RequestContext, responder: BaseResponder): session, context.connection_record.connection_id ) response = await mgr.update_keylist(record, updates=context.message.updates) + response.assign_thread_from(context.message) await responder.send_reply(response) except (StorageNotFoundError, MediationNotGrantedError): reply = CMProblemReport( diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/keylist_update_response_handler.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/keylist_update_response_handler.py index bcca040072..79e2b56aec 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/keylist_update_response_handler.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/keylist_update_response_handler.py @@ -25,3 +25,6 @@ async def handle(self, context: RequestContext, responder: BaseResponder): await mgr.store_update_results( context.connection_record.connection_id, context.message.updated ) + await mgr.notify_keylist_updated( + context.connection_record.connection_id, context.message + ) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/tests/test_keylist_update_response_handler.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/tests/test_keylist_update_response_handler.py index c9e3bb868f..75218935d0 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/tests/test_keylist_update_response_handler.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/tests/test_keylist_update_response_handler.py @@ -46,6 +46,9 @@ async def test_handler(self): handler, responder = KeylistUpdateResponseHandler(), MockResponder() with async_mock.patch.object( MediationManager, "store_update_results" - ) as mock_method: + ) as mock_store, async_mock.patch.object( + MediationManager, "notify_keylist_updated" + ) as mock_notify: await handler.handle(self.context, responder) - mock_method.assert_called_once_with(TEST_CONN_ID, self.updated) + mock_store.assert_called_once_with(TEST_CONN_ID, self.updated) + mock_notify.assert_called_once_with(TEST_CONN_ID, self.context.message) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py index 6da7845a94..195dd5fd5a 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py @@ -596,11 +596,16 @@ async def store_update_results( for record_for_removal in to_remove: await record_for_removal.delete_record(session) + async def notify_keylist_updated( + self, connection_id: str, response: KeylistUpdateResponse + ): + """Notify of keylist update response received.""" await self._profile.notify( self.KEYLIST_UPDATED_EVENT, { "connection_id": connection_id, - "updated": [update.serialize() for update in results], + "thread_id": response._thread_id, + "updated": [update.serialize() for update in response.updated], }, ) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py index 3f834f589d..f3a03c0a30 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py @@ -2,21 +2,15 @@ import logging from typing import AsyncIterable, Iterable -import pytest - from asynctest import mock as async_mock +import pytest -from .....core.profile import Profile, ProfileSession -from .....core.in_memory import InMemoryProfile +from .. import manager as test_module from .....core.event_bus import EventBus, MockEventBus -from .....connections.models.conn_record import ConnRecord -from .....messaging.request_context import RequestContext +from .....core.in_memory import InMemoryProfile +from .....core.profile import Profile, ProfileSession from .....storage.error import StorageNotFoundError -from .....transport.inbound.receipt import MessageReceipt - from ....routing.v1_0.models.route_record import RouteRecord - -from .. import manager as test_module from ..manager import ( MediationAlreadyExists, MediationManager, @@ -25,12 +19,14 @@ ) from ..messages.inner.keylist_update_rule import KeylistUpdateRule from ..messages.inner.keylist_updated import KeylistUpdated +from ..messages.keylist_update_response import KeylistUpdateResponse from ..messages.mediate_deny import MediationDeny from ..messages.mediate_grant import MediationGrant from ..messages.mediate_request import MediationRequest from ..models.mediation_record import MediationRecord TEST_CONN_ID = "conn-id" +TEST_THREAD_ID = "thread-id" TEST_ENDPOINT = "https://example.com" TEST_VERKEY = "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx" TEST_ROUTE_VERKEY = "9WCgWKUaAJj3VWxxtzvvMQN3AoFxoBtBDo9ntwJnVVCC" @@ -373,7 +369,6 @@ async def test_store_update_results( self, session: ProfileSession, manager: MediationManager, - mock_event_bus: MockEventBus, ): """test_store_update_results.""" await RouteRecord( @@ -394,12 +389,6 @@ async def test_store_update_results( ), ] await manager.store_update_results(TEST_CONN_ID, results) - assert mock_event_bus.events - assert mock_event_bus.events[0][1].topic == manager.KEYLIST_UPDATED_EVENT - assert mock_event_bus.events[0][1].payload == { - "connection_id": TEST_CONN_ID, - "updated": [result.serialize() for result in results], - } routes = await RouteRecord.query(session) assert len(routes) == 1 @@ -484,6 +473,34 @@ async def test_store_update_results_errors(self, caplog, manager): assert "server_error" in caplog.text print(caplog.text) + async def test_notify_keylist_updated( + self, manager: MediationManager, mock_event_bus: MockEventBus + ): + """test notify_keylist_updated.""" + response = KeylistUpdateResponse( + updated=[ + KeylistUpdated( + recipient_key=TEST_ROUTE_VERKEY, + action=KeylistUpdateRule.RULE_ADD, + result=KeylistUpdated.RESULT_SUCCESS, + ), + KeylistUpdated( + recipient_key=TEST_VERKEY, + action=KeylistUpdateRule.RULE_REMOVE, + result=KeylistUpdated.RESULT_SUCCESS, + ), + ], + ) + response.assign_thread_id(TEST_THREAD_ID) + await manager.notify_keylist_updated(TEST_CONN_ID, response) + assert mock_event_bus.events + assert mock_event_bus.events[0][1].topic == manager.KEYLIST_UPDATED_EVENT + assert mock_event_bus.events[0][1].payload == { + "connection_id": TEST_CONN_ID, + "thread_id": TEST_THREAD_ID, + "updated": [result.serialize() for result in response.updated], + } + async def test_get_my_keylist(self, session, manager): """test_get_my_keylist.""" await RouteRecord( From f79d77476e4cf37b4d66d0f7a0bcc74818945bcb Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Wed, 22 Jun 2022 12:50:54 -0400 Subject: [PATCH 295/872] feat: improve typing of Settings classes Signed-off-by: Daniel Bluhm --- aries_cloudagent/config/base.py | 24 ++++++++++++++++++------ aries_cloudagent/config/base_context.py | 4 ++-- aries_cloudagent/config/settings.py | 10 +++++----- 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/aries_cloudagent/config/base.py b/aries_cloudagent/config/base.py index 92c48f4756..05e61720a9 100644 --- a/aries_cloudagent/config/base.py +++ b/aries_cloudagent/config/base.py @@ -1,7 +1,7 @@ """Configuration base classes.""" from abc import ABC, abstractmethod -from typing import Mapping, Optional, Type, TypeVar +from typing import Any, Iterator, Mapping, Optional, Type, TypeVar from ..core.error import BaseError @@ -16,7 +16,7 @@ class SettingsError(ConfigError): """The base exception raised by `BaseSettings` implementations.""" -class BaseSettings(Mapping[str, object]): +class BaseSettings(Mapping[str, Any]): """Base settings class.""" @abstractmethod @@ -42,6 +42,10 @@ def get_bool(self, *var_names, default=None) -> bool: value = self.get_value(*var_names, default) if value is not None: value = bool(value and value not in ("false", "False", "0")) + + if value is None: + raise KeyError(f"{var_names} not found in settings") + return value def get_int(self, *var_names, default=None) -> int: @@ -54,6 +58,10 @@ def get_int(self, *var_names, default=None) -> int: value = self.get_value(*var_names, default) if value is not None: value = int(value) + + if value is None: + raise KeyError(f"{var_names} not found in settings") + return value def get_str(self, *var_names, default=None) -> str: @@ -66,10 +74,14 @@ def get_str(self, *var_names, default=None) -> str: value = self.get_value(*var_names, default=default) if value is not None: value = str(value) + + if value is None: + raise KeyError(f"{var_names} not found in settings") + return value @abstractmethod - def __iter__(self): + def __iter__(self) -> Iterator: """Iterate settings keys.""" def __getitem__(self, index): @@ -91,7 +103,7 @@ def copy(self) -> "BaseSettings": """Produce a copy of the settings instance.""" @abstractmethod - def extend(self, other: Mapping[str, object]) -> "BaseSettings": + def extend(self, other: Mapping[str, Any]) -> "BaseSettings": """Merge another mapping to produce a new settings instance.""" def __repr__(self) -> str: @@ -111,7 +123,7 @@ class BaseInjector(ABC): def inject( self, base_cls: Type[InjectType], - settings: Mapping[str, object] = None, + settings: Optional[Mapping[str, Any]] = None, ) -> InjectType: """ Get the provided instance of a given class identifier. @@ -129,7 +141,7 @@ def inject( def inject_or( self, base_cls: Type[InjectType], - settings: Mapping[str, object] = None, + settings: Optional[Mapping[str, Any]] = None, default: Optional[InjectType] = None, ) -> Optional[InjectType]: """ diff --git a/aries_cloudagent/config/base_context.py b/aries_cloudagent/config/base_context.py index 4fde34759d..788a91940f 100644 --- a/aries_cloudagent/config/base_context.py +++ b/aries_cloudagent/config/base_context.py @@ -1,7 +1,7 @@ """Base injection context builder classes.""" from abc import ABC, abstractmethod -from typing import Mapping +from typing import Any, Mapping, Optional from .injection_context import InjectionContext from .settings import Settings @@ -10,7 +10,7 @@ class ContextBuilder(ABC): """Base injection context builder class.""" - def __init__(self, settings: Mapping[str, object] = None): + def __init__(self, settings: Optional[Mapping[str, Any]] = None): """ Initialize an instance of the context builder. diff --git a/aries_cloudagent/config/settings.py b/aries_cloudagent/config/settings.py index cc20aeb6f1..291182b68a 100644 --- a/aries_cloudagent/config/settings.py +++ b/aries_cloudagent/config/settings.py @@ -1,14 +1,14 @@ """Settings implementation.""" -from typing import Mapping +from typing import Any, Mapping, MutableMapping, Optional from .base import BaseSettings -class Settings(BaseSettings): +class Settings(BaseSettings, MutableMapping[str, Any]): """Mutable settings implementation.""" - def __init__(self, values: Mapping[str, object] = None): + def __init__(self, values: Optional[Mapping[str, Any]] = None): """Initialize a Settings object. Args: @@ -90,12 +90,12 @@ def copy(self) -> BaseSettings: """Produce a copy of the settings instance.""" return Settings(self._values) - def extend(self, other: Mapping[str, object]) -> BaseSettings: + def extend(self, other: Mapping[str, Any]) -> BaseSettings: """Merge another settings instance to produce a new instance.""" vals = self._values.copy() vals.update(other) return Settings(vals) - def update(self, other: Mapping[str, object]): + def update(self, other: Mapping[str, Any]): """Update the settings in place.""" self._values.update(other) From 707ec093b09f37732793a027e8a445687c8ae928 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Wed, 22 Jun 2022 12:52:11 -0400 Subject: [PATCH 296/872] feat: add plugin settings object and helpers Signed-off-by: Daniel Bluhm --- aries_cloudagent/config/argparse.py | 10 ++- aries_cloudagent/config/base.py | 5 ++ aries_cloudagent/config/plugin_settings.py | 77 +++++++++++++++++++ .../config/tests/test_settings.py | 27 +++++++ 4 files changed, 115 insertions(+), 4 deletions(-) create mode 100644 aries_cloudagent/config/plugin_settings.py diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index 2c2ac13dd4..fa0ef66255 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -18,6 +18,8 @@ from .error import ArgsParseError from .util import BoundedInt, ByteSize +from .plugin_settings import PLUGIN_CONFIG_KEY + CAT_PROVISION = "general" CAT_START = "start" CAT_UPGRADE = "upgrade" @@ -630,17 +632,17 @@ def get_settings(self, args: Namespace) -> dict: if args.plugin_config: with open(args.plugin_config, "r") as stream: - settings["plugin_config"] = yaml.safe_load(stream) + settings[PLUGIN_CONFIG_KEY] = yaml.safe_load(stream) if args.plugin_config_values: - if "plugin_config" not in settings: - settings["plugin_config"] = {} + if PLUGIN_CONFIG_KEY not in settings: + settings[PLUGIN_CONFIG_KEY] = {} for value_str in chain(*args.plugin_config_values): key, value = value_str.split("=", maxsplit=1) value = yaml.safe_load(value) deepmerge.always_merger.merge( - settings["plugin_config"], + settings[PLUGIN_CONFIG_KEY], reduce(lambda v, k: {k: v}, key.split(".")[::-1], value), ) diff --git a/aries_cloudagent/config/base.py b/aries_cloudagent/config/base.py index 05e61720a9..3ba405c085 100644 --- a/aries_cloudagent/config/base.py +++ b/aries_cloudagent/config/base.py @@ -4,6 +4,7 @@ from typing import Any, Iterator, Mapping, Optional, Type, TypeVar from ..core.error import BaseError +from .plugin_settings import PluginSettings InjectType = TypeVar("InjectType") @@ -111,6 +112,10 @@ def __repr__(self) -> str: items = ("{}={}".format(k, self[k]) for k in self) return "<{}({})>".format(self.__class__.__name__, ", ".join(items)) + def for_plugin(self, plugin: str, default: Optional[Mapping[str, Any]] = None): + """Retrieve settings for plugin.""" + return PluginSettings.for_plugin(self, plugin, default) + class InjectionError(ConfigError): """The base exception raised by Injector and Provider implementations.""" diff --git a/aries_cloudagent/config/plugin_settings.py b/aries_cloudagent/config/plugin_settings.py new file mode 100644 index 0000000000..278ccf8de4 --- /dev/null +++ b/aries_cloudagent/config/plugin_settings.py @@ -0,0 +1,77 @@ +"""Settings implementation for plugins.""" + +from typing import Any, Mapping, Optional + +from .base import BaseSettings + + +PLUGIN_CONFIG_KEY = "plugin_config" + + +class PluginSettings(BaseSettings): + """Retrieve immutable settings for plugins. + + Plugin settings should be retrieved by calling: + + PluginSettings.for_plugin(settings, "my_plugin", {"default": "values"}) + + This will extract the PLUGIN_CONFIG_KEY in "settings" and return a new + PluginSettings instance. + """ + + def __init__(self, values: Optional[Mapping[str, Any]] = None): + """Initialize a Settings object. + + Args: + values: An optional dictionary of settings + """ + self._values = {} + if values: + self._values.update(values) + + def __contains__(self, index): + """Define 'in' operator.""" + return index in self._values + + def __iter__(self): + """Iterate settings keys.""" + return iter(self._values) + + def __len__(self): + """Fetch the length of the mapping.""" + return len(self._values) + + def __bool__(self): + """Convert settings to a boolean.""" + return True + + def copy(self) -> BaseSettings: + """Produce a copy of the settings instance.""" + return PluginSettings(self._values) + + def extend(self, other: Mapping[str, Any]) -> BaseSettings: + """Merge another settings instance to produce a new instance.""" + vals = self._values.copy() + vals.update(other) + return PluginSettings(vals) + + def get_value(self, *var_names: str, default: Any = None): + """Fetch a setting. + + Args: + var_names: A list of variable name alternatives + default: The default value to return if none are defined + """ + for k in var_names: + if k in self._values: + return self._values[k] + return default + + @classmethod + def for_plugin( + cls, + settings: BaseSettings, + plugin: str, + default: Optional[Mapping[str, Any]] = None, + ) -> "PluginSettings": + return cls(settings.get(PLUGIN_CONFIG_KEY, {}).get(plugin, default)) diff --git a/aries_cloudagent/config/tests/test_settings.py b/aries_cloudagent/config/tests/test_settings.py index 583d8a42ef..d95948aedd 100644 --- a/aries_cloudagent/config/tests/test_settings.py +++ b/aries_cloudagent/config/tests/test_settings.py @@ -2,8 +2,11 @@ from unittest import TestCase +from aries_cloudagent.config.plugin_settings import PluginSettings + from ..base import SettingsError from ..settings import Settings +from ..plugin_settings import PLUGIN_CONFIG_KEY class TestSettings(TestCase): @@ -59,3 +62,27 @@ def test_set_default(self): assert self.test_instance[self.test_key] == self.test_value self.test_instance.set_default("BOOL", "True") assert self.test_instance["BOOL"] == "True" + + def test_plugin_setting_retrieval(self): + plugin_setting_values = { + "value0": 0, + "value1": 1, + "value2": 2, + "value3": 3, + "value4": 4, + } + self.test_instance[PLUGIN_CONFIG_KEY] = {"my_plugin": plugin_setting_values} + + plugin_settings = self.test_instance.for_plugin("my_plugin") + assert isinstance(plugin_settings, PluginSettings) + assert plugin_settings._values == plugin_setting_values + for key in plugin_setting_values: + assert key in plugin_settings + assert plugin_settings[key] == plugin_setting_values[key] + assert ( + plugin_settings.get_value(self.test_key) == plugin_setting_values[key] + ) + with self.assertRaises(KeyError): + plugin_settings["MISSING"] + assert len(plugin_settings) == 5 + assert len(plugin_settings) == 5 From f203a5be2fc15fb7f1948f092211b6883b439572 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Wed, 22 Jun 2022 13:14:34 -0400 Subject: [PATCH 297/872] fix: misplaced helper method Signed-off-by: Daniel Bluhm --- aries_cloudagent/config/base.py | 5 ----- aries_cloudagent/config/settings.py | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/aries_cloudagent/config/base.py b/aries_cloudagent/config/base.py index 3ba405c085..05e61720a9 100644 --- a/aries_cloudagent/config/base.py +++ b/aries_cloudagent/config/base.py @@ -4,7 +4,6 @@ from typing import Any, Iterator, Mapping, Optional, Type, TypeVar from ..core.error import BaseError -from .plugin_settings import PluginSettings InjectType = TypeVar("InjectType") @@ -112,10 +111,6 @@ def __repr__(self) -> str: items = ("{}={}".format(k, self[k]) for k in self) return "<{}({})>".format(self.__class__.__name__, ", ".join(items)) - def for_plugin(self, plugin: str, default: Optional[Mapping[str, Any]] = None): - """Retrieve settings for plugin.""" - return PluginSettings.for_plugin(self, plugin, default) - class InjectionError(ConfigError): """The base exception raised by Injector and Provider implementations.""" diff --git a/aries_cloudagent/config/settings.py b/aries_cloudagent/config/settings.py index 291182b68a..d4a7677d13 100644 --- a/aries_cloudagent/config/settings.py +++ b/aries_cloudagent/config/settings.py @@ -3,6 +3,7 @@ from typing import Any, Mapping, MutableMapping, Optional from .base import BaseSettings +from .plugin_settings import PluginSettings class Settings(BaseSettings, MutableMapping[str, Any]): @@ -99,3 +100,7 @@ def extend(self, other: Mapping[str, Any]) -> BaseSettings: def update(self, other: Mapping[str, Any]): """Update the settings in place.""" self._values.update(other) + + def for_plugin(self, plugin: str, default: Optional[Mapping[str, Any]] = None): + """Retrieve settings for plugin.""" + return PluginSettings.for_plugin(self, plugin, default) From e464c64e315a963fdde511f0fc491b7332af17c4 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Wed, 22 Jun 2022 13:22:20 -0400 Subject: [PATCH 298/872] style: docstring for for_plugin Signed-off-by: Daniel Bluhm --- aries_cloudagent/config/plugin_settings.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/aries_cloudagent/config/plugin_settings.py b/aries_cloudagent/config/plugin_settings.py index 278ccf8de4..d1e78cdb74 100644 --- a/aries_cloudagent/config/plugin_settings.py +++ b/aries_cloudagent/config/plugin_settings.py @@ -74,4 +74,8 @@ def for_plugin( plugin: str, default: Optional[Mapping[str, Any]] = None, ) -> "PluginSettings": + """Construct a PluginSettings object from another settings object. + + PLUGIN_CONFIG_KEY is read from settings. + """ return cls(settings.get(PLUGIN_CONFIG_KEY, {}).get(plugin, default)) From a375b616aa9638944e5d92110469ef5fa1ed3235 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Wed, 22 Jun 2022 13:24:00 -0400 Subject: [PATCH 299/872] fix: test case checking wrong key Signed-off-by: Daniel Bluhm --- aries_cloudagent/config/tests/test_settings.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/aries_cloudagent/config/tests/test_settings.py b/aries_cloudagent/config/tests/test_settings.py index d95948aedd..3415d4c260 100644 --- a/aries_cloudagent/config/tests/test_settings.py +++ b/aries_cloudagent/config/tests/test_settings.py @@ -79,9 +79,7 @@ def test_plugin_setting_retrieval(self): for key in plugin_setting_values: assert key in plugin_settings assert plugin_settings[key] == plugin_setting_values[key] - assert ( - plugin_settings.get_value(self.test_key) == plugin_setting_values[key] - ) + assert plugin_settings.get_value(key) == plugin_setting_values[key] with self.assertRaises(KeyError): plugin_settings["MISSING"] assert len(plugin_settings) == 5 From 67173b2c1be8cd9e60a2a1492ba7c5e1f932fbcd Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Wed, 22 Jun 2022 13:30:54 -0400 Subject: [PATCH 300/872] fix: optional rather than error for setting get helpers Signed-off-by: Daniel Bluhm --- aries_cloudagent/config/base.py | 17 ++++------------- aries_cloudagent/transport/inbound/ws.py | 5 +++-- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/aries_cloudagent/config/base.py b/aries_cloudagent/config/base.py index 05e61720a9..cdc30ba507 100644 --- a/aries_cloudagent/config/base.py +++ b/aries_cloudagent/config/base.py @@ -20,7 +20,7 @@ class BaseSettings(Mapping[str, Any]): """Base settings class.""" @abstractmethod - def get_value(self, *var_names, default=None): + def get_value(self, *var_names, default: Optional[Any] = None) -> Any: """Fetch a setting. Args: @@ -32,7 +32,7 @@ def get_value(self, *var_names, default=None): """ - def get_bool(self, *var_names, default=None) -> bool: + def get_bool(self, *var_names, default: Optional[bool] = None) -> Optional[bool]: """Fetch a setting as a boolean value. Args: @@ -43,12 +43,9 @@ def get_bool(self, *var_names, default=None) -> bool: if value is not None: value = bool(value and value not in ("false", "False", "0")) - if value is None: - raise KeyError(f"{var_names} not found in settings") - return value - def get_int(self, *var_names, default=None) -> int: + def get_int(self, *var_names, default: Optional[int] = None) -> Optional[int]: """Fetch a setting as an integer value. Args: @@ -59,12 +56,9 @@ def get_int(self, *var_names, default=None) -> int: if value is not None: value = int(value) - if value is None: - raise KeyError(f"{var_names} not found in settings") - return value - def get_str(self, *var_names, default=None) -> str: + def get_str(self, *var_names, default: Optional[str] = None) -> Optional[str]: """Fetch a setting as a string value. Args: @@ -75,9 +69,6 @@ def get_str(self, *var_names, default=None) -> str: if value is not None: value = str(value) - if value is None: - raise KeyError(f"{var_names} not found in settings") - return value @abstractmethod diff --git a/aries_cloudagent/transport/inbound/ws.py b/aries_cloudagent/transport/inbound/ws.py index 0b74a46990..0348146b9e 100644 --- a/aries_cloudagent/transport/inbound/ws.py +++ b/aries_cloudagent/transport/inbound/ws.py @@ -2,6 +2,7 @@ import asyncio import logging +from typing import Optional from aiohttp import WSMessage, WSMsgType, web @@ -30,10 +31,10 @@ def __init__(self, host: str, port: int, create_session, **kwargs) -> None: self.host = host self.port = port self.site: web.BaseSite = None - self.heartbeat_interval: int = self.root_profile.settings.get_int( + self.heartbeat_interval: Optional[int] = self.root_profile.settings.get_int( "transport.ws.heartbeat_interval" ) - self.timout_interval: int = self.root_profile.settings.get_int( + self.timout_interval: Optional[int] = self.root_profile.settings.get_int( "transport.ws.timout_interval" ) From 4a7640e71f4c6225ae62e9b39a366b52c969a3a6 Mon Sep 17 00:00:00 2001 From: Ry Jones Date: Wed, 22 Jun 2022 11:52:14 -0700 Subject: [PATCH 301/872] Use local deps only Signed-off-by: Ry Jones --- .github/workflows/pip-audit.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pip-audit.yml b/.github/workflows/pip-audit.yml index 4d81158b21..4aef21a011 100644 --- a/.github/workflows/pip-audit.yml +++ b/.github/workflows/pip-audit.yml @@ -12,5 +12,11 @@ jobs: steps: - uses: actions/checkout@v3 - name: install - run: python -m pip install . + run: | + python -m venv env/ + source env/bin/activate + python -m pip install . - uses: trailofbits/gh-action-pip-audit@v0.0.4 + with: + virtual-environment: env/ + local: true From 75954ab3d80100e4fcac726a6fcfcb3ec3e2ef22 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Wed, 22 Jun 2022 14:57:36 -0400 Subject: [PATCH 302/872] feat: make base wallet route access configurable Signed-off-by: Daniel Bluhm --- aries_cloudagent/admin/server.py | 26 +++++++++++++++++++++++++- aries_cloudagent/config/argparse.py | 13 +++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/admin/server.py b/aries_cloudagent/admin/server.py index 0bbada1da6..fd58757b8a 100644 --- a/aries_cloudagent/admin/server.py +++ b/aries_cloudagent/admin/server.py @@ -4,7 +4,7 @@ from hmac import compare_digest import logging import re -from typing import Callable, Coroutine +from typing import Callable, Coroutine, Optional, Pattern, Sequence, cast import uuid import warnings @@ -249,9 +249,32 @@ def __init__( self.websocket_queues = {} self.site = None self.multitenant_manager = context.inject_or(BaseMultitenantManager) + self._additional_route_pattern: Optional[Pattern] = None self.server_paths = [] + @property + def additional_routes_pattern(self) -> Optional[Pattern]: + """Pattern for configured addtional routes to permit base wallet to access.""" + if self._additional_route_pattern: + return self._additional_route_pattern + + base_wallet_routes = self.context.settings.get("multitenant.base_wallet_routes") + base_wallet_routes = cast(Sequence[str], base_wallet_routes) + if base_wallet_routes: + self._additional_route_pattern = re.compile( + "^(?:" + "|".join(base_wallet_routes) + ")" + ) + return None + + def _matches_additional_routes(self, path: str) -> bool: + """Path matches additional_routes_pattern.""" + pattern = self.additional_routes_pattern + if pattern: + return bool(pattern.match(path)) + + return False + async def make_application(self) -> web.Application: """Get the aiohttp application instance.""" @@ -324,6 +347,7 @@ async def check_multitenant_authorization(request: web.Request, handler): path, ) or path.startswith("/mediation/default-mediator") + or self._matches_additional_routes(path) ) # base wallet is not allowed to perform ssi related actions. diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index 2c2ac13dd4..4cfd18da0f 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -1629,6 +1629,16 @@ def add_arguments(self, parser: ArgumentParser): '"wallet_name" is only used when "wallet_type" is "askar-profile"' ), ) + parser.add_argument( + "--base-wallet-routes", + type=str, + nargs="+", + required=False, + metavar="", + help=( + "Patterns matching admin routes that should be permitted for base wallet." + ), + ) def get_settings(self, args: Namespace): """Extract multitenant settings.""" @@ -1664,6 +1674,9 @@ def get_settings(self, args: Namespace): "multitenant.key_derivation_method" ] = multitenancyConfig.get("key_derivation_method") + if args.base_wallet_routes: + settings["multitenant.base_wallet_routes"] = args.base_wallet_routes + return settings From 6106e302d1b7b1589671803732aa4084ba2d65ca Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Wed, 22 Jun 2022 15:12:33 -0400 Subject: [PATCH 303/872] test: --base-wallet-routes arg Signed-off-by: Daniel Bluhm --- aries_cloudagent/admin/tests/test_admin_server.py | 14 +++++++++++++- aries_cloudagent/config/tests/test_argparse.py | 3 +++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/admin/tests/test_admin_server.py b/aries_cloudagent/admin/tests/test_admin_server.py index 7707315e12..74731a3d9e 100644 --- a/aries_cloudagent/admin/tests/test_admin_server.py +++ b/aries_cloudagent/admin/tests/test_admin_server.py @@ -195,7 +195,9 @@ async def test_import_routes(self): async def test_import_routes_multitenant_middleware(self): # imports all default admin routes - context = InjectionContext() + context = InjectionContext( + settings={"multitenant.base_wallet_routes": ["/test"]} + ) context.injector.bind_instance(ProtocolRegistry, ProtocolRegistry()) context.injector.bind_instance(GoalCodeRegistry, GoalCodeRegistry()) profile = InMemoryProfile.test_profile() @@ -250,6 +252,16 @@ async def test_import_routes_multitenant_middleware(self): await mt_authz_middle(mock_request, mock_handler) assert mock_handler.called_once_with(mock_request) + mock_request = async_mock.MagicMock( + method="GET", + headers={"Authorization": "Non-bearer ..."}, + path="/test", + text=async_mock.CoroutineMock(return_value="abc123"), + ) + mock_handler = async_mock.CoroutineMock() + await mt_authz_middle(mock_request, mock_handler) + assert mock_handler.called_once_with(mock_request) + # multitenant setup context exception paths [setup_ctx_middle] = [m for m in app.middlewares if ".setup_context" in str(m)] diff --git a/aries_cloudagent/config/tests/test_argparse.py b/aries_cloudagent/config/tests/test_argparse.py index 08c47256dc..23e01bbed9 100644 --- a/aries_cloudagent/config/tests/test_argparse.py +++ b/aries_cloudagent/config/tests/test_argparse.py @@ -232,6 +232,8 @@ async def test_multitenancy_settings(self): "secret", "--multitenancy-config", '{"wallet_type":"askar","wallet_name":"test"}', + "--base-wallet-routes", + "/my_route", ] ) @@ -241,6 +243,7 @@ async def test_multitenancy_settings(self): assert settings.get("multitenant.jwt_secret") == "secret" assert settings.get("multitenant.wallet_type") == "askar" assert settings.get("multitenant.wallet_name") == "test" + assert settings.get("multitenant.base_wallet_routes") == ["/my_route"] async def test_endorser_settings(self): """Test required argument parsing.""" From 8d856c946a2899da0763898dd61074a3fab6a931 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Wed, 22 Jun 2022 15:19:25 -0400 Subject: [PATCH 304/872] docs: include warning in help text of base wallet routes arg Signed-off-by: Daniel Bluhm --- aries_cloudagent/config/argparse.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index 4cfd18da0f..950cab3a05 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -1636,7 +1636,9 @@ def add_arguments(self, parser: ArgumentParser): required=False, metavar="", help=( - "Patterns matching admin routes that should be permitted for base wallet." + "Patterns matching admin routes that should be permitted for " + "base wallet. The base wallet is preconfigured to have access to " + "essential endpoints. This argument should be used sparingly." ), ) From 65b97856e97d2929f64626d00073fbd7c085e51e Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Thu, 23 Jun 2022 10:46:48 -0700 Subject: [PATCH 305/872] 0.7.4-rc5 changelog, version and ReadTheDocs updates Signed-off-by: Stephen Curran --- CHANGELOG.md | 35 +++++++++++-------- aries_cloudagent/version.py | 2 +- docs/generated/aries_cloudagent.config.rst | 8 +++++ .../aries_cloudagent.multitenant.rst | 8 +++++ open-api/openapi.json | 2 +- 5 files changed, 39 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 747243e778..8a0bd9c94a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ -# 0.7.4-RC4 +# 0.7.4-RC5 -## June 21, 2022 +## June 23, 2022 + +**NOTE** We expect 0.7.4-rc5 will the be the last Release Candidate prior to the +official 0.7.4 release. **NOTE:** 0.7.4-rc4 contains a fix for a revocation-related issue introduced in 0.7.4-rc3. We recommend updating to 0.7.4-rc4 immediately if you have been using @@ -11,18 +14,18 @@ The 0.7.4 release consists largely of internal fixes to ACA-Py, big increases in performance resulting from the now recommended use of [Aries -Askar](https://github.com/bcgov/aries-askar) instead of the Indy-SDK, tools for -dealing with revocation-related issues, plus a number of enhancements. There have been -a lot of groups exercising ACA-Py and the updates made in this release are a -reflection of those efforts. We have PRs that have been contributed by 20 -different people, which is likely a record for a single ACA-Py release. - -The largest enhancement is in the area of the Hyperledger Indy endorser, -enabling an instance of ACA-Py to act as an Endorser for Indy authors needed -endorsing to write objects to an Indy ledger. We're hoping to see an -"aries-endorser-service" come from that work, an Endorser to be easily operated -by an organization, ideally with a controller starter kit to allow an approvals -business flow. +Askar](https://github.com/bcgov/aries-askar) instead of the Indy-SDK, +improvements and tools for dealing with revocation-related issues, plus a number +of general enhancements. There have been a lot of groups exercising ACA-Py and the +updates made in this release are a reflection of those efforts. We have PRs that +have been contributed by 21 different people. + +The largest enhancement is in the area of the endorsing of Hyperledger Indy +ledger transactions, enabling an instance of ACA-Py to act as an Endorser for +Indy authors needing endorsements to write objects to an Indy ledger. We're +hoping to see (and working on!) an "aries-endorser-service" come from that work, +an Endorser to be easily operated by an organization, ideally with a controller +starter kit supporting a basic approvals business workflow. A focus towards the end of the 0.7.4 development and release cycle was on the handling of AnonCreds revocation in ACA-Py. Most important, a production issue @@ -82,6 +85,7 @@ stuff needed for a growing codebase. - Use provided connection_id if provided [\#1726](https://github.com/hyperledger/aries-cloudagent-python/pull/1726) ([ianco](https://github.com/ianco)) - Additions to the startup parameters, Admin API and Web Hooks + - Improve typing of settings and add plugin settings object [\#1833](https://github.com/hyperledger/aries-cloudagent-python/pull/1833) ([dbluhm](https://github.com/dbluhm)) - feat: accept taa using startup parameter --accept-taa [\#1643](https://github.com/hyperledger/aries-cloudagent-python/pull/1643) ([TimoGlastra](https://github.com/TimoGlastra)) - Add auto_verify flag in present-proof protocol [\#1702](https://github.com/hyperledger/aries-cloudagent-python/pull/1702) ([DaevMithran](https://github.com/DaevMithran)) - feat: query connections by their_public_did [\#1637](https://github.com/hyperledger/aries-cloudagent-python/pull/1637) ([TimoGlastra](https://github.com/TimoGlastra)) @@ -133,6 +137,7 @@ stuff needed for a growing codebase. - Multitenacy updates and fixes - feat: create new JWT tokens and invalidate older for multitenancy [\#1725](https://github.com/hyperledger/aries-cloudagent-python/pull/1725) ([TimoGlastra](https://github.com/TimoGlastra)) + - Multi-tenancy stale wallet clean up [\#1692](https://github.com/hyperledger/aries-cloudagent-python/pull/1692) ([dbluhm](https://github.com/dbluhm)) - Dependencies and internal code updates/fixes - Update pyjwt to 2.4 [\#1829](https://github.com/hyperledger/aries-cloudagent-python/pull/1829) ([andrewwhitehead](https://github.com/andrewwhitehead)) @@ -176,6 +181,8 @@ stuff needed for a growing codebase. - Remove references to play with von [\#1688](https://github.com/hyperledger/aries-cloudagent-python/pull/1688) ([ianco](https://github.com/ianco)) - Add pre-commit as optional developer tool [\#1671](https://github.com/hyperledger/aries-cloudagent-python/pull/1671) ([dbluhm](https://github.com/dbluhm)) - run_docker start - pass environment variables [\#1715](https://github.com/hyperledger/aries-cloudagent-python/pull/1715) ([shaangill025](https://github.com/shaangill025)) + - Use local deps only [\#1834](https://github.com/hyperledger/aries-cloudagent-python/pull/1834) ([ryjones](https://github.com/ryjones)) + - Enable pip-audit [\#1831](https://github.com/hyperledger/aries-cloudagent-python/pull/1831) ([ryjones](https://github.com/ryjones)) - Release management pull requests - Update changelog and version for 0.7.4-rc4 [\#1830](https://github.com/hyperledger/aries-cloudagent-python/pull/1830) ([swcurran](https://github.com/swcurran)) diff --git a/aries_cloudagent/version.py b/aries_cloudagent/version.py index 35c49c1091..c71e06b7b1 100644 --- a/aries_cloudagent/version.py +++ b/aries_cloudagent/version.py @@ -1,4 +1,4 @@ """Library version information.""" -__version__ = "0.7.4-rc4" +__version__ = "0.7.4-rc5" RECORD_TYPE_ACAPY_VERSION = "acapy_version" diff --git a/docs/generated/aries_cloudagent.config.rst b/docs/generated/aries_cloudagent.config.rst index 8fae3838c6..43b1ef34a5 100644 --- a/docs/generated/aries_cloudagent.config.rst +++ b/docs/generated/aries_cloudagent.config.rst @@ -89,6 +89,14 @@ aries\_cloudagent.config.logging module :undoc-members: :show-inheritance: +aries\_cloudagent.config.plugin\_settings module +------------------------------------------------ + +.. automodule:: aries_cloudagent.config.plugin_settings + :members: + :undoc-members: + :show-inheritance: + aries\_cloudagent.config.provider module ---------------------------------------- diff --git a/docs/generated/aries_cloudagent.multitenant.rst b/docs/generated/aries_cloudagent.multitenant.rst index ad9325f00b..2748e843d7 100644 --- a/docs/generated/aries_cloudagent.multitenant.rst +++ b/docs/generated/aries_cloudagent.multitenant.rst @@ -33,6 +33,14 @@ aries\_cloudagent.multitenant.base module :undoc-members: :show-inheritance: +aries\_cloudagent.multitenant.cache module +------------------------------------------ + +.. automodule:: aries_cloudagent.multitenant.cache + :members: + :undoc-members: + :show-inheritance: + aries\_cloudagent.multitenant.error module ------------------------------------------ diff --git a/open-api/openapi.json b/open-api/openapi.json index 5988f2e0ef..d397ebf4b9 100644 --- a/open-api/openapi.json +++ b/open-api/openapi.json @@ -1,7 +1,7 @@ { "swagger" : "2.0", "info" : { - "version" : "v0.7.4-rc4", + "version" : "v0.7.4-rc5", "title" : "Aries Cloud Agent" }, "tags" : [ { From f12a3b775fe1554d011f350dd2c1dda03fd057d6 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Thu, 23 Jun 2022 10:48:34 -0700 Subject: [PATCH 306/872] Added this PR to the changelog Signed-off-by: Stephen Curran --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a0bd9c94a..61f8005bf9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -185,6 +185,7 @@ stuff needed for a growing codebase. - Enable pip-audit [\#1831](https://github.com/hyperledger/aries-cloudagent-python/pull/1831) ([ryjones](https://github.com/ryjones)) - Release management pull requests + - 0.7.4-rc5 changelog, version and ReadTheDocs updates [\#1838](https://github.com/hyperledger/aries-cloudagent-python/pull/1838) ([swcurran](https://github.com/swcurran)) - Update changelog and version for 0.7.4-rc4 [\#1830](https://github.com/hyperledger/aries-cloudagent-python/pull/1830) ([swcurran](https://github.com/swcurran)) - Changelog, version and ReadTheDocs updates for 0.7.4-rc3 release [\#1817](https://github.com/hyperledger/aries-cloudagent-python/pull/1817) ([swcurran](https://github.com/swcurran)) - 0.7.4-rc2 update [\#1771](https://github.com/hyperledger/aries-cloudagent-python/pull/1771) ([swcurran](https://github.com/swcurran)) From f149e9e9fb31e8dac9090d63b86b117d1f2b5db5 Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Thu, 23 Jun 2022 12:48:53 -0700 Subject: [PATCH 307/872] set prefix for demo agents; some code cleanup Signed-off-by: Andrew Whitehead --- demo/bdd_support/agent_backchannel_client.py | 67 +++++--------------- demo/runners/agent_container.py | 7 +- 2 files changed, 19 insertions(+), 55 deletions(-) diff --git a/demo/bdd_support/agent_backchannel_client.py b/demo/bdd_support/agent_backchannel_client.py index 5a2f312812..9adb2d1d9c 100644 --- a/demo/bdd_support/agent_backchannel_client.py +++ b/demo/bdd_support/agent_backchannel_client.py @@ -1,19 +1,8 @@ import asyncio -from aiohttp import ( - web, - ClientSession, - ClientRequest, - ClientResponse, - ClientError, - ClientTimeout, -) import json -import os -from time import sleep import uuid from runners.agent_container import AgentContainer, create_agent_with_args_list -from runners.support.agent import DemoAgent ###################################################################### @@ -21,31 +10,7 @@ ###################################################################### -def run_coroutine(coroutine): - loop = asyncio.get_event_loop() - if not loop: - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) - try: - return loop.run_until_complete(coroutine()) - finally: - pass - # loop.close() - - -def run_coroutine_with_args(coroutine, *args): - loop = asyncio.get_event_loop() - if not loop: - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) - try: - return loop.run_until_complete(coroutine(*args)) - finally: - pass - # loop.close() - - -def run_coroutine_with_kwargs(coroutine, *args, **kwargs): +def run_coroutine(coroutine, *args, **kwargs): loop = asyncio.get_event_loop() if not loop: loop = asyncio.new_event_loop() @@ -58,14 +23,14 @@ def run_coroutine_with_kwargs(coroutine, *args, **kwargs): def async_sleep(delay): - run_coroutine_with_args(asyncio.sleep, delay) + run_coroutine(asyncio.sleep, delay) ###################################################################### # high level aries agent interface ###################################################################### def create_agent_container_with_args(in_args: list): - return run_coroutine_with_args(create_agent_with_args_list, in_args) + return run_coroutine(create_agent_with_args_list, in_args) def aries_container_initialize( @@ -73,7 +38,7 @@ def aries_container_initialize( schema_name: str = None, schema_attrs: list = None, ): - run_coroutine_with_kwargs( + run_coroutine( the_container.initialize, schema_name=schema_name, schema_attrs=schema_attrs, @@ -86,7 +51,7 @@ def agent_container_register_did( verkey: str, role: str, ): - run_coroutine_with_args( + run_coroutine( the_container.register_did, did, verkey, @@ -103,7 +68,7 @@ def aries_container_terminate( def aries_container_generate_invitation( the_container: AgentContainer, ): - return run_coroutine_with_kwargs( + return run_coroutine( the_container.generate_invitation, ) @@ -112,7 +77,7 @@ def aries_container_receive_invitation( the_container: AgentContainer, invite_details: dict, ): - return run_coroutine_with_kwargs( + return run_coroutine( the_container.input_invitation, invite_details, ) @@ -130,7 +95,7 @@ def aries_container_create_schema_cred_def( schema_attrs: list, version: str = None, ): - return run_coroutine_with_kwargs( + return run_coroutine( the_container.create_schema_and_cred_def, schema_name, schema_attrs, @@ -143,7 +108,7 @@ def aries_container_issue_credential( cred_def_id: str, cred_attrs: list, ): - return run_coroutine_with_args( + return run_coroutine( the_container.issue_credential, cred_def_id, cred_attrs, @@ -155,7 +120,7 @@ def aries_container_receive_credential( cred_def_id: str, cred_attrs: list, ): - return run_coroutine_with_args( + return run_coroutine( the_container.receive_credential, cred_def_id, cred_attrs, @@ -166,7 +131,7 @@ def aries_container_request_proof( the_container: AgentContainer, proof_request: dict, ): - return run_coroutine_with_args( + return run_coroutine( the_container.request_proof, proof_request, ) @@ -176,7 +141,7 @@ def aries_container_verify_proof( the_container: AgentContainer, proof_request: dict, ): - return run_coroutine_with_args( + return run_coroutine( the_container.verify_proof, proof_request, ) @@ -228,7 +193,7 @@ def agent_container_GET( text: bool = False, params: dict = None, ) -> dict: - return run_coroutine_with_kwargs( + return run_coroutine( the_container.admin_GET, path, text=text, @@ -243,7 +208,7 @@ def agent_container_POST( text: bool = False, params: dict = None, ) -> dict: - return run_coroutine_with_kwargs( + return run_coroutine( the_container.admin_POST, path, data=data, @@ -259,7 +224,7 @@ def agent_container_PATCH( text: bool = False, params: dict = None, ) -> dict: - return run_coroutine_with_kwargs( + return run_coroutine( the_container.admin_PATCH, path, data=data, @@ -275,7 +240,7 @@ def agent_container_PUT( text: bool = False, params: dict = None, ) -> dict: - return run_coroutine_with_kwargs( + return run_coroutine( the_container.admin_PUT, path, data=data, diff --git a/demo/runners/agent_container.py b/demo/runners/agent_container.py index 7c16d81d7d..065a3cb4ea 100644 --- a/demo/runners/agent_container.py +++ b/demo/runners/agent_container.py @@ -20,14 +20,10 @@ start_mediator_agent, connect_wallet_to_mediator, start_endorser_agent, - connect_wallet_to_endorser, CRED_FORMAT_INDY, CRED_FORMAT_JSON_LD, - DID_METHOD_SOV, DID_METHOD_KEY, - KEY_TYPE_ED255, KEY_TYPE_BLS, - SIG_TYPE_BLS, ) from runners.support.utils import ( # noqa:E402 check_requires, @@ -615,6 +611,7 @@ def __init__( self, ident: str, start_port: int, + prefix: str = None, no_auto: bool = False, revocation: bool = False, genesis_txns: str = None, @@ -639,6 +636,7 @@ def __init__( self.genesis_txn_list = genesis_txn_list self.ident = ident self.start_port = start_port + self.prefix = prefix or ident self.no_auto = no_auto self.revocation = revocation self.tails_server_base_url = tails_server_base_url @@ -685,6 +683,7 @@ async def initialize( self.ident, self.start_port, self.start_port + 1, + prefix=self.prefix, genesis_data=self.genesis_txns, genesis_txn_list=self.genesis_txn_list, no_auto=self.no_auto, From 2aba885599219df0297f776544e9f4b22ce08d18 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Fri, 24 Jun 2022 08:32:39 -0230 Subject: [PATCH 308/872] Adjust intesgration tests included in github actions Signed-off-by: Ian Costanzo --- demo/features/0453-issue-credential.feature | 16 +++++++++++++++- demo/features/0454-present-proof.feature | 4 ++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/demo/features/0453-issue-credential.feature b/demo/features/0453-issue-credential.feature index 5e069862ec..0c74f53645 100644 --- a/demo/features/0453-issue-credential.feature +++ b/demo/features/0453-issue-credential.feature @@ -54,5 +54,19 @@ Feature: RFC 0453 Aries agent issue credential | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | | --revocation --public-did | | driverslicense | Data_DL_NormalizedValues | | --revocation --public-did --did-exchange | --did-exchange | driverslicense | Data_DL_NormalizedValues | - | --revocation --public-did --mediation | --mediation | driverslicense | Data_DL_NormalizedValues | | --revocation --public-did --multitenant | --multitenant | driverslicense | Data_DL_NormalizedValues | + + @T004.1-RFC0453 + Scenario Outline: Issue a credential with revocation, with the Issuer beginning with an offer, and then revoking the credential + Given we have "2" agents + | name | role | capabilities | + | Acme | issuer | | + | Bob | holder | | + And "Acme" and "Bob" have an existing connection + And "Bob" has an issued credential from "Acme" + Then "Acme" revokes the credential + And "Bob" has the credential issued + + Examples: + | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | + | --revocation --public-did --mediation | --mediation | driverslicense | Data_DL_NormalizedValues | diff --git a/demo/features/0454-present-proof.feature b/demo/features/0454-present-proof.feature index a95686da00..9abece8cd1 100644 --- a/demo/features/0454-present-proof.feature +++ b/demo/features/0454-present-proof.feature @@ -97,7 +97,7 @@ Feature: RFC 0454 Aries agent present proof | Acme | --revocation --public-did --mediation | | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | | Acme | --revocation --public-did --multitenant | --multitenant | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | - @T003-RFC0454.1 + @T003-RFC0454.1 @GHA Scenario Outline: Present Proof for multiple credentials where the one is revocable and one isn't Given we have "4" agents | name | role | capabilities | @@ -117,7 +117,7 @@ Feature: RFC 0454 Aries agent present proof | issuer1 | Acme1_capabilities | issuer2 | Acme2_capabilities | Bob_cap | Schema_name_1 | Credential_data_1 | Schema_name_2 | Credential_data_2 | Proof_request | | Acme1 | --revocation --public-did | Acme2 | --public-did | | driverslicense_v2 | Data_DL_MaxValues | health_id | Data_DL_MaxValues | DL_age_over_19_v2_with_health_id | - @T003-RFC0454.2 + @T003-RFC0454.2 @GHA Scenario Outline: Present Proof for multiple credentials where the one is revocable and one isn't, and the revocable credential is revoked Given we have "4" agents | name | role | capabilities | From 73c510d126a1a224c628e03f9918b4298211a258 Mon Sep 17 00:00:00 2001 From: Ry Jones Date: Mon, 27 Jun 2022 10:23:29 -0700 Subject: [PATCH 309/872] Only run workflows on main repo Fixes #1839 Signed-off-by: Ry Jones --- .github/workflows/codeql.yml | 1 + .github/workflows/pip-audit.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index e240dd8308..9d5ee6a7ea 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -10,6 +10,7 @@ jobs: CodeQL-Build: # CodeQL runs on ubuntu-latest and windows-latest runs-on: ubuntu-latest + if: (github.event_name == 'pull_request' && github.repository == 'hyperledger/aries-cloudagent-python') || (github.event_name != 'pull_request') steps: - name: Checkout repository diff --git a/.github/workflows/pip-audit.yml b/.github/workflows/pip-audit.yml index 4aef21a011..1f068b8738 100644 --- a/.github/workflows/pip-audit.yml +++ b/.github/workflows/pip-audit.yml @@ -9,6 +9,7 @@ permissions: jobs: selftest: runs-on: ubuntu-latest + if: (github.event_name == 'pull_request' && github.repository == 'hyperledger/aries-cloudagent-python') || (github.event_name != 'pull_request') steps: - uses: actions/checkout@v3 - name: install From cac3f4126fe9d3d879835aafd6a14d14718f0480 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Tue, 28 Jun 2022 13:43:14 -0400 Subject: [PATCH 310/872] fix: merge artifact in test_argparse Signed-off-by: Daniel Bluhm --- aries_cloudagent/config/tests/test_argparse.py | 1 - 1 file changed, 1 deletion(-) diff --git a/aries_cloudagent/config/tests/test_argparse.py b/aries_cloudagent/config/tests/test_argparse.py index 6ec9a2fd75..de680bb8f3 100644 --- a/aries_cloudagent/config/tests/test_argparse.py +++ b/aries_cloudagent/config/tests/test_argparse.py @@ -231,7 +231,6 @@ async def test_multitenancy_settings(self): "--jwt-secret", "secret", "--multitenancy-config", - '{"wallet_type":"askar","wallet_name":"test"}', '{"wallet_type":"askar","wallet_name":"test", "cache_size": 10}', "--base-wallet-routes", "/my_route", From 261c52fa914125838a5d2f081c7fa6f2c16593df Mon Sep 17 00:00:00 2001 From: feknall Date: Tue, 28 Jun 2022 18:50:43 -0400 Subject: [PATCH 311/872] fix a typo in DevReadMe.md Signed-off-by: feknall --- DevReadMe.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DevReadMe.md b/DevReadMe.md index e37f1c624a..e16484ce92 100644 --- a/DevReadMe.md +++ b/DevReadMe.md @@ -149,7 +149,7 @@ To enable the [ptvsd](https://github.com/Microsoft/ptvsd) Python debugger for Vi Any ports you will be using from the docker container should be published using the `PORTS` environment variable. For example: ```bash -PORTS="5000:5000 8000:8000 1000:1000" ./scripts/run_docker start --inbound-transport http 0.0.0.0 10000 --outbound-transport http --debug --log-level DEBUG +PORTS="5000:5000 8000:8000 10000:10000" ./scripts/run_docker start --inbound-transport http 0.0.0.0 10000 --outbound-transport http --debug --log-level DEBUG ``` Refer to [the previous section](#Running) for instructions on how to run the software. From 144196fab131d45aa38cafdc19b390e1ce9bf77f Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Wed, 29 Jun 2022 10:09:21 -0700 Subject: [PATCH 312/872] Update the Supported RFCs document for 0.7.4 release Signed-off-by: Stephen Curran --- SupportedRFCs.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/SupportedRFCs.md b/SupportedRFCs.md index 029923b427..adaa7ba54f 100644 --- a/SupportedRFCs.md +++ b/SupportedRFCs.md @@ -45,10 +45,11 @@ A summary of the Aries Interop Profiles and Aries RFCs supported in ACA-Py can b | Issuer | :white_check_mark: | | | Holder | :white_check_mark: | | | Verifier | :white_check_mark: | | -| Mediator Service | :white_check_mark: | Coming Soon: An `aries-mediator-service` repository that is a pre-configured, production ready Aries Mediator Service based on a released version of ACA-Py. | +| Mediator Service | :white_check_mark: | See the [aries-mediator-service](https://github.com/hyperledger/aries-mediator-service), a pre-configured, production ready Aries Mediator Service based on a released version of ACA-Py. | | Mediator Client | :white_check_mark: | | Indy Transaction Author | :white_check_mark: | | | Indy Transaction Endorser | :white_check_mark: | | +| Indy Endorser Service | :construction: | Help Wanted! See the [aries-endorser-service](https://github.com/bcgov/aries-endorser-service), an under-construction, pre-configured, production ready Aries Endorser Service based on a released version of ACA-Py. On completion, we expect this repository to be moved into the Hyperledger GitHub organization. | ## Credential Types @@ -65,13 +66,14 @@ A summary of the Aries Interop Profiles and Aries RFCs supported in ACA-Py can b | `did:web` | :white_check_mark: | Resolution only | | `did:key` | :white_check_mark: | | | `did:peer` | :warning:| AIP 1.0-based `did:peer` DIDs are used, meaning the DIDs are not prefixed with `did:peer` and are not following the conventions of AIP 2.0's [RFC 0627: Static Peer DIDs](https://github.com/hyperledger/aries-rfcs/tree/b3a3942ef052039e73cd23d847f42947f8287da2/features/0627-static-peer-dids) | +| Universal Resolver | :construction: | A [plug in](https://github.com/sicpa-dlab/acapy-resolver-universal) from [SICPA](https://www.sicpa.com/) is available that can be added to an ACA-Py installation to support a [universal resolver](https://dev.uniresolver.io/) capability, providing support for most DID methods in the [W3C DID Method Registry](https://w3c.github.io/did-spec-registries/#did-methods). | ## Secure Storage Types | Secure Storage Types | Supported | Notes | --- | :--: | -- | +| [Aries Askar](https://github.com/hyperledger/aries-askar) | :white_check_mark: | Recommended - Aries Askar provides equivalent/evolved secure storage and cryptography support to the "indy-wallet" part of the Indy SDK. When using Askar (via the `--wallet-type askar` startup parameter), other Indy SDK functionality is handled by [Indy Shared RS](https://github.com/hyperledger/indy-shared-rs) (AnonCreds) and [Indy VDR](https://github.com/hyperledger/indy-vdr) (Indy ledger interactions). | | [Indy SDK "indy-wallet"](https://github.com/hyperledger/indy-sdk/tree/master/docs/design/003-wallet-storage) | :white_check_mark: | Full support for the features of the "indy-wallet" secure storage capabilities found in the Indy SDK. | -| [Aries Askar](https://github.com/hyperledger/aries-askar) | :warning: | Aries Askar provides equivalent/evolved secure storage and cryptography support to the "indy-wallet" part of the Indy SDK. Available in ACA-Py (activated using a startup parameters but not yet widely used. When using Askar, other Indy SDK capabilities are handled by [Indy Shared RS](https://github.com/hyperledger/indy-shared-rs) (AnonCreds) and [Indy VDR](https://github.com/hyperledger/indy-vdr) (Indy ledger interactions). | ## Miscellaneous Features @@ -86,8 +88,8 @@ A summary of the Aries Interop Profiles and Aries RFCs supported in ACA-Py can b | Connection-less (OOB protocol / AIP 2.0) | :white_check_mark: | Only for present proof | | Signed Attachments | :white_check_mark: | Used for OOB | | Multi Indy ledger support (with automatic detection) | :white_check_mark: | Support added in the 0.7.3 Release. | -| Persistence of mediated messages | :construction: | Messages are currently stored in an in-memory and so are subject to loss in the case of a sudden termination of an ACA-Py process. The in-memory queue is properly handled in the case of a graceful shutdown of an ACA-Py process (e.g. processing of the queue completes and no new messages are accepted). Work is underway to add useful external queues handling, including support for multiple external queue implementations (e.g., redis and kafka). | -| Storage Import & Export | :warning: | Supported by directly interacting with the indy-sdk or Aries Askar (e.g., no Admin API endpoint available for wallet import & export). Aries Askar support includes the ability to import storage exported from the Indy SDK's "indy-wallet" component. | +| Persistence of mediated messages | :construction: | Work is mostly complete to add external, persistent queue handling, including support for multiple external queue implementations (notably, plugins for [Redis](https://github.com/bcgov/aries-acapy-plugin-redis-events) and [Kafka](https://github.com/sicpa-dlab/aries-acapy-plugin-kafka-events)). Documentation for that is being worked on. Without persistent queue support, messages are stored in an in-memory queue and so are subject to loss in the case of a sudden termination of an ACA-Py process. The in-memory queue is properly handled in the case of a graceful shutdown of an ACA-Py process (e.g. processing of the queue completes and no new messages are accepted). | +| Storage Import & Export | :warning: | Supported by directly interacting with the indy-sdk or Aries Askar (e.g., no Admin API endpoint available for wallet import & export). Aries Askar support includes the ability to import storage exported from the Indy SDK's "indy-wallet" component. However, a full migration approach from a production ACA-Py using the Indy-SDK storage to use Aries Askar storage has not been implemeted and documented. | ## Supported RFCs @@ -109,12 +111,9 @@ are fully supported in ACA-Py **EXCEPT** as noted in the table below. | RFC | Supported | Notes | --- | :--: | -- | | [0023-did-exchange](https://github.com/hyperledger/aries-rfcs/tree/b3a3942ef052039e73cd23d847f42947f8287da2/features/0023-did-exchange) | :warning: | Not using DIDDoc conventions yet, still using DID format of 0160-connections (which is incorrect and outdated). Also using incorrect format for `did:peer` (or not using a `did:` prefix at all) | -| [0183-revocation-notification](https://github.com/hyperledger/aries-rfcs/tree/b3a3942ef052039e73cd23d847f42947f8287da2/features/0183-revocation-notification) | :white_check_mark: | :new: This was added in release 0.7.3 and will be removed from this list with the next update. | | [0211-route-coordination](https://github.com/hyperledger/aries-rfcs/tree/b3a3942ef052039e73cd23d847f42947f8287da2/features/0211-route-coordination) | :warning: | Only pre-AIP 2.0 version. Must be updated to use `did:key` for full AIP 2.0 support | | [0317-please-ack](https://github.com/hyperledger/aries-rfcs/tree/main/features/0317-please-ack) | :x: | | | [0360-use-did-key](https://github.com/hyperledger/aries-rfcs/tree/b3a3942ef052039e73cd23d847f42947f8287da2/features/0360-use-did-key) | :warning: | Creating and resolving `did:key` DIDs is supported, but not all protocols are updated yet to use `did:key`. This is a breaking change for AIP 1.0 -> AIP 2.0. | -| [0519-goal-codes](https://github.com/hyperledger/aries-rfcs/tree/b3a3942ef052039e73cd23d847f42947f8287da2/concepts/0519-goal-codes) | :white_check_mark: | :new: This was added in release 0.7.3 and will be removed from this list with the next update. | -| [0557-discover-features-v2](https://github.com/hyperledger/aries-rfcs/tree/b3a3942ef052039e73cd23d847f42947f8287da2/features/0557-discover-features-v2) | :white_check_mark: | :new: This was added in release 0.7.3 and will be removed from this list with the next update. | | [0587-encryption-envelope-v2](https://github.com/hyperledger/aries-rfcs/tree/b3a3942ef052039e73cd23d847f42947f8287da2/features/0587-encryption-envelope-v2) | :construction: | Support for the DIDComm V2 envelope format is a work in progress, including the PRs ([AIP-2 base64url consistency](https://github.com/hyperledger/aries-cloudagent-python/pull/1188) and [Small AIP-2 updates](https://github.com/hyperledger/aries-cloudagent-python/pull/1056)) | | [0627-static-peer-dids](https://github.com/hyperledger/aries-rfcs/tree/b3a3942ef052039e73cd23d847f42947f8287da2/features/0627-static-peer-dids) | :x: | | From be27d36ae572095cd3bef7e568d751cb26f65bfe Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Wed, 29 Jun 2022 15:41:55 -0700 Subject: [PATCH 313/872] fix handling of non-revocable credential when timestamp is specified (credx) Signed-off-by: Andrew Whitehead --- aries_cloudagent/indy/credx/holder.py | 52 +++++++++++++-------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/aries_cloudagent/indy/credx/holder.py b/aries_cloudagent/indy/credx/holder.py index d0ad6d5110..a212661cbc 100644 --- a/aries_cloudagent/indy/credx/holder.py +++ b/aries_cloudagent/indy/credx/holder.py @@ -6,7 +6,7 @@ import re import uuid -from typing import Sequence, Tuple, Union +from typing import Dict, Sequence, Tuple, Union from aries_askar import AskarError, AskarErrorCode from indy_credx import ( @@ -471,27 +471,26 @@ async def create_presentation( """ - creds = {} - - def get_rev_state(cred_id, timestamp): - reg_id = creds[cred_id].rev_reg_id - if not reg_id: - raise IndyHolderError( - f"Cannot prove credential '{cred_id}' for " - "specific timestamp, credential has no rev_reg_id" - ) - if not rev_states or reg_id not in rev_states: - raise IndyHolderError( - f"No revocation states provided for credential '{cred_id}'" - f"with rev_reg_id '{reg_id}'" - ) - state = rev_states[reg_id].get(timestamp) - if not state: - raise IndyHolderError( - f"No revocation states provided for credential '{cred_id}'" - f"with rev_reg_id '{reg_id}' at timestamp {timestamp}" - ) - return state + creds: Dict[str, Credential] = {} + + def get_rev_state(cred_id: str, detail: dict): + cred = creds[cred_id] + rev_reg_id = cred.rev_reg_id + timestamp = detail.get("timestamp") if rev_reg_id else None + rev_state = None + if timestamp: + if not rev_states or rev_reg_id not in rev_states: + raise IndyHolderError( + f"No revocation states provided for credential '{cred_id}' " + f"with rev_reg_id '{rev_reg_id}'" + ) + rev_state = rev_states[rev_reg_id].get(timestamp) + if not rev_state: + raise IndyHolderError( + f"No revocation states provided for credential '{cred_id}' " + f"with rev_reg_id '{rev_reg_id}' at timestamp {timestamp}" + ) + return timestamp, rev_state self_attest = requested_credentials.get("self_attested_attributes") or {} present_creds = PresentCredentials() @@ -501,25 +500,26 @@ def get_rev_state(cred_id, timestamp): if cred_id not in creds: # NOTE: could be optimized if multiple creds are requested creds[cred_id] = await self._get_credential(cred_id) - timestamp = detail.get("timestamp") + timestamp, rev_state = get_rev_state(cred_id, detail) present_creds.add_attributes( creds[cred_id], reft, reveal=detail["revealed"], timestamp=timestamp, - rev_state=get_rev_state(cred_id, timestamp) if timestamp else None, + rev_state=rev_state, ) req_preds = requested_credentials.get("requested_predicates") or {} for reft, detail in req_preds.items(): + cred_id = detail["cred_id"] if cred_id not in creds: # NOTE: could be optimized if multiple creds are requested creds[cred_id] = await self._get_credential(cred_id) - timestamp = detail.get("timestamp") + timestamp, rev_state = get_rev_state(cred_id, detail) present_creds.add_predicates( creds[cred_id], reft, timestamp=timestamp, - rev_state=get_rev_state(cred_id, timestamp) if timestamp else None, + rev_state=rev_state, ) try: From 7ea41a6e388d13a6482ed3caf260925a3355a209 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Thu, 30 Jun 2022 10:11:36 -0700 Subject: [PATCH 314/872] 0.7.4 Release Changelog and version update Signed-off-by: Stephen Curran --- CHANGELOG.md | 111 ++++++++++++++++++++---------------- PUBLISHING.md | 2 +- aries_cloudagent/version.py | 2 +- open-api/openapi.json | 2 +- 4 files changed, 65 insertions(+), 52 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 61f8005bf9..a5cdf72fae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,43 +1,34 @@ -# 0.7.4-RC5 +# 0.7.4 -## June 23, 2022 +## June 30, 2022 -**NOTE** We expect 0.7.4-rc5 will the be the last Release Candidate prior to the -official 0.7.4 release. +0.7.4 is a significant release focused on stability and production deployments. +As the "patch" release number indicates, there were no breaking changes in the +Admin API, but a huge volume of updates and improvements. Highlights of this +release include: -**NOTE:** 0.7.4-rc4 contains a fix for a revocation-related issue introduced in -0.7.4-rc3. We recommend updating to 0.7.4-rc4 immediately if you have been using -0.7.4-rc3. +- A major performance and stability improvement resulting from the now +recommended use of [Aries Askar](https://github.com/bcgov/aries-askar) instead +of the Indy-SDK. +- There are significant improvements and tools for dealing with +revocation-related issues. +- A lot of work has been on the handling of Hyperledger Indy transaction +endorsements. +- ACA-Py now has a pluggable persistent queues mechanism in place, with Redis +and Kafka support available (albeit with work still to come on documentation). -- See issue: [\#1823](https://github.com/hyperledger/aries-cloudagent-python/issues/1823) -- See Pull Request: [\#1827](https://github.com/hyperledger/aries-cloudagent-python/pull/1827) +In addition, there are a significant number of general enhancements, bug fixes, +documentation updates and code management improvements. -The 0.7.4 release consists largely of internal fixes to ACA-Py, big increases in -performance resulting from the now recommended use of [Aries -Askar](https://github.com/bcgov/aries-askar) instead of the Indy-SDK, -improvements and tools for dealing with revocation-related issues, plus a number -of general enhancements. There have been a lot of groups exercising ACA-Py and the -updates made in this release are a reflection of those efforts. We have PRs that -have been contributed by 21 different people. +This release is a reflection of the many groups stressing ACA-Py in production +environments, reporting issues and the resulting solutions. We also have a very +large number of contributors to ACA-Py, with this release having PRs from 22 +different individuals. A big thank you to all of those using ACA-Py, raising +issues and providing solutions. -The largest enhancement is in the area of the endorsing of Hyperledger Indy -ledger transactions, enabling an instance of ACA-Py to act as an Endorser for -Indy authors needing endorsements to write objects to an Indy ledger. We're -hoping to see (and working on!) an "aries-endorser-service" come from that work, -an Endorser to be easily operated by an organization, ideally with a controller -starter kit supporting a basic approvals business workflow. - -A focus towards the end of the 0.7.4 development and release cycle was on the -handling of AnonCreds revocation in ACA-Py. Most important, a production issue -was uncovered where by an ACA-Py issuer's local Revocation Registry data could -get out of sync with what was published on an Indy ledger, resulting in an -inability to publish new RevRegEntry transactions -- making new revocations -impossible. As a result, we have added some new endpoints to enable an update to -the RevReg storage such that RevRegEntry transactions can again be published to -the ledger. Other changes were added related to revocation in general -and in the handling of tails files in particular. +### Major Enhancements -A lot of work has been put in for this release related to performance and load +A lot of work has been put into this release related to performance and load testing, with significant updates being made to the key "shared component" ACA-Py dependencies ([Aries Askar](https://github.com/bcgov/aries-askar), [Indy VDR](https://github.comyperledger/indy-vdr)) and [Indy Shared RS (including @@ -51,15 +42,36 @@ especially the team at LISSI for creating the to make load testing so easy! And of course to the core ACA-Py team for addressing the findings. +The largest enhancement is in the area of the endorsing of Hyperledger Indy +ledger transactions, enabling an instance of ACA-Py to act as an Endorser for +Indy authors needing endorsements to write objects to an Indy ledger. We're +working on an [Aries Endorser +Service](https://github.com/bcgov/aries-endorser-service) based on the new +capabilities in ACA-Py, an Endorser to be easily operated by an organization, +ideally with a controller starter kit supporting a basic human and automated +approvals business workflow. Contributions welcome! + +A focus towards the end of the 0.7.4 development and release cycle was on the +handling of AnonCreds revocation in ACA-Py. Most important, a production issue +was uncovered where by an ACA-Py issuer's local Revocation Registry data could +get out of sync with what was published on an Indy ledger, resulting in an +inability to publish new RevRegEntry transactions -- making new revocations +impossible. As a result, we have added some new endpoints to enable an update to +the RevReg storage such that RevRegEntry transactions can again be published to +the ledger. Other changes were added related to revocation in general +and in the handling of tails files in particular. + The team has worked a lot on evolving the persistent queue (PQ) approach -available in ACA-Py. We have landed on a design whereby the ability to use -queues for inbound and outbound messages is within ACA-Py, with a default -in-memory implementation, and the implementations of external persistent queues -solutions is handled by referencing a plugin from a separate repository. There -will shortly be two concrete, out-of-the-box solutions available, one for Kafka -and one for Redis, and anyone else can implement their own PQ plugin as long as -it uses the same ACA-Py queuing interface. Look for the new PQ repos shortly -within Hyperledger Aries. +available in ACA-Py. We have landed on a design for the queues for inbound and +outbound messages using a default in-memory implementation, and the ability to +replace the default method with implementations created via an ACA-Py plugin. +There are two concrete, out-of-the-box external persistent queuing solutions +available for [Redis](https://github.com/bcgov/aries-acapy-plugin-redis-events) +and [Kafka](https://github.com/sicpa-dlab/aries-acapy-plugin-kafka-events). +Those ACA-Py persistent queue implementation repositories will soon be migrated +to the Aries project within the Hyperledger Foundation's GitHub organization. +Anyone else can implement their own queuing plugin as long as it uses the same +interface. Several new ways to control ACA-Py configurations were added, including new startup parameters, Admin API parameters to control instances of protocols, and @@ -71,7 +83,9 @@ there no changes in the APIs. As well there were a number of internal fixes, dependency updates, documentation and demo changes, developer tools and release management updates. All the usual -stuff needed for a growing codebase. +stuff needed for a healthy, growing codebase. + +### Categorized List of Pull Requests - Hyperledger Indy Endorser related updates: - Fix order of operations connecting faber to endorser [\#1716](https://github.com/hyperledger/aries-cloudagent-python/pull/1716) ([ianco](https://github.com/ianco)) @@ -100,6 +114,7 @@ stuff needed for a growing codebase. - Redis PQ Cleanup in preparation for enabling the uses of plugin PQ implementations \[Issue\#1659\] [\#1659](https://github.com/hyperledger/aries-cloudagent-python/pull/1690) ([shaangill025](https://github.com/shaangill025)) - Credential Revocation and Tails File Handling + - Fix handling of non-revocable credential when timestamp is specified \(askar/credx\) [\#1847](https://github.com/hyperledger/aries-cloudagent-python/pull/1847) ([andrewwhitehead](https://github.com/andrewwhitehead)) - Additional endpoints to get revocation details and fix "published" status [\#1783](https://github.com/hyperledger/aries-cloudagent-python/pull/1783) ([ianco](https://github.com/ianco)) - Fix IssuerCredRevRecord state update on revocation publish [\#1827](https://github.com/hyperledger/aries-cloudagent-python/pull/1827) ([andrewwhitehead](https://github.com/andrewwhitehead)) - Fix put_file when the server returns a redirect [\#1808](https://github.com/hyperledger/aries-cloudagent-python/pull/1808) ([andrewwhitehead](https://github.com/andrewwhitehead)) @@ -157,13 +172,9 @@ stuff needed for a growing codebase. - Replace async workaround within document loader [\#1774](https://github.com/hyperledger/aries-cloudagent-python/pull/1774) ([frostyfrog](https://github.com/frostyfrog)) - Documentation and Demo Updates - - Changelog, version and ReadTheDocs updates for 0.7.4-rc3 release [\#1817](https://github.com/hyperledger/aries-cloudagent-python/pull/1817) ([swcurran](https://github.com/swcurran)) - - 0.7.4-rc2 update [\#1771](https://github.com/hyperledger/aries-cloudagent-python/pull/1771) ([swcurran](https://github.com/swcurran)) - - Some ReadTheDocs File updates [\#1770](https://github.com/hyperledger/aries-cloudagent-python/pull/1770) ([swcurran](https://github.com/swcurran)) - - 0.7.4-RC1 Changelog intro paragraph - fix copy/paste error [\#1753](https://github.com/hyperledger/aries-cloudagent-python/pull/1753) ([swcurran](https://github.com/swcurran)) - - Fixing the intro paragraph and heading in the changelog of this 0.7.4RC1 [\#1752](https://github.com/hyperledger/aries-cloudagent-python/pull/1752) ([swcurran](https://github.com/swcurran)) - - Updates to Changelog for 0.7.4. RC1 release [\#1747](https://github.com/hyperledger/aries-cloudagent-python/pull/1747) ([swcurran](https://github.com/swcurran)) - - Prep for adding the 0.7.4-rc0 tag [\#1722](https://github.com/hyperledger/aries-cloudagent-python/pull/1722) ([swcurran](https://github.com/swcurran)) + - Use default wallet type askar for alice/faber demo and bdd tests [\#1761](https://github.com/hyperledger/aries-cloudagent-python/pull/1761) ([ianco](https://github.com/ianco)) + - Update the Supported RFCs document for 0.7.4 release [\#1846](https://github.com/hyperledger/aries-cloudagent-python/pull/1846) ([swcurran](https://github.com/swcurran)) + - Fix a typo in DevReadMe.md [\#1844](https://github.com/hyperledger/aries-cloudagent-python/pull/1844) ([feknall](https://github.com/feknall)) - Add troubleshooting document, include initial examples - ledger connection, out-of-sync RevReg [\#1818](https://github.com/hyperledger/aries-cloudagent-python/pull/1818) ([swcurran](https://github.com/swcurran)) - Update POST /present-proof/send-request to POST /present-proof-2.0/send-request [\#1824](https://github.com/hyperledger/aries-cloudagent-python/pull/1824) ([lineko](https://github.com/lineko)) - Fetch from --genesis-url likely to fail in composed container [\#1746](https://github.com/hyperledger/aries-cloudagent-python/pull/1739) ([tdiesler](https://github.com/tdiesler)) @@ -176,6 +187,7 @@ stuff needed for a growing codebase. - Document impact of multi-ledger on TAA acceptance [\#1778](https://github.com/hyperledger/aries-cloudagent-python/pull/1778) ([ianco](https://github.com/ianco)) - Code management and contributor/developer support updates + - Set prefix for integration test demo agents; some code cleanup [\#1840](https://github.com/hyperledger/aries-cloudagent-python/pull/1840) ([andrewwhitehead](https://github.com/andrewwhitehead)) - Pin markupsafe at version 2.0.1 [\#1642](https://github.com/hyperledger/aries-cloudagent-python/pull/1642) ([andrewwhitehead](https://github.com/andrewwhitehead)) - style: format with stable black release [\#1615](https://github.com/hyperledger/aries-cloudagent-python/pull/1615) ([TimoGlastra](https://github.com/TimoGlastra)) - Remove references to play with von [\#1688](https://github.com/hyperledger/aries-cloudagent-python/pull/1688) ([ianco](https://github.com/ianco)) @@ -183,6 +195,7 @@ stuff needed for a growing codebase. - run_docker start - pass environment variables [\#1715](https://github.com/hyperledger/aries-cloudagent-python/pull/1715) ([shaangill025](https://github.com/shaangill025)) - Use local deps only [\#1834](https://github.com/hyperledger/aries-cloudagent-python/pull/1834) ([ryjones](https://github.com/ryjones)) - Enable pip-audit [\#1831](https://github.com/hyperledger/aries-cloudagent-python/pull/1831) ([ryjones](https://github.com/ryjones)) + - Only run pip-audit on main repo [\#1845](https://github.com/hyperledger/aries-cloudagent-python/pull/1845) ([ryjones](https://github.com/ryjones)) - Release management pull requests - 0.7.4-rc5 changelog, version and ReadTheDocs updates [\#1838](https://github.com/hyperledger/aries-cloudagent-python/pull/1838) ([swcurran](https://github.com/swcurran)) diff --git a/PUBLISHING.md b/PUBLISHING.md index 3fda774ed8..d8bd13a15d 100644 --- a/PUBLISHING.md +++ b/PUBLISHING.md @@ -8,7 +8,7 @@ Once ready to do a release, create a local branch that includes the following up 1. Create a PR branch from an updated `main` branch. -2. Update the CHANGELOG.md to add the new release. If transitioning for a Release Candidate to the final release for the tag, do not create a new section -- just drop the "RC" designation. Check the date of the new release. +2. Update the CHANGELOG.md to add the new release. Only create a new section when working on the first release candidate for a new release. When transitioning from one release candidate to the next, or to an official release, just update the title and date of the change log section. 3. Include details of the merged PRs included in this release. General process to follow: diff --git a/aries_cloudagent/version.py b/aries_cloudagent/version.py index c71e06b7b1..302f20a3ad 100644 --- a/aries_cloudagent/version.py +++ b/aries_cloudagent/version.py @@ -1,4 +1,4 @@ """Library version information.""" -__version__ = "0.7.4-rc5" +__version__ = "0.7.4" RECORD_TYPE_ACAPY_VERSION = "acapy_version" diff --git a/open-api/openapi.json b/open-api/openapi.json index d397ebf4b9..3494407fdf 100644 --- a/open-api/openapi.json +++ b/open-api/openapi.json @@ -1,7 +1,7 @@ { "swagger" : "2.0", "info" : { - "version" : "v0.7.4-rc5", + "version" : "v0.7.4", "title" : "Aries Cloud Agent" }, "tags" : [ { From 1141251082c2698e2a64ea75d69106c47c98088d Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Thu, 30 Jun 2022 10:13:19 -0700 Subject: [PATCH 315/872] Add this PR to changelog Signed-off-by: Stephen Curran --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5cdf72fae..6ab0f51c6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -198,6 +198,7 @@ stuff needed for a healthy, growing codebase. - Only run pip-audit on main repo [\#1845](https://github.com/hyperledger/aries-cloudagent-python/pull/1845) ([ryjones](https://github.com/ryjones)) - Release management pull requests + - 0.7.4 Release Changelog and version update [\#1849](https://github.com/hyperledger/aries-cloudagent-python/pull/1849) ([swcurran](https://github.com/swcurran)) - 0.7.4-rc5 changelog, version and ReadTheDocs updates [\#1838](https://github.com/hyperledger/aries-cloudagent-python/pull/1838) ([swcurran](https://github.com/swcurran)) - Update changelog and version for 0.7.4-rc4 [\#1830](https://github.com/hyperledger/aries-cloudagent-python/pull/1830) ([swcurran](https://github.com/swcurran)) - Changelog, version and ReadTheDocs updates for 0.7.4-rc3 release [\#1817](https://github.com/hyperledger/aries-cloudagent-python/pull/1817) ([swcurran](https://github.com/swcurran)) From aad950564161a668d8971b22e6d979cb9b1765e1 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Thu, 30 Jun 2022 20:47:27 -0400 Subject: [PATCH 316/872] test: out of band manager with route manager Signed-off-by: Daniel Bluhm --- .../out_of_band/v1_0/tests/test_manager.py | 55 ++++--------------- 1 file changed, 12 insertions(+), 43 deletions(-) diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py index f7ff19144a..cb515bf729 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py @@ -22,6 +22,7 @@ from .....multitenant.base import BaseMultitenantManager from .....multitenant.manager import MultitenantManager from .....protocols.coordinate_mediation.v1_0.manager import MediationManager +from .....protocols.coordinate_mediation.v1_0.route_manager import RouteManager from .....protocols.coordinate_mediation.v1_0.models.mediation_record import ( MediationRecord, ) @@ -359,6 +360,12 @@ def setUp(self): self.test_mediator_conn_id = "mediator-conn-id" self.test_mediator_endpoint = "http://mediator.example.com" + self.route_manager = async_mock.MagicMock(RouteManager) + self.route_manager.routing_info = async_mock.CoroutineMock( + return_value=(self.test_mediator_routing_keys, self.test_mediator_endpoint) + ) + self.manager._route_manager = self.route_manager + async def test_create_invitation_handshake_succeeds(self): self.profile.context.update_settings({"public_invites": True}) @@ -388,37 +395,6 @@ async def test_create_invitation_handshake_succeeds(self): ) assert invi_rec.invitation.services == [f"did:sov:{TestConfig.test_did}"] - async def test_create_invitation_mediation_overwrites_routing_and_endpoint(self): - async with self.profile.session() as session: - mock_conn_rec = async_mock.MagicMock() - - mediation_record = MediationRecord( - role=MediationRecord.ROLE_CLIENT, - state=MediationRecord.STATE_GRANTED, - connection_id=self.test_mediator_conn_id, - routing_keys=self.test_mediator_routing_keys, - endpoint=self.test_mediator_endpoint, - ) - await mediation_record.save(session) - with async_mock.patch.object( - MediationManager, - "get_default_mediator_id", - ) as mock_get_default_mediator, async_mock.patch.object( - mock_conn_rec, "metadata_set", async_mock.CoroutineMock() - ) as mock_metadata_set: - invite = await self.manager.create_invitation( - my_endpoint=TestConfig.test_endpoint, - my_label="test123", - hs_protos=[HSProto.RFC23], - mediation_id=mediation_record.mediation_id, - ) - assert isinstance(invite, InvitationRecord) - assert invite.invitation._type == DIDCommPrefix.qualify_current( - INVITATION - ) - assert invite.invitation.label == "test123" - mock_get_default_mediator.assert_not_called() - async def test_create_invitation_multitenant_local(self): self.profile.context.update_settings( { @@ -427,8 +403,6 @@ async def test_create_invitation_multitenant_local(self): } ) - self.multitenant_mgr.add_key = async_mock.CoroutineMock() - with async_mock.patch.object( InMemoryWallet, "create_signing_key", autospec=True ) as mock_wallet_create_signing_key, async_mock.patch.object( @@ -444,9 +418,7 @@ async def test_create_invitation_multitenant_local(self): multi_use=False, ) - self.multitenant_mgr.add_key.assert_called_once_with( - "test_wallet", TestConfig.test_verkey - ) + self.route_manager.route_invitation.assert_called_once() async def test_create_invitation_multitenant_public(self): self.profile.context.update_settings( @@ -457,8 +429,6 @@ async def test_create_invitation_multitenant_public(self): } ) - self.multitenant_mgr.add_key = async_mock.CoroutineMock() - with async_mock.patch.object( InMemoryWallet, "get_public_did", autospec=True ) as mock_wallet_get_public_did: @@ -475,9 +445,7 @@ async def test_create_invitation_multitenant_public(self): multi_use=False, ) - self.multitenant_mgr.add_key.assert_called_once_with( - "test_wallet", TestConfig.test_verkey, skip_if_exists=True - ) + self.route_manager.route_invitation.assert_called_once() async def test_create_invitation_no_handshake_no_attachments_x(self): with self.assertRaises(OutOfBandManagerError) as context: @@ -1214,6 +1182,9 @@ async def test_receive_invitation_with_invalid_mediation(self): mock_didx_recv_invi.return_value = mock_conn mock_retrieve_conn_by_id.return_value = mock_conn invi_msg = invite.invitation + self.route_manager.mediation_record_if_id = async_mock.CoroutineMock( + side_effect=StorageNotFoundError + ) await self.manager.receive_invitation( invi_msg, mediation_id="test-mediation-id", @@ -1457,8 +1428,6 @@ async def test_receive_invitation_handshake_reuse_failed(self): ConnRecord, "retrieve_by_id", async_mock.CoroutineMock(return_value=test_exist_conn), - ), async_mock.patch.object( - test_module, "mediation_record_if_id", async_mock.CoroutineMock() ): oob_invitation = InvitationMessage( handshake_protocols=[ From 16d987a31c16d984554c5b52b8eaf6e1d6307f87 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Thu, 30 Jun 2022 20:57:48 -0400 Subject: [PATCH 317/872] style: flake8 fixes for route manager Signed-off-by: Daniel Bluhm --- aries_cloudagent/multitenant/base.py | 1 + aries_cloudagent/multitenant/route_manager.py | 4 +++ .../v1_0/route_manager.py | 29 +++++++++++++------ 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/aries_cloudagent/multitenant/base.py b/aries_cloudagent/multitenant/base.py index b6e2c3cdd1..f316f7eadb 100644 --- a/aries_cloudagent/multitenant/base.py +++ b/aries_cloudagent/multitenant/base.py @@ -292,6 +292,7 @@ async def remove_wallet_profile(self, profile: Profile): """ def get_route_manager(self, sub_profile: Profile, wallet_id: str): + """Return a route manager for handling multitenant routing.""" return MultitenantRouteManager(self._profile, sub_profile, wallet_id) async def add_key( diff --git a/aries_cloudagent/multitenant/route_manager.py b/aries_cloudagent/multitenant/route_manager.py index fb9d96de36..c79cdd6faa 100644 --- a/aries_cloudagent/multitenant/route_manager.py +++ b/aries_cloudagent/multitenant/route_manager.py @@ -26,15 +26,18 @@ class MultitenantRouteManager(RouteManager): """Multitenancy route manager.""" def __init__(self, root_profile: Profile, sub_profile: Profile, wallet_id: str): + """Initialize multitenant route manager.""" self.root_profile = root_profile self.wallet_id = wallet_id super().__init__(sub_profile) @property def sub_profile(self) -> Profile: + """Return reference to sub wallet profile.""" return self.profile async def get_base_wallet_mediator(self) -> Optional[MediationRecord]: + """Get base wallet's default mediator.""" return await MediationManager(self.root_profile).get_default_mediator() async def _route_for_key( @@ -87,6 +90,7 @@ async def _route_for_key( async def routing_info( self, my_endpoint: str, mediation_record: Optional[MediationRecord] = None ) -> Tuple[List[str], str]: + """Return routing info.""" routing_keys = [] base_mediation_record = await self.get_base_wallet_mediator() diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py index adbcd9c18d..6dca92c02c 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py @@ -8,16 +8,19 @@ import logging from typing import List, Optional, Tuple -from aries_cloudagent.protocols.routing.v1_0.models.route_record import RouteRecord -from aries_cloudagent.storage.error import StorageNotFoundError +from aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.keylist_update import ( + KeylistUpdate, +) from ....connections.models.conn_record import ConnRecord from ....core.profile import Profile from ....messaging.responder import BaseResponder +from ....storage.error import StorageNotFoundError from ....wallet.base import BaseWallet from ....wallet.did_info import DIDInfo from ....wallet.did_method import DIDMethod from ....wallet.key_type import KeyType +from ...routing.v1_0.models.route_record import RouteRecord from .manager import MediationManager from .models.mediation_record import MediationRecord @@ -36,6 +39,7 @@ def __init__(self, profile: Profile): self.profile = profile async def get_or_create_my_did(self, conn_record: ConnRecord) -> DIDInfo: + """Create or retrieve DID info for a conneciton.""" if not conn_record.my_did: async with self.profile.session() as session: wallet = session.inject(BaseWallet) @@ -51,6 +55,7 @@ async def get_or_create_my_did(self, conn_record: ConnRecord) -> DIDInfo: return my_info def _validate_mediation_state(self, mediation_record: MediationRecord): + """Perform mediation state validation""" if mediation_record.state != MediationRecord.STATE_GRANTED: raise RouteManagerError( "Mediation is not granted for mediation identified by " @@ -114,14 +119,14 @@ async def _route_for_key( *, skip_if_exists: bool = False, replace_key: Optional[str] = None, - ): + ) -> Optional[KeylistUpdate]: """Route a key.""" async def route_connection_as_invitee( self, conn_record: ConnRecord, mediation_record: Optional[MediationRecord] = None, - ): + ) -> Optional[KeylistUpdate]: """Set up routing for a new connection when we are the invitee.""" LOGGER.debug("Routing connection as invitee") my_info = await self.get_or_create_my_did(conn_record) @@ -133,7 +138,7 @@ async def route_connection_as_inviter( self, conn_record: ConnRecord, mediation_record: Optional[MediationRecord] = None, - ): + ) -> Optional[KeylistUpdate]: """Set up routing for a new connection when we are the inviter.""" LOGGER.debug("Routing connection as inviter") my_info = await self.get_or_create_my_did(conn_record) @@ -148,7 +153,11 @@ async def route_connection( self, conn_record: ConnRecord, mediation_record: Optional[MediationRecord] = None, - ): + ) -> Optional[KeylistUpdate]: + """Setup routing for a connection. + + This method will evaluate connection state and call the appropriate methods. + """ if conn_record.rfc23_state == ConnRecord.State.INVITATION.rfc23strict( ConnRecord.Role.RESPONDER ): @@ -165,7 +174,7 @@ async def route_invitation( self, conn_record: ConnRecord, mediation_record: Optional[MediationRecord] = None, - ): + ) -> Optional[KeylistUpdate]: """Set up routing for receiving a response to an invitation.""" await self.save_mediator_for_connection(conn_record, mediation_record) @@ -184,7 +193,8 @@ async def route_static( self, conn_record: ConnRecord, mediation_record: Optional[MediationRecord] = None, - ): + ) -> Optional[KeylistUpdate]: + """Establish routing for a static connection.""" my_info = await self.get_or_create_my_did(conn_record) return await self._route_for_key( my_info.verkey, mediation_record, skip_if_exists=True @@ -196,6 +206,7 @@ async def save_mediator_for_connection( mediation_record: Optional[MediationRecord] = None, mediation_id: Optional[str] = None, ): + """Save mediator info to connection metadata.""" async with self.profile.session() as session: if mediation_id: mediation_record = await MediationRecord.retrieve_by_id( @@ -228,7 +239,7 @@ async def _route_for_key( *, skip_if_exists: bool = False, replace_key: Optional[str] = None, - ): + ) -> Optional[KeylistUpdate]: if not mediation_record: return None From f19e16019656ee0f9321e6f91eebd11f2a075372 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Thu, 30 Jun 2022 21:29:05 -0400 Subject: [PATCH 318/872] refactor: remove add_key from base multitenant mgr Signed-off-by: Daniel Bluhm --- aries_cloudagent/multitenant/base.py | 45 --------- .../multitenant/tests/test_base.py | 91 ++++--------------- 2 files changed, 16 insertions(+), 120 deletions(-) diff --git a/aries_cloudagent/multitenant/base.py b/aries_cloudagent/multitenant/base.py index f316f7eadb..78e9b184b3 100644 --- a/aries_cloudagent/multitenant/base.py +++ b/aries_cloudagent/multitenant/base.py @@ -10,7 +10,6 @@ from ..config.injection_context import InjectionContext from ..core.error import BaseError from ..core.profile import Profile, ProfileSession -from ..messaging.responder import BaseResponder from ..multitenant.route_manager import MultitenantRouteManager from ..protocols.coordinate_mediation.v1_0.manager import ( MediationManager, @@ -19,7 +18,6 @@ from ..protocols.routing.v1_0.manager import RouteNotFoundError, RoutingManager from ..protocols.routing.v1_0.models.route_record import RouteRecord from ..storage.base import BaseStorage -from ..storage.error import StorageNotFoundError from ..transport.wire_format import BaseWireFormat from ..wallet.base import BaseWallet from ..wallet.models.wallet_record import WalletRecord @@ -295,49 +293,6 @@ def get_route_manager(self, sub_profile: Profile, wallet_id: str): """Return a route manager for handling multitenant routing.""" return MultitenantRouteManager(self._profile, sub_profile, wallet_id) - async def add_key( - self, wallet_id: str, recipient_key: str, *, skip_if_exists: bool = False - ): - """ - Add a wallet key to map incoming messages to specific subwallets. - - Args: - wallet_id: The wallet id the key corresponds to - recipient_key: The recipient key belonging to the wallet - skip_if_exists: Whether to skip the action if the key is already registered - for relaying / mediation - """ - - LOGGER.info( - f"Add route record for recipient {recipient_key} to wallet {wallet_id}" - ) - routing_mgr = RoutingManager(self._profile) - mediation_mgr = MediationManager(self._profile) - mediation_record = await mediation_mgr.get_default_mediator() - - if skip_if_exists: - try: - async with self._profile.session() as session: - await RouteRecord.retrieve_by_recipient_key(session, recipient_key) - - # If no error is thrown, it means there is already a record - return - except (StorageNotFoundError): - pass - - await routing_mgr.create_route_record( - recipient_key=recipient_key, internal_wallet_id=wallet_id - ) - - # External mediation - if mediation_record: - keylist_updates = await mediation_mgr.add_key(recipient_key) - - responder = self._profile.inject(BaseResponder) - await responder.send( - keylist_updates, connection_id=mediation_record.connection_id - ) - async def create_auth_token( self, wallet_record: WalletRecord, wallet_key: str = None ) -> str: diff --git a/aries_cloudagent/multitenant/tests/test_base.py b/aries_cloudagent/multitenant/tests/test_base.py index f20605e9cb..121cf6af08 100644 --- a/aries_cloudagent/multitenant/tests/test_base.py +++ b/aries_cloudagent/multitenant/tests/test_base.py @@ -191,13 +191,18 @@ async def test_create_wallet_fails_if_wallet_name_exists(self): async def test_create_wallet_saves_wallet_record_creates_profile(self): + mock_route_manager = async_mock.MagicMock() + mock_route_manager.route_public_did = async_mock.CoroutineMock() + with async_mock.patch.object( WalletRecord, "save" ) as wallet_record_save, async_mock.patch.object( BaseMultitenantManager, "get_wallet_profile" ) as get_wallet_profile, async_mock.patch.object( - BaseMultitenantManager, "add_key" - ) as add_key: + BaseMultitenantManager, + "get_route_manager", + async_mock.MagicMock(return_value=mock_route_manager), + ): get_wallet_profile.return_value = InMemoryProfile.test_profile() wallet_record = await self.manager.create_wallet( @@ -212,7 +217,7 @@ async def test_create_wallet_saves_wallet_record_creates_profile(self): {"wallet.key": "test_key"}, provision=True, ) - add_key.assert_not_called() + mock_route_manager.route_public_did.assert_not_called() assert isinstance(wallet_record, WalletRecord) assert wallet_record.wallet_name == "test_wallet" assert wallet_record.key_management_mode == WalletRecord.MODE_MANAGED @@ -227,13 +232,18 @@ async def test_create_wallet_adds_wallet_route(self): key_type=KeyType.ED25519, ) + mock_route_manager = async_mock.MagicMock() + mock_route_manager.route_public_did = async_mock.CoroutineMock() + with async_mock.patch.object( WalletRecord, "save" ) as wallet_record_save, async_mock.patch.object( BaseMultitenantManager, "get_wallet_profile" ) as get_wallet_profile, async_mock.patch.object( - BaseMultitenantManager, "add_key" - ) as add_key, async_mock.patch.object( + BaseMultitenantManager, + "get_route_manager", + async_mock.MagicMock(return_value=mock_route_manager), + ), async_mock.patch.object( InMemoryWallet, "get_public_did" ) as get_public_did: get_wallet_profile.return_value = InMemoryProfile.test_profile() @@ -244,9 +254,7 @@ async def test_create_wallet_adds_wallet_route(self): WalletRecord.MODE_MANAGED, ) - add_key.assert_called_once_with( - wallet_record.wallet_id, did_info.verkey, skip_if_exists=True - ) + mock_route_manager.route_public_did.assert_called_once_with(did_info.verkey) wallet_record_save.assert_called_once() get_wallet_profile.assert_called_once_with( @@ -337,73 +345,6 @@ async def test_remove_wallet_removes_profile_wallet_storage_records(self): RouteRecord.RECORD_TYPE, {"wallet_id": "test"} ) - async def test_add_key_no_mediation(self): - with async_mock.patch.object( - RoutingManager, "create_route_record" - ) as create_route_record, async_mock.patch.object( - MediationManager, "add_key" - ) as mediation_add_key: - await self.manager.add_key("wallet_id", "recipient_key") - - create_route_record.assert_called_once_with( - recipient_key="recipient_key", internal_wallet_id="wallet_id" - ) - mediation_add_key.assert_not_called() - - async def test_add_key_skip_if_exists_does_not_exist(self): - with async_mock.patch.object( - RoutingManager, "create_route_record" - ) as create_route_record, async_mock.patch.object( - RouteRecord, "retrieve_by_recipient_key" - ) as retrieve_by_recipient_key: - retrieve_by_recipient_key.side_effect = StorageNotFoundError() - - await self.manager.add_key( - "wallet_id", "recipient_key", skip_if_exists=True - ) - - create_route_record.assert_called_once_with( - recipient_key="recipient_key", internal_wallet_id="wallet_id" - ) - - async def test_add_key_skip_if_exists_does_exist(self): - with async_mock.patch.object( - RoutingManager, "create_route_record" - ) as create_route_record, async_mock.patch.object( - RouteRecord, "retrieve_by_recipient_key" - ) as retrieve_by_recipient_key: - await self.manager.add_key( - "wallet_id", "recipient_key", skip_if_exists=True - ) - - create_route_record.assert_not_called() - - async def test_add_key_mediation(self): - with async_mock.patch.object( - RoutingManager, "create_route_record" - ) as create_route_record, async_mock.patch.object( - MediationManager, "get_default_mediator" - ) as get_default_mediator, async_mock.patch.object( - MediationManager, "add_key" - ) as mediation_add_key: - default_mediator = async_mock.CoroutineMock() - keylist_updates = async_mock.CoroutineMock() - - get_default_mediator.return_value = default_mediator - mediation_add_key.return_value = keylist_updates - - await self.manager.add_key("wallet_id", "recipient_key") - - create_route_record.assert_called_once_with( - recipient_key="recipient_key", internal_wallet_id="wallet_id" - ) - - get_default_mediator.assert_called_once() - mediation_add_key.assert_called_once_with("recipient_key") - self.responder.send.assert_called_once_with( - keylist_updates, connection_id=default_mediator.connection_id - ) - async def test_create_auth_token_fails_no_wallet_key_but_required(self): self.profile.settings["multitenant.jwt_secret"] = "very_secret_jwt" wallet_record = WalletRecord( From a99a95222cccd00ff0c258ffc1ac0cb34c89de10 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Thu, 30 Jun 2022 21:36:51 -0400 Subject: [PATCH 319/872] test: conn rec handler mediation id Signed-off-by: Daniel Bluhm --- .../handlers/tests/test_request_handler.py | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/aries_cloudagent/protocols/connections/v1_0/handlers/tests/test_request_handler.py b/aries_cloudagent/protocols/connections/v1_0/handlers/tests/test_request_handler.py index ec16a72cef..7fa1d3ab31 100644 --- a/aries_cloudagent/protocols/connections/v1_0/handlers/tests/test_request_handler.py +++ b/aries_cloudagent/protocols/connections/v1_0/handlers/tests/test_request_handler.py @@ -83,7 +83,7 @@ async def test_called(self, mock_conn_mgr, request_context): responder = MockResponder() await handler_inst.handle(request_context, responder) mock_conn_mgr.return_value.receive_request.assert_called_once_with( - request_context.message, request_context.message_receipt, mediation_id=None + request_context.message, request_context.message_receipt ) assert not responder.messages @@ -101,31 +101,38 @@ async def test_called_with_auto_response(self, mock_conn_mgr, request_context): responder = MockResponder() await handler_inst.handle(request_context, responder) mock_conn_mgr.return_value.receive_request.assert_called_once_with( - request_context.message, request_context.message_receipt, mediation_id=None + request_context.message, request_context.message_receipt + ) + mock_conn_mgr.return_value.create_response.assert_called_once_with( + mock_conn_rec, mediation_id=None ) assert responder.messages @pytest.mark.asyncio @async_mock.patch.object(handler, "ConnectionManager") - async def test_connection_record_with_mediation_metadata( + async def test_connection_record_with_mediation_metadata_auto_response( self, mock_conn_mgr, request_context, connection_record ): - mock_conn_mgr.return_value.receive_request = async_mock.CoroutineMock() + mock_conn_rec = async_mock.MagicMock() + mock_conn_rec.accept = ConnRecord.ACCEPT_AUTO + mock_conn_mgr.return_value.receive_request = async_mock.CoroutineMock( + return_value=mock_conn_rec + ) + mock_conn_mgr.return_value.create_response = async_mock.CoroutineMock() request_context.message = ConnectionRequest() with async_mock.patch.object( connection_record, "metadata_get", async_mock.CoroutineMock(return_value={"id": "test-mediation-id"}), - ) as mock_metadata_get: + ): handler_inst = handler.ConnectionRequestHandler() responder = MockResponder() await handler_inst.handle(request_context, responder) - mock_conn_mgr.return_value.receive_request.assert_called_once_with( - request_context.message, - request_context.message_receipt, - mediation_id="test-mediation-id", + mock_conn_mgr.return_value.receive_request.assert_called_once() + mock_conn_mgr.return_value.create_response.assert_called_once_with( + mock_conn_rec, mediation_id="test-mediation-id" ) - assert not responder.messages + assert responder.messages @pytest.mark.asyncio @async_mock.patch.object(handler, "ConnectionManager") @@ -146,7 +153,6 @@ async def test_connection_record_without_mediation_metadata( mock_conn_mgr.return_value.receive_request.assert_called_once_with( request_context.message, request_context.message_receipt, - mediation_id=None, ) assert not responder.messages From 835e9ead18203dba9b51acf1561182be86bd1c64 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Thu, 30 Jun 2022 23:03:26 -0400 Subject: [PATCH 320/872] test: connection manager with route manager Signed-off-by: Daniel Bluhm --- .../connections/v1_0/tests/test_manager.py | 694 +++++------------- .../out_of_band/v1_0/tests/test_manager.py | 41 ++ 2 files changed, 238 insertions(+), 497 deletions(-) diff --git a/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py index c1b1bb0ba1..bbb681645e 100644 --- a/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py @@ -24,12 +24,12 @@ from .....storage.error import StorageNotFoundError from .....transport.inbound.receipt import MessageReceipt from .....wallet.base import DIDInfo -from .....wallet.did_info import KeyInfo from .....wallet.did_method import DIDMethod from .....wallet.error import WalletNotFoundError from .....wallet.in_memory import InMemoryWallet from .....wallet.key_type import KeyType from ....coordinate_mediation.v1_0.manager import MediationManager +from ....coordinate_mediation.v1_0.route_manager import RouteManager from ....coordinate_mediation.v1_0.messages.inner.keylist_update_rule import ( KeylistUpdateRule, ) @@ -106,6 +106,14 @@ async def setUp(self): self.test_mediator_endpoint = "http://mediator.example.com" self.manager = ConnectionManager(self.profile) + self.route_manager = async_mock.MagicMock(RouteManager) + self.route_manager.routing_info = async_mock.CoroutineMock( + return_value=([], self.test_endpoint) + ) + self.route_manager.mediation_record_if_id = async_mock.CoroutineMock( + return_value=None + ) + self.manager._route_manager = self.route_manager assert self.manager.profile async def test_create_invitation_public_and_multi_use_fails(self): @@ -155,6 +163,7 @@ async def test_create_invitation_non_multi_use_invitation_fails_on_reuse(self): async def test_create_invitation_public(self): self.context.update_settings({"public_invites": True}) + self.route_manager.route_public_did = async_mock.CoroutineMock() with async_mock.patch.object( InMemoryWallet, "get_public_did", autospec=True ) as mock_wallet_get_public_did: @@ -171,45 +180,8 @@ async def test_create_invitation_public(self): assert connect_record is None assert connect_invite.did.endswith(self.test_did) - - async def test_create_invitation_multitenant(self): - self.context.update_settings( - {"wallet.id": "test_wallet", "multitenant.enabled": True} - ) - - with async_mock.patch.object( - InMemoryWallet, "create_signing_key", autospec=True - ) as mock_wallet_create_signing_key: - mock_wallet_create_signing_key.return_value = KeyInfo( - self.test_verkey, None, KeyType.ED25519 - ) - await self.manager.create_invitation() - self.multitenant_mgr.add_key.assert_called_once_with( - "test_wallet", self.test_verkey - ) - - async def test_create_invitation_public_multitenant(self): - self.context.update_settings( - { - "public_invites": True, - "wallet.id": "test_wallet", - "multitenant.enabled": True, - } - ) - - with async_mock.patch.object( - InMemoryWallet, "get_public_did", autospec=True - ) as mock_wallet_get_public_did: - mock_wallet_get_public_did.return_value = DIDInfo( - self.test_did, - self.test_verkey, - None, - method=DIDMethod.SOV, - key_type=KeyType.ED25519, - ) - await self.manager.create_invitation(public=True) - self.multitenant_mgr.add_key.assert_called_once_with( - "test_wallet", self.test_verkey, skip_if_exists=True + self.route_manager.route_public_did.assert_called_once_with( + self.test_verkey ) async def test_create_invitation_public_no_public_invites(self): @@ -338,6 +310,9 @@ async def test_create_invitation_multi_use_metadata_transfers_to_connection(self assert await new_conn_rec.metadata_get_all(session) == {"test": "value"} async def test_create_invitation_mediation_overwrites_routing_and_endpoint(self): + self.route_manager.routing_info = async_mock.CoroutineMock( + return_value=(self.test_mediator_routing_keys, self.test_mediator_endpoint) + ) async with self.profile.session() as session: mediation_record = MediationRecord( role=MediationRecord.ROLE_CLIENT, @@ -361,6 +336,9 @@ async def test_create_invitation_mediation_overwrites_routing_and_endpoint(self) mock_get_default_mediator.assert_not_called() async def test_create_invitation_mediation_using_default(self): + self.route_manager.routing_info = async_mock.CoroutineMock( + return_value=(self.test_mediator_routing_keys, self.test_mediator_endpoint) + ) async with self.profile.session() as session: mediation_record = MediationRecord( role=MediationRecord.ROLE_CLIENT, @@ -371,17 +349,19 @@ async def test_create_invitation_mediation_using_default(self): ) await mediation_record.save(session) with async_mock.patch.object( - MediationManager, - "get_default_mediator", + self.route_manager, + "mediation_record_if_id", async_mock.CoroutineMock(return_value=mediation_record), - ) as mock_get_default_mediator: + ): _, invite = await self.manager.create_invitation( routing_keys=[self.test_verkey], my_endpoint=self.test_endpoint, ) assert invite.routing_keys == self.test_mediator_routing_keys assert invite.endpoint == self.test_mediator_endpoint - mock_get_default_mediator.assert_called_once() + self.route_manager.routing_info.assert_awaited_once_with( + self.test_endpoint, mediation_record + ) async def test_receive_invitation(self): (_, connect_invite) = await self.manager.create_invitation( @@ -434,41 +414,6 @@ async def test_receive_invitation_mediation_passes_id_when_auto_accept(self): invitee_record, mediation_id="test-mediation-id" ) - async def test_receive_invitation_bad_mediation(self): - _, connect_invite = await self.manager.create_invitation( - my_endpoint="testendpoint" - ) - with self.assertRaises(StorageNotFoundError): - await self.manager.receive_invitation( - connect_invite, mediation_id="not-a-mediation-id" - ) - - async def test_receive_invitation_mediation_not_granted(self): - async with self.profile.session() as session: - _, connect_invite = await self.manager.create_invitation( - my_endpoint="testendpoint" - ) - - mediation_record = MediationRecord( - role=MediationRecord.ROLE_CLIENT, - state=MediationRecord.STATE_DENIED, - connection_id=self.test_mediator_conn_id, - routing_keys=self.test_mediator_routing_keys, - endpoint=self.test_mediator_endpoint, - ) - await mediation_record.save(session) - with self.assertRaises(BaseConnectionManagerError): - await self.manager.receive_invitation( - connect_invite, mediation_id=mediation_record.mediation_id - ) - - mediation_record.state = MediationRecord.STATE_REQUEST - await mediation_record.save(session) - with self.assertRaises(BaseConnectionManagerError): - await self.manager.receive_invitation( - connect_invite, mediation_id=mediation_record.mediation_id - ) - async def test_create_request(self): conn_req = await self.manager.create_request( ConnRecord( @@ -515,10 +460,27 @@ async def test_create_request_multitenant(self): self.context.update_settings( {"wallet.id": "test_wallet", "multitenant.enabled": True} ) + mediation_record = MediationRecord( + role=MediationRecord.ROLE_CLIENT, + state=MediationRecord.STATE_GRANTED, + connection_id=self.test_mediator_conn_id, + routing_keys=self.test_mediator_routing_keys, + endpoint=self.test_mediator_endpoint, + ) with async_mock.patch.object( InMemoryWallet, "create_local_did", autospec=True - ) as mock_wallet_create_local_did: + ) as mock_wallet_create_local_did, async_mock.patch.object( + self.multitenant_mgr, + "get_default_mediator", + async_mock.CoroutineMock(return_value=mediation_record), + ), async_mock.patch.object( + ConnectionManager, "create_did_document", autospec=True + ) as create_did_document, async_mock.patch.object( + self.route_manager, + "mediation_record_for_connection", + async_mock.CoroutineMock(return_value=None), + ): mock_wallet_create_local_did.return_value = DIDInfo( self.test_did, self.test_verkey, @@ -532,69 +494,66 @@ async def test_create_request_multitenant(self): their_label="Hello", their_role=ConnRecord.Role.RESPONDER.rfc160, alias="Bob", - ) + ), + my_endpoint=self.test_endpoint, ) - self.multitenant_mgr.add_key.assert_called_once_with( - "test_wallet", self.test_verkey + create_did_document.assert_called_once_with( + self.manager, + mock_wallet_create_local_did.return_value, + None, + [self.test_endpoint], + mediation_records=[mediation_record], ) + self.route_manager.route_connection_as_invitee.assert_called_once() async def test_create_request_mediation_id(self): - async with self.profile.session() as session: - mediation_record = MediationRecord( - role=MediationRecord.ROLE_CLIENT, - state=MediationRecord.STATE_GRANTED, - connection_id=self.test_mediator_conn_id, - routing_keys=self.test_mediator_routing_keys, - endpoint=self.test_mediator_endpoint, - ) - await mediation_record.save(session) - - record = ConnRecord( - invitation_key=self.test_verkey, - their_label="Hello", - their_role=ConnRecord.Role.RESPONDER.rfc160, - alias="Bob", - ) + mediation_record = MediationRecord( + role=MediationRecord.ROLE_CLIENT, + state=MediationRecord.STATE_GRANTED, + connection_id=self.test_mediator_conn_id, + routing_keys=self.test_mediator_routing_keys, + endpoint=self.test_mediator_endpoint, + ) - # Ensure the path with new did creation is hit - record.my_did = None + record = ConnRecord( + invitation_key=self.test_verkey, + their_label="Hello", + their_role=ConnRecord.Role.RESPONDER.rfc160, + alias="Bob", + ) - with async_mock.patch.object( - ConnectionManager, "create_did_document", autospec=True - ) as create_did_document, async_mock.patch.object( - InMemoryWallet, "create_local_did" - ) as create_local_did, async_mock.patch.object( - MediationManager, "get_default_mediator" - ) as mock_get_default_mediator: - did_info = DIDInfo( - did=self.test_did, - verkey=self.test_verkey, - metadata={}, - method=DIDMethod.SOV, - key_type=KeyType.ED25519, - ) - create_local_did.return_value = did_info - await self.manager.create_request( - record, - mediation_id=mediation_record.mediation_id, - my_endpoint=self.test_endpoint, - ) - create_local_did.assert_called_once_with(DIDMethod.SOV, KeyType.ED25519) - create_did_document.assert_called_once_with( - self.manager, - did_info, - None, - [self.test_endpoint], - mediation_records=[mediation_record], - ) - mock_get_default_mediator.assert_not_called() + # Ensure the path with new did creation is hit + record.my_did = None - assert len(self.responder.messages) == 1 - message, used_kwargs = self.responder.messages[0] - assert isinstance(message, KeylistUpdate) - assert ( - "connection_id" in used_kwargs - and used_kwargs["connection_id"] == self.test_mediator_conn_id + with async_mock.patch.object( + ConnectionManager, "create_did_document", autospec=True + ) as create_did_document, async_mock.patch.object( + InMemoryWallet, "create_local_did" + ) as create_local_did, async_mock.patch.object( + self.route_manager, + "mediation_record_for_connection", + async_mock.CoroutineMock(return_value=mediation_record), + ): + did_info = DIDInfo( + did=self.test_did, + verkey=self.test_verkey, + metadata={}, + method=DIDMethod.SOV, + key_type=KeyType.ED25519, + ) + create_local_did.return_value = did_info + await self.manager.create_request( + record, + mediation_id=mediation_record.mediation_id, + my_endpoint=self.test_endpoint, + ) + create_local_did.assert_called_once_with(DIDMethod.SOV, KeyType.ED25519) + create_did_document.assert_called_once_with( + self.manager, + did_info, + None, + [self.test_endpoint], + mediation_records=[mediation_record], ) async def test_create_request_default_mediator(self): @@ -623,10 +582,10 @@ async def test_create_request_default_mediator(self): ) as create_did_document, async_mock.patch.object( InMemoryWallet, "create_local_did" ) as create_local_did, async_mock.patch.object( - MediationManager, - "get_default_mediator", + self.route_manager, + "mediation_record_for_connection", async_mock.CoroutineMock(return_value=mediation_record), - ) as mock_get_default_mediator: + ): did_info = DIDInfo( did=self.test_did, verkey=self.test_verkey, @@ -647,44 +606,6 @@ async def test_create_request_default_mediator(self): [self.test_endpoint], mediation_records=[mediation_record], ) - mock_get_default_mediator.assert_called_once() - - assert len(self.responder.messages) == 1 - message, used_kwargs = self.responder.messages[0] - assert isinstance(message, KeylistUpdate) - assert ( - "connection_id" in used_kwargs - and used_kwargs["connection_id"] == self.test_mediator_conn_id - ) - - async def test_create_request_bad_mediation(self): - record, _ = await self.manager.create_invitation(my_endpoint="testendpoint") - with self.assertRaises(StorageNotFoundError): - await self.manager.create_request(record, mediation_id="not-a-mediation-id") - - async def test_create_request_mediation_not_granted(self): - async with self.profile.session() as session: - record, _ = await self.manager.create_invitation(my_endpoint="testendpoint") - - mediation_record = MediationRecord( - role=MediationRecord.ROLE_CLIENT, - state=MediationRecord.STATE_DENIED, - connection_id=self.test_mediator_conn_id, - routing_keys=self.test_mediator_routing_keys, - endpoint=self.test_mediator_endpoint, - ) - await mediation_record.save(session) - with self.assertRaises(BaseConnectionManagerError): - await self.manager.create_request( - record, mediation_id=mediation_record.mediation_id - ) - - mediation_record.state = MediationRecord.STATE_REQUEST - await mediation_record.save(session) - with self.assertRaises(BaseConnectionManagerError): - await self.manager.create_request( - record, mediation_id=mediation_record.mediation_id - ) async def test_receive_request_public_did_oob_invite(self): async with self.profile.session() as session: @@ -762,114 +683,6 @@ async def test_receive_request_public_did_conn_invite(self): conn_rec = await self.manager.receive_request(mock_request, receipt) assert conn_rec - async def test_receive_request_multi_use_multitenant(self): - async with self.profile.session() as session: - multiuse_info = await session.wallet.create_local_did( - DIDMethod.SOV, KeyType.ED25519 - ) - new_info = await session.wallet.create_local_did( - DIDMethod.SOV, KeyType.ED25519 - ) - - mock_request = async_mock.MagicMock() - mock_request.connection = async_mock.MagicMock( - is_multiuse_invitation=True, invitation_key=multiuse_info.verkey - ) - mock_request.connection.did = self.test_did - mock_request.connection.did_doc = async_mock.MagicMock() - mock_request.connection.did_doc.did = self.test_did - receipt = MessageReceipt(recipient_verkey=multiuse_info.verkey) - - self.context.update_settings( - {"wallet.id": "test_wallet", "multitenant.enabled": True} - ) - with async_mock.patch.object( - ConnRecord, "attach_request", autospec=True - ), async_mock.patch.object( - ConnRecord, "save", autospec=True - ), async_mock.patch.object( - ConnRecord, "retrieve_by_invitation_key" - ) as mock_conn_retrieve_by_invitation_key, async_mock.patch.object( - InMemoryWallet, "create_local_did", autospec=True - ) as mock_wallet_create_local_did: - mock_wallet_create_local_did.return_value = DIDInfo( - new_info.did, - new_info.verkey, - None, - method=DIDMethod.SOV, - key_type=KeyType.ED25519, - ) - mock_conn_retrieve_by_invitation_key.return_value = ( - async_mock.MagicMock( - connection_id="dummy", - retrieve_invitation=async_mock.CoroutineMock(return_value={}), - metadata_get_all=async_mock.CoroutineMock(return_value={}), - ) - ) - await self.manager.receive_request(mock_request, receipt) - - self.multitenant_mgr.add_key.assert_called_once_with( - "test_wallet", new_info.verkey - ) - - async def test_receive_request_public_multitenant(self): - async with self.profile.session() as session: - new_info = await session.wallet.create_local_did( - DIDMethod.SOV, KeyType.ED25519 - ) - - mock_request = async_mock.MagicMock() - mock_request.connection = async_mock.MagicMock( - accept=ConnRecord.ACCEPT_MANUAL - ) - mock_request.connection.did = self.test_did - mock_request.connection.did_doc = async_mock.MagicMock() - mock_request.connection.did_doc.did = self.test_did - receipt = MessageReceipt(recipient_did_public=True) - - self.context.update_settings( - { - "wallet.id": "test_wallet", - "multitenant.enabled": True, - "public_invites": True, - "debug.auto_accept_requests": False, - } - ) - - with async_mock.patch.object( - ConnRecord, "retrieve_request", autospec=True - ), async_mock.patch.object( - ConnRecord, "attach_request", autospec=True - ), async_mock.patch.object( - ConnRecord, "save", autospec=True - ), async_mock.patch.object( - InMemoryWallet, "create_local_did", autospec=True - ) as mock_wallet_create_local_did, async_mock.patch.object( - InMemoryWallet, "get_local_did", autospec=True - ) as mock_wallet_get_local_did, async_mock.patch.object( - ConnRecord, "retrieve_by_invitation_msg_id", async_mock.CoroutineMock() - ) as mock_conn_retrieve_by_invitation_msg_id: - mock_conn_retrieve_by_invitation_msg_id.return_value = ConnRecord() - mock_wallet_create_local_did.return_value = DIDInfo( - new_info.did, - new_info.verkey, - None, - method=DIDMethod.SOV, - key_type=KeyType.ED25519, - ) - mock_wallet_get_local_did.return_value = DIDInfo( - self.test_did, - self.test_verkey, - None, - method=DIDMethod.SOV, - key_type=KeyType.ED25519, - ) - await self.manager.receive_request(mock_request, receipt) - - self.multitenant_mgr.add_key.assert_called_once_with( - "test_wallet", new_info.verkey - ) - async def test_receive_request_public_did_no_did_doc(self): async with self.profile.session() as session: mock_request = async_mock.MagicMock() @@ -999,136 +812,6 @@ async def test_receive_request_public_did_no_auto_accept(self): messages = self.responder.messages assert not messages - async def test_receive_request_mediation_id(self): - async with self.profile.session() as session: - - mock_request = async_mock.MagicMock() - mock_request.connection = async_mock.MagicMock() - mock_request.connection.did = self.test_did - mock_request.connection.did_doc = async_mock.MagicMock() - mock_request.connection.did_doc.did = self.test_did - - receipt = MessageReceipt( - recipient_did=self.test_did, recipient_did_public=False - ) - await session.wallet.create_local_did( - method=DIDMethod.SOV, - key_type=KeyType.ED25519, - seed=None, - did=self.test_did, - ) - - mediation_record = MediationRecord( - role=MediationRecord.ROLE_CLIENT, - state=MediationRecord.STATE_GRANTED, - connection_id=self.test_mediator_conn_id, - routing_keys=self.test_mediator_routing_keys, - endpoint=self.test_mediator_endpoint, - ) - await mediation_record.save(session) - - record, invite = await self.manager.create_invitation() - record.accept = ConnRecord.ACCEPT_MANUAL - - await record.save(session) - - with async_mock.patch.object( - ConnRecord, "save", autospec=True - ) as mock_conn_rec_save, async_mock.patch.object( - ConnRecord, "attach_request", autospec=True - ) as mock_conn_attach_request, async_mock.patch.object( - ConnRecord, "retrieve_by_invitation_key" - ) as mock_conn_retrieve_by_invitation_key, async_mock.patch.object( - ConnRecord, "retrieve_request", autospec=True - ): - mock_conn_retrieve_by_invitation_key.return_value = record - conn_rec = await self.manager.receive_request( - mock_request, receipt, mediation_id=mediation_record.mediation_id - ) - - assert len(self.responder.messages) == 1 - message, target = self.responder.messages[0] - assert isinstance(message, KeylistUpdate) - assert len(message.updates) == 1 - (remove,) = message.updates - assert remove.action == KeylistUpdateRule.RULE_REMOVE - assert remove.recipient_key == record.invitation_key - - async def test_receive_request_bad_mediation(self): - mock_request = async_mock.MagicMock() - mock_request.connection = async_mock.MagicMock() - mock_request.connection.did = self.test_did - mock_request.connection.did_doc = async_mock.MagicMock() - mock_request.connection.did_doc.did = self.test_did - receipt = MessageReceipt( - recipient_did=self.test_did, recipient_did_public=False - ) - record, invite = await self.manager.create_invitation() - with async_mock.patch.object( - ConnRecord, "save", autospec=True - ) as mock_conn_rec_save, async_mock.patch.object( - ConnRecord, "attach_request", autospec=True - ) as mock_conn_attach_request, async_mock.patch.object( - ConnRecord, "retrieve_by_invitation_key" - ) as mock_conn_retrieve_by_invitation_key, async_mock.patch.object( - ConnRecord, "retrieve_request", autospec=True - ): - mock_conn_retrieve_by_invitation_key.return_value = record - with self.assertRaises(StorageNotFoundError): - await self.manager.receive_request( - mock_request, receipt, mediation_id="not-a-mediation-id" - ) - - async def test_receive_request_mediation_not_granted(self): - async with self.profile.session() as session: - mock_request = async_mock.MagicMock() - mock_request.connection = async_mock.MagicMock() - mock_request.connection.did = self.test_did - mock_request.connection.did_doc = self.make_did_doc( - self.test_target_did, self.test_target_verkey - ) - mock_request.connection.did_doc.did = self.test_did - receipt = MessageReceipt( - recipient_did=self.test_did, recipient_did_public=False - ) - record, invite = await self.manager.create_invitation() - - mediation_record = MediationRecord( - role=MediationRecord.ROLE_CLIENT, - state=MediationRecord.STATE_DENIED, - connection_id=self.test_mediator_conn_id, - routing_keys=self.test_mediator_routing_keys, - endpoint=self.test_mediator_endpoint, - ) - - await mediation_record.save(session) - - with async_mock.patch.object( - ConnRecord, "save", autospec=True - ) as mock_conn_rec_save, async_mock.patch.object( - ConnRecord, "attach_request", autospec=True - ) as mock_conn_attach_request, async_mock.patch.object( - ConnRecord, "retrieve_by_invitation_key" - ) as mock_conn_retrieve_by_invitation_key, async_mock.patch.object( - ConnRecord, "retrieve_request", autospec=True - ): - mock_conn_retrieve_by_invitation_key.return_value = record - with self.assertRaises(BaseConnectionManagerError): - await self.manager.receive_request( - mock_request, - receipt, - mediation_id=mediation_record.mediation_id, - ) - - mediation_record.state = MediationRecord.STATE_REQUEST - await mediation_record.save(session) - with self.assertRaises(BaseConnectionManagerError): - await self.manager.receive_request( - mock_request, - receipt, - mediation_id=mediation_record.mediation_id, - ) - async def test_create_response(self): conn_rec = ConnRecord(state=ConnRecord.State.REQUEST.rfc160) @@ -1150,13 +833,41 @@ async def test_create_response_multitenant(self): {"wallet.id": "test_wallet", "multitenant.enabled": True} ) + mediation_record = MediationRecord( + role=MediationRecord.ROLE_CLIENT, + state=MediationRecord.STATE_GRANTED, + connection_id=self.test_mediator_conn_id, + routing_keys=self.test_mediator_routing_keys, + endpoint=self.test_mediator_endpoint, + ) + with async_mock.patch.object( - ConnectionResponse, "sign_field", autospec=True + ConnRecord, "log_state", autospec=True + ), async_mock.patch.object( + ConnRecord, "save", autospec=True + ), async_mock.patch.object( + ConnRecord, "metadata_get", async_mock.CoroutineMock(return_value=False) + ), async_mock.patch.object( + self.route_manager, + "mediation_record_for_connection", + async_mock.CoroutineMock(return_value=mediation_record), ), async_mock.patch.object( ConnRecord, "retrieve_request", autospec=True + ), async_mock.patch.object( + ConnectionResponse, "sign_field", autospec=True ), async_mock.patch.object( InMemoryWallet, "create_local_did", autospec=True - ) as mock_wallet_create_local_did: + ) as mock_wallet_create_local_did, async_mock.patch.object( + self.multitenant_mgr, + "get_default_mediator", + async_mock.CoroutineMock(return_value=mediation_record), + ), async_mock.patch.object( + ConnectionManager, "create_did_document", autospec=True + ) as create_did_document, async_mock.patch.object( + self.route_manager, + "mediation_record_for_connection", + async_mock.CoroutineMock(return_value=None), + ): mock_wallet_create_local_did.return_value = DIDInfo( self.test_did, self.test_verkey, @@ -1167,11 +878,17 @@ async def test_create_response_multitenant(self): await self.manager.create_response( ConnRecord( state=ConnRecord.State.REQUEST, - ) + ), + my_endpoint=self.test_endpoint, ) - self.multitenant_mgr.add_key.assert_called_once_with( - "test_wallet", self.test_verkey + create_did_document.assert_called_once_with( + self.manager, + mock_wallet_create_local_did.return_value, + None, + [self.test_endpoint], + mediation_records=[mediation_record], ) + self.route_manager.route_connection_as_inviter.assert_called_once() async def test_create_response_bad_state(self): with self.assertRaises(ConnectionManagerError): @@ -1186,78 +903,67 @@ async def test_create_response_bad_state(self): ) async def test_create_response_mediation(self): - async with self.profile.session() as session: - mediation_record = MediationRecord( - role=MediationRecord.ROLE_CLIENT, - state=MediationRecord.STATE_GRANTED, - connection_id=self.test_mediator_conn_id, - routing_keys=self.test_mediator_routing_keys, - endpoint=self.test_mediator_endpoint, - ) - await mediation_record.save(session) + mediation_record = MediationRecord( + role=MediationRecord.ROLE_CLIENT, + state=MediationRecord.STATE_GRANTED, + connection_id=self.test_mediator_conn_id, + routing_keys=self.test_mediator_routing_keys, + endpoint=self.test_mediator_endpoint, + ) - conn_rec = ConnRecord( - state=ConnRecord.State.REQUEST.rfc160, - ) - conn_rec.my_did = None + record = ConnRecord( + connection_id="test-conn-id", + invitation_key=self.test_verkey, + their_label="Hello", + their_role=ConnRecord.Role.RESPONDER.rfc160, + alias="Bob", + state=ConnRecord.State.REQUEST.rfc160, + ) - with async_mock.patch.object( - ConnRecord, "log_state", autospec=True - ) as mock_conn_log_state, async_mock.patch.object( - ConnRecord, "retrieve_request", autospec=True - ) as mock_conn_retrieve_request, async_mock.patch.object( - ConnRecord, "save", autospec=True - ) as mock_conn_save, async_mock.patch.object( - ConnectionResponse, "sign_field", autospec=True - ) as mock_sign, async_mock.patch.object( - conn_rec, "metadata_get", async_mock.CoroutineMock(return_value=False) - ): - await self.manager.create_response( - conn_rec, mediation_id=mediation_record.mediation_id - ) + # Ensure the path with new did creation is hit + record.my_did = None - assert len(self.responder.messages) == 1 - message, target = self.responder.messages[0] - assert isinstance(message, KeylistUpdate) - assert len(message.updates) == 1 - (add,) = message.updates - assert add.action == KeylistUpdateRule.RULE_ADD - assert add.recipient_key - - async def test_create_response_bad_mediation(self): - record = async_mock.MagicMock() - with self.assertRaises(StorageNotFoundError): + with async_mock.patch.object( + ConnRecord, "log_state", autospec=True + ), async_mock.patch.object( + ConnRecord, "save", autospec=True + ), async_mock.patch.object( + record, "metadata_get", async_mock.CoroutineMock(return_value=False) + ), async_mock.patch.object( + ConnectionManager, "create_did_document", autospec=True + ) as create_did_document, async_mock.patch.object( + InMemoryWallet, "create_local_did" + ) as create_local_did, async_mock.patch.object( + self.route_manager, + "mediation_record_for_connection", + async_mock.CoroutineMock(return_value=mediation_record), + ), async_mock.patch.object( + record, "retrieve_request", autospec=True + ), async_mock.patch.object( + ConnectionResponse, "sign_field", autospec=True + ): + did_info = DIDInfo( + did=self.test_did, + verkey=self.test_verkey, + metadata={}, + method=DIDMethod.SOV, + key_type=KeyType.ED25519, + ) + create_local_did.return_value = did_info await self.manager.create_response( - record, mediation_id="not-a-mediation-id" + record, + mediation_id=mediation_record.mediation_id, + my_endpoint=self.test_endpoint, ) - - async def test_create_response_mediation_not_granted(self): - async with self.profile.session() as session: - record = ConnRecord(state=ConnRecord.State.REQUEST) - with async_mock.patch.object( - ConnRecord, "retrieve_request" - ) as retrieve_request, async_mock.patch.object( - ConnectionResponse, "sign_field", autospec=True - ) as mock_sign: - mediation_record = MediationRecord( - role=MediationRecord.ROLE_CLIENT, - state=MediationRecord.STATE_DENIED, - connection_id=self.test_mediator_conn_id, - routing_keys=self.test_mediator_routing_keys, - endpoint=self.test_mediator_endpoint, - ) - await mediation_record.save(session) - with self.assertRaises(BaseConnectionManagerError): - await self.manager.create_response( - record, mediation_id=mediation_record.mediation_id - ) - - mediation_record.state = MediationRecord.STATE_REQUEST - await mediation_record.save(session) - with self.assertRaises(BaseConnectionManagerError): - await self.manager.create_response( - record, mediation_id=mediation_record.mediation_id - ) + create_local_did.assert_called_once_with(DIDMethod.SOV, KeyType.ED25519) + create_did_document.assert_called_once_with( + self.manager, + did_info, + None, + [self.test_endpoint], + mediation_records=[mediation_record], + ) + self.route_manager.route_connection_as_inviter.assert_called_once() async def test_create_response_auto_send_mediation_request(self): conn_rec = ConnRecord( @@ -1576,9 +1282,7 @@ async def test_create_static_connection_multitenant(self): their_endpoint=self.test_endpoint, ) - self.multitenant_mgr.add_key.assert_called_once_with( - "test_wallet", self.test_verkey - ) + self.route_manager.route_static.assert_called_once() async def test_create_static_connection_multitenant_auto_disclose_features(self): self.context.update_settings( @@ -1609,9 +1313,7 @@ async def test_create_static_connection_multitenant_auto_disclose_features(self) their_verkey=self.test_target_verkey, their_endpoint=self.test_endpoint, ) - self.multitenant_mgr.add_key.assert_called_once_with( - "test_wallet", self.test_verkey - ) + self.route_manager.route_static.assert_called_once() mock_proactive_disclose_features.assert_called_once() async def test_create_static_connection_multitenant_mediator(self): @@ -1656,7 +1358,7 @@ async def test_create_static_connection_multitenant_mediator(self): their_endpoint=self.test_endpoint, ) - assert self.multitenant_mgr.add_key.call_count is 2 + assert self.route_manager.route_static.call_count == 2 their_info = DIDInfo( self.test_target_did, @@ -1673,9 +1375,7 @@ async def test_create_static_connection_multitenant_mediator(self): [self.test_endpoint], mediation_records=[default_mediator], ), - call( - their_info, None, [self.test_endpoint], mediation_records=None - ), + call(their_info, None, [self.test_endpoint], mediation_records=[]), ] ) diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py index cb515bf729..df6a3a9ca4 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py @@ -447,6 +447,47 @@ async def test_create_invitation_multitenant_public(self): self.route_manager.route_invitation.assert_called_once() + async def test_create_invitation_mediation_overwrites_routing_and_endpoint(self): + async with self.profile.session() as session: + mock_conn_rec = async_mock.MagicMock() + + mediation_record = MediationRecord( + role=MediationRecord.ROLE_CLIENT, + state=MediationRecord.STATE_GRANTED, + connection_id=self.test_mediator_conn_id, + routing_keys=self.test_mediator_routing_keys, + endpoint=self.test_mediator_endpoint, + ) + await mediation_record.save(session) + with async_mock.patch.object( + MediationManager, + "get_default_mediator_id", + ) as mock_get_default_mediator, async_mock.patch.object( + mock_conn_rec, "metadata_set", async_mock.CoroutineMock() + ) as mock_metadata_set: + invite = await self.manager.create_invitation( + my_endpoint=TestConfig.test_endpoint, + my_label="test123", + hs_protos=[HSProto.RFC23], + mediation_id=mediation_record.mediation_id, + ) + assert isinstance(invite, InvitationRecord) + assert invite.invitation._type == DIDCommPrefix.qualify_current( + INVITATION + ) + assert invite.invitation.label == "test123" + assert ( + DIDKey.from_did( + invite.invitation.services[0].routing_keys[0] + ).public_key_b58 + == self.test_mediator_routing_keys[0] + ) + assert ( + invite.invitation.services[0].service_endpoint + == self.test_mediator_endpoint + ) + mock_get_default_mediator.assert_not_called() + async def test_create_invitation_no_handshake_no_attachments_x(self): with self.assertRaises(OutOfBandManagerError) as context: await self.manager.create_invitation( From 5d4a3edb9db4e09020d3776433946171d229c9db Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Thu, 30 Jun 2022 23:25:22 -0400 Subject: [PATCH 321/872] test: didex request handler with route mgr Signed-off-by: Daniel Bluhm --- .../v1_0/handlers/request_handler.py | 1 - .../handlers/tests/test_request_handler.py | 81 ++++++++----------- 2 files changed, 35 insertions(+), 47 deletions(-) diff --git a/aries_cloudagent/protocols/didexchange/v1_0/handlers/request_handler.py b/aries_cloudagent/protocols/didexchange/v1_0/handlers/request_handler.py index efcc02c64d..766ecb8571 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/handlers/request_handler.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/handlers/request_handler.py @@ -43,7 +43,6 @@ async def handle(self, context: RequestContext, responder: BaseResponder): if context.message_receipt.recipient_did_public else context.message_receipt.recipient_verkey ), - mediation_id=mediation_id, ) # Auto respond diff --git a/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_request_handler.py b/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_request_handler.py index 21eb4c2688..ff76d6a9c6 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_request_handler.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_request_handler.py @@ -8,15 +8,12 @@ PublicKeyType, Service, ) -from ......core.profile import ProfileSession from ......core.in_memory import InMemoryProfile from ......wallet.key_type import KeyType from ......wallet.did_method import DIDMethod from ......messaging.decorators.attach_decorator import AttachDecorator from ......messaging.request_context import RequestContext from ......messaging.responder import MockResponder -from ......storage.base import BaseStorage -from ......storage.error import StorageNotFoundError from ......transport.inbound.receipt import MessageReceipt from .....problem_report.v1_0.message import ProblemReport @@ -103,47 +100,6 @@ async def setUp(self): did_doc_attach=self.did_doc_attach, ) - async def test_connection_record_with_mediation_metadata(self): - test_exist_conn = conn_record.ConnRecord( - my_did="did:sov:LjgpST2rjsoxYegQDRm7EL", - their_did="did:sov:LjgpST2rjsoxYegQDRm7EL", - their_public_did="did:sov:LjgpST2rjsoxYegQDRm7EL", - invitation_msg_id="12345678-1234-5678-1234-567812345678", - their_role=conn_record.ConnRecord.Role.REQUESTER, - ) - await test_exist_conn.save(self.session) - await test_exist_conn.metadata_set( - self.session, "mediation", {"id": "mediation-test-id"} - ) - test_ctx = RequestContext.test_context() - test_ctx.message = DIDXRequest() - test_ctx.message_receipt = MessageReceipt() - test_ctx.connection_record = test_exist_conn - responder = MockResponder() - handler_inst = test_module.DIDXRequestHandler() - await handler_inst.handle(test_ctx, responder) - mediation_metadata = await test_ctx.connection_record.metadata_get( - self.session, "mediation", {} - ) - assert mediation_metadata.get("id") == "mediation-test-id" - assert not responder.messages - - @async_mock.patch.object(test_module, "DIDXManager") - async def test_connection_record_without_mediation_metadata(self, mock_didx_mgr): - mock_didx_mgr.return_value.receive_request = async_mock.CoroutineMock() - self.ctx.message = DIDXRequest() - self.ctx.connection_record = None - handler_inst = test_module.DIDXRequestHandler() - responder = MockResponder() - await handler_inst.handle(self.ctx, responder) - mock_didx_mgr.return_value.receive_request.assert_called_once_with( - request=self.ctx.message, - recipient_did=self.ctx.message_receipt.recipient_did, - recipient_verkey=None, - mediation_id=None, - ) - assert not responder.messages - @async_mock.patch.object(test_module, "DIDXManager") async def test_called(self, mock_didx_mgr): mock_didx_mgr.return_value.receive_request = async_mock.CoroutineMock() @@ -156,7 +112,6 @@ async def test_called(self, mock_didx_mgr): request=self.ctx.message, recipient_did=self.ctx.message_receipt.recipient_did, recipient_verkey=None, - mediation_id=None, ) assert not responder.messages @@ -178,7 +133,41 @@ async def test_called_with_auto_response(self, mock_didx_mgr): request=self.ctx.message, recipient_did=self.ctx.message_receipt.recipient_did, recipient_verkey=None, - mediation_id=None, + ) + mock_didx_mgr.return_value.create_response.assert_called_once_with( + mock_conn_rec, mediation_id=None + ) + assert responder.messages + + @async_mock.patch.object(test_module, "DIDXManager") + async def test_connection_record_with_mediation_metadata_auto_response( + self, mock_didx_mgr + ): + test_exist_conn = conn_record.ConnRecord( + my_did="did:sov:LjgpST2rjsoxYegQDRm7EL", + their_did="did:sov:LjgpST2rjsoxYegQDRm7EL", + their_public_did="did:sov:LjgpST2rjsoxYegQDRm7EL", + invitation_msg_id="12345678-1234-5678-1234-567812345678", + their_role=conn_record.ConnRecord.Role.REQUESTER, + ) + test_exist_conn.metadata_get = async_mock.CoroutineMock( + return_value={"id": "mediation-test-id"} + ) + test_exist_conn.accept = conn_record.ConnRecord.ACCEPT_AUTO + test_exist_conn.save = async_mock.CoroutineMock() + mock_didx_mgr.return_value.receive_request = async_mock.CoroutineMock( + return_value=test_exist_conn + ) + mock_didx_mgr.return_value.create_response = async_mock.CoroutineMock() + test_ctx = RequestContext.test_context() + test_ctx.message = DIDXRequest() + test_ctx.message_receipt = MessageReceipt() + test_ctx.connection_record = test_exist_conn + responder = MockResponder() + handler_inst = test_module.DIDXRequestHandler() + await handler_inst.handle(test_ctx, responder) + mock_didx_mgr.return_value.create_response.assert_called_once_with( + test_exist_conn, mediation_id="mediation-test-id" ) assert responder.messages From 23a2981b028f89b49696258546a94f9c540abb16 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Thu, 30 Jun 2022 23:50:07 -0400 Subject: [PATCH 322/872] test: didexchange manager with route manager Signed-off-by: Daniel Bluhm --- .../didexchange/v1_0/tests/test_manager.py | 406 +----------------- 1 file changed, 23 insertions(+), 383 deletions(-) diff --git a/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py index 3ffd26a181..0cbec5deef 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py @@ -35,6 +35,7 @@ from .....connections.base_manager import BaseConnectionManagerError from ....coordinate_mediation.v1_0.manager import MediationManager +from ....coordinate_mediation.v1_0.route_manager import RouteManager from ....coordinate_mediation.v1_0.messages.keylist_update import ( KeylistUpdate, KeylistUpdateRule, @@ -124,6 +125,9 @@ async def setUp(self): self.context.injector.bind_instance( BaseMultitenantManager, self.multitenant_mgr ) + self.multitenant_mgr.get_default_mediator = async_mock.CoroutineMock( + return_value=None + ) self.manager = DIDXManager(self.profile) assert self.manager.profile @@ -136,6 +140,19 @@ async def setUp(self): self.test_mediator_conn_id = "mediator-conn-id" self.test_mediator_endpoint = "http://mediator.example.com" + self.route_manager = async_mock.MagicMock(RouteManager) + self.route_manager.routing_info = async_mock.CoroutineMock( + return_value=([], self.test_endpoint) + ) + self.route_manager.mediation_record_if_id = async_mock.CoroutineMock( + return_value=None + ) + self.route_manager.mediation_record_for_connection = async_mock.CoroutineMock( + return_value=None + ) + self.manager._route_manager = self.route_manager + self.oob_manager._route_manager = self.route_manager + async def test_verify_diddoc(self): async with self.profile.session() as session: did_doc = self.make_did_doc( @@ -290,15 +307,6 @@ async def test_create_request_implicit(self): async def test_create_request_implicit_use_public_did(self): async with self.profile.session() as session: - mediation_record = MediationRecord( - role=MediationRecord.ROLE_CLIENT, - state=MediationRecord.STATE_GRANTED, - connection_id=self.test_mediator_conn_id, - routing_keys=self.test_mediator_routing_keys, - endpoint=self.test_mediator_endpoint, - ) - await mediation_record.save(session) - info_public = await session.wallet.create_public_did( DIDMethod.SOV, KeyType.ED25519, @@ -307,7 +315,7 @@ async def test_create_request_implicit_use_public_did(self): their_public_did=TestConfig.test_target_did, my_label=None, my_endpoint=None, - mediation_id=mediation_record._id, + mediation_id=None, use_public_did=True, alias="Tester", ) @@ -396,9 +404,7 @@ async def test_create_request_multitenant(self): ) ) - self.multitenant_mgr.add_key.assert_called_once_with( - "test_wallet", TestConfig.test_verkey - ) + self.route_manager.route_connection_as_invitee.assert_called_once() async def test_create_request_mediation_id(self): async with self.profile.session() as session: @@ -443,13 +449,8 @@ async def test_create_request_mediation_id(self): mediation_id=mediation_record._id, ) assert didx_req - assert len(self.responder.messages) == 1 - message, used_kwargs = self.responder.messages[0] - assert isinstance(message, KeylistUpdate) - assert ( - "connection_id" in used_kwargs - and used_kwargs["connection_id"] == self.test_mediator_conn_id - ) + + self.route_manager.route_connection_as_invitee.assert_called_once() async def test_create_request_my_endpoint(self): mock_conn_rec = async_mock.MagicMock( @@ -590,7 +591,6 @@ async def test_receive_request_explicit_public_did(self): my_endpoint=None, alias=None, auto_accept_implicit=None, - mediation_id=None, ) assert conn_rec self.oob_mock.clean_finished_oob_record.assert_called_once_with( @@ -626,183 +626,9 @@ async def test_receive_request_invi_not_found(self): my_endpoint=None, alias=None, auto_accept_implicit=None, - mediation_id=None, ) assert "No explicit invitation found" in str(context.exception) - async def test_receive_request_with_mediator_without_multi_use_multitenant(self): - async with self.profile.session() as session: - multiuse_info = await session.wallet.create_local_did( - method=DIDMethod.SOV, - key_type=KeyType.ED25519, - ) - did_doc_dict = self.make_did_doc( - did=TestConfig.test_target_did, - verkey=TestConfig.test_target_verkey, - ).serialize() - del did_doc_dict["authentication"] - del did_doc_dict["service"] - new_info = await session.wallet.create_local_did( - method=DIDMethod.SOV, - key_type=KeyType.ED25519, - ) - - mock_request = async_mock.MagicMock() - mock_request.connection = async_mock.MagicMock( - is_multiuse_invitation=False, invitation_key=multiuse_info.verkey - ) - mock_request.connection.did = TestConfig.test_did - mock_request.connection.did_doc = async_mock.MagicMock() - mock_request.connection.did_doc.did = TestConfig.test_did - mock_request.did = self.test_target_did - mock_request.did_doc_attach = async_mock.MagicMock( - data=async_mock.MagicMock( - verify=async_mock.CoroutineMock(return_value=True), - signed=async_mock.MagicMock( - decode=async_mock.MagicMock( - return_value=json.dumps(did_doc_dict) - ) - ), - ) - ) - - await session.wallet.create_local_did( - method=DIDMethod.SOV, - key_type=KeyType.ED25519, - seed=None, - did=TestConfig.test_did, - ) - - mediation_record = MediationRecord( - role=MediationRecord.ROLE_CLIENT, - state=MediationRecord.STATE_GRANTED, - connection_id=self.test_mediator_conn_id, - routing_keys=self.test_mediator_routing_keys, - endpoint=self.test_mediator_endpoint, - ) - await mediation_record.save(session) - - record = ConnRecord( - invitation_key=TestConfig.test_verkey, - their_label="Hello", - their_role=ConnRecord.Role.RESPONDER.rfc160, - alias="Bob", - ) - record.accept = ConnRecord.ACCEPT_MANUAL - await record.save(session) - - with async_mock.patch.object( - ConnRecord, "save", autospec=True - ) as mock_conn_rec_save, async_mock.patch.object( - ConnRecord, "attach_request", autospec=True - ) as mock_conn_attach_request, async_mock.patch.object( - ConnRecord, "retrieve_by_invitation_key" - ) as mock_conn_retrieve_by_invitation_key, async_mock.patch.object( - ConnRecord, "retrieve_request", autospec=True - ): - mock_conn_retrieve_by_invitation_key.return_value = record - conn_rec = await self.manager.receive_request( - request=mock_request, - recipient_did=TestConfig.test_did, - recipient_verkey=TestConfig.test_verkey, - my_endpoint=None, - alias=None, - auto_accept_implicit=None, - mediation_id=mediation_record.mediation_id, - ) - - assert len(self.responder.messages) == 1 - message, target = self.responder.messages[0] - assert isinstance(message, KeylistUpdate) - assert len(message.updates) == 1 - (remove,) = message.updates - assert remove.action == KeylistUpdateRule.RULE_REMOVE - assert remove.recipient_key == record.invitation_key - - async def test_receive_request_with_mediator_without_multi_use_multitenant_mismatch( - self, - ): - async with self.profile.session() as session: - multiuse_info = await session.wallet.create_local_did( - method=DIDMethod.SOV, - key_type=KeyType.ED25519, - ) - did_doc_dict = self.make_did_doc( - did=TestConfig.test_target_did, - verkey=TestConfig.test_target_verkey, - ).serialize() - del did_doc_dict["authentication"] - del did_doc_dict["service"] - new_info = await session.wallet.create_local_did( - method=DIDMethod.SOV, - key_type=KeyType.ED25519, - ) - - mock_request = async_mock.MagicMock() - mock_request.connection = async_mock.MagicMock( - is_multiuse_invitation=False, invitation_key=multiuse_info.verkey - ) - mock_request.connection.did = TestConfig.test_did - mock_request.connection.did_doc = async_mock.MagicMock() - mock_request.connection.did_doc.did = TestConfig.test_did - mock_request.did_doc_attach = async_mock.MagicMock( - data=async_mock.MagicMock( - verify=async_mock.CoroutineMock(return_value=True), - signed=async_mock.MagicMock( - decode=async_mock.MagicMock( - return_value=json.dumps(did_doc_dict) - ) - ), - ) - ) - - await session.wallet.create_local_did( - method=DIDMethod.SOV, - key_type=KeyType.ED25519, - seed=None, - did=TestConfig.test_did, - ) - - mediation_record = MediationRecord( - role=MediationRecord.ROLE_CLIENT, - state=MediationRecord.STATE_GRANTED, - connection_id=self.test_mediator_conn_id, - routing_keys=self.test_mediator_routing_keys, - endpoint=self.test_mediator_endpoint, - ) - await mediation_record.save(session) - - record = ConnRecord( - invitation_key=TestConfig.test_verkey, - their_label="Hello", - their_role=ConnRecord.Role.RESPONDER.rfc160, - alias="Bob", - ) - record.accept = ConnRecord.ACCEPT_MANUAL - await record.save(session) - - with async_mock.patch.object( - ConnRecord, "save", autospec=True - ) as mock_conn_rec_save, async_mock.patch.object( - ConnRecord, "attach_request", autospec=True - ) as mock_conn_attach_request, async_mock.patch.object( - ConnRecord, "retrieve_by_invitation_key" - ) as mock_conn_retrieve_by_invitation_key, async_mock.patch.object( - ConnRecord, "retrieve_request", autospec=True - ): - mock_conn_retrieve_by_invitation_key.return_value = record - with self.assertRaises(DIDXManagerError) as context: - conn_rec = await self.manager.receive_request( - request=mock_request, - recipient_did=TestConfig.test_did, - recipient_verkey=TestConfig.test_verkey, - my_endpoint=None, - alias=None, - auto_accept_implicit=None, - mediation_id=mediation_record.mediation_id, - ) - assert "does not match" in str(context.exception) - async def test_receive_request_public_did_no_did_doc_attachment(self): async with self.profile.session() as session: mock_request = async_mock.MagicMock( @@ -851,7 +677,6 @@ async def test_receive_request_public_did_no_did_doc_attachment(self): my_endpoint=TestConfig.test_endpoint, alias="Alias", auto_accept_implicit=None, - mediation_id=None, ) assert "DID Doc attachment missing or has no data" in str( context.exception @@ -896,7 +721,6 @@ async def test_receive_request_public_did_x_not_public(self): my_endpoint=TestConfig.test_endpoint, alias="Alias", auto_accept_implicit=False, - mediation_id=None, ) assert "is not public" in str(context.exception) @@ -964,7 +788,6 @@ async def test_receive_request_public_did_x_wrong_did(self): my_endpoint=TestConfig.test_endpoint, alias="Alias", auto_accept_implicit=False, - mediation_id=None, ) assert "does not match" in str(context.exception) @@ -1027,7 +850,6 @@ async def test_receive_request_public_did_x_did_doc_attach_bad_sig(self): my_endpoint=TestConfig.test_endpoint, alias="Alias", auto_accept_implicit=False, - mediation_id=None, ) async def test_receive_request_public_did_no_public_invites(self): @@ -1075,7 +897,6 @@ async def test_receive_request_public_did_no_public_invites(self): my_endpoint=TestConfig.test_endpoint, alias="Alias", auto_accept_implicit=False, - mediation_id=None, ) assert "Public invitations are not enabled" in str(context.exception) @@ -1149,7 +970,6 @@ async def test_receive_request_public_did_no_auto_accept(self): my_endpoint=TestConfig.test_endpoint, alias="Alias", auto_accept_implicit=False, - mediation_id=None, ) assert conn_rec @@ -1240,183 +1060,12 @@ async def test_receive_request_peer_did(self): my_endpoint=TestConfig.test_endpoint, alias="Alias", auto_accept_implicit=False, - mediation_id=None, ) assert conn_rec mock_conn_rec_cls.return_value.metadata_set.assert_called() assert not self.responder.messages - async def test_receive_request_multiuse_multitenant(self): - async with self.profile.session() as session: - multiuse_info = await session.wallet.create_local_did( - method=DIDMethod.SOV, - key_type=KeyType.ED25519, - ) - new_info = await session.wallet.create_local_did( - method=DIDMethod.SOV, - key_type=KeyType.ED25519, - ) - - mock_request = async_mock.MagicMock( - did=TestConfig.test_did, - did_doc_attach=async_mock.MagicMock( - data=async_mock.MagicMock( - signed=async_mock.MagicMock( - decode=async_mock.MagicMock(return_value="dummy-did-doc") - ), - ) - ), - _thread=async_mock.MagicMock(pthid="dummy-pthid"), - ) - - self.context.update_settings( - {"wallet.id": "test_wallet", "multitenant.enabled": True} - ) - ACCEPT_MANUAL = ConnRecord.ACCEPT_MANUAL - with async_mock.patch.object( - test_module, "ConnRecord", autospec=True - ) as mock_conn_rec_cls, async_mock.patch.object( - InMemoryWallet, "create_local_did", autospec=True - ) as mock_wallet_create_local_did, async_mock.patch.object( - test_module, "DIDDoc", autospec=True - ) as mock_did_doc, async_mock.patch.object( - self.manager, - "verify_diddoc", - async_mock.CoroutineMock(return_value=DIDDoc(TestConfig.test_did)), - ): - mock_conn_rec = async_mock.CoroutineMock( - connection_id="dummy", - accept=ACCEPT_MANUAL, - is_multiuse_invitation=True, - attach_request=async_mock.CoroutineMock(), - save=async_mock.CoroutineMock(), - retrieve_invitation=async_mock.CoroutineMock(return_value={}), - metadata_get_all=async_mock.CoroutineMock(return_value={}), - retrieve_request=async_mock.CoroutineMock(), - ) - mock_conn_rec_cls.return_value = mock_conn_rec - mock_conn_rec_cls.retrieve_by_invitation_key = async_mock.CoroutineMock( - return_value=mock_conn_rec - ) - mock_wallet_create_local_did.return_value = DIDInfo( - new_info.did, - new_info.verkey, - None, - method=DIDMethod.SOV, - key_type=KeyType.ED25519, - ) - mock_did_doc.from_json = async_mock.MagicMock( - return_value=async_mock.MagicMock(did=TestConfig.test_did) - ) - await self.manager.receive_request( - request=mock_request, - recipient_did=TestConfig.test_did, - recipient_verkey=TestConfig.test_verkey, - my_endpoint=TestConfig.test_endpoint, - alias="Alias", - auto_accept_implicit=False, - mediation_id=None, - ) - - self.multitenant_mgr.add_key.assert_called_once_with( - "test_wallet", new_info.verkey - ) - - async def test_receive_request_implicit_multitenant(self): - async with self.profile.session() as session: - new_info = await session.wallet.create_local_did( - method=DIDMethod.SOV, - key_type=KeyType.ED25519, - ) - - mock_request = async_mock.MagicMock( - did=TestConfig.test_did, - did_doc_attach=async_mock.MagicMock( - data=async_mock.MagicMock( - signed=async_mock.MagicMock( - decode=async_mock.MagicMock(return_value="dummy-did-doc") - ), - ) - ), - _thread=async_mock.MagicMock(pthid="did:sov:publicdid0000000000000"), - ) - - self.context.update_settings( - { - "wallet.id": "test_wallet", - "multitenant.enabled": True, - "public_invites": True, - "debug.auto_accept_requests": False, - } - ) - - ACCEPT_MANUAL = ConnRecord.ACCEPT_MANUAL - with async_mock.patch.object( - test_module, "ConnRecord", autospec=True - ) as mock_conn_rec_cls, async_mock.patch.object( - InMemoryWallet, "create_local_did", autospec=True - ) as mock_wallet_create_local_did, async_mock.patch.object( - InMemoryWallet, "get_local_did", autospec=True - ) as mock_wallet_get_local_did, async_mock.patch.object( - test_module, "DIDPosture", autospec=True - ) as mock_did_posture, async_mock.patch.object( - test_module, "DIDDoc", autospec=True - ) as mock_did_doc, async_mock.patch.object( - self.manager, - "verify_diddoc", - async_mock.CoroutineMock(return_value=DIDDoc(TestConfig.test_did)), - ): - mock_conn_rec = async_mock.CoroutineMock( - connection_id="dummy", - accept=ACCEPT_MANUAL, - is_multiuse_invitation=False, - attach_request=async_mock.CoroutineMock(), - save=async_mock.CoroutineMock(), - retrieve_invitation=async_mock.CoroutineMock(return_value={}), - metadata_get_all=async_mock.CoroutineMock(return_value={}), - retrieve_request=async_mock.CoroutineMock(), - ) - mock_conn_rec_cls.return_value = mock_conn_rec - mock_conn_rec_cls.retrieve_by_invitation_msg_id = ( - async_mock.CoroutineMock(return_value=[]) - ) - - mock_did_posture.get = async_mock.MagicMock( - return_value=test_module.DIDPosture.PUBLIC - ) - - mock_wallet_create_local_did.return_value = DIDInfo( - new_info.did, - new_info.verkey, - None, - method=DIDMethod.SOV, - key_type=KeyType.ED25519, - ) - mock_did_doc.from_json = async_mock.MagicMock( - return_value=async_mock.MagicMock(did=TestConfig.test_did) - ) - mock_wallet_get_local_did.return_value = DIDInfo( - TestConfig.test_did, - TestConfig.test_verkey, - None, - method=DIDMethod.SOV, - key_type=KeyType.ED25519, - ) - await self.manager.receive_request( - request=mock_request, - recipient_did=TestConfig.test_did, - recipient_verkey=None, - my_endpoint=TestConfig.test_endpoint, - alias="Alias", - auto_accept_implicit=False, - mediation_id=None, - ) - - self.multitenant_mgr.add_key.assert_called_once_with( - "test_wallet", new_info.verkey - ) - async def test_receive_request_peer_did_not_found_x(self): async with self.profile.session() as session: mock_request = async_mock.MagicMock( @@ -1453,7 +1102,6 @@ async def test_receive_request_peer_did_not_found_x(self): my_endpoint=TestConfig.test_endpoint, alias="Alias", auto_accept_implicit=False, - mediation_id=None, ) async def test_create_response(self): @@ -1536,13 +1184,7 @@ async def test_create_response_mediation_id(self): record, mediation_id=mediation_record.mediation_id ) - assert len(self.responder.messages) == 1 - message, target = self.responder.messages[0] - assert isinstance(message, KeylistUpdate) - assert len(message.updates) == 1 - (add,) = message.updates - assert add.action == KeylistUpdateRule.RULE_ADD - assert add.recipient_key + self.route_manager.route_connection_as_inviter.assert_called_once() async def test_create_response_mediation_id_invalid_conn_state(self): async with self.profile.session() as session: @@ -1629,9 +1271,7 @@ async def test_create_response_multitenant(self): ) await self.manager.create_response(conn_rec) - self.multitenant_mgr.add_key.assert_called_once_with( - "test_wallet", TestConfig.test_verkey - ) + self.route_manager.route_connection_as_inviter.assert_called_once() async def test_create_response_conn_rec_my_did(self): conn_rec = ConnRecord( From 58faf2015c9c2633648480cfb799681e6ac7bda4 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Thu, 30 Jun 2022 23:53:28 -0400 Subject: [PATCH 323/872] style: flake8 fixes for route manager Signed-off-by: Daniel Bluhm --- .../protocols/coordinate_mediation/v1_0/route_manager.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py index 6dca92c02c..32f42805da 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py @@ -36,6 +36,7 @@ class RouteManager(ABC): """Base Route Manager.""" def __init__(self, profile: Profile): + """Initialize route manager.""" self.profile = profile async def get_or_create_my_did(self, conn_record: ConnRecord) -> DIDInfo: @@ -55,7 +56,7 @@ async def get_or_create_my_did(self, conn_record: ConnRecord) -> DIDInfo: return my_info def _validate_mediation_state(self, mediation_record: MediationRecord): - """Perform mediation state validation""" + """Perform mediation state validation.""" if mediation_record.state != MediationRecord.STATE_GRANTED: raise RouteManagerError( "Mediation is not granted for mediation identified by " @@ -154,7 +155,7 @@ async def route_connection( conn_record: ConnRecord, mediation_record: Optional[MediationRecord] = None, ) -> Optional[KeylistUpdate]: - """Setup routing for a connection. + """Set up routing for a connection. This method will evaluate connection state and call the appropriate methods. """ @@ -267,6 +268,7 @@ async def _route_for_key( async def routing_info( self, my_endpoint: str, mediation_record: Optional[MediationRecord] = None ) -> Tuple[List[str], str]: + """Return routing info for mediator.""" if mediation_record: return mediation_record.routing_keys, mediation_record.endpoint From 0e3e03084bd57d51b9feb6eb4915ff9a4216de37 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Fri, 1 Jul 2022 11:58:17 -0400 Subject: [PATCH 324/872] test: route manager and mediation route manager Signed-off-by: Daniel Bluhm --- .../v1_0/route_manager.py | 7 +- .../v1_0/tests/test_route_manager.py | 590 ++++++++++++++++++ 2 files changed, 591 insertions(+), 6 deletions(-) create mode 100644 aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_route_manager.py diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py index 32f42805da..be67b3c9e6 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py @@ -69,12 +69,7 @@ async def mediation_record_for_connection( mediation_id: Optional[str] = None, or_default: bool = False, ): - """Validate mediation and return record. - - If mediation_id is not None, - validate mediation record state and return record - else, return None - """ + """Return relevant mediator for connection.""" async with self.profile.session() as session: mediation_metadata = await conn_record.metadata_get( session, MediationManager.METADATA_KEY, {} diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_route_manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_route_manager.py new file mode 100644 index 0000000000..3bd3d9e7ff --- /dev/null +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_route_manager.py @@ -0,0 +1,590 @@ +from asynctest import mock +import pytest + +from .....connections.models.conn_record import ConnRecord +from .....core.in_memory import InMemoryProfile +from .....core.profile import Profile +from .....messaging.responder import BaseResponder, MockResponder +from .....storage.error import StorageNotFoundError +from .....wallet.did_info import DIDInfo +from .....wallet.in_memory import InMemoryWallet +from ....routing.v1_0.models.route_record import RouteRecord +from ..manager import MediationManager +from ..messages.keylist_update import KeylistUpdate +from ..models.mediation_record import MediationRecord +from ..route_manager import ( + CoordinateMediationV1RouteManager, + RouteManager, + RouteManagerError, +) + + +class TestRouteManager(RouteManager): + """Concretion of RouteManager for testing.""" + + _route_for_key = mock.CoroutineMock() + routing_info = mock.CoroutineMock() + + +@pytest.fixture +def mock_responder(): + yield MockResponder() + + +@pytest.fixture +def profile(mock_responder: MockResponder): + yield InMemoryProfile.test_profile(bind={BaseResponder: mock_responder}) + + +@pytest.fixture +def route_manager(profile: Profile): + manager = TestRouteManager(profile) + manager._route_for_key = mock.CoroutineMock( + return_value=mock.MagicMock(KeylistUpdate) + ) + manager.routing_info = mock.CoroutineMock(return_value=([], "http://example.com")) + yield manager + + +@pytest.fixture +def mediation_route_manager(profile: Profile): + yield CoordinateMediationV1RouteManager(profile) + + +@pytest.fixture +def conn_record(): + record = ConnRecord() + record.metadata_get = mock.CoroutineMock(return_value={}) + record.metadata_set = mock.CoroutineMock() + yield record + + +@pytest.mark.asyncio +async def test_get_or_create_my_did_no_did( + route_manager: RouteManager, conn_record: ConnRecord +): + conn_record.my_did = None + mock_did_info = mock.MagicMock() + with mock.patch.object( + InMemoryWallet, + "create_local_did", + mock.CoroutineMock(return_value=mock_did_info), + ) as mock_create_local_did, mock.patch.object( + conn_record, "save", mock.CoroutineMock() + ) as mock_save: + info = await route_manager.get_or_create_my_did(conn_record) + assert mock_did_info == info + mock_create_local_did.assert_called_once() + mock_save.assert_called_once() + + +@pytest.mark.asyncio +async def test_get_or_create_my_did_existing_did( + route_manager: RouteManager, conn_record: ConnRecord +): + conn_record.my_did = "test-did" + mock_did_info = mock.MagicMock(DIDInfo) + with mock.patch.object( + InMemoryWallet, "get_local_did", mock.CoroutineMock(return_value=mock_did_info) + ) as mock_get_local_did: + info = await route_manager.get_or_create_my_did(conn_record) + assert mock_did_info == info + mock_get_local_did.assert_called_once() + + +@pytest.mark.asyncio +async def test_mediation_record_for_connection_mediation_id( + route_manager: RouteManager, conn_record: ConnRecord +): + mediation_record = MediationRecord(mediation_id="test-mediation-id") + with mock.patch.object( + route_manager, + "mediation_record_if_id", + mock.CoroutineMock(return_value=mediation_record), + ) as mock_mediation_record_if_id, mock.patch.object( + route_manager, "save_mediator_for_connection", mock.CoroutineMock() + ): + assert ( + await route_manager.mediation_record_for_connection( + conn_record, mediation_record.mediation_id + ) + == mediation_record + ) + mock_mediation_record_if_id.assert_called_once_with( + mediation_record.mediation_id, False + ) + + +@pytest.mark.asyncio +async def test_mediation_record_for_connection_mediation_metadata( + route_manager: RouteManager, conn_record: ConnRecord +): + mediation_record = MediationRecord(mediation_id="test-mediation-id") + conn_record.metadata_get.return_value = { + MediationManager.METADATA_ID: mediation_record.mediation_id + } + with mock.patch.object( + route_manager, + "mediation_record_if_id", + mock.CoroutineMock(return_value=mediation_record), + ) as mock_mediation_record_if_id, mock.patch.object( + route_manager, "save_mediator_for_connection", mock.CoroutineMock() + ): + assert ( + await route_manager.mediation_record_for_connection( + conn_record, "another-mediation-id" + ) + == mediation_record + ) + mock_mediation_record_if_id.assert_called_once_with( + mediation_record.mediation_id, False + ) + + +@pytest.mark.asyncio +async def test_mediation_record_for_connection_default( + route_manager: RouteManager, conn_record: ConnRecord +): + mediation_record = MediationRecord(mediation_id="test-mediation-id") + with mock.patch.object( + route_manager, + "mediation_record_if_id", + mock.CoroutineMock(return_value=mediation_record), + ) as mock_mediation_record_if_id, mock.patch.object( + route_manager, "save_mediator_for_connection", mock.CoroutineMock() + ): + assert ( + await route_manager.mediation_record_for_connection( + conn_record, None, or_default=True + ) + == mediation_record + ) + mock_mediation_record_if_id.assert_called_once_with(None, True) + + +@pytest.mark.asyncio +async def test_mediation_record_if_id_with_id(route_manager: RouteManager): + mediation_record = MediationRecord( + mediation_id="test-mediation-id", state=MediationRecord.STATE_GRANTED + ) + with mock.patch.object( + MediationRecord, + "retrieve_by_id", + mock.CoroutineMock(return_value=mediation_record), + ) as mock_retrieve_by_id: + actual = await route_manager.mediation_record_if_id( + mediation_id=mediation_record.mediation_id + ) + assert mediation_record == actual + mock_retrieve_by_id.assert_called_once() + + +@pytest.mark.asyncio +async def test_mediation_record_if_id_with_id_bad_state(route_manager: RouteManager): + mediation_record = MediationRecord( + mediation_id="test-mediation-id", state=MediationRecord.STATE_DENIED + ) + with mock.patch.object( + MediationRecord, + "retrieve_by_id", + mock.CoroutineMock(return_value=mediation_record), + ): + with pytest.raises(RouteManagerError): + await route_manager.mediation_record_if_id( + mediation_id=mediation_record.mediation_id + ) + + +@pytest.mark.asyncio +async def test_mediation_record_if_id_with_id_and_default(route_manager: RouteManager): + mediation_record = MediationRecord( + mediation_id="test-mediation-id", state=MediationRecord.STATE_GRANTED + ) + with mock.patch.object( + MediationRecord, + "retrieve_by_id", + mock.CoroutineMock(return_value=mediation_record), + ) as mock_retrieve_by_id, mock.patch.object( + MediationManager, "get_default_mediator", mock.CoroutineMock() + ) as mock_get_default_mediator: + actual = await route_manager.mediation_record_if_id( + mediation_id=mediation_record.mediation_id, or_default=True + ) + assert mediation_record == actual + mock_retrieve_by_id.assert_called_once() + mock_get_default_mediator.assert_not_called() + + +@pytest.mark.asyncio +async def test_mediation_record_if_id_without_id_and_default( + route_manager: RouteManager, +): + mediation_record = MediationRecord( + mediation_id="test-mediation-id", state=MediationRecord.STATE_GRANTED + ) + with mock.patch.object( + MediationRecord, "retrieve_by_id", mock.CoroutineMock() + ) as mock_retrieve_by_id, mock.patch.object( + MediationManager, + "get_default_mediator", + mock.CoroutineMock(return_value=mediation_record), + ) as mock_get_default_mediator: + actual = await route_manager.mediation_record_if_id( + mediation_id=None, or_default=True + ) + assert mediation_record == actual + mock_retrieve_by_id.assert_not_called() + mock_get_default_mediator.assert_called_once() + + +@pytest.mark.asyncio +async def test_mediation_record_if_id_without_id_and_no_default( + route_manager: RouteManager, +): + with mock.patch.object( + MediationRecord, "retrieve_by_id", mock.CoroutineMock(return_value=None) + ) as mock_retrieve_by_id, mock.patch.object( + MediationManager, "get_default_mediator", mock.CoroutineMock(return_value=None) + ) as mock_get_default_mediator: + assert ( + await route_manager.mediation_record_if_id( + mediation_id=None, or_default=True + ) + is None + ) + mock_retrieve_by_id.assert_not_called() + mock_get_default_mediator.assert_called_once() + + +@pytest.mark.asyncio +async def test_route_connection_as_invitee( + route_manager: RouteManager, conn_record: ConnRecord +): + mediation_record = MediationRecord(mediation_id="test-mediation-id") + mock_did_info = mock.MagicMock(DIDInfo) + with mock.patch.object( + route_manager, + "get_or_create_my_did", + mock.CoroutineMock(return_value=mock_did_info), + ): + await route_manager.route_connection_as_invitee(conn_record, mediation_record) + route_manager._route_for_key.assert_called_once_with( + mock_did_info.verkey, mediation_record, skip_if_exists=True + ) + + +@pytest.mark.asyncio +async def test_route_connection_as_inviter( + route_manager: RouteManager, conn_record: ConnRecord +): + mediation_record = MediationRecord(mediation_id="test-mediation-id") + mock_did_info = mock.MagicMock(DIDInfo) + conn_record.invitation_key = "test-invitation-key" + with mock.patch.object( + route_manager, + "get_or_create_my_did", + mock.CoroutineMock(return_value=mock_did_info), + ): + await route_manager.route_connection_as_inviter(conn_record, mediation_record) + route_manager._route_for_key.assert_called_once_with( + mock_did_info.verkey, + mediation_record, + replace_key="test-invitation-key", + skip_if_exists=True, + ) + + +@pytest.mark.asyncio +async def test_route_connection_state_invitee( + route_manager: RouteManager, conn_record: ConnRecord +): + mediation_record = MediationRecord(mediation_id="test-mediation-id") + conn_record.state = "invitation" + conn_record.their_role = "responder" + with mock.patch.object( + route_manager, "route_connection_as_invitee", mock.CoroutineMock() + ) as mock_route_connection_as_invitee, mock.patch.object( + route_manager, "route_connection_as_inviter", mock.CoroutineMock() + ) as mock_route_connection_as_inviter: + await route_manager.route_connection(conn_record, mediation_record) + mock_route_connection_as_invitee.assert_called_once() + mock_route_connection_as_inviter.assert_not_called() + + +@pytest.mark.asyncio +async def test_route_connection_state_inviter( + route_manager: RouteManager, conn_record: ConnRecord +): + mediation_record = MediationRecord(mediation_id="test-mediation-id") + conn_record.state = "request" + conn_record.their_role = "requester" + with mock.patch.object( + route_manager, "route_connection_as_invitee", mock.CoroutineMock() + ) as mock_route_connection_as_invitee, mock.patch.object( + route_manager, "route_connection_as_inviter", mock.CoroutineMock() + ) as mock_route_connection_as_inviter: + await route_manager.route_connection(conn_record, mediation_record) + mock_route_connection_as_inviter.assert_called_once() + mock_route_connection_as_invitee.assert_not_called() + + +@pytest.mark.asyncio +async def test_route_connection_state_other( + route_manager: RouteManager, conn_record: ConnRecord +): + mediation_record = MediationRecord(mediation_id="test-mediation-id") + conn_record.state = "response" + conn_record.their_role = "requester" + assert await route_manager.route_connection(conn_record, mediation_record) is None + + +@pytest.mark.asyncio +async def test_route_invitation_with_key( + route_manager: RouteManager, conn_record: ConnRecord +): + mediation_record = MediationRecord(mediation_id="test-mediation-id") + conn_record.invitation_key = "test-invitation-key" + with mock.patch.object( + route_manager, "save_mediator_for_connection", mock.CoroutineMock() + ): + await route_manager.route_invitation(conn_record, mediation_record) + route_manager._route_for_key.assert_called_once() + + +@pytest.mark.asyncio +async def test_route_invitation_without_key( + route_manager: RouteManager, conn_record: ConnRecord +): + mediation_record = MediationRecord(mediation_id="test-mediation-id") + with mock.patch.object( + route_manager, "save_mediator_for_connection", mock.CoroutineMock() + ): + with pytest.raises(ValueError): + await route_manager.route_invitation(conn_record, mediation_record) + route_manager._route_for_key.assert_not_called() + + +@pytest.mark.asyncio +async def test_route_public_did(route_manager: RouteManager): + await route_manager.route_public_did("test-verkey") + route_manager._route_for_key.assert_called_once_with( + "test-verkey", skip_if_exists=True + ) + + +@pytest.mark.asyncio +async def test_route_static(route_manager: RouteManager, conn_record: ConnRecord): + mediation_record = MediationRecord(mediation_id="test-mediation-id") + mock_did_info = mock.MagicMock(DIDInfo) + conn_record.invitation_key = "test-invitation-key" + with mock.patch.object( + route_manager, + "get_or_create_my_did", + mock.CoroutineMock(return_value=mock_did_info), + ): + await route_manager.route_static(conn_record, mediation_record) + route_manager._route_for_key.assert_called_once_with( + mock_did_info.verkey, + mediation_record, + skip_if_exists=True, + ) + + +@pytest.mark.asyncio +async def test_save_mediator_for_connection_record( + route_manager: RouteManager, conn_record: ConnRecord, profile: Profile +): + mediation_record = MediationRecord(mediation_id="test-mediation-id") + session = mock.MagicMock() + profile.session = mock.MagicMock(return_value=session) + session.__aenter__ = mock.CoroutineMock(return_value=session) + session.__aexit__ = mock.CoroutineMock() + with mock.patch.object( + MediationRecord, "retrieve_by_id", mock.CoroutineMock() + ) as mock_retrieve_by_id: + await route_manager.save_mediator_for_connection(conn_record, mediation_record) + mock_retrieve_by_id.assert_not_called() + conn_record.metadata_set.assert_called_once_with( + session, + MediationManager.METADATA_KEY, + {MediationManager.METADATA_ID: mediation_record.mediation_id}, + ) + + +@pytest.mark.asyncio +async def test_save_mediator_for_connection_id( + route_manager: RouteManager, conn_record: ConnRecord, profile: Profile +): + mediation_record = MediationRecord(mediation_id="test-mediation-id") + session = mock.MagicMock() + profile.session = mock.MagicMock(return_value=session) + session.__aenter__ = mock.CoroutineMock(return_value=session) + session.__aexit__ = mock.CoroutineMock() + with mock.patch.object( + MediationRecord, + "retrieve_by_id", + mock.CoroutineMock(return_value=mediation_record), + ) as mock_retrieve_by_id: + await route_manager.save_mediator_for_connection( + conn_record, mediation_id=mediation_record.mediation_id + ) + mock_retrieve_by_id.assert_called_once() + conn_record.metadata_set.assert_called_once_with( + session, + MediationManager.METADATA_KEY, + {MediationManager.METADATA_ID: mediation_record.mediation_id}, + ) + + +@pytest.mark.asyncio +async def test_save_mediator_for_connection_no_mediator( + route_manager: RouteManager, conn_record: ConnRecord, profile: Profile +): + with mock.patch.object( + MediationRecord, "retrieve_by_id", mock.CoroutineMock() + ) as mock_retrieve_by_id: + await route_manager.save_mediator_for_connection(conn_record) + mock_retrieve_by_id.assert_not_called() + conn_record.metadata_set.assert_not_called() + + +@pytest.mark.asyncio +async def test_mediation_route_for_key( + mediation_route_manager: CoordinateMediationV1RouteManager, + mock_responder: MockResponder, +): + mediation_record = MediationRecord( + mediation_id="test-mediation-id", connection_id="test-mediator-conn-id" + ) + keylist_update = await mediation_route_manager._route_for_key( + "test-recipient-key", mediation_record, skip_if_exists=False, replace_key=None + ) + assert keylist_update + assert keylist_update.serialize()["updates"] == [ + {"action": "add", "recipient_key": "test-recipient-key"} + ] + assert mock_responder.messages + assert ( + keylist_update, + {"connection_id": "test-mediator-conn-id"}, + ) == mock_responder.messages[0] + + +@pytest.mark.asyncio +async def test_mediation_route_for_key_skip_if_exists_and_exists( + mediation_route_manager: CoordinateMediationV1RouteManager, + mock_responder: MockResponder, +): + mediation_record = MediationRecord( + mediation_id="test-mediation-id", connection_id="test-mediator-conn-id" + ) + with mock.patch.object( + RouteRecord, "retrieve_by_recipient_key", mock.CoroutineMock() + ): + keylist_update = await mediation_route_manager._route_for_key( + "test-recipient-key", + mediation_record, + skip_if_exists=True, + replace_key=None, + ) + assert keylist_update is None + assert not mock_responder.messages + + +@pytest.mark.asyncio +async def test_mediation_route_for_key_skip_if_exists_and_absent( + mediation_route_manager: CoordinateMediationV1RouteManager, + mock_responder: MockResponder, +): + mediation_record = MediationRecord( + mediation_id="test-mediation-id", connection_id="test-mediator-conn-id" + ) + with mock.patch.object( + RouteRecord, + "retrieve_by_recipient_key", + mock.CoroutineMock(side_effect=StorageNotFoundError), + ): + keylist_update = await mediation_route_manager._route_for_key( + "test-recipient-key", + mediation_record, + skip_if_exists=True, + replace_key=None, + ) + assert keylist_update + assert keylist_update.serialize()["updates"] == [ + {"action": "add", "recipient_key": "test-recipient-key"} + ] + assert mock_responder.messages + assert ( + keylist_update, + {"connection_id": "test-mediator-conn-id"}, + ) == mock_responder.messages[0] + + +@pytest.mark.asyncio +async def test_mediation_route_for_key_replace_key( + mediation_route_manager: CoordinateMediationV1RouteManager, + mock_responder: MockResponder, +): + mediation_record = MediationRecord( + mediation_id="test-mediation-id", connection_id="test-mediator-conn-id" + ) + keylist_update = await mediation_route_manager._route_for_key( + "test-recipient-key", + mediation_record, + skip_if_exists=False, + replace_key="test-replace-key", + ) + assert keylist_update + assert keylist_update.serialize()["updates"] == [ + {"action": "add", "recipient_key": "test-recipient-key"}, + {"action": "remove", "recipient_key": "test-replace-key"}, + ] + assert mock_responder.messages + assert ( + keylist_update, + {"connection_id": "test-mediator-conn-id"}, + ) == mock_responder.messages[0] + + +@pytest.mark.asyncio +async def test_mediation_route_for_key_no_mediator( + mediation_route_manager: CoordinateMediationV1RouteManager, +): + assert ( + await mediation_route_manager._route_for_key( + "test-recipient-key", + None, + skip_if_exists=True, + replace_key="test-replace-key", + ) + is None + ) + + +@pytest.mark.asyncio +async def test_mediation_routing_info_with_mediator( + mediation_route_manager: CoordinateMediationV1RouteManager, +): + mediation_record = MediationRecord( + mediation_id="test-mediation-id", + connection_id="test-mediator-conn-id", + routing_keys=["test-key-0", "test-key-1"], + endpoint="http://mediator.example.com", + ) + keys, endpoint = await mediation_route_manager.routing_info( + "http://example.com", mediation_record + ) + assert keys == mediation_record.routing_keys + assert endpoint == mediation_record.endpoint + + +@pytest.mark.asyncio +async def test_mediation_routing_info_no_mediator( + mediation_route_manager: CoordinateMediationV1RouteManager, +): + keys, endpoint = await mediation_route_manager.routing_info( + "http://example.com", None + ) + assert keys == [] + assert endpoint == "http://example.com" From 805c43d6df7a3fccf5bed2233ebeeb70d773777d Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Fri, 1 Jul 2022 14:08:38 -0400 Subject: [PATCH 325/872] test: multitenant route manager Signed-off-by: Daniel Bluhm --- aries_cloudagent/multitenant/route_manager.py | 7 +- .../multitenant/tests/test_route_manager.py | 348 ++++++++++++++++++ 2 files changed, 352 insertions(+), 3 deletions(-) create mode 100644 aries_cloudagent/multitenant/tests/test_route_manager.py diff --git a/aries_cloudagent/multitenant/route_manager.py b/aries_cloudagent/multitenant/route_manager.py index c79cdd6faa..0832f7ec26 100644 --- a/aries_cloudagent/multitenant/route_manager.py +++ b/aries_cloudagent/multitenant/route_manager.py @@ -53,9 +53,10 @@ async def _route_for_key( ) routing_mgr = RoutingManager(self.root_profile) mediation_mgr = MediationManager(self.root_profile) - # Passed in mediation_record is ignored altogether. - # Only the base mediator needs updates. - mediation_record = await self.get_base_wallet_mediator() + # If base wallet had mediator, only notify that mediator. + # Else, if subwallet has mediator, notify that mediator. + base_mediation_record = await self.get_base_wallet_mediator() + mediation_record = base_mediation_record or mediation_record if skip_if_exists: try: diff --git a/aries_cloudagent/multitenant/tests/test_route_manager.py b/aries_cloudagent/multitenant/tests/test_route_manager.py new file mode 100644 index 0000000000..0dc996f087 --- /dev/null +++ b/aries_cloudagent/multitenant/tests/test_route_manager.py @@ -0,0 +1,348 @@ +import pytest +from asynctest import mock + +from aries_cloudagent.protocols.routing.v1_0.manager import RoutingManager + +from ...connections.models.conn_record import ConnRecord +from ...core.in_memory import InMemoryProfile +from ...core.profile import Profile +from ...messaging.responder import BaseResponder, MockResponder +from ...messaging.responder import BaseResponder, MockResponder +from ...protocols.coordinate_mediation.v1_0.manager import MediationManager +from ...protocols.coordinate_mediation.v1_0.messages.keylist_update import KeylistUpdate +from ...protocols.coordinate_mediation.v1_0.models.mediation_record import ( + MediationRecord, +) +from ...protocols.coordinate_mediation.v1_0.route_manager import ( + RouteManager, + RouteManagerError, +) +from ...protocols.routing.v1_0.models.route_record import RouteRecord +from ...storage.error import StorageNotFoundError +from ...wallet.did_info import DIDInfo +from ...wallet.in_memory import InMemoryWallet +from ..route_manager import MultitenantRouteManager + + +@pytest.fixture +def wallet_id(): + yield "test-wallet-id" + + +@pytest.fixture +def mock_responder(): + yield MockResponder() + + +@pytest.fixture +def root_profile(mock_responder: MockResponder): + yield InMemoryProfile.test_profile( + bind={ + BaseResponder: mock_responder, + } + ) + + +@pytest.fixture +def sub_profile(mock_responder: MockResponder, wallet_id: str): + yield InMemoryProfile.test_profile( + settings={ + "wallet.id": wallet_id, + }, + bind={ + BaseResponder: mock_responder, + }, + ) + + +@pytest.fixture +def route_manager(root_profile: Profile, sub_profile: Profile, wallet_id: str): + yield MultitenantRouteManager(root_profile, sub_profile, wallet_id) + + +def test_sub_profile_access( + route_manager: MultitenantRouteManager, sub_profile: Profile +): + assert route_manager.sub_profile == sub_profile + + +@pytest.mark.asyncio +async def test_route_for_key_sub_mediator_no_base_mediator( + route_manager: MultitenantRouteManager, + mock_responder: MockResponder, + wallet_id: str, +): + mediation_record = MediationRecord( + mediation_id="test-mediation-id", connection_id="test-mediator-conn-id" + ) + + with mock.patch.object( + route_manager, "get_base_wallet_mediator", mock.CoroutineMock(return_value=None) + ), mock.patch.object( + RoutingManager, "create_route_record", mock.CoroutineMock() + ) as mock_create_route_record: + keylist_update = await route_manager._route_for_key( + "test-recipient-key", + mediation_record, + skip_if_exists=False, + replace_key=None, + ) + + mock_create_route_record.assert_called_once_with( + recipient_key="test-recipient-key", internal_wallet_id=wallet_id + ) + assert keylist_update + assert keylist_update.serialize()["updates"] == [ + {"action": "add", "recipient_key": "test-recipient-key"} + ] + assert mock_responder.messages + assert ( + keylist_update, + {"connection_id": "test-mediator-conn-id"}, + ) == mock_responder.messages[0] + + +@pytest.mark.asyncio +async def test_route_for_key_sub_mediator_and_base_mediator( + route_manager: MultitenantRouteManager, + mock_responder: MockResponder, + wallet_id: str, +): + mediation_record = MediationRecord( + mediation_id="test-mediation-id", connection_id="test-mediator-conn-id" + ) + base_mediation_record = MediationRecord( + mediation_id="test-base-mediation-id", + connection_id="test-base-mediator-conn-id", + ) + + with mock.patch.object( + route_manager, + "get_base_wallet_mediator", + mock.CoroutineMock(return_value=base_mediation_record), + ), mock.patch.object( + RoutingManager, "create_route_record", mock.CoroutineMock() + ) as mock_create_route_record: + keylist_update = await route_manager._route_for_key( + "test-recipient-key", + mediation_record, + skip_if_exists=False, + replace_key=None, + ) + + mock_create_route_record.assert_called_once_with( + recipient_key="test-recipient-key", internal_wallet_id=wallet_id + ) + assert keylist_update + assert keylist_update.serialize()["updates"] == [ + {"action": "add", "recipient_key": "test-recipient-key"} + ] + assert mock_responder.messages + assert ( + keylist_update, + {"connection_id": "test-base-mediator-conn-id"}, + ) == mock_responder.messages[0] + + +@pytest.mark.asyncio +async def test_route_for_key_base_mediator_no_sub_mediator( + route_manager: MultitenantRouteManager, + mock_responder: MockResponder, + wallet_id: str, +): + base_mediation_record = MediationRecord( + mediation_id="test-base-mediation-id", + connection_id="test-base-mediator-conn-id", + ) + + with mock.patch.object( + route_manager, + "get_base_wallet_mediator", + mock.CoroutineMock(return_value=base_mediation_record), + ), mock.patch.object( + RoutingManager, "create_route_record", mock.CoroutineMock() + ) as mock_create_route_record: + keylist_update = await route_manager._route_for_key( + "test-recipient-key", None, skip_if_exists=False, replace_key=None + ) + + mock_create_route_record.assert_called_once_with( + recipient_key="test-recipient-key", internal_wallet_id=wallet_id + ) + assert keylist_update + assert keylist_update.serialize()["updates"] == [ + {"action": "add", "recipient_key": "test-recipient-key"} + ] + assert mock_responder.messages + assert ( + keylist_update, + {"connection_id": "test-base-mediator-conn-id"}, + ) == mock_responder.messages[0] + + +@pytest.mark.asyncio +async def test_route_for_key_skip_if_exists_and_exists( + route_manager: MultitenantRouteManager, + mock_responder: MockResponder, +): + mediation_record = MediationRecord( + mediation_id="test-mediation-id", connection_id="test-mediator-conn-id" + ) + with mock.patch.object( + RouteRecord, "retrieve_by_recipient_key", mock.CoroutineMock() + ): + keylist_update = await route_manager._route_for_key( + "test-recipient-key", + mediation_record, + skip_if_exists=True, + replace_key=None, + ) + assert keylist_update is None + assert not mock_responder.messages + + +@pytest.mark.asyncio +async def test_route_for_key_skip_if_exists_and_absent( + route_manager: MultitenantRouteManager, + mock_responder: MockResponder, +): + mediation_record = MediationRecord( + mediation_id="test-mediation-id", connection_id="test-mediator-conn-id" + ) + with mock.patch.object( + RouteRecord, + "retrieve_by_recipient_key", + mock.CoroutineMock(side_effect=StorageNotFoundError), + ): + keylist_update = await route_manager._route_for_key( + "test-recipient-key", + mediation_record, + skip_if_exists=True, + replace_key=None, + ) + assert keylist_update + assert keylist_update.serialize()["updates"] == [ + {"action": "add", "recipient_key": "test-recipient-key"} + ] + assert mock_responder.messages + assert ( + keylist_update, + {"connection_id": "test-mediator-conn-id"}, + ) == mock_responder.messages[0] + + +@pytest.mark.asyncio +async def test_route_for_key_replace_key( + route_manager: MultitenantRouteManager, + mock_responder: MockResponder, +): + mediation_record = MediationRecord( + mediation_id="test-mediation-id", connection_id="test-mediator-conn-id" + ) + keylist_update = await route_manager._route_for_key( + "test-recipient-key", + mediation_record, + skip_if_exists=False, + replace_key="test-replace-key", + ) + assert keylist_update + assert keylist_update.serialize()["updates"] == [ + {"action": "add", "recipient_key": "test-recipient-key"}, + {"action": "remove", "recipient_key": "test-replace-key"}, + ] + assert mock_responder.messages + assert ( + keylist_update, + {"connection_id": "test-mediator-conn-id"}, + ) == mock_responder.messages[0] + + +@pytest.mark.asyncio +async def test_route_for_key_no_mediator( + route_manager: MultitenantRouteManager, +): + assert ( + await route_manager._route_for_key( + "test-recipient-key", + None, + skip_if_exists=True, + replace_key="test-replace-key", + ) + is None + ) + + +@pytest.mark.asyncio +async def test_routing_info_with_mediator( + route_manager: MultitenantRouteManager, +): + mediation_record = MediationRecord( + mediation_id="test-mediation-id", + connection_id="test-mediator-conn-id", + routing_keys=["test-key-0", "test-key-1"], + endpoint="http://mediator.example.com", + ) + keys, endpoint = await route_manager.routing_info( + "http://example.com", mediation_record + ) + assert keys == mediation_record.routing_keys + assert endpoint == mediation_record.endpoint + + +@pytest.mark.asyncio +async def test_routing_info_no_mediator( + route_manager: MultitenantRouteManager, +): + keys, endpoint = await route_manager.routing_info("http://example.com", None) + assert keys == [] + assert endpoint == "http://example.com" + + +@pytest.mark.asyncio +async def test_routing_info_with_base_mediator( + route_manager: MultitenantRouteManager, +): + base_mediation_record = MediationRecord( + mediation_id="test-base-mediation-id", + connection_id="test-base-mediator-conn-id", + routing_keys=["test-key-0", "test-key-1"], + endpoint="http://base.mediator.example.com", + ) + + with mock.patch.object( + route_manager, + "get_base_wallet_mediator", + mock.CoroutineMock(return_value=base_mediation_record), + ): + keys, endpoint = await route_manager.routing_info("http://example.com", None) + assert keys == base_mediation_record.routing_keys + assert endpoint == base_mediation_record.endpoint + + +@pytest.mark.asyncio +async def test_routing_info_with_base_mediator_and_sub_mediator( + route_manager: MultitenantRouteManager, +): + mediation_record = MediationRecord( + mediation_id="test-mediation-id", + connection_id="test-mediator-conn-id", + routing_keys=["test-key-0", "test-key-1"], + endpoint="http://mediator.example.com", + ) + base_mediation_record = MediationRecord( + mediation_id="test-base-mediation-id", + connection_id="test-base-mediator-conn-id", + routing_keys=["test-base-key-0", "test-base-key-1"], + endpoint="http://base.mediator.example.com", + ) + + with mock.patch.object( + route_manager, + "get_base_wallet_mediator", + mock.CoroutineMock(return_value=base_mediation_record), + ): + keys, endpoint = await route_manager.routing_info( + "http://example.com", mediation_record + ) + assert keys == [*base_mediation_record.routing_keys, *mediation_record.routing_keys] + assert endpoint == mediation_record.endpoint From eb226198b02eaf31dbf2e27caf2eccff4f1faf3d Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Fri, 1 Jul 2022 14:09:14 -0400 Subject: [PATCH 326/872] fix: remove unused imports Signed-off-by: Daniel Bluhm --- aries_cloudagent/multitenant/tests/test_route_manager.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/aries_cloudagent/multitenant/tests/test_route_manager.py b/aries_cloudagent/multitenant/tests/test_route_manager.py index 0dc996f087..f140afd8a0 100644 --- a/aries_cloudagent/multitenant/tests/test_route_manager.py +++ b/aries_cloudagent/multitenant/tests/test_route_manager.py @@ -3,24 +3,15 @@ from aries_cloudagent.protocols.routing.v1_0.manager import RoutingManager -from ...connections.models.conn_record import ConnRecord from ...core.in_memory import InMemoryProfile from ...core.profile import Profile from ...messaging.responder import BaseResponder, MockResponder from ...messaging.responder import BaseResponder, MockResponder -from ...protocols.coordinate_mediation.v1_0.manager import MediationManager -from ...protocols.coordinate_mediation.v1_0.messages.keylist_update import KeylistUpdate from ...protocols.coordinate_mediation.v1_0.models.mediation_record import ( MediationRecord, ) -from ...protocols.coordinate_mediation.v1_0.route_manager import ( - RouteManager, - RouteManagerError, -) from ...protocols.routing.v1_0.models.route_record import RouteRecord from ...storage.error import StorageNotFoundError -from ...wallet.did_info import DIDInfo -from ...wallet.in_memory import InMemoryWallet from ..route_manager import MultitenantRouteManager From d582a16af7997746c2ee09a1aa44bd97deb8a2e9 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Fri, 1 Jul 2022 15:07:20 -0400 Subject: [PATCH 327/872] fix: public did routing in wallet routes Signed-off-by: Daniel Bluhm --- aries_cloudagent/wallet/routes.py | 4 +++- aries_cloudagent/wallet/tests/test_routes.py | 17 +++++++++++------ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/aries_cloudagent/wallet/routes.py b/aries_cloudagent/wallet/routes.py index 0acff9d4cc..6f97eebc69 100644 --- a/aries_cloudagent/wallet/routes.py +++ b/aries_cloudagent/wallet/routes.py @@ -582,7 +582,9 @@ async def promote_wallet_public_did( multitenant_mgr = profile.inject_or(BaseMultitenantManager) # Add multitenant relay mapping so implicit invitations are still routed if multitenant_mgr and wallet_id: - await multitenant_mgr.add_key(wallet_id, info.verkey, skip_if_exists=True) + await multitenant_mgr.get_route_manager( + profile, wallet_id + ).route_public_did(info.verkey) return info, attrib_def diff --git a/aries_cloudagent/wallet/tests/test_routes.py b/aries_cloudagent/wallet/tests/test_routes.py index e7869576fb..4d9f7cfccd 100644 --- a/aries_cloudagent/wallet/tests/test_routes.py +++ b/aries_cloudagent/wallet/tests/test_routes.py @@ -1,15 +1,15 @@ -from asynctest import mock as async_mock, TestCase as AsyncTestCase from aiohttp.web import HTTPForbidden +from asynctest import TestCase as AsyncTestCase, mock as async_mock +from .. import routes as test_module from ...admin.request_context import AdminRequestContext from ...core.in_memory import InMemoryProfile from ...ledger.base import BaseLedger from ...multitenant.base import BaseMultitenantManager from ...multitenant.manager import MultitenantManager -from ...wallet.key_type import KeyType +from ...protocols.coordinate_mediation.v1_0.route_manager import RouteManager from ...wallet.did_method import DIDMethod - -from .. import routes as test_module +from ...wallet.key_type import KeyType from ..base import BaseWallet from ..did_info import DIDInfo from ..did_posture import DIDPosture @@ -421,6 +421,10 @@ async def test_set_public_did_multitenant(self): self.profile.context.injector.bind_instance(BaseLedger, ledger) multitenant_mgr = async_mock.MagicMock(MultitenantManager, autospec=True) + route_manager = async_mock.MagicMock(RouteManager) + multitenant_mgr.get_route_manager = async_mock.MagicMock( + return_value=route_manager + ) self.profile.context.injector.bind_instance( BaseMultitenantManager, multitenant_mgr ) @@ -436,9 +440,10 @@ async def test_set_public_did_multitenant(self): ) await test_module.wallet_set_public_did(self.request) - multitenant_mgr.add_key.assert_called_once_with( - "test_wallet", self.test_verkey, skip_if_exists=True + multitenant_mgr.get_route_manager.assert_called_once_with( + self.profile, "test_wallet" ) + route_manager.route_public_did.assert_called_once_with(self.test_verkey) async def test_set_public_did_no_query_did(self): with self.assertRaises(test_module.web.HTTPBadRequest): From b1131d88a800242d369dda6b4f58e0c316fe9dc3 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 4 Jul 2022 22:26:59 +0200 Subject: [PATCH 328/872] fix: return if return route but no response Signed-off-by: Timo Glastra --- aries_cloudagent/transport/inbound/http.py | 7 ++++- aries_cloudagent/transport/inbound/manager.py | 2 ++ aries_cloudagent/transport/inbound/message.py | 10 +++++++ .../transport/inbound/tests/test_message.py | 30 +++++++++++++++++++ 4 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 aries_cloudagent/transport/inbound/tests/test_message.py diff --git a/aries_cloudagent/transport/inbound/http.py b/aries_cloudagent/transport/inbound/http.py index 8179023379..696cab4bd3 100644 --- a/aries_cloudagent/transport/inbound/http.py +++ b/aries_cloudagent/transport/inbound/http.py @@ -96,7 +96,12 @@ async def inbound_message_handler(self, request: web.BaseRequest): raise web.HTTPBadRequest() if inbound.receipt.direct_response_requested: - response = await session.wait_response() + # Wait for the message to be processed. Only send a response if a response + # buffer is present. + await inbound.wait_processing_complete() + response = ( + await session.wait_response() if session.response_buffer else None + ) # no more responses session.can_respond = False diff --git a/aries_cloudagent/transport/inbound/manager.py b/aries_cloudagent/transport/inbound/manager.py index 65d081454d..909703e7c5 100644 --- a/aries_cloudagent/transport/inbound/manager.py +++ b/aries_cloudagent/transport/inbound/manager.py @@ -181,6 +181,8 @@ def dispatch_complete(self, message: InboundMessage, completed: CompletedTask): if session and session.accept_undelivered and not session.response_buffered: self.process_undelivered(session) + message.dispatch_processing_complete() + def closed_session(self, session: InboundSession): """ Clean up a closed session. diff --git a/aries_cloudagent/transport/inbound/message.py b/aries_cloudagent/transport/inbound/message.py index 169b2dc35c..2def0e1d92 100644 --- a/aries_cloudagent/transport/inbound/message.py +++ b/aries_cloudagent/transport/inbound/message.py @@ -1,5 +1,6 @@ """Classes representing inbound messages.""" +import asyncio from typing import Union from .receipt import MessageReceipt @@ -23,3 +24,12 @@ def __init__( self.receipt = receipt self.session_id = session_id self.transport_type = transport_type + self.processing_complete_event = asyncio.Event() + + def dispatch_processing_complete(self): + """Dispatch processing complete.""" + self.processing_complete_event.set() + + async def wait_processing_complete(self): + """Wait for processing to complete.""" + await self.processing_complete_event.wait() diff --git a/aries_cloudagent/transport/inbound/tests/test_message.py b/aries_cloudagent/transport/inbound/tests/test_message.py new file mode 100644 index 0000000000..71a8defee8 --- /dev/null +++ b/aries_cloudagent/transport/inbound/tests/test_message.py @@ -0,0 +1,30 @@ +import asyncio + +from asynctest import TestCase + +from ..message import InboundMessage +from ..receipt import MessageReceipt + + +class TestInboundMessage(TestCase): + async def test_wait_response(self): + message = InboundMessage( + payload="test", + connection_id="conn_id", + receipt=MessageReceipt(), + session_id="session_id", + ) + assert not message.processing_complete_event.is_set() + message.dispatch_processing_complete() + assert message.processing_complete_event.is_set() + + message = InboundMessage( + payload="test", + connection_id="conn_id", + receipt=MessageReceipt(), + session_id="session_id", + ) + assert not message.processing_complete_event.is_set() + task = message.wait_processing_complete() + message.dispatch_processing_complete() + await asyncio.wait_for(task, 1) From f1a7ba33f01368f65cab6f070eb10f75fc6936ae Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 5 Jul 2022 09:23:41 +0200 Subject: [PATCH 329/872] test: fix transport unit test Signed-off-by: Timo Glastra --- .../transport/inbound/tests/test_http_transport.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/transport/inbound/tests/test_http_transport.py b/aries_cloudagent/transport/inbound/tests/test_http_transport.py index c440fe7a57..c561ba7827 100644 --- a/aries_cloudagent/transport/inbound/tests/test_http_transport.py +++ b/aries_cloudagent/transport/inbound/tests/test_http_transport.py @@ -119,13 +119,15 @@ async def test_send_message_outliers(self): mock_session.return_value = async_mock.MagicMock( receive=async_mock.CoroutineMock( return_value=async_mock.MagicMock( - receipt=async_mock.MagicMock(direct_response_requested=True) + receipt=async_mock.MagicMock(direct_response_requested=True), + wait_processing_complete=async_mock.CoroutineMock(), ) ), can_respond=True, profile=InMemoryProfile.test_profile(), clear_response=async_mock.MagicMock(), wait_response=async_mock.CoroutineMock(return_value=b"Hello world"), + response_buffer="something", ) async with self.client.post("/", data=test_message) as resp: result = await resp.text() From 2dcaf68855f12419618fba4236a2b44ff708d430 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Wed, 6 Jul 2022 08:57:41 -0500 Subject: [PATCH 330/872] fix: allow mediation record to be none in update-keylist for connection Signed-off-by: Daniel Bluhm --- .../protocols/coordinate_mediation/v1_0/routes.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/routes.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/routes.py index 3ebfc87f1c..651255bbcd 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/routes.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/routes.py @@ -531,24 +531,21 @@ async def update_keylist_for_connection(request: web.BaseRequest): multitenant_mgr = context.inject_or(BaseMultitenantManager) wallet_id = context.settings.get_str("wallet.id") if multitenant_mgr and wallet_id: - routing_manager = multitenant_mgr.get_route_manager( + route_manager = multitenant_mgr.get_route_manager( context.profile, wallet_id ) else: - routing_manager = CoordinateMediationV1RouteManager(context.profile) + route_manager = CoordinateMediationV1RouteManager(context.profile) async with context.session() as session: connection_record = await ConnRecord.retrieve_by_id(session, connection_id) - mediation_record = await routing_manager.mediation_record_for_connection( + mediation_record = await route_manager.mediation_record_for_connection( connection_record, mediation_id, or_default=True ) - if not mediation_record: - raise web.HTTPBadRequest( - reason="No mediation_id specified and no default mediator" - ) - - keylist_update = await routing_manager.route_connection( + # MediationRecord is permitted to be None; route manager will + # ensure the correct mediator is notified. + keylist_update = await route_manager.route_connection( connection_record, mediation_record ) From 960b1b80dcdda2bc44fc0350ecb51d9f80e92825 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Wed, 6 Jul 2022 09:38:35 -0500 Subject: [PATCH 331/872] fix: pytest attempting to collect mock class Signed-off-by: Daniel Bluhm --- .../coordinate_mediation/v1_0/tests/test_route_manager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_route_manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_route_manager.py index 3bd3d9e7ff..61b72bb975 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_route_manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_route_manager.py @@ -19,7 +19,7 @@ ) -class TestRouteManager(RouteManager): +class MockRouteManager(RouteManager): """Concretion of RouteManager for testing.""" _route_for_key = mock.CoroutineMock() @@ -38,7 +38,7 @@ def profile(mock_responder: MockResponder): @pytest.fixture def route_manager(profile: Profile): - manager = TestRouteManager(profile) + manager = MockRouteManager(profile) manager._route_for_key = mock.CoroutineMock( return_value=mock.MagicMock(KeylistUpdate) ) From 0a352a791419287755f30a6abf5d7a99589e9cbb Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Wed, 6 Jul 2022 09:39:18 -0500 Subject: [PATCH 332/872] test: update keylist for connection endpoint Signed-off-by: Daniel Bluhm --- .../v1_0/tests/test_routes.py | 89 ++++++++++++++----- 1 file changed, 66 insertions(+), 23 deletions(-) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_routes.py index a19070a6e6..29e2224f3f 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_routes.py @@ -1,22 +1,20 @@ -import json +from asynctest import TestCase as AsyncTestCase, mock as async_mock +from aries_cloudagent.admin.request_context import AdminRequestContext -from asynctest import mock as async_mock, TestCase as AsyncTestCase - -from .....admin.request_context import AdminRequestContext -from .....core.in_memory import InMemoryProfile -from .....config.injection_context import InjectionContext -from .....messaging.request_context import RequestContext +from aries_cloudagent.protocols.coordinate_mediation.v1_0.route_manager import ( + RouteManager, +) from .. import routes as test_module -from ..manager import MediationManager +from .....core.in_memory import InMemoryProfile +from .....multitenant.base import BaseMultitenantManager from ..models.mediation_record import MediationRecord class TestCoordinateMediationRoutes(AsyncTestCase): def setUp(self): self.profile = InMemoryProfile.test_profile() - self.context = self.profile.context - setattr(self.context, "profile", self.profile) + self.context = AdminRequestContext.test_context(profile=self.profile) self.outbound_message_router = async_mock.CoroutineMock() self.request_dict = { "context": self.context, @@ -77,7 +75,7 @@ async def test_list_mediation_requests(self): ) as mock_query, async_mock.patch.object( test_module.web, "json_response" ) as json_response, async_mock.patch.object( - self.context.profile, + self.profile, "session", async_mock.MagicMock(return_value=InMemoryProfile.test_session()), ) as session: @@ -99,7 +97,7 @@ async def test_list_mediation_requests_filters(self): ) as mock_query, async_mock.patch.object( test_module.web, "json_response" ) as json_response, async_mock.patch.object( - self.context.profile, + self.profile, "session", async_mock.MagicMock(return_value=InMemoryProfile.test_session()), ) as session: @@ -394,7 +392,7 @@ async def test_mediation_request_deny_x_storage_error(self): await test_module.mediation_request_deny(self.request) async def test_get_keylist(self): - session = await self.context.profile.session() + session = await self.profile.session() self.request.query["role"] = MediationRecord.ROLE_SERVER self.request.query["conn_id"] = "test-id" @@ -411,7 +409,7 @@ async def test_get_keylist(self): "query", async_mock.CoroutineMock(return_value=query_results), ) as mock_query, async_mock.patch.object( - self.context.profile, + self.profile, "session", async_mock.MagicMock(return_value=session), ) as mock_session, async_mock.patch.object( @@ -427,13 +425,13 @@ async def test_get_keylist(self): ) async def test_get_keylist_no_matching_records(self): - session = await self.context.profile.session() + session = await self.profile.session() with async_mock.patch.object( test_module.RouteRecord, "query", async_mock.CoroutineMock(return_value=[]), ) as mock_query, async_mock.patch.object( - self.context.profile, + self.profile, "session", async_mock.MagicMock(return_value=session), ) as mock_session, async_mock.patch.object( @@ -583,7 +581,6 @@ async def test_send_keylist_query_x_storage_error(self): async def test_get_default_mediator(self): self.request.query = {} - self.context.session = async_mock.CoroutineMock() with async_mock.patch.object( test_module.web, "json_response" ) as json_response, async_mock.patch.object( @@ -599,7 +596,6 @@ async def test_get_default_mediator(self): async def test_get_empty_default_mediator(self): self.request.query = {} - self.context.session = async_mock.CoroutineMock() with async_mock.patch.object( test_module.web, "json_response" ) as json_response, async_mock.patch.object( @@ -615,7 +611,6 @@ async def test_get_empty_default_mediator(self): async def test_get_default_mediator_storage_error(self): self.request.query = {} - self.context.session = async_mock.CoroutineMock() with async_mock.patch.object( test_module.web, "json_response" ) as json_response, async_mock.patch.object( @@ -631,7 +626,6 @@ async def test_set_default_mediator(self): "mediation_id": "fake_id", } self.request.query = {} - self.context.session = async_mock.CoroutineMock() with async_mock.patch.object( test_module.MediationManager, "get_default_mediator", @@ -654,7 +648,6 @@ async def test_set_default_mediator_storage_error(self): "mediation_id": "bad_id", } self.request.query = {} - self.context.session = async_mock.CoroutineMock() with async_mock.patch.object( test_module.MediationManager, "get_default_mediator", @@ -671,7 +664,6 @@ async def test_set_default_mediator_storage_error(self): async def test_clear_default_mediator(self): self.request.query = {} - self.context.session = async_mock.CoroutineMock() with async_mock.patch.object( test_module.MediationManager, "get_default_mediator", @@ -691,7 +683,6 @@ async def test_clear_default_mediator(self): async def test_clear_default_mediator_storage_error(self): self.request.query = {} - self.context.session = async_mock.CoroutineMock() with async_mock.patch.object( test_module.MediationManager, "get_default_mediator", @@ -706,6 +697,58 @@ async def test_clear_default_mediator_storage_error(self): with self.assertRaises(test_module.web.HTTPBadRequest): await test_module.clear_default_mediator(self.request) + async def test_update_keylist_for_connection_multitenant(self): + self.request.query = {} + self.request.json.return_value = {"mediation_id": "test-mediation-id"} + self.request.match_info = { + "conn_id": "test-conn-id", + } + multitenant_mgr = async_mock.MagicMock(BaseMultitenantManager) + self.context.injector.bind_instance(BaseMultitenantManager, multitenant_mgr) + self.context.settings["wallet.id"] = "test-wallet-id" + mock_route_manager = async_mock.MagicMock(RouteManager) + mock_keylist_update = async_mock.MagicMock() + mock_keylist_update.serialize.return_value = {"mock": "serialized"} + mock_route_manager.route_connection = async_mock.CoroutineMock( + return_value=mock_keylist_update + ) + mock_route_manager.mediation_record_for_connection = async_mock.CoroutineMock() + multitenant_mgr.get_route_manager = async_mock.MagicMock( + return_value=mock_route_manager + ) + with async_mock.patch.object( + test_module.ConnRecord, "retrieve_by_id", async_mock.CoroutineMock() + ) as mock_conn_rec_retrieve_by_id, async_mock.patch.object( + test_module.web, "json_response" + ) as json_response: + await test_module.update_keylist_for_connection(self.request) + json_response.assert_called_once_with({"mock": "serialized"}, status=200) + + async def test_update_keylist_for_connection(self): + self.request.query = {} + self.request.json.return_value = {"mediation_id": "test-mediation-id"} + self.request.match_info = { + "conn_id": "test-conn-id", + } + mock_route_manager = async_mock.MagicMock(RouteManager) + mock_keylist_update = async_mock.MagicMock() + mock_keylist_update.serialize.return_value = {"mock": "serialized"} + mock_route_manager.route_connection = async_mock.CoroutineMock( + return_value=mock_keylist_update + ) + mock_route_manager.mediation_record_for_connection = async_mock.CoroutineMock() + with async_mock.patch.object( + test_module.ConnRecord, "retrieve_by_id", async_mock.CoroutineMock() + ) as mock_conn_rec_retrieve_by_id, async_mock.patch.object( + test_module.web, "json_response" + ) as json_response, async_mock.patch.object( + test_module, + "CoordinateMediationV1RouteManager", + async_mock.MagicMock(return_value=mock_route_manager), + ): + await test_module.update_keylist_for_connection(self.request) + json_response.assert_called_once_with({"mock": "serialized"}, status=200) + async def test_register(self): mock_app = async_mock.MagicMock() mock_app.add_routes = async_mock.MagicMock() From d576262369edb4d77de13834a7f262ee640e3ed4 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Wed, 6 Jul 2022 09:43:38 -0500 Subject: [PATCH 333/872] test: error conditions on update keylist endpoint Signed-off-by: Daniel Bluhm --- .../v1_0/tests/test_routes.py | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_routes.py index 29e2224f3f..9023643fdd 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_routes.py @@ -4,6 +4,7 @@ from aries_cloudagent.protocols.coordinate_mediation.v1_0.route_manager import ( RouteManager, ) +from aries_cloudagent.storage.error import StorageError, StorageNotFoundError from .. import routes as test_module from .....core.in_memory import InMemoryProfile @@ -749,6 +750,56 @@ async def test_update_keylist_for_connection(self): await test_module.update_keylist_for_connection(self.request) json_response.assert_called_once_with({"mock": "serialized"}, status=200) + async def test_update_keylist_for_connection_not_found(self): + self.request.query = {} + self.request.json.return_value = {"mediation_id": "test-mediation-id"} + self.request.match_info = { + "conn_id": "test-conn-id", + } + mock_route_manager = async_mock.MagicMock(RouteManager) + mock_keylist_update = async_mock.MagicMock() + mock_keylist_update.serialize.return_value = {"mock": "serialized"} + mock_route_manager.route_connection = async_mock.CoroutineMock( + return_value=mock_keylist_update + ) + mock_route_manager.mediation_record_for_connection = async_mock.CoroutineMock() + with async_mock.patch.object( + test_module.ConnRecord, + "retrieve_by_id", + async_mock.CoroutineMock(side_effect=StorageNotFoundError), + ) as mock_conn_rec_retrieve_by_id, async_mock.patch.object( + test_module, + "CoordinateMediationV1RouteManager", + async_mock.MagicMock(return_value=mock_route_manager), + ): + with self.assertRaises(test_module.web.HTTPNotFound): + await test_module.update_keylist_for_connection(self.request) + + async def test_update_keylist_for_connection_storage_error(self): + self.request.query = {} + self.request.json.return_value = {"mediation_id": "test-mediation-id"} + self.request.match_info = { + "conn_id": "test-conn-id", + } + mock_route_manager = async_mock.MagicMock(RouteManager) + mock_keylist_update = async_mock.MagicMock() + mock_keylist_update.serialize.return_value = {"mock": "serialized"} + mock_route_manager.route_connection = async_mock.CoroutineMock( + return_value=mock_keylist_update + ) + mock_route_manager.mediation_record_for_connection = async_mock.CoroutineMock() + with async_mock.patch.object( + test_module.ConnRecord, + "retrieve_by_id", + async_mock.CoroutineMock(side_effect=StorageError), + ) as mock_conn_rec_retrieve_by_id, async_mock.patch.object( + test_module, + "CoordinateMediationV1RouteManager", + async_mock.MagicMock(return_value=mock_route_manager), + ): + with self.assertRaises(test_module.web.HTTPBadRequest): + await test_module.update_keylist_for_connection(self.request) + async def test_register(self): mock_app = async_mock.MagicMock() mock_app.add_routes = async_mock.MagicMock() From add2f243f6ec282c2fa72467a66421be2152ac32 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Wed, 6 Jul 2022 09:47:07 -0500 Subject: [PATCH 334/872] fix: relative imports Signed-off-by: Daniel Bluhm --- .../coordinate_mediation/v1_0/tests/test_routes.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_routes.py index 9023643fdd..940df53bad 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_routes.py @@ -1,15 +1,13 @@ from asynctest import TestCase as AsyncTestCase, mock as async_mock -from aries_cloudagent.admin.request_context import AdminRequestContext -from aries_cloudagent.protocols.coordinate_mediation.v1_0.route_manager import ( - RouteManager, -) -from aries_cloudagent.storage.error import StorageError, StorageNotFoundError +from aries_cloudagent.admin.request_context import AdminRequestContext from .. import routes as test_module from .....core.in_memory import InMemoryProfile from .....multitenant.base import BaseMultitenantManager +from .....storage.error import StorageError, StorageNotFoundError from ..models.mediation_record import MediationRecord +from ..route_manager import RouteManager class TestCoordinateMediationRoutes(AsyncTestCase): From af412449d5bc2c0933220cedbadf17004947262a Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Wed, 6 Jul 2022 09:48:06 -0500 Subject: [PATCH 335/872] fix: more relative imports Signed-off-by: Daniel Bluhm --- aries_cloudagent/multitenant/route_manager.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/aries_cloudagent/multitenant/route_manager.py b/aries_cloudagent/multitenant/route_manager.py index 0832f7ec26..302cc5c72c 100644 --- a/aries_cloudagent/multitenant/route_manager.py +++ b/aries_cloudagent/multitenant/route_manager.py @@ -3,20 +3,18 @@ import logging from typing import List, Optional, Tuple + from aries_cloudagent.core.profile import Profile from aries_cloudagent.messaging.responder import BaseResponder -from aries_cloudagent.protocols.coordinate_mediation.v1_0.manager import ( - MediationManager, -) -from aries_cloudagent.protocols.routing.v1_0.manager import RoutingManager -from aries_cloudagent.protocols.routing.v1_0.models.route_record import RouteRecord -from aries_cloudagent.storage.error import StorageNotFoundError - +from ..protocols.coordinate_mediation.v1_0.manager import MediationManager from ..protocols.coordinate_mediation.v1_0.models.mediation_record import ( MediationRecord, ) from ..protocols.coordinate_mediation.v1_0.route_manager import RouteManager +from ..protocols.routing.v1_0.manager import RoutingManager +from ..protocols.routing.v1_0.models.route_record import RouteRecord +from ..storage.error import StorageNotFoundError LOGGER = logging.getLogger(__name__) From f8120a6f04d23e76b2a92b302eb42c9300d7f0e6 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Wed, 6 Jul 2022 09:50:40 -0500 Subject: [PATCH 336/872] fix: more relative imports Signed-off-by: Daniel Bluhm --- aries_cloudagent/multitenant/route_manager.py | 5 ++--- aries_cloudagent/multitenant/tests/test_route_manager.py | 5 ++--- .../protocols/coordinate_mediation/v1_0/route_manager.py | 5 +---- .../protocols/coordinate_mediation/v1_0/tests/test_routes.py | 3 +-- 4 files changed, 6 insertions(+), 12 deletions(-) diff --git a/aries_cloudagent/multitenant/route_manager.py b/aries_cloudagent/multitenant/route_manager.py index 302cc5c72c..e20094bf71 100644 --- a/aries_cloudagent/multitenant/route_manager.py +++ b/aries_cloudagent/multitenant/route_manager.py @@ -4,9 +4,8 @@ import logging from typing import List, Optional, Tuple -from aries_cloudagent.core.profile import Profile -from aries_cloudagent.messaging.responder import BaseResponder - +from ..core.profile import Profile +from ..messaging.responder import BaseResponder from ..protocols.coordinate_mediation.v1_0.manager import MediationManager from ..protocols.coordinate_mediation.v1_0.models.mediation_record import ( MediationRecord, diff --git a/aries_cloudagent/multitenant/tests/test_route_manager.py b/aries_cloudagent/multitenant/tests/test_route_manager.py index f140afd8a0..856ce7d839 100644 --- a/aries_cloudagent/multitenant/tests/test_route_manager.py +++ b/aries_cloudagent/multitenant/tests/test_route_manager.py @@ -1,7 +1,5 @@ -import pytest from asynctest import mock - -from aries_cloudagent.protocols.routing.v1_0.manager import RoutingManager +import pytest from ...core.in_memory import InMemoryProfile from ...core.profile import Profile @@ -10,6 +8,7 @@ from ...protocols.coordinate_mediation.v1_0.models.mediation_record import ( MediationRecord, ) +from ...protocols.routing.v1_0.manager import RoutingManager from ...protocols.routing.v1_0.models.route_record import RouteRecord from ...storage.error import StorageNotFoundError from ..route_manager import MultitenantRouteManager diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py index be67b3c9e6..6197c88fd8 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py @@ -8,10 +8,6 @@ import logging from typing import List, Optional, Tuple -from aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.keylist_update import ( - KeylistUpdate, -) - from ....connections.models.conn_record import ConnRecord from ....core.profile import Profile from ....messaging.responder import BaseResponder @@ -22,6 +18,7 @@ from ....wallet.key_type import KeyType from ...routing.v1_0.models.route_record import RouteRecord from .manager import MediationManager +from .messages.keylist_update import KeylistUpdate from .models.mediation_record import MediationRecord diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_routes.py index 940df53bad..f392eab61d 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_routes.py @@ -1,8 +1,7 @@ from asynctest import TestCase as AsyncTestCase, mock as async_mock -from aries_cloudagent.admin.request_context import AdminRequestContext - from .. import routes as test_module +from .....admin.request_context import AdminRequestContext from .....core.in_memory import InMemoryProfile from .....multitenant.base import BaseMultitenantManager from .....storage.error import StorageError, StorageNotFoundError From ad17f68d63537d6303fa304a05ea89a7cdd2e44e Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Wed, 6 Jul 2022 10:08:10 -0500 Subject: [PATCH 337/872] feat: inject weak ref to profile Signed-off-by: Daniel Bluhm --- aries_cloudagent/config/wallet.py | 10 ++++--- .../v1_0/route_manager_provider.py | 27 +++++++++++++++++++ 2 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager_provider.py diff --git a/aries_cloudagent/config/wallet.py b/aries_cloudagent/config/wallet.py index 9ade368c52..85e080b0fa 100644 --- a/aries_cloudagent/config/wallet.py +++ b/aries_cloudagent/config/wallet.py @@ -2,18 +2,18 @@ import logging from typing import Tuple +import weakref from ..core.error import ProfileNotFoundError from ..core.profile import Profile, ProfileManager, ProfileSession from ..storage.base import BaseStorage from ..storage.error import StorageNotFoundError -from ..version import __version__, RECORD_TYPE_ACAPY_VERSION +from ..version import RECORD_TYPE_ACAPY_VERSION, __version__ from ..wallet.base import BaseWallet -from ..wallet.did_info import DIDInfo from ..wallet.crypto import seed_to_did -from ..wallet.key_type import KeyType +from ..wallet.did_info import DIDInfo from ..wallet.did_method import DIDMethod - +from ..wallet.key_type import KeyType from .base import ConfigError from .injection_context import InjectionContext @@ -137,6 +137,8 @@ async def wallet_config( await txn.commit() + context.injector.bind_instance(Profile, weakref.ref(profile)) + return (profile, public_did_info) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager_provider.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager_provider.py new file mode 100644 index 0000000000..d053094a52 --- /dev/null +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager_provider.py @@ -0,0 +1,27 @@ +"""RouteManager provider.""" +from ....config.base import BaseInjector, BaseProvider, BaseSettings +from ....core.profile import Profile +from ....multitenant.base import BaseMultitenantManager +from ....multitenant.route_manager import MultitenantRouteManager +from .route_manager import CoordinateMediationV1RouteManager + + +class RouteManagerProvider(BaseProvider): + """Route manager provider. + + Decides whcih route manager to use based on settings. + """ + + def __init__(self, root_profile: Profile): + """Initialize route manager provider.""" + self.root_profile = root_profile + + def provide(self, settings: BaseSettings, injector: BaseInjector): + """Create the appropriate route manager instance.""" + wallet_id = settings.get("wallet.id") + multitenant_mgr = injector.inject_or(BaseMultitenantManager) + profile = injector.inject(Profile) + if multitenant_mgr and wallet_id: + return MultitenantRouteManager(self.root_profile, profile, wallet_id) + + return CoordinateMediationV1RouteManager(profile) From b3dabdc7a31e955f432d4b8f8d6f7766cdbd80d7 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Wed, 6 Jul 2022 10:21:17 -0500 Subject: [PATCH 338/872] feat: inject route manager Signed-off-by: Daniel Bluhm --- aries_cloudagent/connections/base_manager.py | 14 +------ aries_cloudagent/multitenant/base.py | 8 +--- .../multitenant/tests/test_base.py | 40 ++++++++----------- .../coordinate_mediation/v1_0/routes.py | 12 +----- .../v1_0/tests/test_routes.py | 8 +--- aries_cloudagent/wallet/routes.py | 38 ++++++------------ aries_cloudagent/wallet/tests/test_routes.py | 11 +---- 7 files changed, 36 insertions(+), 95 deletions(-) diff --git a/aries_cloudagent/connections/base_manager.py b/aries_cloudagent/connections/base_manager.py index 9a1df8e393..162fb26c39 100644 --- a/aries_cloudagent/connections/base_manager.py +++ b/aries_cloudagent/connections/base_manager.py @@ -18,7 +18,6 @@ from ..core.error import BaseError from ..core.profile import Profile from ..did.did_key import DIDKey -from ..multitenant.base import BaseMultitenantManager from ..protocols.connections.v1_0.messages.connection_invitation import ( ConnectionInvitation, ) @@ -26,7 +25,6 @@ MediationRecord, ) from ..protocols.coordinate_mediation.v1_0.route_manager import ( - CoordinateMediationV1RouteManager, RouteManager, ) from ..resolver.base import ResolverError @@ -61,17 +59,7 @@ def __init__(self, profile: Profile): """ self._logger = logging.getLogger(__name__) self._profile = profile - - multitenant_mgr = profile.inject_or(BaseMultitenantManager) - wallet_id = profile.settings.get_str("wallet.id") - if multitenant_mgr and wallet_id: - self._route_manager: RouteManager = multitenant_mgr.get_route_manager( - profile, wallet_id - ) - else: - self._route_manager: RouteManager = CoordinateMediationV1RouteManager( - profile - ) + self._route_manager = profile.inject(RouteManager) async def create_did_document( self, diff --git a/aries_cloudagent/multitenant/base.py b/aries_cloudagent/multitenant/base.py index 785a53f625..10b403ac6e 100644 --- a/aries_cloudagent/multitenant/base.py +++ b/aries_cloudagent/multitenant/base.py @@ -199,8 +199,8 @@ async def create_wallet( public_did_info = await wallet.get_public_did() if public_did_info: - await self.get_route_manager( - profile, wallet_record.wallet_id + await MultitenantRouteManager( + self._profile, profile, wallet_record.wallet_id ).route_public_did(public_did_info.verkey) except Exception: await wallet_record.delete_record(session) @@ -280,10 +280,6 @@ async def remove_wallet_profile(self, profile: Profile): """ - def get_route_manager(self, sub_profile: Profile, wallet_id: str): - """Return a route manager for handling multitenant routing.""" - return MultitenantRouteManager(self._profile, sub_profile, wallet_id) - async def create_auth_token( self, wallet_record: WalletRecord, wallet_key: str = None ) -> str: diff --git a/aries_cloudagent/multitenant/tests/test_base.py b/aries_cloudagent/multitenant/tests/test_base.py index 846d1d78a4..d52c8da678 100644 --- a/aries_cloudagent/multitenant/tests/test_base.py +++ b/aries_cloudagent/multitenant/tests/test_base.py @@ -1,29 +1,29 @@ +from datetime import datetime + from asynctest import TestCase as AsyncTestCase from asynctest import mock as async_mock - import jwt -from datetime import datetime - -from ...core.in_memory import InMemoryProfile +from .. import base as test_module from ...config.base import InjectionError +from ...core.in_memory import InMemoryProfile from ...messaging.responder import BaseResponder -from ...wallet.models.wallet_record import WalletRecord -from ...wallet.in_memory import InMemoryWallet -from ...wallet.did_info import DIDInfo -from ...storage.error import StorageNotFoundError -from ...storage.in_memory import InMemoryStorage -from ...protocols.routing.v1_0.manager import RoutingManager -from ...protocols.routing.v1_0.models.route_record import RouteRecord from ...protocols.coordinate_mediation.v1_0.manager import ( - MediationRecord, MediationManager, + MediationRecord, ) -from ...wallet.key_type import KeyType +from ...protocols.coordinate_mediation.v1_0.route_manager import RouteManager +from ...protocols.routing.v1_0.manager import RoutingManager +from ...protocols.routing.v1_0.models.route_record import RouteRecord +from ...storage.error import StorageNotFoundError +from ...storage.in_memory import InMemoryStorage +from ...wallet.did_info import DIDInfo from ...wallet.did_method import DIDMethod +from ...wallet.in_memory import InMemoryWallet +from ...wallet.key_type import KeyType +from ...wallet.models.wallet_record import WalletRecord from ..base import BaseMultitenantManager, MultitenantManagerError from ..error import WalletKeyMissingError -from .. import base as test_module class MockMultitenantManager(BaseMultitenantManager): @@ -212,16 +212,13 @@ async def test_create_wallet_saves_wallet_record_creates_profile(self): mock_route_manager = async_mock.MagicMock() mock_route_manager.route_public_did = async_mock.CoroutineMock() + self.context.injector.bind_instance(RouteManager, mock_route_manager) with async_mock.patch.object( WalletRecord, "save" ) as wallet_record_save, async_mock.patch.object( self.manager, "get_wallet_profile" - ) as get_wallet_profile, async_mock.patch.object( - self.manager, - "get_route_manager", - async_mock.MagicMock(return_value=mock_route_manager), - ): + ) as get_wallet_profile: get_wallet_profile.return_value = InMemoryProfile.test_profile() wallet_record = await self.manager.create_wallet( @@ -253,16 +250,13 @@ async def test_create_wallet_adds_wallet_route(self): mock_route_manager = async_mock.MagicMock() mock_route_manager.route_public_did = async_mock.CoroutineMock() + self.context.injector.bind_instance(RouteManager, mock_route_manager) with async_mock.patch.object( WalletRecord, "save" ) as wallet_record_save, async_mock.patch.object( self.manager, "get_wallet_profile" ) as get_wallet_profile, async_mock.patch.object( - self.manager, - "get_route_manager", - async_mock.MagicMock(return_value=mock_route_manager), - ), async_mock.patch.object( InMemoryWallet, "get_public_did" ) as get_public_did: get_wallet_profile.return_value = InMemoryProfile.test_profile() diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/routes.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/routes.py index 651255bbcd..94abb9dcb4 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/routes.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/routes.py @@ -15,7 +15,6 @@ from ....messaging.models.base import BaseModelError from ....messaging.models.openapi import OpenAPISchema from ....messaging.valid import UUIDFour -from ....multitenant.base import BaseMultitenantManager from ....storage.error import StorageError, StorageNotFoundError from ...connections.v1_0.routes import ConnectionsConnIdMatchInfoSchema from ...routing.v1_0.models.route_record import RouteRecord, RouteRecordSchema @@ -30,7 +29,7 @@ from .messages.mediate_deny import MediationDenySchema from .messages.mediate_grant import MediationGrantSchema from .models.mediation_record import MediationRecord, MediationRecordSchema -from .route_manager import CoordinateMediationV1RouteManager +from .route_manager import RouteManager CONNECTION_ID_SCHEMA = fields.UUID( @@ -528,14 +527,7 @@ async def update_keylist_for_connection(request: web.BaseRequest): mediation_id = body.get("mediation_id") connection_id = request.match_info["conn_id"] try: - multitenant_mgr = context.inject_or(BaseMultitenantManager) - wallet_id = context.settings.get_str("wallet.id") - if multitenant_mgr and wallet_id: - route_manager = multitenant_mgr.get_route_manager( - context.profile, wallet_id - ) - else: - route_manager = CoordinateMediationV1RouteManager(context.profile) + route_manager = context.inject(RouteManager) async with context.session() as session: connection_record = await ConnRecord.retrieve_by_id(session, connection_id) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_routes.py index f392eab61d..c7d72b2a76 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_routes.py @@ -3,7 +3,6 @@ from .. import routes as test_module from .....admin.request_context import AdminRequestContext from .....core.in_memory import InMemoryProfile -from .....multitenant.base import BaseMultitenantManager from .....storage.error import StorageError, StorageNotFoundError from ..models.mediation_record import MediationRecord from ..route_manager import RouteManager @@ -701,9 +700,6 @@ async def test_update_keylist_for_connection_multitenant(self): self.request.match_info = { "conn_id": "test-conn-id", } - multitenant_mgr = async_mock.MagicMock(BaseMultitenantManager) - self.context.injector.bind_instance(BaseMultitenantManager, multitenant_mgr) - self.context.settings["wallet.id"] = "test-wallet-id" mock_route_manager = async_mock.MagicMock(RouteManager) mock_keylist_update = async_mock.MagicMock() mock_keylist_update.serialize.return_value = {"mock": "serialized"} @@ -711,9 +707,7 @@ async def test_update_keylist_for_connection_multitenant(self): return_value=mock_keylist_update ) mock_route_manager.mediation_record_for_connection = async_mock.CoroutineMock() - multitenant_mgr.get_route_manager = async_mock.MagicMock( - return_value=mock_route_manager - ) + self.context.injector.bind_instance(RouteManager, mock_route_manager) with async_mock.patch.object( test_module.ConnRecord, "retrieve_by_id", async_mock.CoroutineMock() ) as mock_conn_rec_retrieve_by_id, async_mock.patch.object( diff --git a/aries_cloudagent/wallet/routes.py b/aries_cloudagent/wallet/routes.py index 6f97eebc69..76c3fef5e8 100644 --- a/aries_cloudagent/wallet/routes.py +++ b/aries_cloudagent/wallet/routes.py @@ -1,21 +1,16 @@ """Wallet admin routes.""" import json +import logging from aiohttp import web -from aiohttp_apispec import ( - docs, - querystring_schema, - request_schema, - response_schema, -) -import logging +from aiohttp_apispec import docs, querystring_schema, request_schema, response_schema from marshmallow import fields, validate from ..admin.request_context import AdminRequestContext +from ..connections.models.conn_record import ConnRecord from ..core.event_bus import Event, EventBus from ..core.profile import Profile -from ..connections.models.conn_record import ConnRecord from ..ledger.base import BaseLedger from ..ledger.endpoint_type import EndpointType from ..ledger.error import LedgerConfigError, LedgerError @@ -23,27 +18,26 @@ from ..messaging.models.openapi import OpenAPISchema from ..messaging.valid import ( DID_POSTURE, - INDY_OR_KEY_DID, - INDY_DID, ENDPOINT, ENDPOINT_TYPE, + INDY_DID, + INDY_OR_KEY_DID, INDY_RAW_PUBLIC_KEY, ) -from ..multitenant.base import BaseMultitenantManager +from ..protocols.coordinate_mediation.v1_0.route_manager import RouteManager from ..protocols.endorse_transaction.v1_0.manager import ( TransactionManager, TransactionManagerError, ) from ..protocols.endorse_transaction.v1_0.util import ( - is_author_role, get_endorser_connection_id, + is_author_role, ) -from ..storage.error import StorageNotFoundError, StorageError - +from ..storage.error import StorageError, StorageNotFoundError from .base import BaseWallet from .did_info import DIDInfo -from .did_posture import DIDPosture from .did_method import DIDMethod +from .did_posture import DIDPosture from .error import WalletError, WalletNotFoundError from .key_type import KeyType from .util import EVENT_LISTENER_PATTERN @@ -495,10 +489,6 @@ async def promote_wallet_public_did( connection_id: str = None, ) -> DIDInfo: """Promote supplied DID to the wallet public DID.""" - - # if running in multitenant mode this will be the sub-wallet - wallet_id = context.settings.get("wallet.id") - info: DIDInfo = None endorser_did = None ledger = profile.inject_or(BaseLedger) @@ -578,13 +568,9 @@ async def promote_wallet_public_did( # async with ledger: # await ledger.update_endpoint_for_did(info.did, endpoint) - # Multitenancy setup - multitenant_mgr = profile.inject_or(BaseMultitenantManager) - # Add multitenant relay mapping so implicit invitations are still routed - if multitenant_mgr and wallet_id: - await multitenant_mgr.get_route_manager( - profile, wallet_id - ).route_public_did(info.verkey) + # Route the public DID + route_manager = context.inject(RouteManager) + await route_manager.route_public_did(info.verkey) return info, attrib_def diff --git a/aries_cloudagent/wallet/tests/test_routes.py b/aries_cloudagent/wallet/tests/test_routes.py index 4d9f7cfccd..8faac7be96 100644 --- a/aries_cloudagent/wallet/tests/test_routes.py +++ b/aries_cloudagent/wallet/tests/test_routes.py @@ -420,14 +420,8 @@ async def test_set_public_did_multitenant(self): ledger.__aenter__ = async_mock.CoroutineMock(return_value=ledger) self.profile.context.injector.bind_instance(BaseLedger, ledger) - multitenant_mgr = async_mock.MagicMock(MultitenantManager, autospec=True) route_manager = async_mock.MagicMock(RouteManager) - multitenant_mgr.get_route_manager = async_mock.MagicMock( - return_value=route_manager - ) - self.profile.context.injector.bind_instance( - BaseMultitenantManager, multitenant_mgr - ) + self.profile.context.injector.bind_instance(RouteManager, route_manager) with async_mock.patch.object( test_module.web, "json_response", async_mock.Mock() ): @@ -440,9 +434,6 @@ async def test_set_public_did_multitenant(self): ) await test_module.wallet_set_public_did(self.request) - multitenant_mgr.get_route_manager.assert_called_once_with( - self.profile, "test_wallet" - ) route_manager.route_public_did.assert_called_once_with(self.test_verkey) async def test_set_public_did_no_query_did(self): From 44ef00caf75c8e509a755933c08c18e8e3fb89b7 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Wed, 6 Jul 2022 14:06:47 -0500 Subject: [PATCH 339/872] fix: tests use injected route manager Signed-off-by: Daniel Bluhm --- aries_cloudagent/core/conductor.py | 23 +++++++---- .../core/tests/test_dispatcher.py | 2 + aries_cloudagent/multitenant/base.py | 8 ++-- .../multitenant/tests/test_base.py | 5 ++- .../connections/v1_0/tests/test_manager.py | 20 ++++----- .../v1_0/tests/test_routes.py | 41 +++---------------- .../didexchange/v1_0/tests/test_manager.py | 29 ++++++------- .../didexchange/v1_0/tests/test_routes.py | 8 +++- .../out_of_band/v1_0/tests/test_manager.py | 28 +++++++------ aries_cloudagent/wallet/routes.py | 2 +- aries_cloudagent/wallet/tests/test_routes.py | 41 ++++--------------- 11 files changed, 81 insertions(+), 126 deletions(-) diff --git a/aries_cloudagent/core/conductor.py b/aries_cloudagent/core/conductor.py index 377e04b6f0..c11327e5f9 100644 --- a/aries_cloudagent/core/conductor.py +++ b/aries_cloudagent/core/conductor.py @@ -11,19 +11,20 @@ import hashlib import json import logging + from qrcode import QRCode from ..admin.base_server import BaseAdminServer from ..admin.server import AdminResponder, AdminServer from ..config.default_context import ContextBuilder from ..config.injection_context import InjectionContext -from ..config.provider import ClassProvider from ..config.ledger import ( get_genesis_transactions, ledger_config, load_multiple_genesis_transactions_from_config, ) from ..config.logging import LoggingConfigurator +from ..config.provider import ClassProvider from ..config.wallet import wallet_config from ..core.profile import Profile from ..indy.verifier import IndyVerifier @@ -33,8 +34,8 @@ BaseMultipleLedgerManager, MultipleLedgerManagerError, ) -from ..ledger.multiple_ledger.manager_provider import MultiIndyLedgerManagerProvider from ..ledger.multiple_ledger.ledger_requests_executor import IndyLedgerRequestsExecutor +from ..ledger.multiple_ledger.manager_provider import MultiIndyLedgerManagerProvider from ..messaging.responder import BaseResponder from ..multitenant.base import BaseMultitenantManager from ..multitenant.manager_provider import MultitenantManagerProvider @@ -45,8 +46,12 @@ from ..protocols.connections.v1_0.messages.connection_invitation import ( ConnectionInvitation, ) -from ..protocols.coordinate_mediation.v1_0.manager import MediationManager from ..protocols.coordinate_mediation.mediation_invite_store import MediationInviteStore +from ..protocols.coordinate_mediation.v1_0.manager import MediationManager +from ..protocols.coordinate_mediation.v1_0.route_manager import RouteManager +from ..protocols.coordinate_mediation.v1_0.route_manager_provider import ( + RouteManagerProvider, +) from ..protocols.out_of_band.v1_0.manager import OutOfBandManager from ..protocols.out_of_band.v1_0.messages.invitation import HSProto, InvitationMessage from ..storage.base import BaseStorage @@ -61,12 +66,11 @@ from ..utils.stats import Collector from ..utils.task_queue import CompletedTask, TaskQueue from ..vc.ld_proofs.document_loader import DocumentLoader -from ..version import __version__, RECORD_TYPE_ACAPY_VERSION +from ..version import RECORD_TYPE_ACAPY_VERSION, __version__ from ..wallet.did_info import DIDInfo -from .oob_processor import OobMessageProcessor - from .dispatcher import Dispatcher -from .util import STARTUP_EVENT_TOPIC, SHUTDOWN_EVENT_TOPIC +from .oob_processor import OobMessageProcessor +from .util import SHUTDOWN_EVENT_TOPIC, STARTUP_EVENT_TOPIC LOGGER = logging.getLogger(__name__) @@ -204,6 +208,11 @@ async def setup(self): BaseMultitenantManager, MultitenantManagerProvider(self.root_profile) ) + # Bind route manager provider + context.injector.bind_provider( + RouteManager, RouteManagerProvider(self.root_profile) + ) + # Bind oob message processor to be able to receive and process un-encrypted # messages context.injector.bind_instance( diff --git a/aries_cloudagent/core/tests/test_dispatcher.py b/aries_cloudagent/core/tests/test_dispatcher.py index b9f00564b1..5caef86516 100644 --- a/aries_cloudagent/core/tests/test_dispatcher.py +++ b/aries_cloudagent/core/tests/test_dispatcher.py @@ -18,6 +18,7 @@ V20CredProblemReport, ) from ...protocols.problem_report.v1_0.message import ProblemReport +from ...protocols.coordinate_mediation.v1_0.route_manager import RouteManager from ...transport.inbound.message import InboundMessage from ...transport.inbound.receipt import MessageReceipt from ...transport.outbound.message import OutboundMessage @@ -31,6 +32,7 @@ def make_profile() -> Profile: profile.context.injector.bind_instance(ProtocolRegistry, ProtocolRegistry()) profile.context.injector.bind_instance(Collector, Collector()) profile.context.injector.bind_instance(EventBus, EventBus()) + profile.context.injector.bind_instance(RouteManager, async_mock.MagicMock()) return profile diff --git a/aries_cloudagent/multitenant/base.py b/aries_cloudagent/multitenant/base.py index 10b403ac6e..4c1b8e8ba7 100644 --- a/aries_cloudagent/multitenant/base.py +++ b/aries_cloudagent/multitenant/base.py @@ -10,11 +10,11 @@ from ..config.injection_context import InjectionContext from ..core.error import BaseError from ..core.profile import Profile, ProfileSession -from ..multitenant.route_manager import MultitenantRouteManager from ..protocols.coordinate_mediation.v1_0.manager import ( MediationManager, MediationRecord, ) +from ..protocols.coordinate_mediation.v1_0.route_manager import RouteManager from ..protocols.routing.v1_0.manager import RouteNotFoundError, RoutingManager from ..protocols.routing.v1_0.models.route_record import RouteRecord from ..storage.base import BaseStorage @@ -199,9 +199,9 @@ async def create_wallet( public_did_info = await wallet.get_public_did() if public_did_info: - await MultitenantRouteManager( - self._profile, profile, wallet_record.wallet_id - ).route_public_did(public_did_info.verkey) + await profile.inject(RouteManager).route_public_did( + public_did_info.verkey + ) except Exception: await wallet_record.delete_record(session) raise diff --git a/aries_cloudagent/multitenant/tests/test_base.py b/aries_cloudagent/multitenant/tests/test_base.py index d52c8da678..ccc09fd21e 100644 --- a/aries_cloudagent/multitenant/tests/test_base.py +++ b/aries_cloudagent/multitenant/tests/test_base.py @@ -250,7 +250,6 @@ async def test_create_wallet_adds_wallet_route(self): mock_route_manager = async_mock.MagicMock() mock_route_manager.route_public_did = async_mock.CoroutineMock() - self.context.injector.bind_instance(RouteManager, mock_route_manager) with async_mock.patch.object( WalletRecord, "save" @@ -259,7 +258,9 @@ async def test_create_wallet_adds_wallet_route(self): ) as get_wallet_profile, async_mock.patch.object( InMemoryWallet, "get_public_did" ) as get_public_did: - get_wallet_profile.return_value = InMemoryProfile.test_profile() + get_wallet_profile.return_value = InMemoryProfile.test_profile( + bind={RouteManager: mock_route_manager} + ) get_public_did.return_value = did_info wallet_record = await self.manager.create_wallet( diff --git a/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py index bbb681645e..66d284123d 100644 --- a/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py @@ -30,10 +30,6 @@ from .....wallet.key_type import KeyType from ....coordinate_mediation.v1_0.manager import MediationManager from ....coordinate_mediation.v1_0.route_manager import RouteManager -from ....coordinate_mediation.v1_0.messages.inner.keylist_update_rule import ( - KeylistUpdateRule, -) -from ....coordinate_mediation.v1_0.messages.keylist_update import KeylistUpdate from ....coordinate_mediation.v1_0.messages.mediate_request import MediationRequest from ....coordinate_mediation.v1_0.models.mediation_record import MediationRecord from ....discovery.v2_0.manager import V20DiscoveryMgr @@ -77,6 +73,13 @@ async def setUp(self): self.oob_mock = async_mock.MagicMock( clean_finished_oob_record=async_mock.CoroutineMock(return_value=None) ) + self.route_manager = async_mock.MagicMock(RouteManager) + self.route_manager.routing_info = async_mock.CoroutineMock( + return_value=([], self.test_endpoint) + ) + self.route_manager.mediation_record_if_id = async_mock.CoroutineMock( + return_value=None + ) self.profile = InMemoryProfile.test_profile( { @@ -90,6 +93,7 @@ async def setUp(self): BaseResponder: self.responder, BaseCache: InMemoryCache(), OobMessageProcessor: self.oob_mock, + RouteManager: self.route_manager, }, ) self.context = self.profile.context @@ -106,14 +110,6 @@ async def setUp(self): self.test_mediator_endpoint = "http://mediator.example.com" self.manager = ConnectionManager(self.profile) - self.route_manager = async_mock.MagicMock(RouteManager) - self.route_manager.routing_info = async_mock.CoroutineMock( - return_value=([], self.test_endpoint) - ) - self.route_manager.mediation_record_if_id = async_mock.CoroutineMock( - return_value=None - ) - self.manager._route_manager = self.route_manager assert self.manager.profile async def test_create_invitation_public_and_multi_use_fails(self): diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_routes.py index c7d72b2a76..91cea3c999 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_routes.py @@ -694,7 +694,7 @@ async def test_clear_default_mediator_storage_error(self): with self.assertRaises(test_module.web.HTTPBadRequest): await test_module.clear_default_mediator(self.request) - async def test_update_keylist_for_connection_multitenant(self): + async def test_update_keylist_for_connection(self): self.request.query = {} self.request.json.return_value = {"mediation_id": "test-mediation-id"} self.request.match_info = { @@ -716,31 +716,6 @@ async def test_update_keylist_for_connection_multitenant(self): await test_module.update_keylist_for_connection(self.request) json_response.assert_called_once_with({"mock": "serialized"}, status=200) - async def test_update_keylist_for_connection(self): - self.request.query = {} - self.request.json.return_value = {"mediation_id": "test-mediation-id"} - self.request.match_info = { - "conn_id": "test-conn-id", - } - mock_route_manager = async_mock.MagicMock(RouteManager) - mock_keylist_update = async_mock.MagicMock() - mock_keylist_update.serialize.return_value = {"mock": "serialized"} - mock_route_manager.route_connection = async_mock.CoroutineMock( - return_value=mock_keylist_update - ) - mock_route_manager.mediation_record_for_connection = async_mock.CoroutineMock() - with async_mock.patch.object( - test_module.ConnRecord, "retrieve_by_id", async_mock.CoroutineMock() - ) as mock_conn_rec_retrieve_by_id, async_mock.patch.object( - test_module.web, "json_response" - ) as json_response, async_mock.patch.object( - test_module, - "CoordinateMediationV1RouteManager", - async_mock.MagicMock(return_value=mock_route_manager), - ): - await test_module.update_keylist_for_connection(self.request) - json_response.assert_called_once_with({"mock": "serialized"}, status=200) - async def test_update_keylist_for_connection_not_found(self): self.request.query = {} self.request.json.return_value = {"mediation_id": "test-mediation-id"} @@ -754,15 +729,12 @@ async def test_update_keylist_for_connection_not_found(self): return_value=mock_keylist_update ) mock_route_manager.mediation_record_for_connection = async_mock.CoroutineMock() + self.context.injector.bind_instance(RouteManager, mock_route_manager) with async_mock.patch.object( test_module.ConnRecord, "retrieve_by_id", async_mock.CoroutineMock(side_effect=StorageNotFoundError), - ) as mock_conn_rec_retrieve_by_id, async_mock.patch.object( - test_module, - "CoordinateMediationV1RouteManager", - async_mock.MagicMock(return_value=mock_route_manager), - ): + ) as mock_conn_rec_retrieve_by_id: with self.assertRaises(test_module.web.HTTPNotFound): await test_module.update_keylist_for_connection(self.request) @@ -779,15 +751,12 @@ async def test_update_keylist_for_connection_storage_error(self): return_value=mock_keylist_update ) mock_route_manager.mediation_record_for_connection = async_mock.CoroutineMock() + self.context.injector.bind_instance(RouteManager, mock_route_manager) with async_mock.patch.object( test_module.ConnRecord, "retrieve_by_id", async_mock.CoroutineMock(side_effect=StorageError), - ) as mock_conn_rec_retrieve_by_id, async_mock.patch.object( - test_module, - "CoordinateMediationV1RouteManager", - async_mock.MagicMock(return_value=mock_route_manager), - ): + ) as mock_conn_rec_retrieve_by_id: with self.assertRaises(test_module.web.HTTPBadRequest): await test_module.update_keylist_for_connection(self.request) diff --git a/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py index 0cbec5deef..131d87670e 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py @@ -36,10 +36,6 @@ from ....coordinate_mediation.v1_0.manager import MediationManager from ....coordinate_mediation.v1_0.route_manager import RouteManager -from ....coordinate_mediation.v1_0.messages.keylist_update import ( - KeylistUpdate, - KeylistUpdateRule, -) from ....coordinate_mediation.v1_0.models.mediation_record import MediationRecord from ....discovery.v2_0.manager import V20DiscoveryMgr from ....didcomm_prefix import DIDCommPrefix @@ -87,6 +83,17 @@ async def setUp(self): clean_finished_oob_record=async_mock.CoroutineMock(return_value=None) ) + self.route_manager = async_mock.MagicMock(RouteManager) + self.route_manager.routing_info = async_mock.CoroutineMock( + return_value=([], self.test_endpoint) + ) + self.route_manager.mediation_record_if_id = async_mock.CoroutineMock( + return_value=None + ) + self.route_manager.mediation_record_for_connection = async_mock.CoroutineMock( + return_value=None + ) + self.profile = InMemoryProfile.test_profile( { "default_endpoint": "http://aries.ca/endpoint", @@ -101,6 +108,7 @@ async def setUp(self): BaseResponder: self.responder, BaseCache: InMemoryCache(), OobMessageProcessor: self.oob_mock, + RouteManager: self.route_manager, }, ) self.context = self.profile.context @@ -140,19 +148,6 @@ async def setUp(self): self.test_mediator_conn_id = "mediator-conn-id" self.test_mediator_endpoint = "http://mediator.example.com" - self.route_manager = async_mock.MagicMock(RouteManager) - self.route_manager.routing_info = async_mock.CoroutineMock( - return_value=([], self.test_endpoint) - ) - self.route_manager.mediation_record_if_id = async_mock.CoroutineMock( - return_value=None - ) - self.route_manager.mediation_record_for_connection = async_mock.CoroutineMock( - return_value=None - ) - self.manager._route_manager = self.route_manager - self.oob_manager._route_manager = self.route_manager - async def test_verify_diddoc(self): async with self.profile.session() as session: did_doc = self.make_did_doc( diff --git a/aries_cloudagent/protocols/didexchange/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/didexchange/v1_0/tests/test_routes.py index 2f7bef8d74..44584c5d1e 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/tests/test_routes.py @@ -1,16 +1,17 @@ from asynctest import TestCase as AsyncTestCase from asynctest import mock as async_mock +from .. import routes as test_module from .....admin.request_context import AdminRequestContext from .....storage.error import StorageNotFoundError - -from .. import routes as test_module +from ....coordinate_mediation.v1_0.route_manager import RouteManager class TestDIDExchangeConnRoutes(AsyncTestCase): async def setUp(self): self.session_inject = {} self.context = AdminRequestContext.test_context(self.session_inject) + self.profile = self.context.profile self.request_dict = { "context": self.context, "outbound_message_router": async_mock.CoroutineMock(), @@ -21,6 +22,9 @@ async def setUp(self): query={}, __getitem__=lambda _, k: self.request_dict[k], ) + self.profile.context.injector.bind_instance( + RouteManager, async_mock.MagicMock() + ) async def test_didx_accept_invitation(self): self.request.match_info = {"conn_id": "dummy"} diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py index df6a3a9ca4..903f50a410 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py @@ -313,6 +313,17 @@ def setUp(self): self.responder = MockResponder() self.responder.send = async_mock.CoroutineMock() + self.test_mediator_routing_keys = [ + "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRR" + ] + self.test_mediator_conn_id = "mediator-conn-id" + self.test_mediator_endpoint = "http://mediator.example.com" + + self.route_manager = async_mock.MagicMock(RouteManager) + self.route_manager.routing_info = async_mock.CoroutineMock( + return_value=(self.test_mediator_routing_keys, self.test_mediator_endpoint) + ) + self.profile = InMemoryProfile.test_profile( { "default_endpoint": TestConfig.test_endpoint, @@ -320,7 +331,10 @@ def setUp(self): "additional_endpoints": ["http://aries.ca/another-endpoint"], "debug.auto_accept_invites": True, "debug.auto_accept_requests": True, - } + }, + bind={ + RouteManager: self.route_manager, + }, ) self.profile.context.injector.bind_instance(BaseResponder, self.responder) @@ -354,18 +368,6 @@ def setUp(self): save=async_mock.CoroutineMock(), ) - self.test_mediator_routing_keys = [ - "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRR" - ] - self.test_mediator_conn_id = "mediator-conn-id" - self.test_mediator_endpoint = "http://mediator.example.com" - - self.route_manager = async_mock.MagicMock(RouteManager) - self.route_manager.routing_info = async_mock.CoroutineMock( - return_value=(self.test_mediator_routing_keys, self.test_mediator_endpoint) - ) - self.manager._route_manager = self.route_manager - async def test_create_invitation_handshake_succeeds(self): self.profile.context.update_settings({"public_invites": True}) diff --git a/aries_cloudagent/wallet/routes.py b/aries_cloudagent/wallet/routes.py index 76c3fef5e8..ff8da41b7a 100644 --- a/aries_cloudagent/wallet/routes.py +++ b/aries_cloudagent/wallet/routes.py @@ -569,7 +569,7 @@ async def promote_wallet_public_did( # await ledger.update_endpoint_for_did(info.did, endpoint) # Route the public DID - route_manager = context.inject(RouteManager) + route_manager = profile.inject(RouteManager) await route_manager.route_public_did(info.verkey) return info, attrib_def diff --git a/aries_cloudagent/wallet/tests/test_routes.py b/aries_cloudagent/wallet/tests/test_routes.py index 8faac7be96..6d67d0f17f 100644 --- a/aries_cloudagent/wallet/tests/test_routes.py +++ b/aries_cloudagent/wallet/tests/test_routes.py @@ -5,8 +5,6 @@ from ...admin.request_context import AdminRequestContext from ...core.in_memory import InMemoryProfile from ...ledger.base import BaseLedger -from ...multitenant.base import BaseMultitenantManager -from ...multitenant.manager import MultitenantManager from ...protocols.coordinate_mediation.v1_0.route_manager import RouteManager from ...wallet.did_method import DIDMethod from ...wallet.key_type import KeyType @@ -380,6 +378,9 @@ async def test_set_public_did(self): ledger.update_endpoint_for_did = async_mock.CoroutineMock() ledger.__aenter__ = async_mock.CoroutineMock(return_value=ledger) self.profile.context.injector.bind_instance(BaseLedger, ledger) + mock_route_manager = async_mock.MagicMock() + mock_route_manager.route_public_did = async_mock.CoroutineMock() + self.profile.context.injector.bind_instance(RouteManager, mock_route_manager) with async_mock.patch.object( test_module.web, "json_response", async_mock.Mock() @@ -406,36 +407,6 @@ async def test_set_public_did(self): ) assert result is json_response.return_value - async def test_set_public_did_multitenant(self): - self.context.update_settings( - {"multitenant.enabled": True, "wallet.id": "test_wallet"} - ) - - self.request.query = {"did": self.test_did} - - Ledger = async_mock.MagicMock() - ledger = Ledger() - ledger.get_key_for_did = async_mock.CoroutineMock() - ledger.update_endpoint_for_did = async_mock.CoroutineMock() - ledger.__aenter__ = async_mock.CoroutineMock(return_value=ledger) - self.profile.context.injector.bind_instance(BaseLedger, ledger) - - route_manager = async_mock.MagicMock(RouteManager) - self.profile.context.injector.bind_instance(RouteManager, route_manager) - with async_mock.patch.object( - test_module.web, "json_response", async_mock.Mock() - ): - self.wallet.set_public_did.return_value = DIDInfo( - self.test_did, - self.test_verkey, - DIDPosture.PUBLIC.metadata, - DIDMethod.SOV, - KeyType.ED25519, - ) - await test_module.wallet_set_public_did(self.request) - - route_manager.route_public_did.assert_called_once_with(self.test_verkey) - async def test_set_public_did_no_query_did(self): with self.assertRaises(test_module.web.HTTPBadRequest): await test_module.wallet_set_public_did(self.request) @@ -527,6 +498,9 @@ async def test_set_public_did_update_endpoint(self): ledger.get_key_for_did = async_mock.CoroutineMock() ledger.__aenter__ = async_mock.CoroutineMock(return_value=ledger) self.profile.context.injector.bind_instance(BaseLedger, ledger) + mock_route_manager = async_mock.MagicMock() + mock_route_manager.route_public_did = async_mock.CoroutineMock() + self.profile.context.injector.bind_instance(RouteManager, mock_route_manager) with async_mock.patch.object( test_module.web, "json_response", async_mock.Mock() @@ -565,6 +539,9 @@ async def test_set_public_did_update_endpoint_use_default_update_in_wallet(self) ledger.get_key_for_did = async_mock.CoroutineMock() ledger.__aenter__ = async_mock.CoroutineMock(return_value=ledger) self.profile.context.injector.bind_instance(BaseLedger, ledger) + mock_route_manager = async_mock.MagicMock() + mock_route_manager.route_public_did = async_mock.CoroutineMock() + self.profile.context.injector.bind_instance(RouteManager, mock_route_manager) with async_mock.patch.object( test_module.web, "json_response", async_mock.Mock() From 822fdfb71d643ce6456638111d8d96410e4153f5 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Wed, 6 Jul 2022 14:32:07 -0500 Subject: [PATCH 340/872] refactor: don't inject profile into context Signed-off-by: Daniel Bluhm --- aries_cloudagent/config/wallet.py | 3 - aries_cloudagent/multitenant/base.py | 2 +- aries_cloudagent/multitenant/route_manager.py | 23 +-- .../multitenant/tests/test_base.py | 4 +- .../multitenant/tests/test_route_manager.py | 43 +++-- .../protocols/connections/v1_0/manager.py | 23 ++- .../connections/v1_0/tests/test_manager.py | 4 +- .../v1_0/route_manager.py | 86 ++++++---- .../v1_0/route_manager_provider.py | 5 +- .../v1_0/tests/test_route_manager.py | 150 +++++++++++------- .../protocols/didexchange/v1_0/manager.py | 9 +- .../protocols/out_of_band/v1_0/manager.py | 11 +- aries_cloudagent/wallet/routes.py | 2 +- 13 files changed, 231 insertions(+), 134 deletions(-) diff --git a/aries_cloudagent/config/wallet.py b/aries_cloudagent/config/wallet.py index 85e080b0fa..61e34b2a73 100644 --- a/aries_cloudagent/config/wallet.py +++ b/aries_cloudagent/config/wallet.py @@ -2,7 +2,6 @@ import logging from typing import Tuple -import weakref from ..core.error import ProfileNotFoundError from ..core.profile import Profile, ProfileManager, ProfileSession @@ -137,8 +136,6 @@ async def wallet_config( await txn.commit() - context.injector.bind_instance(Profile, weakref.ref(profile)) - return (profile, public_did_info) diff --git a/aries_cloudagent/multitenant/base.py b/aries_cloudagent/multitenant/base.py index 4c1b8e8ba7..cecd2314dd 100644 --- a/aries_cloudagent/multitenant/base.py +++ b/aries_cloudagent/multitenant/base.py @@ -200,7 +200,7 @@ async def create_wallet( if public_did_info: await profile.inject(RouteManager).route_public_did( - public_did_info.verkey + profile, public_did_info.verkey ) except Exception: await wallet_record.delete_record(session) diff --git a/aries_cloudagent/multitenant/route_manager.py b/aries_cloudagent/multitenant/route_manager.py index e20094bf71..a7d6cf6878 100644 --- a/aries_cloudagent/multitenant/route_manager.py +++ b/aries_cloudagent/multitenant/route_manager.py @@ -22,16 +22,12 @@ class MultitenantRouteManager(RouteManager): """Multitenancy route manager.""" - def __init__(self, root_profile: Profile, sub_profile: Profile, wallet_id: str): + def __init__( + self, + root_profile: Profile, + ): """Initialize multitenant route manager.""" self.root_profile = root_profile - self.wallet_id = wallet_id - super().__init__(sub_profile) - - @property - def sub_profile(self) -> Profile: - """Return reference to sub wallet profile.""" - return self.profile async def get_base_wallet_mediator(self) -> Optional[MediationRecord]: """Get base wallet's default mediator.""" @@ -39,14 +35,16 @@ async def get_base_wallet_mediator(self) -> Optional[MediationRecord]: async def _route_for_key( self, + profile: Profile, recipient_key: str, mediation_record: Optional[MediationRecord] = None, *, skip_if_exists: bool = False, replace_key: Optional[str] = None, ): + wallet_id = profile.settings["wallet.id"] LOGGER.info( - f"Add route record for recipient {recipient_key} to wallet {self.wallet_id}" + f"Add route record for recipient {recipient_key} to wallet {wallet_id}" ) routing_mgr = RoutingManager(self.root_profile) mediation_mgr = MediationManager(self.root_profile) @@ -66,7 +64,7 @@ async def _route_for_key( pass await routing_mgr.create_route_record( - recipient_key=recipient_key, internal_wallet_id=self.wallet_id + recipient_key=recipient_key, internal_wallet_id=wallet_id ) # External mediation @@ -86,7 +84,10 @@ async def _route_for_key( return keylist_updates async def routing_info( - self, my_endpoint: str, mediation_record: Optional[MediationRecord] = None + self, + profile: Profile, + my_endpoint: str, + mediation_record: Optional[MediationRecord] = None, ) -> Tuple[List[str], str]: """Return routing info.""" routing_keys = [] diff --git a/aries_cloudagent/multitenant/tests/test_base.py b/aries_cloudagent/multitenant/tests/test_base.py index ccc09fd21e..6f7c7b7e78 100644 --- a/aries_cloudagent/multitenant/tests/test_base.py +++ b/aries_cloudagent/multitenant/tests/test_base.py @@ -268,7 +268,9 @@ async def test_create_wallet_adds_wallet_route(self): WalletRecord.MODE_MANAGED, ) - mock_route_manager.route_public_did.assert_called_once_with(did_info.verkey) + mock_route_manager.route_public_did.assert_called_once_with( + get_wallet_profile.return_value, did_info.verkey + ) wallet_record_save.assert_called_once() get_wallet_profile.assert_called_once_with( diff --git a/aries_cloudagent/multitenant/tests/test_route_manager.py b/aries_cloudagent/multitenant/tests/test_route_manager.py index 856ce7d839..87bfef7129 100644 --- a/aries_cloudagent/multitenant/tests/test_route_manager.py +++ b/aries_cloudagent/multitenant/tests/test_route_manager.py @@ -47,13 +47,7 @@ def sub_profile(mock_responder: MockResponder, wallet_id: str): @pytest.fixture def route_manager(root_profile: Profile, sub_profile: Profile, wallet_id: str): - yield MultitenantRouteManager(root_profile, sub_profile, wallet_id) - - -def test_sub_profile_access( - route_manager: MultitenantRouteManager, sub_profile: Profile -): - assert route_manager.sub_profile == sub_profile + yield MultitenantRouteManager(root_profile) @pytest.mark.asyncio @@ -61,6 +55,7 @@ async def test_route_for_key_sub_mediator_no_base_mediator( route_manager: MultitenantRouteManager, mock_responder: MockResponder, wallet_id: str, + sub_profile: Profile, ): mediation_record = MediationRecord( mediation_id="test-mediation-id", connection_id="test-mediator-conn-id" @@ -72,6 +67,7 @@ async def test_route_for_key_sub_mediator_no_base_mediator( RoutingManager, "create_route_record", mock.CoroutineMock() ) as mock_create_route_record: keylist_update = await route_manager._route_for_key( + sub_profile, "test-recipient-key", mediation_record, skip_if_exists=False, @@ -94,6 +90,7 @@ async def test_route_for_key_sub_mediator_no_base_mediator( @pytest.mark.asyncio async def test_route_for_key_sub_mediator_and_base_mediator( + sub_profile: Profile, route_manager: MultitenantRouteManager, mock_responder: MockResponder, wallet_id: str, @@ -114,6 +111,7 @@ async def test_route_for_key_sub_mediator_and_base_mediator( RoutingManager, "create_route_record", mock.CoroutineMock() ) as mock_create_route_record: keylist_update = await route_manager._route_for_key( + sub_profile, "test-recipient-key", mediation_record, skip_if_exists=False, @@ -136,6 +134,7 @@ async def test_route_for_key_sub_mediator_and_base_mediator( @pytest.mark.asyncio async def test_route_for_key_base_mediator_no_sub_mediator( + sub_profile: Profile, route_manager: MultitenantRouteManager, mock_responder: MockResponder, wallet_id: str, @@ -153,7 +152,11 @@ async def test_route_for_key_base_mediator_no_sub_mediator( RoutingManager, "create_route_record", mock.CoroutineMock() ) as mock_create_route_record: keylist_update = await route_manager._route_for_key( - "test-recipient-key", None, skip_if_exists=False, replace_key=None + sub_profile, + "test-recipient-key", + None, + skip_if_exists=False, + replace_key=None, ) mock_create_route_record.assert_called_once_with( @@ -172,6 +175,7 @@ async def test_route_for_key_base_mediator_no_sub_mediator( @pytest.mark.asyncio async def test_route_for_key_skip_if_exists_and_exists( + sub_profile: Profile, route_manager: MultitenantRouteManager, mock_responder: MockResponder, ): @@ -182,6 +186,7 @@ async def test_route_for_key_skip_if_exists_and_exists( RouteRecord, "retrieve_by_recipient_key", mock.CoroutineMock() ): keylist_update = await route_manager._route_for_key( + sub_profile, "test-recipient-key", mediation_record, skip_if_exists=True, @@ -193,6 +198,7 @@ async def test_route_for_key_skip_if_exists_and_exists( @pytest.mark.asyncio async def test_route_for_key_skip_if_exists_and_absent( + sub_profile: Profile, route_manager: MultitenantRouteManager, mock_responder: MockResponder, ): @@ -205,6 +211,7 @@ async def test_route_for_key_skip_if_exists_and_absent( mock.CoroutineMock(side_effect=StorageNotFoundError), ): keylist_update = await route_manager._route_for_key( + sub_profile, "test-recipient-key", mediation_record, skip_if_exists=True, @@ -223,6 +230,7 @@ async def test_route_for_key_skip_if_exists_and_absent( @pytest.mark.asyncio async def test_route_for_key_replace_key( + sub_profile: Profile, route_manager: MultitenantRouteManager, mock_responder: MockResponder, ): @@ -230,6 +238,7 @@ async def test_route_for_key_replace_key( mediation_id="test-mediation-id", connection_id="test-mediator-conn-id" ) keylist_update = await route_manager._route_for_key( + sub_profile, "test-recipient-key", mediation_record, skip_if_exists=False, @@ -249,10 +258,12 @@ async def test_route_for_key_replace_key( @pytest.mark.asyncio async def test_route_for_key_no_mediator( + sub_profile: Profile, route_manager: MultitenantRouteManager, ): assert ( await route_manager._route_for_key( + sub_profile, "test-recipient-key", None, skip_if_exists=True, @@ -264,6 +275,7 @@ async def test_route_for_key_no_mediator( @pytest.mark.asyncio async def test_routing_info_with_mediator( + sub_profile: Profile, route_manager: MultitenantRouteManager, ): mediation_record = MediationRecord( @@ -273,7 +285,7 @@ async def test_routing_info_with_mediator( endpoint="http://mediator.example.com", ) keys, endpoint = await route_manager.routing_info( - "http://example.com", mediation_record + sub_profile, "http://example.com", mediation_record ) assert keys == mediation_record.routing_keys assert endpoint == mediation_record.endpoint @@ -281,15 +293,19 @@ async def test_routing_info_with_mediator( @pytest.mark.asyncio async def test_routing_info_no_mediator( + sub_profile: Profile, route_manager: MultitenantRouteManager, ): - keys, endpoint = await route_manager.routing_info("http://example.com", None) + keys, endpoint = await route_manager.routing_info( + sub_profile, "http://example.com", None + ) assert keys == [] assert endpoint == "http://example.com" @pytest.mark.asyncio async def test_routing_info_with_base_mediator( + sub_profile: Profile, route_manager: MultitenantRouteManager, ): base_mediation_record = MediationRecord( @@ -304,13 +320,16 @@ async def test_routing_info_with_base_mediator( "get_base_wallet_mediator", mock.CoroutineMock(return_value=base_mediation_record), ): - keys, endpoint = await route_manager.routing_info("http://example.com", None) + keys, endpoint = await route_manager.routing_info( + sub_profile, "http://example.com", None + ) assert keys == base_mediation_record.routing_keys assert endpoint == base_mediation_record.endpoint @pytest.mark.asyncio async def test_routing_info_with_base_mediator_and_sub_mediator( + sub_profile: Profile, route_manager: MultitenantRouteManager, ): mediation_record = MediationRecord( @@ -332,7 +351,7 @@ async def test_routing_info_with_base_mediator_and_sub_mediator( mock.CoroutineMock(return_value=base_mediation_record), ): keys, endpoint = await route_manager.routing_info( - "http://example.com", mediation_record + sub_profile, "http://example.com", mediation_record ) assert keys == [*base_mediation_record.routing_keys, *mediation_record.routing_keys] assert endpoint == mediation_record.endpoint diff --git a/aries_cloudagent/protocols/connections/v1_0/manager.py b/aries_cloudagent/protocols/connections/v1_0/manager.py index 4cdf1b8d77..d937b7ecb2 100644 --- a/aries_cloudagent/protocols/connections/v1_0/manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/manager.py @@ -122,6 +122,7 @@ async def create_invitation( # Mediation Record can still be None after this operation if no # mediation id passed and no default mediation_record = await self._route_manager.mediation_record_if_id( + self.profile, mediation_id, or_default=True, ) @@ -159,7 +160,7 @@ async def create_invitation( # Add mapping for multitenant relaying. # Mediation of public keys is not supported yet - await self._route_manager.route_public_did(public_did.verkey) + await self._route_manager.route_public_did(self.profile, public_did.verkey) return None, invitation @@ -206,8 +207,11 @@ async def create_invitation( async with self.profile.session() as session: await connection.save(session, reason="Created new invitation") - await self._route_manager.route_invitation(connection, mediation_record) + 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, ) @@ -297,7 +301,7 @@ async def receive_invitation( await connection.attach_invitation(session, invitation) await self._route_manager.save_mediator_for_connection( - connection, mediation_id=mediation_id + self.profile, connection, mediation_id=mediation_id ) if connection.accept == ConnRecord.ACCEPT_AUTO: @@ -335,6 +339,7 @@ async def create_request( """ mediation_record = await self._route_manager.mediation_record_for_connection( + self.profile, connection, mediation_id, or_default=True, @@ -360,7 +365,7 @@ async def create_request( # Idempotent; if routing has already been set up, no action taken await self._route_manager.route_connection_as_invitee( - connection, mediation_record + self.profile, connection, mediation_record ) # Create connection request message @@ -579,7 +584,7 @@ async def create_response( ) mediation_record = await self._route_manager.mediation_record_for_connection( - connection, mediation_id + self.profile, connection, mediation_id ) # Multitenancy setup @@ -613,7 +618,7 @@ async def create_response( # Idempotent; if routing has already been set up, no action taken await self._route_manager.route_connection_as_inviter( - connection, mediation_record + self.profile, connection, mediation_record ) # Create connection response message @@ -863,7 +868,7 @@ async def create_static_connection( # Routing mediation_record = await self._route_manager.mediation_record_if_id( - mediation_id, or_default=True + self.profile, mediation_id, or_default=True ) multitenant_mgr = self.profile.inject_or(BaseMultitenantManager) @@ -873,7 +878,9 @@ async def create_static_connection( if multitenant_mgr and wallet_id: base_mediation_record = await multitenant_mgr.get_default_mediator() - await self._route_manager.route_static(connection, mediation_record) + await self._route_manager.route_static( + self.profile, connection, mediation_record + ) # Synthesize their DID doc did_doc = await self.create_did_document( diff --git a/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py index 66d284123d..d7c3836236 100644 --- a/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py @@ -177,7 +177,7 @@ async def test_create_invitation_public(self): assert connect_record is None assert connect_invite.did.endswith(self.test_did) self.route_manager.route_public_did.assert_called_once_with( - self.test_verkey + self.profile, self.test_verkey ) async def test_create_invitation_public_no_public_invites(self): @@ -356,7 +356,7 @@ async def test_create_invitation_mediation_using_default(self): assert invite.routing_keys == self.test_mediator_routing_keys assert invite.endpoint == self.test_mediator_endpoint self.route_manager.routing_info.assert_awaited_once_with( - self.test_endpoint, mediation_record + self.profile, self.test_endpoint, mediation_record ) async def test_receive_invitation(self): diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py index 6197c88fd8..89fbf92081 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py @@ -32,21 +32,19 @@ class RouteManagerError(Exception): class RouteManager(ABC): """Base Route Manager.""" - def __init__(self, profile: Profile): - """Initialize route manager.""" - self.profile = profile - - async def get_or_create_my_did(self, conn_record: ConnRecord) -> DIDInfo: + async def get_or_create_my_did( + self, profile: Profile, conn_record: ConnRecord + ) -> DIDInfo: """Create or retrieve DID info for a conneciton.""" if not conn_record.my_did: - async with self.profile.session() as session: + async with profile.session() as session: wallet = session.inject(BaseWallet) # Create new DID for connection my_info = await wallet.create_local_did(DIDMethod.SOV, KeyType.ED25519) conn_record.my_did = my_info.did await conn_record.save(session, reason="Connection my did created") else: - async with self.profile.session() as session: + async with profile.session() as session: wallet = session.inject(BaseWallet) my_info = await wallet.get_local_did(conn_record.my_did) @@ -62,12 +60,13 @@ def _validate_mediation_state(self, mediation_record: MediationRecord): async def mediation_record_for_connection( self, + profile: Profile, conn_record: ConnRecord, mediation_id: Optional[str] = None, or_default: bool = False, ): """Return relevant mediator for connection.""" - async with self.profile.session() as session: + async with profile.session() as session: mediation_metadata = await conn_record.metadata_get( session, MediationManager.METADATA_KEY, {} ) @@ -75,13 +74,20 @@ async def mediation_record_for_connection( mediation_metadata.get(MediationManager.METADATA_ID) or mediation_id ) - mediation_record = await self.mediation_record_if_id(mediation_id, or_default) + mediation_record = await self.mediation_record_if_id( + profile, mediation_id, or_default + ) if mediation_record: - await self.save_mediator_for_connection(conn_record, mediation_record) + await self.save_mediator_for_connection( + profile, conn_record, mediation_record + ) return mediation_record async def mediation_record_if_id( - self, mediation_id: Optional[str] = None, or_default: bool = False + self, + profile: Profile, + mediation_id: Optional[str] = None, + or_default: bool = False, ): """Validate mediation and return record. @@ -91,14 +97,12 @@ async def mediation_record_if_id( """ mediation_record = None if mediation_id: - async with self.profile.session() as session: + async with profile.session() as session: mediation_record = await MediationRecord.retrieve_by_id( session, mediation_id ) elif or_default: - mediation_record = await MediationManager( - self.profile - ).get_default_mediator() + mediation_record = await MediationManager(profile).get_default_mediator() if mediation_record: self._validate_mediation_state(mediation_record) @@ -107,6 +111,7 @@ async def mediation_record_if_id( @abstractmethod async def _route_for_key( self, + profile: Profile, recipient_key: str, mediation_record: Optional[MediationRecord] = None, *, @@ -117,25 +122,28 @@ async def _route_for_key( async def route_connection_as_invitee( self, + profile: Profile, conn_record: ConnRecord, mediation_record: Optional[MediationRecord] = None, ) -> Optional[KeylistUpdate]: """Set up routing for a new connection when we are the invitee.""" LOGGER.debug("Routing connection as invitee") - my_info = await self.get_or_create_my_did(conn_record) + my_info = await self.get_or_create_my_did(profile, conn_record) return await self._route_for_key( - my_info.verkey, mediation_record, skip_if_exists=True + profile, my_info.verkey, mediation_record, skip_if_exists=True ) async def route_connection_as_inviter( self, + profile: Profile, conn_record: ConnRecord, mediation_record: Optional[MediationRecord] = None, ) -> Optional[KeylistUpdate]: """Set up routing for a new connection when we are the inviter.""" LOGGER.debug("Routing connection as inviter") - my_info = await self.get_or_create_my_did(conn_record) + my_info = await self.get_or_create_my_did(profile, conn_record) return await self._route_for_key( + profile, my_info.verkey, mediation_record, replace_key=conn_record.invitation_key, @@ -144,6 +152,7 @@ async def route_connection_as_inviter( async def route_connection( self, + profile: Profile, conn_record: ConnRecord, mediation_record: Optional[MediationRecord] = None, ) -> Optional[KeylistUpdate]: @@ -154,53 +163,63 @@ async def route_connection( if conn_record.rfc23_state == ConnRecord.State.INVITATION.rfc23strict( ConnRecord.Role.RESPONDER ): - return await self.route_connection_as_invitee(conn_record, mediation_record) + return await self.route_connection_as_invitee( + profile, conn_record, mediation_record + ) if conn_record.rfc23_state == ConnRecord.State.REQUEST.rfc23strict( ConnRecord.Role.REQUESTER ): - return await self.route_connection_as_inviter(conn_record, mediation_record) + return await self.route_connection_as_inviter( + profile, conn_record, mediation_record + ) return None async def route_invitation( self, + profile: Profile, conn_record: ConnRecord, mediation_record: Optional[MediationRecord] = None, ) -> Optional[KeylistUpdate]: """Set up routing for receiving a response to an invitation.""" - await self.save_mediator_for_connection(conn_record, mediation_record) + await self.save_mediator_for_connection(profile, conn_record, mediation_record) if conn_record.invitation_key: return await self._route_for_key( - conn_record.invitation_key, mediation_record, skip_if_exists=True + profile, + conn_record.invitation_key, + mediation_record, + skip_if_exists=True, ) raise ValueError("Expected connection to have invitation_key") - async def route_public_did(self, verkey: str): + async def route_public_did(self, profile: Profile, verkey: str): """Establish routing for a public DID.""" - return await self._route_for_key(verkey, skip_if_exists=True) + return await self._route_for_key(profile, verkey, skip_if_exists=True) async def route_static( self, + profile: Profile, conn_record: ConnRecord, mediation_record: Optional[MediationRecord] = None, ) -> Optional[KeylistUpdate]: """Establish routing for a static connection.""" - my_info = await self.get_or_create_my_did(conn_record) + my_info = await self.get_or_create_my_did(profile, conn_record) return await self._route_for_key( - my_info.verkey, mediation_record, skip_if_exists=True + profile, my_info.verkey, mediation_record, skip_if_exists=True ) async def save_mediator_for_connection( self, + profile: Profile, conn_record: ConnRecord, mediation_record: Optional[MediationRecord] = None, mediation_id: Optional[str] = None, ): """Save mediator info to connection metadata.""" - async with self.profile.session() as session: + async with profile.session() as session: if mediation_id: mediation_record = await MediationRecord.retrieve_by_id( session, mediation_id @@ -216,6 +235,7 @@ async def save_mediator_for_connection( @abstractmethod async def routing_info( self, + profile: Profile, my_endpoint: str, mediation_record: Optional[MediationRecord] = None, ) -> Tuple[List[str], str]: @@ -227,6 +247,7 @@ class CoordinateMediationV1RouteManager(RouteManager): async def _route_for_key( self, + profile: Profile, recipient_key: str, mediation_record: Optional[MediationRecord] = None, *, @@ -238,7 +259,7 @@ async def _route_for_key( if skip_if_exists: try: - async with self.profile.session() as session: + async with profile.session() as session: await RouteRecord.retrieve_by_recipient_key(session, recipient_key) return None @@ -246,19 +267,22 @@ async def _route_for_key( pass # Keylist update is idempotent, skip_if_exists ignored - mediation_mgr = MediationManager(self.profile) + mediation_mgr = MediationManager(profile) keylist_update = await mediation_mgr.add_key(recipient_key) if replace_key: keylist_update = await mediation_mgr.remove_key(replace_key, keylist_update) - responder = self.profile.inject(BaseResponder) + responder = profile.inject(BaseResponder) await responder.send( keylist_update, connection_id=mediation_record.connection_id ) return keylist_update async def routing_info( - self, my_endpoint: str, mediation_record: Optional[MediationRecord] = None + self, + profile: Profile, + my_endpoint: str, + mediation_record: Optional[MediationRecord] = None, ) -> Tuple[List[str], str]: """Return routing info for mediator.""" if mediation_record: diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager_provider.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager_provider.py index d053094a52..693766c922 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager_provider.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager_provider.py @@ -20,8 +20,7 @@ def provide(self, settings: BaseSettings, injector: BaseInjector): """Create the appropriate route manager instance.""" wallet_id = settings.get("wallet.id") multitenant_mgr = injector.inject_or(BaseMultitenantManager) - profile = injector.inject(Profile) if multitenant_mgr and wallet_id: - return MultitenantRouteManager(self.root_profile, profile, wallet_id) + return MultitenantRouteManager(self.root_profile) - return CoordinateMediationV1RouteManager(profile) + return CoordinateMediationV1RouteManager() diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_route_manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_route_manager.py index 61b72bb975..dcb315337b 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_route_manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_route_manager.py @@ -37,8 +37,8 @@ def profile(mock_responder: MockResponder): @pytest.fixture -def route_manager(profile: Profile): - manager = MockRouteManager(profile) +def route_manager(): + manager = MockRouteManager() manager._route_for_key = mock.CoroutineMock( return_value=mock.MagicMock(KeylistUpdate) ) @@ -47,8 +47,8 @@ def route_manager(profile: Profile): @pytest.fixture -def mediation_route_manager(profile: Profile): - yield CoordinateMediationV1RouteManager(profile) +def mediation_route_manager(): + yield CoordinateMediationV1RouteManager() @pytest.fixture @@ -61,7 +61,7 @@ def conn_record(): @pytest.mark.asyncio async def test_get_or_create_my_did_no_did( - route_manager: RouteManager, conn_record: ConnRecord + profile: Profile, route_manager: RouteManager, conn_record: ConnRecord ): conn_record.my_did = None mock_did_info = mock.MagicMock() @@ -72,7 +72,7 @@ async def test_get_or_create_my_did_no_did( ) as mock_create_local_did, mock.patch.object( conn_record, "save", mock.CoroutineMock() ) as mock_save: - info = await route_manager.get_or_create_my_did(conn_record) + info = await route_manager.get_or_create_my_did(profile, conn_record) assert mock_did_info == info mock_create_local_did.assert_called_once() mock_save.assert_called_once() @@ -80,21 +80,21 @@ async def test_get_or_create_my_did_no_did( @pytest.mark.asyncio async def test_get_or_create_my_did_existing_did( - route_manager: RouteManager, conn_record: ConnRecord + profile: Profile, route_manager: RouteManager, conn_record: ConnRecord ): conn_record.my_did = "test-did" mock_did_info = mock.MagicMock(DIDInfo) with mock.patch.object( InMemoryWallet, "get_local_did", mock.CoroutineMock(return_value=mock_did_info) ) as mock_get_local_did: - info = await route_manager.get_or_create_my_did(conn_record) + info = await route_manager.get_or_create_my_did(profile, conn_record) assert mock_did_info == info mock_get_local_did.assert_called_once() @pytest.mark.asyncio async def test_mediation_record_for_connection_mediation_id( - route_manager: RouteManager, conn_record: ConnRecord + profile: Profile, route_manager: RouteManager, conn_record: ConnRecord ): mediation_record = MediationRecord(mediation_id="test-mediation-id") with mock.patch.object( @@ -106,18 +106,18 @@ async def test_mediation_record_for_connection_mediation_id( ): assert ( await route_manager.mediation_record_for_connection( - conn_record, mediation_record.mediation_id + profile, conn_record, mediation_record.mediation_id ) == mediation_record ) mock_mediation_record_if_id.assert_called_once_with( - mediation_record.mediation_id, False + profile, mediation_record.mediation_id, False ) @pytest.mark.asyncio async def test_mediation_record_for_connection_mediation_metadata( - route_manager: RouteManager, conn_record: ConnRecord + profile: Profile, route_manager: RouteManager, conn_record: ConnRecord ): mediation_record = MediationRecord(mediation_id="test-mediation-id") conn_record.metadata_get.return_value = { @@ -132,18 +132,18 @@ async def test_mediation_record_for_connection_mediation_metadata( ): assert ( await route_manager.mediation_record_for_connection( - conn_record, "another-mediation-id" + profile, conn_record, "another-mediation-id" ) == mediation_record ) mock_mediation_record_if_id.assert_called_once_with( - mediation_record.mediation_id, False + profile, mediation_record.mediation_id, False ) @pytest.mark.asyncio async def test_mediation_record_for_connection_default( - route_manager: RouteManager, conn_record: ConnRecord + profile: Profile, route_manager: RouteManager, conn_record: ConnRecord ): mediation_record = MediationRecord(mediation_id="test-mediation-id") with mock.patch.object( @@ -155,15 +155,17 @@ async def test_mediation_record_for_connection_default( ): assert ( await route_manager.mediation_record_for_connection( - conn_record, None, or_default=True + profile, conn_record, None, or_default=True ) == mediation_record ) - mock_mediation_record_if_id.assert_called_once_with(None, True) + mock_mediation_record_if_id.assert_called_once_with(profile, None, True) @pytest.mark.asyncio -async def test_mediation_record_if_id_with_id(route_manager: RouteManager): +async def test_mediation_record_if_id_with_id( + profile: Profile, route_manager: RouteManager +): mediation_record = MediationRecord( mediation_id="test-mediation-id", state=MediationRecord.STATE_GRANTED ) @@ -173,14 +175,16 @@ async def test_mediation_record_if_id_with_id(route_manager: RouteManager): mock.CoroutineMock(return_value=mediation_record), ) as mock_retrieve_by_id: actual = await route_manager.mediation_record_if_id( - mediation_id=mediation_record.mediation_id + profile, mediation_id=mediation_record.mediation_id ) assert mediation_record == actual mock_retrieve_by_id.assert_called_once() @pytest.mark.asyncio -async def test_mediation_record_if_id_with_id_bad_state(route_manager: RouteManager): +async def test_mediation_record_if_id_with_id_bad_state( + profile: Profile, route_manager: RouteManager +): mediation_record = MediationRecord( mediation_id="test-mediation-id", state=MediationRecord.STATE_DENIED ) @@ -191,12 +195,14 @@ async def test_mediation_record_if_id_with_id_bad_state(route_manager: RouteMana ): with pytest.raises(RouteManagerError): await route_manager.mediation_record_if_id( - mediation_id=mediation_record.mediation_id + profile, mediation_id=mediation_record.mediation_id ) @pytest.mark.asyncio -async def test_mediation_record_if_id_with_id_and_default(route_manager: RouteManager): +async def test_mediation_record_if_id_with_id_and_default( + profile: Profile, route_manager: RouteManager +): mediation_record = MediationRecord( mediation_id="test-mediation-id", state=MediationRecord.STATE_GRANTED ) @@ -208,7 +214,7 @@ async def test_mediation_record_if_id_with_id_and_default(route_manager: RouteMa MediationManager, "get_default_mediator", mock.CoroutineMock() ) as mock_get_default_mediator: actual = await route_manager.mediation_record_if_id( - mediation_id=mediation_record.mediation_id, or_default=True + profile, mediation_id=mediation_record.mediation_id, or_default=True ) assert mediation_record == actual mock_retrieve_by_id.assert_called_once() @@ -217,6 +223,7 @@ async def test_mediation_record_if_id_with_id_and_default(route_manager: RouteMa @pytest.mark.asyncio async def test_mediation_record_if_id_without_id_and_default( + profile: Profile, route_manager: RouteManager, ): mediation_record = MediationRecord( @@ -230,7 +237,7 @@ async def test_mediation_record_if_id_without_id_and_default( mock.CoroutineMock(return_value=mediation_record), ) as mock_get_default_mediator: actual = await route_manager.mediation_record_if_id( - mediation_id=None, or_default=True + profile, mediation_id=None, or_default=True ) assert mediation_record == actual mock_retrieve_by_id.assert_not_called() @@ -239,6 +246,7 @@ async def test_mediation_record_if_id_without_id_and_default( @pytest.mark.asyncio async def test_mediation_record_if_id_without_id_and_no_default( + profile: Profile, route_manager: RouteManager, ): with mock.patch.object( @@ -248,7 +256,7 @@ async def test_mediation_record_if_id_without_id_and_no_default( ) as mock_get_default_mediator: assert ( await route_manager.mediation_record_if_id( - mediation_id=None, or_default=True + profile, mediation_id=None, or_default=True ) is None ) @@ -258,7 +266,7 @@ async def test_mediation_record_if_id_without_id_and_no_default( @pytest.mark.asyncio async def test_route_connection_as_invitee( - route_manager: RouteManager, conn_record: ConnRecord + profile: Profile, route_manager: RouteManager, conn_record: ConnRecord ): mediation_record = MediationRecord(mediation_id="test-mediation-id") mock_did_info = mock.MagicMock(DIDInfo) @@ -267,15 +275,17 @@ async def test_route_connection_as_invitee( "get_or_create_my_did", mock.CoroutineMock(return_value=mock_did_info), ): - await route_manager.route_connection_as_invitee(conn_record, mediation_record) + await route_manager.route_connection_as_invitee( + profile, conn_record, mediation_record + ) route_manager._route_for_key.assert_called_once_with( - mock_did_info.verkey, mediation_record, skip_if_exists=True + profile, mock_did_info.verkey, mediation_record, skip_if_exists=True ) @pytest.mark.asyncio async def test_route_connection_as_inviter( - route_manager: RouteManager, conn_record: ConnRecord + profile: Profile, route_manager: RouteManager, conn_record: ConnRecord ): mediation_record = MediationRecord(mediation_id="test-mediation-id") mock_did_info = mock.MagicMock(DIDInfo) @@ -285,8 +295,11 @@ async def test_route_connection_as_inviter( "get_or_create_my_did", mock.CoroutineMock(return_value=mock_did_info), ): - await route_manager.route_connection_as_inviter(conn_record, mediation_record) + await route_manager.route_connection_as_inviter( + profile, conn_record, mediation_record + ) route_manager._route_for_key.assert_called_once_with( + profile, mock_did_info.verkey, mediation_record, replace_key="test-invitation-key", @@ -296,7 +309,7 @@ async def test_route_connection_as_inviter( @pytest.mark.asyncio async def test_route_connection_state_invitee( - route_manager: RouteManager, conn_record: ConnRecord + profile: Profile, route_manager: RouteManager, conn_record: ConnRecord ): mediation_record = MediationRecord(mediation_id="test-mediation-id") conn_record.state = "invitation" @@ -306,14 +319,14 @@ async def test_route_connection_state_invitee( ) as mock_route_connection_as_invitee, mock.patch.object( route_manager, "route_connection_as_inviter", mock.CoroutineMock() ) as mock_route_connection_as_inviter: - await route_manager.route_connection(conn_record, mediation_record) + await route_manager.route_connection(profile, conn_record, mediation_record) mock_route_connection_as_invitee.assert_called_once() mock_route_connection_as_inviter.assert_not_called() @pytest.mark.asyncio async def test_route_connection_state_inviter( - route_manager: RouteManager, conn_record: ConnRecord + profile: Profile, route_manager: RouteManager, conn_record: ConnRecord ): mediation_record = MediationRecord(mediation_id="test-mediation-id") conn_record.state = "request" @@ -323,57 +336,62 @@ async def test_route_connection_state_inviter( ) as mock_route_connection_as_invitee, mock.patch.object( route_manager, "route_connection_as_inviter", mock.CoroutineMock() ) as mock_route_connection_as_inviter: - await route_manager.route_connection(conn_record, mediation_record) + await route_manager.route_connection(profile, conn_record, mediation_record) mock_route_connection_as_inviter.assert_called_once() mock_route_connection_as_invitee.assert_not_called() @pytest.mark.asyncio async def test_route_connection_state_other( - route_manager: RouteManager, conn_record: ConnRecord + profile: Profile, route_manager: RouteManager, conn_record: ConnRecord ): mediation_record = MediationRecord(mediation_id="test-mediation-id") conn_record.state = "response" conn_record.their_role = "requester" - assert await route_manager.route_connection(conn_record, mediation_record) is None + assert ( + await route_manager.route_connection(profile, conn_record, mediation_record) + is None + ) @pytest.mark.asyncio async def test_route_invitation_with_key( - route_manager: RouteManager, conn_record: ConnRecord + profile: Profile, route_manager: RouteManager, conn_record: ConnRecord ): mediation_record = MediationRecord(mediation_id="test-mediation-id") conn_record.invitation_key = "test-invitation-key" with mock.patch.object( route_manager, "save_mediator_for_connection", mock.CoroutineMock() ): - await route_manager.route_invitation(conn_record, mediation_record) + await route_manager.route_invitation(profile, conn_record, mediation_record) route_manager._route_for_key.assert_called_once() @pytest.mark.asyncio async def test_route_invitation_without_key( - route_manager: RouteManager, conn_record: ConnRecord + profile: Profile, route_manager: RouteManager, conn_record: ConnRecord ): mediation_record = MediationRecord(mediation_id="test-mediation-id") with mock.patch.object( route_manager, "save_mediator_for_connection", mock.CoroutineMock() ): with pytest.raises(ValueError): - await route_manager.route_invitation(conn_record, mediation_record) + await route_manager.route_invitation(profile, conn_record, mediation_record) route_manager._route_for_key.assert_not_called() @pytest.mark.asyncio -async def test_route_public_did(route_manager: RouteManager): - await route_manager.route_public_did("test-verkey") +async def test_route_public_did(profile: Profile, route_manager: RouteManager): + await route_manager.route_public_did(profile, "test-verkey") route_manager._route_for_key.assert_called_once_with( - "test-verkey", skip_if_exists=True + profile, "test-verkey", skip_if_exists=True ) @pytest.mark.asyncio -async def test_route_static(route_manager: RouteManager, conn_record: ConnRecord): +async def test_route_static( + profile: Profile, route_manager: RouteManager, conn_record: ConnRecord +): mediation_record = MediationRecord(mediation_id="test-mediation-id") mock_did_info = mock.MagicMock(DIDInfo) conn_record.invitation_key = "test-invitation-key" @@ -382,8 +400,9 @@ async def test_route_static(route_manager: RouteManager, conn_record: ConnRecord "get_or_create_my_did", mock.CoroutineMock(return_value=mock_did_info), ): - await route_manager.route_static(conn_record, mediation_record) + await route_manager.route_static(profile, conn_record, mediation_record) route_manager._route_for_key.assert_called_once_with( + profile, mock_did_info.verkey, mediation_record, skip_if_exists=True, @@ -392,7 +411,9 @@ async def test_route_static(route_manager: RouteManager, conn_record: ConnRecord @pytest.mark.asyncio async def test_save_mediator_for_connection_record( - route_manager: RouteManager, conn_record: ConnRecord, profile: Profile + profile: Profile, + route_manager: RouteManager, + conn_record: ConnRecord, ): mediation_record = MediationRecord(mediation_id="test-mediation-id") session = mock.MagicMock() @@ -402,7 +423,9 @@ async def test_save_mediator_for_connection_record( with mock.patch.object( MediationRecord, "retrieve_by_id", mock.CoroutineMock() ) as mock_retrieve_by_id: - await route_manager.save_mediator_for_connection(conn_record, mediation_record) + await route_manager.save_mediator_for_connection( + profile, conn_record, mediation_record + ) mock_retrieve_by_id.assert_not_called() conn_record.metadata_set.assert_called_once_with( session, @@ -413,7 +436,9 @@ async def test_save_mediator_for_connection_record( @pytest.mark.asyncio async def test_save_mediator_for_connection_id( - route_manager: RouteManager, conn_record: ConnRecord, profile: Profile + profile: Profile, + route_manager: RouteManager, + conn_record: ConnRecord, ): mediation_record = MediationRecord(mediation_id="test-mediation-id") session = mock.MagicMock() @@ -426,7 +451,7 @@ async def test_save_mediator_for_connection_id( mock.CoroutineMock(return_value=mediation_record), ) as mock_retrieve_by_id: await route_manager.save_mediator_for_connection( - conn_record, mediation_id=mediation_record.mediation_id + profile, conn_record, mediation_id=mediation_record.mediation_id ) mock_retrieve_by_id.assert_called_once() conn_record.metadata_set.assert_called_once_with( @@ -438,18 +463,21 @@ async def test_save_mediator_for_connection_id( @pytest.mark.asyncio async def test_save_mediator_for_connection_no_mediator( - route_manager: RouteManager, conn_record: ConnRecord, profile: Profile + profile: Profile, + route_manager: RouteManager, + conn_record: ConnRecord, ): with mock.patch.object( MediationRecord, "retrieve_by_id", mock.CoroutineMock() ) as mock_retrieve_by_id: - await route_manager.save_mediator_for_connection(conn_record) + await route_manager.save_mediator_for_connection(profile, conn_record) mock_retrieve_by_id.assert_not_called() conn_record.metadata_set.assert_not_called() @pytest.mark.asyncio async def test_mediation_route_for_key( + profile: Profile, mediation_route_manager: CoordinateMediationV1RouteManager, mock_responder: MockResponder, ): @@ -457,7 +485,11 @@ async def test_mediation_route_for_key( mediation_id="test-mediation-id", connection_id="test-mediator-conn-id" ) keylist_update = await mediation_route_manager._route_for_key( - "test-recipient-key", mediation_record, skip_if_exists=False, replace_key=None + profile, + "test-recipient-key", + mediation_record, + skip_if_exists=False, + replace_key=None, ) assert keylist_update assert keylist_update.serialize()["updates"] == [ @@ -472,6 +504,7 @@ async def test_mediation_route_for_key( @pytest.mark.asyncio async def test_mediation_route_for_key_skip_if_exists_and_exists( + profile: Profile, mediation_route_manager: CoordinateMediationV1RouteManager, mock_responder: MockResponder, ): @@ -482,6 +515,7 @@ async def test_mediation_route_for_key_skip_if_exists_and_exists( RouteRecord, "retrieve_by_recipient_key", mock.CoroutineMock() ): keylist_update = await mediation_route_manager._route_for_key( + profile, "test-recipient-key", mediation_record, skip_if_exists=True, @@ -493,6 +527,7 @@ async def test_mediation_route_for_key_skip_if_exists_and_exists( @pytest.mark.asyncio async def test_mediation_route_for_key_skip_if_exists_and_absent( + profile: Profile, mediation_route_manager: CoordinateMediationV1RouteManager, mock_responder: MockResponder, ): @@ -505,6 +540,7 @@ async def test_mediation_route_for_key_skip_if_exists_and_absent( mock.CoroutineMock(side_effect=StorageNotFoundError), ): keylist_update = await mediation_route_manager._route_for_key( + profile, "test-recipient-key", mediation_record, skip_if_exists=True, @@ -523,6 +559,7 @@ async def test_mediation_route_for_key_skip_if_exists_and_absent( @pytest.mark.asyncio async def test_mediation_route_for_key_replace_key( + profile: Profile, mediation_route_manager: CoordinateMediationV1RouteManager, mock_responder: MockResponder, ): @@ -530,6 +567,7 @@ async def test_mediation_route_for_key_replace_key( mediation_id="test-mediation-id", connection_id="test-mediator-conn-id" ) keylist_update = await mediation_route_manager._route_for_key( + profile, "test-recipient-key", mediation_record, skip_if_exists=False, @@ -549,10 +587,12 @@ async def test_mediation_route_for_key_replace_key( @pytest.mark.asyncio async def test_mediation_route_for_key_no_mediator( + profile: Profile, mediation_route_manager: CoordinateMediationV1RouteManager, ): assert ( await mediation_route_manager._route_for_key( + profile, "test-recipient-key", None, skip_if_exists=True, @@ -564,6 +604,7 @@ async def test_mediation_route_for_key_no_mediator( @pytest.mark.asyncio async def test_mediation_routing_info_with_mediator( + profile: Profile, mediation_route_manager: CoordinateMediationV1RouteManager, ): mediation_record = MediationRecord( @@ -573,7 +614,7 @@ async def test_mediation_routing_info_with_mediator( endpoint="http://mediator.example.com", ) keys, endpoint = await mediation_route_manager.routing_info( - "http://example.com", mediation_record + profile, "http://example.com", mediation_record ) assert keys == mediation_record.routing_keys assert endpoint == mediation_record.endpoint @@ -581,10 +622,11 @@ async def test_mediation_routing_info_with_mediator( @pytest.mark.asyncio async def test_mediation_routing_info_no_mediator( + profile: Profile, mediation_route_manager: CoordinateMediationV1RouteManager, ): keys, endpoint = await mediation_route_manager.routing_info( - "http://example.com", None + profile, "http://example.com", None ) assert keys == [] assert endpoint == "http://example.com" diff --git a/aries_cloudagent/protocols/didexchange/v1_0/manager.py b/aries_cloudagent/protocols/didexchange/v1_0/manager.py index 5ad6c83642..ec80923e9a 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/manager.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/manager.py @@ -155,7 +155,7 @@ async def receive_invitation( ].public_key_base58 await self._route_manager.save_mediator_for_connection( - conn_rec, mediation_id=mediation_id + self.profile, conn_rec, mediation_id=mediation_id ) if conn_rec.accept == ConnRecord.ACCEPT_AUTO: @@ -259,6 +259,7 @@ async def create_request( """ # Mediation Support mediation_record = await self._route_manager.mediation_record_for_connection( + self.profile, conn_rec, mediation_id, or_default=True, @@ -290,7 +291,7 @@ async def create_request( # Idempotent; if routing has already been set up, no action taken await self._route_manager.route_connection_as_invitee( - conn_rec, mediation_record + self.profile, conn_rec, mediation_record ) # Create connection request message @@ -550,7 +551,7 @@ async def create_response( ) mediation_record = await self._route_manager.mediation_record_for_connection( - conn_rec, mediation_id + self.profile, conn_rec, mediation_id ) # Multitenancy setup @@ -583,7 +584,7 @@ async def create_response( # Idempotent; if routing has already been set up, no action taken await self._route_manager.route_connection_as_inviter( - conn_rec, mediation_record + self.profile, conn_rec, mediation_record ) # Create connection response message diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py index e3f4548d47..7b02b328f7 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py @@ -110,6 +110,7 @@ async def create_invitation( """ mediation_record = await self._route_manager.mediation_record_if_id( + self.profile, mediation_id, or_default=True, ) @@ -296,7 +297,7 @@ async def create_invitation( await conn_rec.save(session, reason="Created new connection") routing_keys, my_endpoint = await self._route_manager.routing_info( - my_endpoint, mediation_record + self.profile, my_endpoint, mediation_record ) if not conn_rec: @@ -358,7 +359,9 @@ async def create_invitation( async with self.profile.session() as session: await oob_record.save(session, reason="Created new oob invitation") - await self._route_manager.route_invitation(conn_rec, mediation_record) + await self._route_manager.route_invitation( + self.profile, conn_rec, mediation_record + ) return InvitationRecord( # for return via admin API, not storage oob_id=oob_record.oob_id, @@ -392,7 +395,9 @@ async def receive_invitation( """ if mediation_id: try: - await self._route_manager.mediation_record_if_id(mediation_id) + await self._route_manager.mediation_record_if_id( + self.profile, mediation_id + ) except StorageNotFoundError: mediation_id = None diff --git a/aries_cloudagent/wallet/routes.py b/aries_cloudagent/wallet/routes.py index ff8da41b7a..fd26fe8c45 100644 --- a/aries_cloudagent/wallet/routes.py +++ b/aries_cloudagent/wallet/routes.py @@ -570,7 +570,7 @@ async def promote_wallet_public_did( # Route the public DID route_manager = profile.inject(RouteManager) - await route_manager.route_public_did(info.verkey) + await route_manager.route_public_did(profile, info.verkey) return info, attrib_def From 95c07b3d09e550c301c411b0079afd678a0fa69b Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Mon, 18 Jul 2022 11:44:41 -0500 Subject: [PATCH 341/872] fix: cleanly handle exceptions in wallet finalizer Signed-off-by: Daniel Bluhm --- aries_cloudagent/indy/sdk/profile.py | 8 +++++- .../indy/sdk/tests/test_profile.py | 19 +++++++++----- .../wallet/tests/test_indy_wallet.py | 25 +++++++++++-------- 3 files changed, 35 insertions(+), 17 deletions(-) diff --git a/aries_cloudagent/indy/sdk/profile.py b/aries_cloudagent/indy/sdk/profile.py index d8151a5050..525e52917b 100644 --- a/aries_cloudagent/indy/sdk/profile.py +++ b/aries_cloudagent/indy/sdk/profile.py @@ -128,9 +128,15 @@ def _make_finalizer(self, opened: IndyOpenWallet) -> finalize: See docs for weakref.finalize for more details on behavior of finalizers. """ + async def _closer(opened: IndyOpenWallet): + try: + await opened.close() + except Exception: + LOGGER.exception("Failed to close wallet from finalizer") + def _finalize(opened: IndyOpenWallet): LOGGER.debug("Profile finalizer called; closing wallet") - asyncio.get_event_loop().create_task(opened.close()) + asyncio.get_event_loop().create_task(_closer(opened)) return finalize(self, _finalize, opened) diff --git a/aries_cloudagent/indy/sdk/tests/test_profile.py b/aries_cloudagent/indy/sdk/tests/test_profile.py index 6bd97474e6..8047d4cea6 100644 --- a/aries_cloudagent/indy/sdk/tests/test_profile.py +++ b/aries_cloudagent/indy/sdk/tests/test_profile.py @@ -1,35 +1,42 @@ +import asyncio import logging -import pytest from asynctest import mock as async_mock +import pytest from ....config.injection_context import InjectionContext from ....core.error import ProfileError from ....ledger.indy import IndySdkLedgerPool - from ..profile import IndySdkProfile -from ..wallet_setup import IndyWalletConfig, IndyOpenWallet +from ..wallet_setup import IndyOpenWallet, IndyWalletConfig @pytest.fixture async def open_wallet(): - yield IndyOpenWallet( + opened = IndyOpenWallet( config=IndyWalletConfig({"name": "test-profile"}), created=True, handle=1, master_secret_id="master-secret", ) + with async_mock.patch.object(opened, "close", async_mock.CoroutineMock()): + yield opened @pytest.fixture() async def profile(open_wallet): context = InjectionContext() context.injector.bind_instance(IndySdkLedgerPool, IndySdkLedgerPool("name")) - yield IndySdkProfile(open_wallet, context) + profile = IndySdkProfile(open_wallet, context) + + yield profile + + # Trigger finalizer before event loop fixture is closed + profile._finalizer() @pytest.mark.asyncio -async def test_properties(profile): +async def test_properties(profile: IndySdkProfile): assert profile.name == "test-profile" assert profile.backend == "indy" assert profile.wallet and profile.wallet.handle == 1 diff --git a/aries_cloudagent/wallet/tests/test_indy_wallet.py b/aries_cloudagent/wallet/tests/test_indy_wallet.py index aa3f7ee88d..b2e69245e9 100644 --- a/aries_cloudagent/wallet/tests/test_indy_wallet.py +++ b/aries_cloudagent/wallet/tests/test_indy_wallet.py @@ -1,5 +1,6 @@ import json import os +from typing import cast import indy.anoncreds import indy.crypto @@ -13,7 +14,7 @@ from ...config.injection_context import InjectionContext from ...core.error import ProfileError, ProfileDuplicateError, ProfileNotFoundError from ...indy.sdk import wallet_setup as test_setup_module -from ...indy.sdk.profile import IndySdkProfileManager +from ...indy.sdk.profile import IndySdkProfile, IndySdkProfileManager from ...indy.sdk.wallet_setup import IndyWalletConfig from ...ledger.endpoint_type import EndpointType from ...wallet.key_type import KeyType @@ -40,18 +41,22 @@ async def wallet(): key = await IndySdkWallet.generate_wallet_key() context = InjectionContext() context.injector.bind_instance(IndySdkLedgerPool, IndySdkLedgerPool("name")) - profile = await IndySdkProfileManager().provision( - context, - { - "auto_recreate": True, - "auto_remove": True, - "name": "test-wallet", - "key": key, - "key_derivation_method": "RAW", # much slower tests with argon-hashed keys - }, + profile = cast( + IndySdkProfile, + await IndySdkProfileManager().provision( + context, + { + "auto_recreate": True, + "auto_remove": True, + "name": "test-wallet", + "key": key, + "key_derivation_method": "RAW", # much slower tests with argon-hashed keys + }, + ), ) async with profile.session() as session: yield session.inject(BaseWallet) + profile._finalizer() await profile.close() From 239fe78dc519bff368b910826a0cd93cd0ae063e Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Mon, 18 Jul 2022 13:02:41 -0500 Subject: [PATCH 342/872] fix: resolve dids following new endpoint rules Signed-off-by: Daniel Bluhm --- aries_cloudagent/resolver/default/indy.py | 79 ++++++++++++++++--- .../resolver/default/tests/test_indy.py | 18 +++++ 2 files changed, 85 insertions(+), 12 deletions(-) diff --git a/aries_cloudagent/resolver/default/indy.py b/aries_cloudagent/resolver/default/indy.py index d1088121ba..7cac98daf4 100644 --- a/aries_cloudagent/resolver/default/indy.py +++ b/aries_cloudagent/resolver/default/indy.py @@ -3,10 +3,10 @@ Resolution is performed using the IndyLedger class. """ -from typing import Pattern +from typing import Any, Mapping, Pattern from pydid import DID, DIDDocumentBuilder -from pydid.verification_method import Ed25519VerificationKey2018 +from pydid.verification_method import Ed25519VerificationKey2018, VerificationMethod from ...config.injection_context import InjectionContext from ...core.profile import Profile @@ -29,7 +29,10 @@ class NoIndyLedger(ResolverError): class IndyDIDResolver(BaseDIDResolver): """Indy DID Resolver.""" - AGENT_SERVICE_TYPE = "did-communication" + SERVICE_TYPE_DID_COMMUNICATION = "did-communication" + SERVICE_TYPE_DIDCOMM = "DIDComm" + SERVICE_TYPE_ENDPOINT = "endpoint" + CONTEXT_DIDCOMM_V2 = "https://didcomm.org/messaging/contexts/v2" def __init__(self): """Initialize Indy Resolver.""" @@ -43,6 +46,60 @@ def supported_did_regex(self) -> Pattern: """Return supported_did_regex of Indy DID Resolver.""" return IndyDID.PATTERN + def _add_endpoint_as_endpoint_value_pair( + self, + builder: DIDDocumentBuilder, + endpoint: str, + recipient_key: VerificationMethod, + ): + builder.service.add_didcomm( + ident=self.SERVICE_TYPE_DID_COMMUNICATION, + type_=self.SERVICE_TYPE_DID_COMMUNICATION, + service_endpoint=endpoint, + priority=1, + recipient_keys=[recipient_key], + routing_keys=[], + ) + + def _add_endpoint_as_map( + self, + builder: DIDDocumentBuilder, + endpoint: Mapping[str, Any], + recipient_key: VerificationMethod, + ): + types = endpoint.get("types", [self.SERVICE_TYPE_DID_COMMUNICATION]) + routing_keys = endpoint.get("routingKeys", []) + endpoint_url = endpoint.get("endpoint") + if not endpoint_url: + raise ValueError("endpoint url not found in endpoint attrib") + + if self.SERVICE_TYPE_DIDCOMM in types: + builder.service.add( + ident="#didcomm-1", + type_=self.SERVICE_TYPE_DIDCOMM, + service_endpoint=endpoint_url, + recipient_keys=[recipient_key.id], + routing_keys=routing_keys, + accept=["didcomm/v2"], + ) + builder.context.append(self.CONTEXT_DIDCOMM_V2) + if self.SERVICE_TYPE_DID_COMMUNICATION in types: + builder.service.add( + ident="did-communication", + type_=self.SERVICE_TYPE_DID_COMMUNICATION, + service_endpoint=endpoint_url, + priority=0, + routing_keys=routing_keys, + recipient_keys=[recipient_key.id], + accept=["didcomm/aip2;env=rfc19"], + ) + if self.SERVICE_TYPE_ENDPOINT in types: + builder.service.add( + ident="endpoint", + service_endpoint=endpoint_url, + type_=self.SERVICE_TYPE_ENDPOINT, + ) + async def _resolve(self, profile: Profile, did: str) -> dict: """Resolve an indy DID.""" multitenant_mgr = profile.inject_or(BaseMultitenantManager) @@ -76,16 +133,14 @@ async def _resolve(self, profile: Profile, did: str) -> dict: if endpoints: for type_, endpoint in endpoints.items(): if type_ == EndpointType.ENDPOINT.indy: - builder.service.add_didcomm( - ident=self.AGENT_SERVICE_TYPE, - type_=self.AGENT_SERVICE_TYPE, - service_endpoint=endpoint, - priority=1, - recipient_keys=[vmethod], - routing_keys=[], - ) + if isinstance(endpoint, dict): + self._add_endpoint_as_map(builder, endpoint, vmethod) + else: + self._add_endpoint_as_endpoint_value_pair( + builder, endpoint, vmethod + ) else: - # Accept all service types for now + # Accept all service types for now, i.e. profile, linked_domains builder.service.add( ident=type_, type_=type_, diff --git a/aries_cloudagent/resolver/default/tests/test_indy.py b/aries_cloudagent/resolver/default/tests/test_indy.py index 7781dac13f..ee1bc8311e 100644 --- a/aries_cloudagent/resolver/default/tests/test_indy.py +++ b/aries_cloudagent/resolver/default/tests/test_indy.py @@ -107,3 +107,21 @@ async def test_resolve_x_did_not_found( ledger.get_key_for_did.side_effect = LedgerError with pytest.raises(DIDNotFound): await resolver.resolve(profile, TEST_DID0) + + @pytest.mark.asyncio + async def test_supports_updated_did_sov_rules( + self, resolver: IndyDIDResolver, ledger: BaseLedger, profile: Profile + ): + """Test that new attrib structure is supported.""" + example = { + "endpoint": { + "endpoint": "https://example.com/endpoint", + "routingKeys": ["a-routing-key"], + "types": ["DIDComm", "did-communication", "endpoint"], + } + } + + ledger.get_all_endpoints_for_did = async_mock.CoroutineMock( + return_value=example + ) + assert await resolver.resolve(profile, TEST_DID0) From deffe04d7b019630ed103314a80b907928f52f58 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Tue, 19 Jul 2022 11:33:14 -0500 Subject: [PATCH 343/872] fix: indy sdk profile finalizer and test warnings Signed-off-by: Daniel Bluhm --- .../indy/sdk/tests/test_issuer.py | 3 +- .../tests/test_manager_provider.py | 19 +++++----- aries_cloudagent/ledger/tests/test_indy.py | 9 ++--- aries_cloudagent/ledger/tests/test_routes.py | 2 -- .../storage/tests/test_indy_storage.py | 35 +++++++++++-------- .../vc_holder/tests/test_indy_vc_holder.py | 25 +++++++------ .../wallet/tests/test_indy_wallet.py | 28 +++++++-------- 7 files changed, 66 insertions(+), 55 deletions(-) diff --git a/aries_cloudagent/indy/sdk/tests/test_issuer.py b/aries_cloudagent/indy/sdk/tests/test_issuer.py index 0378044314..a1b76509a0 100644 --- a/aries_cloudagent/indy/sdk/tests/test_issuer.py +++ b/aries_cloudagent/indy/sdk/tests/test_issuer.py @@ -51,7 +51,8 @@ async def setUp(self): "name": "test-wallet", } ).create_wallet() - self.profile = IndySdkProfile(self.wallet, self.context) + with async_mock.patch.object(IndySdkProfile, "_make_finalizer"): + self.profile = IndySdkProfile(self.wallet, self.context) self.issuer = test_module.IndySdkIssuer(self.profile) async def tearDown(self): diff --git a/aries_cloudagent/ledger/multiple_ledger/tests/test_manager_provider.py b/aries_cloudagent/ledger/multiple_ledger/tests/test_manager_provider.py index bf876a7a51..14c75c514b 100644 --- a/aries_cloudagent/ledger/multiple_ledger/tests/test_manager_provider.py +++ b/aries_cloudagent/ledger/multiple_ledger/tests/test_manager_provider.py @@ -66,15 +66,16 @@ async def test_provide_invalid_manager(self): @pytest.mark.indy async def test_provide_indy_manager(self): context = InjectionContext() - profile = IndySdkProfile( - IndyOpenWallet( - config=IndyWalletConfig({"name": "test-profile"}), - created=True, - handle=1, - master_secret_id="master-secret", - ), - context, - ) + with async_mock.patch.object(IndySdkProfile, "_make_finalizer"): + profile = IndySdkProfile( + IndyOpenWallet( + config=IndyWalletConfig({"name": "test-profile"}), + created=True, + handle=1, + master_secret_id="master-secret", + ), + context, + ) context.injector.bind_instance( BaseLedger, IndySdkLedger(IndySdkLedgerPool("name"), profile) ) diff --git a/aries_cloudagent/ledger/tests/test_indy.py b/aries_cloudagent/ledger/tests/test_indy.py index c558e1c332..effa67cac2 100644 --- a/aries_cloudagent/ledger/tests/test_indy.py +++ b/aries_cloudagent/ledger/tests/test_indy.py @@ -76,10 +76,11 @@ async def setUp(self): self.test_verkey = "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx" context = InjectionContext() context.injector.bind_instance(IndySdkLedgerPool, IndySdkLedgerPool("name")) - self.profile = IndySdkProfile( - async_mock.CoroutineMock(), - context, - ) + with async_mock.patch.object(IndySdkProfile, "_make_finalizer"): + self.profile = IndySdkProfile( + async_mock.CoroutineMock(), + context, + ) self.session = await self.profile.session() @async_mock.patch("indy.pool.create_pool_ledger_config") diff --git a/aries_cloudagent/ledger/tests/test_routes.py b/aries_cloudagent/ledger/tests/test_routes.py index 1310156e45..e3df880112 100644 --- a/aries_cloudagent/ledger/tests/test_routes.py +++ b/aries_cloudagent/ledger/tests/test_routes.py @@ -1,7 +1,6 @@ from asynctest import mock as async_mock, TestCase as AsyncTestCase from typing import Tuple -from ...admin.request_context import AdminRequestContext from ...core.in_memory import InMemoryProfile from ...ledger.base import BaseLedger from ...ledger.endpoint_type import EndpointType @@ -10,7 +9,6 @@ ) from ...ledger.multiple_ledger.base_manager import ( BaseMultipleLedgerManager, - MultipleLedgerManagerError, ) from ...multitenant.base import BaseMultitenantManager from ...multitenant.manager import MultitenantManager diff --git a/aries_cloudagent/storage/tests/test_indy_storage.py b/aries_cloudagent/storage/tests/test_indy_storage.py index 1bb9a17b84..ff1092cf61 100644 --- a/aries_cloudagent/storage/tests/test_indy_storage.py +++ b/aries_cloudagent/storage/tests/test_indy_storage.py @@ -12,7 +12,7 @@ from asynctest import mock as async_mock from ...config.injection_context import InjectionContext -from ...indy.sdk.profile import IndySdkProfileManager +from ...indy.sdk.profile import IndySdkProfileManager, IndySdkProfile from ...storage.base import BaseStorage from ...storage.error import StorageError, StorageSearchError from ...storage.indy import IndySdkStorage @@ -28,16 +28,17 @@ async def make_profile(): key = await IndySdkWallet.generate_wallet_key() context = InjectionContext() context.injector.bind_instance(IndySdkLedgerPool, IndySdkLedgerPool("name")) - return await IndySdkProfileManager().provision( - context, - { - "auto_recreate": True, - "auto_remove": True, - "name": "test-wallet", - "key": key, - "key_derivation_method": "RAW", # much slower tests with argon-hashed keys - }, - ) + with async_mock.patch.object(IndySdkProfile, "_make_finalizer"): + return await IndySdkProfileManager().provision( + context, + { + "auto_recreate": True, + "auto_remove": True, + "name": "test-wallet", + "key": key, + "key_derivation_method": "RAW", # much slower tests with argon-hashed keys + }, + ) @pytest.fixture() @@ -75,7 +76,9 @@ async def test_record(self): indy.wallet, "close_wallet", async_mock.CoroutineMock() ) as mock_close, async_mock.patch.object( indy.wallet, "delete_wallet", async_mock.CoroutineMock() - ) as mock_delete: + ) as mock_delete, async_mock.patch.object( + IndySdkProfile, "_make_finalizer" + ): config = { "auto_recreate": True, "auto_remove": True, @@ -244,7 +247,9 @@ async def test_storage_search_x(self): indy.wallet, "close_wallet", async_mock.CoroutineMock() ) as mock_close, async_mock.patch.object( indy.wallet, "delete_wallet", async_mock.CoroutineMock() - ) as mock_delete: + ) as mock_delete, async_mock.patch.object( + IndySdkProfile, "_make_finalizer" + ): context = InjectionContext() context.injector.bind_instance(IndySdkLedgerPool, IndySdkLedgerPool("name")) fake_profile = await IndySdkProfileManager().provision( @@ -322,7 +327,9 @@ async def test_storage_del_close(self): indy.wallet, "close_wallet", async_mock.CoroutineMock() ) as mock_close, async_mock.patch.object( indy.wallet, "delete_wallet", async_mock.CoroutineMock() - ) as mock_delete: + ) as mock_delete, async_mock.patch.object( + IndySdkProfile, "_make_finalizer" + ): context = InjectionContext() context.injector.bind_instance(IndySdkLedgerPool, IndySdkLedgerPool("name")) fake_profile = await IndySdkProfileManager().provision( diff --git a/aries_cloudagent/storage/vc_holder/tests/test_indy_vc_holder.py b/aries_cloudagent/storage/vc_holder/tests/test_indy_vc_holder.py index 401b0c8750..fa092511b4 100644 --- a/aries_cloudagent/storage/vc_holder/tests/test_indy_vc_holder.py +++ b/aries_cloudagent/storage/vc_holder/tests/test_indy_vc_holder.py @@ -1,8 +1,9 @@ import pytest +from asynctest import mock as async_mock from ....config.injection_context import InjectionContext -from ....indy.sdk.profile import IndySdkProfileManager +from ....indy.sdk.profile import IndySdkProfileManager, IndySdkProfile from ....ledger.indy import IndySdkLedgerPool from ....wallet.indy import IndySdkWallet @@ -25,16 +26,18 @@ async def make_profile(): key = await IndySdkWallet.generate_wallet_key() context = InjectionContext() context.injector.bind_instance(IndySdkLedgerPool, IndySdkLedgerPool("name")) - return await IndySdkProfileManager().provision( - context, - { - "auto_recreate": True, - "auto_remove": True, - "name": "test-wallet", - "key": key, - "key_derivation_method": "RAW", # much slower tests with argon-hashed keys - }, - ) + + with async_mock.patch.object(IndySdkProfile, "_make_finalizer"): + return await IndySdkProfileManager().provision( + context, + { + "auto_recreate": True, + "auto_remove": True, + "name": "test-wallet", + "key": key, + "key_derivation_method": "RAW", # much slower tests with argon-hashed keys + }, + ) @pytest.fixture() diff --git a/aries_cloudagent/wallet/tests/test_indy_wallet.py b/aries_cloudagent/wallet/tests/test_indy_wallet.py index b2e69245e9..e53551161e 100644 --- a/aries_cloudagent/wallet/tests/test_indy_wallet.py +++ b/aries_cloudagent/wallet/tests/test_indy_wallet.py @@ -41,22 +41,22 @@ async def wallet(): key = await IndySdkWallet.generate_wallet_key() context = InjectionContext() context.injector.bind_instance(IndySdkLedgerPool, IndySdkLedgerPool("name")) - profile = cast( - IndySdkProfile, - await IndySdkProfileManager().provision( - context, - { - "auto_recreate": True, - "auto_remove": True, - "name": "test-wallet", - "key": key, - "key_derivation_method": "RAW", # much slower tests with argon-hashed keys - }, - ), - ) + with async_mock.patch.object(IndySdkProfile, "_make_finalizer"): + profile = cast( + IndySdkProfile, + await IndySdkProfileManager().provision( + context, + { + "auto_recreate": True, + "auto_remove": True, + "name": "test-wallet", + "key": key, + "key_derivation_method": "RAW", # much slower tests with argon-hashed keys + }, + ), + ) async with profile.session() as session: yield session.inject(BaseWallet) - profile._finalizer() await profile.close() From 692ec33d5cfc25b0304f658b32b63354ce935f55 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Tue, 19 Jul 2022 12:15:34 -0500 Subject: [PATCH 344/872] test: indy resolver endpoint url missing Signed-off-by: Daniel Bluhm --- aries_cloudagent/resolver/default/indy.py | 2 +- .../resolver/default/tests/test_indy.py | 26 ++++++++++++++++--- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/aries_cloudagent/resolver/default/indy.py b/aries_cloudagent/resolver/default/indy.py index 7cac98daf4..3d65d03a08 100644 --- a/aries_cloudagent/resolver/default/indy.py +++ b/aries_cloudagent/resolver/default/indy.py @@ -88,7 +88,7 @@ def _add_endpoint_as_map( ident="did-communication", type_=self.SERVICE_TYPE_DID_COMMUNICATION, service_endpoint=endpoint_url, - priority=0, + priority=1, routing_keys=routing_keys, recipient_keys=[recipient_key.id], accept=["didcomm/aip2;env=rfc19"], diff --git a/aries_cloudagent/resolver/default/tests/test_indy.py b/aries_cloudagent/resolver/default/tests/test_indy.py index ee1bc8311e..b53c06d7c7 100644 --- a/aries_cloudagent/resolver/default/tests/test_indy.py +++ b/aries_cloudagent/resolver/default/tests/test_indy.py @@ -16,7 +16,6 @@ from ....multitenant.manager import MultitenantManager from ...base import DIDNotFound, ResolverError -from .. import indy as test_module from ..indy import IndyDIDResolver # pylint: disable=W0621 @@ -33,8 +32,11 @@ def resolver(): def ledger(): """Ledger fixture.""" ledger = async_mock.MagicMock(spec=BaseLedger) - ledger.get_endpoint_for_did = async_mock.CoroutineMock( - return_value="https://github.com/" + ledger.get_all_endpoints_for_did = async_mock.CoroutineMock( + return_value={ + "endpoint": "https://github.com/", + "profile": "https://example.com/profile", + } ) ledger.get_key_for_did = async_mock.CoroutineMock(return_value="key") yield ledger @@ -125,3 +127,21 @@ async def test_supports_updated_did_sov_rules( return_value=example ) assert await resolver.resolve(profile, TEST_DID0) + + @pytest.mark.asyncio + async def test_supports_updated_did_sov_rules_x_no_endpoint_url( + self, resolver: IndyDIDResolver, ledger: BaseLedger, profile: Profile + ): + """Test that new attrib structure is supported.""" + example = { + "endpoint": { + "routingKeys": ["a-routing-key"], + "types": ["DIDComm", "did-communication", "endpoint"], + } + } + + ledger.get_all_endpoints_for_did = async_mock.CoroutineMock( + return_value=example + ) + with pytest.raises(ValueError): + await resolver.resolve(profile, TEST_DID0) From c7e10355193dd1c1b96b5cb69c06a774f84641b0 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Tue, 19 Jul 2022 13:18:12 -0500 Subject: [PATCH 345/872] feat: add universal resolver Signed-off-by: Daniel Bluhm --- aries_cloudagent/config/argparse.py | 34 ++++++ .../resolver/default/universal.py | 105 ++++++++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 aries_cloudagent/resolver/default/universal.py diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index c9ae79f9d8..726daf5eb4 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -620,6 +620,28 @@ def add_arguments(self, parser: ArgumentParser): env_var="ACAPY_READ_ONLY_LEDGER", help="Sets ledger to read-only to prevent updates. Default: false.", ) + parser.add_argument( + "--universal-resolver", + type=str, + nargs="?", + metavar="", + env_var="ACAPY_UNIVERSAL_RESOLVER", + const="DEFAULT", + help="Enable resolution from a universal resolver.", + ) + parser.add_argument( + "--universal-resolver-regex", + type=str, + nargs="+", + metavar="", + env_var="ACAPY_UNIVERSAL_RESOLVER_REGEX", + help=( + "Regex matching DIDs to resolve using the unversal resolver. " + "Multiple can be specified. " + "Defaults to a regex matching all DIDs resolvable by universal " + "resolver instance." + ), + ) def get_settings(self, args: Namespace) -> dict: """Extract general settings.""" @@ -659,6 +681,18 @@ def get_settings(self, args: Namespace) -> dict: if args.read_only_ledger: settings["read_only_ledger"] = True + + if args.universal_resolver_regex and not args.universal_resolver: + raise ArgsParseError( + "--universal-resolver-regex cannot be used without --universal-resolver" + ) + + if args.universal_resolver: + settings["resolver.universal"] = args.universal_resolver + + if args.universal_resolver_regex: + settings["resolver.universal.supported"] = args.universal_resolver_regex + return settings diff --git a/aries_cloudagent/resolver/default/universal.py b/aries_cloudagent/resolver/default/universal.py new file mode 100644 index 0000000000..2942a0d33a --- /dev/null +++ b/aries_cloudagent/resolver/default/universal.py @@ -0,0 +1,105 @@ +"""HTTP Universal DID Resolver.""" + +import logging +import re +from typing import Iterable, Optional, Pattern, Union + +import aiohttp + +from ...config.injection_context import InjectionContext +from ...core.profile import Profile +from ..base import ( + BaseDIDResolver, + DIDNotFound, + ResolverError, + ResolverType, +) + +LOGGER = logging.getLogger(__name__) +DEFAULT_ENDPOINT = "https://dev.uniresolver.io" + + +async def _fetch_resolver_props(endpoint: str) -> dict: + """Retrieve universal resolver properties.""" + async with aiohttp.ClientSession() as session: + async with session.get(f"{endpoint}/1.0/properties/") as resp: + if resp.status >= 200 and resp.status < 400: + return await resp.json() + raise ValueError(await resp.text()) + + +async def _get_supported_did_regex(endpoint: str) -> Pattern: + props = await _fetch_resolver_props(endpoint) + return _compile_supported_did_regex( + driver["http"]["pattern"] for driver in props.values() + ) + + +def _compile_supported_did_regex(patterns: Iterable[Union[str, Pattern]]): + """Create regex from list of regex.""" + return re.compile( + "(?:" + + "|".join( + [ + pattern.pattern if isinstance(pattern, Pattern) else pattern + for pattern in patterns + ] + ) + + ")" + ) + + +class UniversalResolver(BaseDIDResolver): + """Universal DID Resolver with HTTP bindings.""" + + def __init__( + self, + *, + endpoint: Optional[str] = None, + supported_did_regex: Optional[Pattern] = None, + ): + """Initialize UniversalResolver.""" + super().__init__(ResolverType.NON_NATIVE) + self._endpoint = endpoint + self._supported_did_regex = supported_did_regex + + async def setup(self, context: InjectionContext): + """Preform setup, populate supported method list, configuration.""" + endpoint = context.settings.get_str("resolver.universal") + if endpoint == "DEFAULT" or not endpoint: + endpoint = DEFAULT_ENDPOINT + + supported = context.settings.get("resolver.universal.supported", []) + if supported: + supported_did_regex = _compile_supported_did_regex(supported) + else: + supported_did_regex = await _get_supported_did_regex(endpoint) + + self._endpoint = endpoint + self._supported_did_regex = supported_did_regex + + @property + def supported_did_regex(self) -> Pattern: + """Return supported methods regex.""" + if not self._supported_did_regex: + raise ResolverError("Resolver has not been set up") + + return self._supported_did_regex + + async def _resolve(self, _profile: Profile, did: str) -> dict: + """Resolve DID through remote universal resolver.""" + + async with aiohttp.ClientSession() as session: + async with session.get(f"{self._endpoint}/1.0/identifiers/{did}") as resp: + if resp.status == 200: + doc = await resp.json() + did_doc = doc["didDocument"] + LOGGER.info("Retrieved doc: %s", did_doc) + return did_doc + if resp.status == 404: + raise DIDNotFound(f"{did} not found by {self.__class__.__name__}") + + text = await resp.text() + raise ResolverError( + f"Unexecpted status from universal resolver ({resp.status}): {text}" + ) From 83cea0f461aeb96a35ba6e4573c0de683eb261c3 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Tue, 19 Jul 2022 13:20:09 -0500 Subject: [PATCH 346/872] test: universal resolver Signed-off-by: Daniel Bluhm --- .../resolver/default/tests/test_universal.py | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 aries_cloudagent/resolver/default/tests/test_universal.py diff --git a/aries_cloudagent/resolver/default/tests/test_universal.py b/aries_cloudagent/resolver/default/tests/test_universal.py new file mode 100644 index 0000000000..3cf242470b --- /dev/null +++ b/aries_cloudagent/resolver/default/tests/test_universal.py @@ -0,0 +1,107 @@ +"""Test universal resolver with http bindings.""" + +import re +from typing import Dict, Union + +import pytest +from asynctest import mock as async_mock + +from aries_cloudagent.resolver.base import DIDNotFound, ResolverError +from .. import universal as test_module +from ..universal import UniversalResolver + + +@pytest.fixture +async def resolver(): + """Resolver fixture.""" + yield UniversalResolver( + endpoint="https://example.com", supported_did_regex=re.compile("^did:sov:.*$") + ) + + +@pytest.fixture +def profile(): + """Profile fixture.""" + yield async_mock.MagicMock() + + +class MockResponse: + """Mock http response.""" + + def __init__(self, status: int, body: Union[str, Dict]): + self.status = status + self.body = body + + async def json(self): + return self.body + + async def text(self): + return self.body + + async def __aenter__(self): + """For use as async context.""" + return self + + async def __aexit__(self, err_type, err_value, err_exc): + """For use as async context.""" + + +class MockClientSession: + """Mock client session.""" + + def __init__(self, response: MockResponse = None): + self.response = response + + def __call__(self): + return self + + async def __aenter__(self): + """For use as async context.""" + return self + + async def __aexit__(self, err_type, err_value, err_exc): + """For use as async context.""" + + def get(self, endpoint): + """Return response.""" + return self.response + + +@pytest.fixture +def mock_client_session(): + temp = test_module.aiohttp.ClientSession + session = MockClientSession() + test_module.aiohttp.ClientSession = session + yield session + test_module.aiohttp.ClientSession = temp + + +@pytest.mark.asyncio +async def test_resolve(profile, resolver, mock_client_session): + mock_client_session.response = MockResponse( + 200, + { + "didDocument": { + "id": "did:example:123", + "@context": "https://www.w3.org/ns/did/v1", + } + }, + ) + doc = await resolver.resolve(profile, "did:sov:WRfXPg8dantKVubE3HX8pw") + assert doc.get("id") == "did:example:123" + + +@pytest.mark.asyncio +async def test_resolve_not_found(profile, resolver, mock_client_session): + mock_client_session.response = MockResponse(404, "Not found") + with pytest.raises(DIDNotFound): + await resolver.resolve(profile, "did:sov:WRfXPg8dantKVubE3HX8pw") + + +@pytest.mark.asyncio +async def test_resolve_unexpeceted_status(profile, resolver, mock_client_session): + mock_client_session.response = MockResponse( + 500, "Server failed to complete request" + ) + with pytest.raises(ResolverError): + await resolver.resolve(profile, "did:sov:WRfXPg8dantKVubE3HX8pw") From f10e8dcb72eacd9b956efc0086640296673769db Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Tue, 19 Jul 2022 13:22:24 -0500 Subject: [PATCH 347/872] feat: load universal resolver when appropriate Signed-off-by: Daniel Bluhm --- aries_cloudagent/resolver/__init__.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/aries_cloudagent/resolver/__init__.py b/aries_cloudagent/resolver/__init__.py index d7dceffb03..51bef53657 100644 --- a/aries_cloudagent/resolver/__init__.py +++ b/aries_cloudagent/resolver/__init__.py @@ -37,3 +37,10 @@ async def setup(context: InjectionContext): ).provide(context.settings, context.injector) await web_resolver.setup(context) registry.register(web_resolver) + + if context.settings.get("resolver.universal"): + universal_resolver = ClassProvider( + "aries_cloudagent.resolver.default.universal.UniversalResolver" + ).provide(context.settings, context.injector) + await universal_resolver.setup(context) + registry.register(universal_resolver) From 240ec99677224299cafbf45d282b64f108f52190 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Tue, 19 Jul 2022 13:29:04 -0500 Subject: [PATCH 348/872] fix: minor import cleanup Signed-off-by: Daniel Bluhm --- aries_cloudagent/resolver/default/tests/test_universal.py | 4 ++-- aries_cloudagent/resolver/default/universal.py | 7 +------ 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/aries_cloudagent/resolver/default/tests/test_universal.py b/aries_cloudagent/resolver/default/tests/test_universal.py index 3cf242470b..5248e62521 100644 --- a/aries_cloudagent/resolver/default/tests/test_universal.py +++ b/aries_cloudagent/resolver/default/tests/test_universal.py @@ -3,11 +3,11 @@ import re from typing import Dict, Union -import pytest from asynctest import mock as async_mock +import pytest -from aries_cloudagent.resolver.base import DIDNotFound, ResolverError from .. import universal as test_module +from ...base import DIDNotFound, ResolverError from ..universal import UniversalResolver diff --git a/aries_cloudagent/resolver/default/universal.py b/aries_cloudagent/resolver/default/universal.py index 2942a0d33a..8fe94fba30 100644 --- a/aries_cloudagent/resolver/default/universal.py +++ b/aries_cloudagent/resolver/default/universal.py @@ -8,12 +8,7 @@ from ...config.injection_context import InjectionContext from ...core.profile import Profile -from ..base import ( - BaseDIDResolver, - DIDNotFound, - ResolverError, - ResolverType, -) +from ..base import BaseDIDResolver, DIDNotFound, ResolverError, ResolverType LOGGER = logging.getLogger(__name__) DEFAULT_ENDPOINT = "https://dev.uniresolver.io" From 0830d18fa4171ea4ac3bc6b0f7ae50ac27984cb7 Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Wed, 20 Jul 2022 11:39:39 -0700 Subject: [PATCH 349/872] move create_and_send_schema, create_and_send_credential_definition implementation to base class, fixes indentation error in indy_vdr method; cleanups Signed-off-by: Andrew Whitehead --- actions/run-integration-tests/action.yml | 5 +- aries_cloudagent/indy/credx/issuer.py | 2 +- aries_cloudagent/ledger/base.py | 248 +++++++++- aries_cloudagent/ledger/indy.py | 432 +++++------------- aries_cloudagent/ledger/indy_vdr.py | 269 ++--------- aries_cloudagent/ledger/tests/test_indy.py | 3 +- .../ledger/tests/test_indy_vdr.py | 5 +- 7 files changed, 410 insertions(+), 554 deletions(-) diff --git a/actions/run-integration-tests/action.yml b/actions/run-integration-tests/action.yml index 474f348ac2..61784bb6ba 100644 --- a/actions/run-integration-tests/action.yml +++ b/actions/run-integration-tests/action.yml @@ -20,9 +20,12 @@ runs: - name: run-integration-tests-acapy # to run with external ledger and tails server run as follows (and remove the ledger and tails actions from the workflow): # run: LEDGER_URL=http://test.bcovrin.vonx.io PUBLIC_TAILS_URL=https://tails.vonx.io ./run_bdd ${{ inputs.TEST_SCOPE }} - run: LEDGER_URL=${{inputs.IN_LEDGER_URL}} PUBLIC_TAILS_URL=${{inputs.IN_PUBLIC_TAILS_URL}} ./run_bdd ${{ inputs.TEST_SCOPE }} + run: ./run_bdd ${{ inputs.TEST_SCOPE }} shell: bash env: + LEDGER_URL: ${{ inputs.IN_LEDGER_URL }} + PUBLIC_TAILS_URL: ${{ inputs.IN_PUBLIC_TAILS_URL }} + LOG_LEVEL: warning NO_TTY: "1" working-directory: acapy/demo branding: diff --git a/aries_cloudagent/indy/credx/issuer.py b/aries_cloudagent/indy/credx/issuer.py index b0108fedbc..cc996ae3f6 100644 --- a/aries_cloudagent/indy/credx/issuer.py +++ b/aries_cloudagent/indy/credx/issuer.py @@ -109,7 +109,7 @@ async def credential_definition_in_wallet( async with self._profile.session() as session: return ( await session.handle.fetch( - CATEGORY_CRED_DEF_KEY_PROOF, credential_definition_id + CATEGORY_CRED_DEF_PRIVATE, credential_definition_id ) ) is not None except AskarError as err: diff --git a/aries_cloudagent/ledger/base.py b/aries_cloudagent/ledger/base.py index b916def364..451f0ffc41 100644 --- a/aries_cloudagent/ledger/base.py +++ b/aries_cloudagent/ledger/base.py @@ -1,5 +1,7 @@ """Ledger base class.""" +import json +import logging import re from abc import ABC, abstractmethod, ABCMeta @@ -7,12 +9,16 @@ from hashlib import sha256 from typing import Sequence, Tuple, Union -from ..indy.issuer import IndyIssuer +from ..indy.issuer import DEFAULT_CRED_DEF_TAG, IndyIssuer, IndyIssuerError from ..utils import sentinel from ..wallet.did_info import DIDInfo +from .error import BadLedgerRequestError, LedgerError, LedgerTransactionError + from .endpoint_type import EndpointType +LOGGER = logging.getLogger(__name__) + class BaseLedger(ABC, metaclass=ABCMeta): """Base class for ledger.""" @@ -133,6 +139,10 @@ def did_to_nym(self, did: str) -> str: if did: return re.sub(r"^did:\w+:", "", did) + @abstractmethod + async def get_wallet_public_did(self) -> DIDInfo: + """Fetch the public DID from the wallet.""" + @abstractmethod async def get_txn_author_agreement(self, reload: bool = False): """Get the current transaction author agreement, fetching it if necessary.""" @@ -171,12 +181,60 @@ async def txn_submit( self, request_json: str, sign: bool, - taa_accept: bool, + taa_accept: bool = None, sign_did: DIDInfo = sentinel, + write_ledger: bool = True, ) -> str: """Write the provided (signed and possibly endorsed) transaction to the ledger.""" @abstractmethod + async def fetch_schema_by_id(self, schema_id: str) -> dict: + """ + Get schema from ledger. + + Args: + schema_id: The schema id (or stringified sequence number) to retrieve + + Returns: + Indy schema dict + + """ + + @abstractmethod + async def fetch_schema_by_seq_no(self, seq_no: int) -> dict: + """ + Fetch a schema by its sequence number. + + Args: + seq_no: schema ledger sequence number + + Returns: + Indy schema dict + + """ + + async def check_existing_schema( + self, + public_did: str, + schema_name: str, + schema_version: str, + attribute_names: Sequence[str], + ) -> Tuple[str, dict]: + """Check if a schema has already been published.""" + fetch_schema_id = f"{public_did}:2:{schema_name}:{schema_version}" + schema = await self.fetch_schema_by_id(fetch_schema_id) + if schema: + fetched_attrs = schema["attrNames"].copy() + fetched_attrs.sort() + cmp_attrs = list(attribute_names) + cmp_attrs.sort() + if fetched_attrs != cmp_attrs: + raise LedgerTransactionError( + "Schema already exists on ledger, but attributes do not match: " + + f"{schema_name}:{schema_version} {fetched_attrs} != {cmp_attrs}" + ) + return fetch_schema_id, schema + async def create_and_send_schema( self, issuer: IndyIssuer, @@ -197,6 +255,92 @@ async def create_and_send_schema( """ + public_info = await self.get_wallet_public_did() + if not public_info: + raise BadLedgerRequestError("Cannot publish schema without a public DID") + + schema_info = await self.check_existing_schema( + public_info.did, schema_name, schema_version, attribute_names + ) + if schema_info: + LOGGER.warning("Schema already exists on ledger. Returning details.") + schema_id, schema_def = schema_info + else: + if self.read_only: + raise LedgerError( + "Error cannot write schema when ledger is in read only mode" + ) + + try: + schema_id, schema_json = await issuer.create_schema( + public_info.did, + schema_name, + schema_version, + attribute_names, + ) + except IndyIssuerError as err: + raise LedgerError(err.message) from err + schema_def = json.loads(schema_json) + + schema_req = await self._create_schema_request( + public_info, + schema_json, + write_ledger=write_ledger, + endorser_did=endorser_did, + ) + + try: + resp = await self.txn_submit( + schema_req, + sign=True, + sign_did=public_info, + write_ledger=write_ledger, + ) + + if not write_ledger: + return schema_id, {"signed_txn": resp} + + try: + # parse sequence number out of response + seq_no = json.loads(resp)["result"]["txnMetadata"]["seqNo"] + schema_def["seqNo"] = seq_no + except KeyError as err: + raise LedgerError( + "Failed to parse schema sequence number from ledger response" + ) from err + except LedgerTransactionError as e: + # Identify possible duplicate schema errors on indy-node < 1.9 and > 1.9 + if ( + "can have one and only one SCHEMA with name" in e.message + or "UnauthorizedClientRequest" in e.message + ): + # handle potential race condition if multiple agents are publishing + # the same schema simultaneously + schema_info = await self.check_existing_schema( + public_info.did, schema_name, schema_version, attribute_names + ) + if schema_info: + LOGGER.warning( + "Schema already exists on ledger. Returning details." + " Error: %s", + e, + ) + schema_id, schema_def = schema_info + else: + raise + + return schema_id, schema_def + + @abstractmethod + async def _create_schema_request( + self, + public_info: DIDInfo, + schema_json: str, + write_ledger: bool = True, + endorser_did: str = None, + ): + """Create the ledger request for publishing a schema.""" + @abstractmethod async def get_revoc_reg_def(self, revoc_reg_id: str) -> dict: """Look up a revocation registry definition by ID.""" @@ -223,7 +367,6 @@ async def send_revoc_reg_entry( ): """Publish a revocation registry entry to the ledger.""" - @abstractmethod async def create_and_send_credential_definition( self, issuer: IndyIssuer, @@ -248,6 +391,105 @@ async def create_and_send_credential_definition( Tuple with cred def id, cred def structure, and whether it's novel """ + public_info = await self.get_wallet_public_did() + if not public_info: + raise BadLedgerRequestError( + "Cannot publish credential definition without a public DID" + ) + + schema = await self.get_schema(schema_id) + if not schema: + raise LedgerError(f"Ledger {self.pool_name} has no schema {schema_id}") + + novel = False + + # check if cred def is on ledger already + for test_tag in [tag] if tag else ["tag", DEFAULT_CRED_DEF_TAG]: + credential_definition_id = issuer.make_credential_definition_id( + public_info.did, schema, signature_type, test_tag + ) + ledger_cred_def = await self.fetch_credential_definition( + credential_definition_id + ) + if ledger_cred_def: + LOGGER.warning( + "Credential definition %s already exists on ledger %s", + credential_definition_id, + self.pool_name, + ) + + try: + if not await issuer.credential_definition_in_wallet( + credential_definition_id + ): + raise LedgerError( + f"Credential definition {credential_definition_id} is on " + f"ledger {self.pool_name} but not in wallet " + f"{self.profile.name}" + ) + except IndyIssuerError as err: + raise LedgerError(err.message) from err + + credential_definition_json = json.dumps(ledger_cred_def) + break + else: # no such cred def on ledger + try: + if await issuer.credential_definition_in_wallet( + credential_definition_id + ): + raise LedgerError( + f"Credential definition {credential_definition_id} is in " + f"wallet {self.profile.name} but not on ledger " + f"{self.pool.name}" + ) + except IndyIssuerError as err: + raise LedgerError(err.message) from err + + # Cred def is neither on ledger nor in wallet: create and send it + novel = True + try: + ( + credential_definition_id, + credential_definition_json, + ) = await issuer.create_and_store_credential_definition( + public_info.did, + schema, + signature_type, + tag, + support_revocation, + ) + except IndyIssuerError as err: + raise LedgerError(err.message) from err + + if self.read_only: + raise LedgerError( + "Error cannot write cred def when ledger is in read only mode" + ) + + cred_def_req = await self._create_credential_definition_request( + public_info, + credential_definition_json, + write_ledger=write_ledger, + endorser_did=endorser_did, + ) + + resp = await self.txn_submit( + cred_def_req, True, sign_did=public_info, write_ledger=write_ledger + ) + if not write_ledger: + return (credential_definition_id, {"signed_txn": resp}, novel) + + return (credential_definition_id, json.loads(credential_definition_json), novel) + + @abstractmethod + async def _create_credential_definition_request( + self, + public_info: DIDInfo, + credential_definition_json: str, + write_ledger: bool = True, + endorser_did: str = None, + ): + """Create the ledger request for publishing a credential definition.""" @abstractmethod async def get_credential_definition(self, credential_definition_id: str) -> dict: diff --git a/aries_cloudagent/ledger/indy.py b/aries_cloudagent/ledger/indy.py index 96ac9e0e3a..36aaef00c7 100644 --- a/aries_cloudagent/ledger/indy.py +++ b/aries_cloudagent/ledger/indy.py @@ -8,7 +8,7 @@ from io import StringIO from os import path from time import time -from typing import Sequence, Tuple, Optional +from typing import TYPE_CHECKING, Tuple, Optional import indy.ledger import indy.pool @@ -16,8 +16,6 @@ from ..cache.base import BaseCache from ..config.base import BaseInjector, BaseProvider, BaseSettings -from ..core.profile import Profile -from ..indy.issuer import DEFAULT_CRED_DEF_TAG, IndyIssuer, IndyIssuerError from ..indy.sdk.error import IndyErrorHandler from ..storage.base import StorageRecord from ..storage.indy import IndySdkStorage @@ -38,6 +36,9 @@ ) from .util import TAA_ACCEPTED_RECORD_TYPE +if TYPE_CHECKING: + from ..indy.sdk.profile import IndySdkProfile + LOGGER = logging.getLogger(__name__) GENESIS_TRANSACTION_FILE = "indy_genesis_transactions.txt" @@ -207,7 +208,7 @@ async def close(self): """Close the pool ledger.""" if self.opened: exc = None - for attempt in range(3): + for _attempt in range(3): try: await indy.pool.close_pool_ledger(self.handle) except IndyError as err: @@ -266,14 +267,14 @@ class IndySdkLedger(BaseLedger): def __init__( self, pool: IndySdkLedgerPool, - profile: Profile, + profile: "IndySdkProfile", ): """ Initialize an IndySdkLedger instance. Args: pool: The pool instance handling the raw ledger connection - wallet: The IndySdkWallet instance + profile: The IndySdkProfile instance """ self.pool = pool self.profile = profile @@ -331,11 +332,9 @@ async def _endorse( raise BadLedgerRequestError( "Cannot endorse transaction without a public DID" ) - async with self.profile.session() as session: - wallet = session.inject(BaseWallet) - endorsed_request_json = await indy.ledger.multi_sign_request( - wallet.opened.handle, public_info.did, request_json - ) + endorsed_request_json = await indy.ledger.multi_sign_request( + self.profile.wallet.handle, public_info.did, request_json + ) return endorsed_request_json async def _submit( @@ -354,6 +353,7 @@ async def _submit( sign: whether or not to sign the request taa_accept: whether to apply TAA acceptance to the (signed, write) request sign_did: override the signing DID + write_ledger: skip the request submission """ @@ -387,20 +387,18 @@ async def _submit( acceptance["time"], ) ) - async with self.profile.session() as session: - wallet = session.inject(BaseWallet) - if write_ledger: - submit_op = indy.ledger.sign_and_submit_request( - self.pool.handle, - wallet.opened.handle, - sign_did.did, - request_json, - ) - else: - # multi-sign, since we expect this to get endorsed later - submit_op = indy.ledger.multi_sign_request( - wallet.opened.handle, sign_did.did, request_json - ) + if write_ledger: + submit_op = indy.ledger.sign_and_submit_request( + self.pool.handle, + self.profile.wallet.handle, + sign_did.did, + request_json, + ) + else: + # multi-sign, since we expect this to get endorsed later + submit_op = indy.ledger.multi_sign_request( + self.profile.wallet.handle, sign_did.did, request_json + ) else: submit_op = indy.ledger.submit_request(self.pool.handle, request_json) @@ -443,125 +441,36 @@ async def txn_submit( sign: bool = None, taa_accept: bool = None, sign_did: DIDInfo = sentinel, + write_ledger: bool = True, ) -> str: """Submit a signed (and endorsed) transaction to the ledger.""" return await self._submit( - request_json, sign=sign, taa_accept=taa_accept, sign_did=sign_did + request_json, + sign=sign, + taa_accept=taa_accept, + sign_did=sign_did, + write_ledger=write_ledger, ) - async def create_and_send_schema( + async def _create_schema_request( self, - issuer: IndyIssuer, - schema_name: str, - schema_version: str, - attribute_names: Sequence[str], + public_info: DIDInfo, + schema_json: str, write_ledger: bool = True, endorser_did: str = None, - ) -> Tuple[str, dict]: - """ - Send schema to ledger. - - Args: - issuer: The issuer instance creating the schema - schema_name: The schema name - schema_version: The schema version - attribute_names: A list of schema attributes - - """ - - public_info = await self.get_wallet_public_did() - if not public_info: - raise BadLedgerRequestError("Cannot publish schema without a public DID") - - schema_info = await self.check_existing_schema( - public_info.did, schema_name, schema_version, attribute_names - ) - if schema_info: - LOGGER.warning("Schema already exists on ledger. Returning details.") - schema_id, schema_def = schema_info - else: - if self.pool.read_only: - raise LedgerError( - "Error cannot write schema when ledger is in read only mode" - ) - - try: - schema_id, schema_json = await issuer.create_schema( - public_info.did, - schema_name, - schema_version, - attribute_names, - ) - except IndyIssuerError as err: - raise LedgerError(err.message) from err - schema_def = json.loads(schema_json) - - with IndyErrorHandler("Exception building schema request", LedgerError): - request_json = await indy.ledger.build_schema_request( - public_info.did, schema_json - ) - - try: - if endorser_did and not write_ledger: - request_json = await indy.ledger.append_request_endorser( - request_json, endorser_did - ) - resp = await self._submit( - request_json, True, sign_did=public_info, write_ledger=write_ledger - ) - if not write_ledger: - return schema_id, {"signed_txn": resp} - try: - # parse sequence number out of response - seq_no = json.loads(resp)["result"]["txnMetadata"]["seqNo"] - schema_def["seqNo"] = seq_no - except KeyError as err: - raise LedgerError( - "Failed to parse schema sequence number from ledger response" - ) from err - except LedgerTransactionError as e: - # Identify possible duplicate schema errors on indy-node < 1.9 and > 1.9 - if "can have one and only one SCHEMA with name" in getattr( - e, "message", "" - ) or "UnauthorizedClientRequest" in getattr(e, "message", ""): - # handle potential race condition if multiple agents are publishing - # the same schema simultaneously - schema_info = await self.check_existing_schema( - public_info.did, schema_name, schema_version, attribute_names - ) - if schema_info: - LOGGER.warning( - "Schema already exists on ledger. Returning details." - " Error: %s", - e, - ) - schema_id, schema_def = schema_info - else: - raise + ): + """Create the ledger request for publishing a schema.""" + with IndyErrorHandler("Exception building schema request", LedgerError): + request_json = await indy.ledger.build_schema_request( + public_info.did, schema_json + ) - return schema_id, schema_def + if endorser_did and not write_ledger: + request_json = await indy.ledger.append_request_endorser( + request_json, endorser_did + ) - async def check_existing_schema( - self, - public_did: str, - schema_name: str, - schema_version: str, - attribute_names: Sequence[str], - ) -> Tuple[str, dict]: - """Check if a schema has already been published.""" - fetch_schema_id = f"{public_did}:2:{schema_name}:{schema_version}" - schema = await self.fetch_schema_by_id(fetch_schema_id) - if schema: - fetched_attrs = schema["attrNames"].copy() - fetched_attrs.sort() - cmp_attrs = list(attribute_names) - cmp_attrs.sort() - if fetched_attrs != cmp_attrs: - raise LedgerTransactionError( - "Schema already exists on ledger, but attributes do not match: " - + f"{schema_name}:{schema_version} {fetched_attrs} != {cmp_attrs}" - ) - return fetch_schema_id, schema + return request_json async def get_schema(self, schema_id: str) -> dict: """ @@ -622,7 +531,7 @@ async def fetch_schema_by_id(self, schema_id: str) -> dict: return parsed_response - async def fetch_schema_by_seq_no(self, seq_no: int): + async def fetch_schema_by_seq_no(self, seq_no: int) -> dict: """ Fetch a schema by its sequence number. @@ -654,123 +563,25 @@ async def fetch_schema_by_seq_no(self, seq_no: int): f"Could not get schema from ledger for seq no {seq_no}" ) - async def create_and_send_credential_definition( + async def _create_credential_definition_request( self, - issuer: IndyIssuer, - schema_id: str, - signature_type: str = None, - tag: str = None, - support_revocation: bool = False, + public_info: DIDInfo, + credential_definition_json: str, write_ledger: bool = True, endorser_did: str = None, - ) -> Tuple[str, dict, bool]: - """ - Send credential definition to ledger and store relevant key matter in wallet. - - Args: - issuer: The issuer instance to use for credential definition creation - schema_id: The schema id of the schema to create cred def for - signature_type: The signature type to use on the credential definition - tag: Optional tag to distinguish multiple credential definitions - support_revocation: Optional flag to enable revocation for this cred def - - Returns: - Tuple with cred def id, cred def structure, and whether it's novel - - """ - public_info = await self.get_wallet_public_did() - if not public_info: - raise BadLedgerRequestError( - "Cannot publish credential definition without a public DID" - ) - - schema = await self.get_schema(schema_id) - if not schema: - raise LedgerError(f"Ledger {self.pool.name} has no schema {schema_id}") - - novel = False - - # check if cred def is on ledger already - for test_tag in [tag] if tag else ["tag", DEFAULT_CRED_DEF_TAG]: - credential_definition_id = issuer.make_credential_definition_id( - public_info.did, schema, signature_type, test_tag - ) - ledger_cred_def = await self.fetch_credential_definition( - credential_definition_id + ): + """Create the ledger request for publishing a credential definition.""" + with IndyErrorHandler("Exception building cred def request", LedgerError): + request_json = await indy.ledger.build_cred_def_request( + public_info.did, credential_definition_json ) - if ledger_cred_def: - LOGGER.warning( - "Credential definition %s already exists on ledger %s", - credential_definition_id, - self.pool.name, - ) - - try: - async with self.profile.session() as session: - wallet = session.inject(BaseWallet) - if not await issuer.credential_definition_in_wallet( - credential_definition_id - ): - raise LedgerError( - f"Credential definition {credential_definition_id} is on " - f"ledger {self.pool.name} but not in wallet " - f"{wallet.opened.name}" - ) - except IndyIssuerError as err: - raise LedgerError(err.message) from err - credential_definition_json = json.dumps(ledger_cred_def) - break - else: # no such cred def on ledger - try: - async with self.profile.session() as session: - wallet = session.inject(BaseWallet) - if await issuer.credential_definition_in_wallet( - credential_definition_id - ): - raise LedgerError( - f"Credential definition {credential_definition_id} is in " - f"wallet {wallet.opened.name} but not on ledger " - f"{self.pool.name}" - ) - except IndyIssuerError as err: - raise LedgerError(err.message) from err - - # Cred def is neither on ledger nor in wallet: create and send it - novel = True - try: - ( - credential_definition_id, - credential_definition_json, - ) = await issuer.create_and_store_credential_definition( - public_info.did, - schema, - signature_type, - tag, - support_revocation, - ) - except IndyIssuerError as err: - raise LedgerError(err.message) from err - - if self.pool.read_only: - raise LedgerError( - "Error cannot write cred def when ledger is in read only mode" - ) - with IndyErrorHandler("Exception building cred def request", LedgerError): - request_json = await indy.ledger.build_cred_def_request( - public_info.did, credential_definition_json - ) - if endorser_did and not write_ledger: - request_json = await indy.ledger.append_request_endorser( - request_json, endorser_did - ) - resp = await self._submit( - request_json, True, sign_did=public_info, write_ledger=write_ledger + if endorser_did and not write_ledger: + request_json = await indy.ledger.append_request_endorser( + request_json, endorser_did ) - if not write_ledger: - return (credential_definition_id, {"signed_txn": resp}, novel) - return (credential_definition_id, json.loads(credential_definition_json), novel) + return request_json async def get_credential_definition(self, credential_definition_id: str) -> dict: """ @@ -1006,27 +817,26 @@ async def register_nym( ) public_info = await self.get_wallet_public_did() + if not public_info: + raise WalletNotFoundError( + f"Cannot register NYM to ledger: wallet {self.profile.name} " + "has no public DID" + ) + with IndyErrorHandler("Exception building nym request", LedgerError): + request_json = await indy.ledger.build_nym_request( + public_info.did, did, verkey, alias, role + ) + if endorser_did and not write_ledger: + request_json = await indy.ledger.append_request_endorser( + request_json, endorser_did + ) + resp = await self._submit( + request_json, sign=True, sign_did=public_info, write_ledger=write_ledger + ) # let ledger raise on insufficient privilege + if not write_ledger: + return True, {"signed_txn": resp} async with self.profile.session() as session: wallet = session.inject(BaseWallet) - if not public_info: - raise WalletNotFoundError( - f"Cannot register NYM to ledger: wallet {wallet.opened.name} " - "has no public DID" - ) - - with IndyErrorHandler("Exception building nym request", LedgerError): - request_json = await indy.ledger.build_nym_request( - public_info.did, did, verkey, alias, role - ) - if endorser_did and not write_ledger: - request_json = await indy.ledger.append_request_endorser( - request_json, endorser_did - ) - resp = await self._submit( - request_json, sign=True, sign_did=public_info, write_ledger=write_ledger - ) # let ledger raise on insufficient privilege - if not write_ledger: - return True, {"signed_txn": resp} try: did_info = await wallet.get_local_did(did) except WalletNotFoundError: @@ -1034,7 +844,7 @@ async def register_nym( else: metadata = {**did_info.metadata, **DIDPosture.POSTED.metadata} await wallet.replace_local_did_metadata(did, metadata) - return True, None + return True, None async def get_nym_role(self, did: str) -> Role: """ @@ -1093,37 +903,37 @@ async def rotate_public_did_keypair(self, next_seed: str = None) -> None: wallet = session.inject(BaseWallet) verkey = await wallet.rotate_did_keypair_start(public_did, next_seed) - # submit to ledger (retain role and alias) - nym = self.did_to_nym(public_did) - with IndyErrorHandler("Exception building nym request", LedgerError): - request_json = await indy.ledger.build_get_nym_request(public_did, nym) + # submit to ledger (retain role and alias) + nym = self.did_to_nym(public_did) + with IndyErrorHandler("Exception building nym request", LedgerError): + request_json = await indy.ledger.build_get_nym_request(public_did, nym) - response_json = await self._submit(request_json) - data = json.loads((json.loads(response_json))["result"]["data"]) - if not data: - raise BadLedgerRequestError( - f"Ledger has no public DID for wallet {wallet.opened.name}" - ) - seq_no = data["seqNo"] + response_json = await self._submit(request_json) + data = json.loads((json.loads(response_json))["result"]["data"]) + if not data: + raise BadLedgerRequestError( + f"Ledger has no public DID for wallet {self.profile.name}" + ) + seq_no = data["seqNo"] - with IndyErrorHandler("Exception building get-txn request", LedgerError): - txn_req_json = await indy.ledger.build_get_txn_request( - None, None, seq_no - ) + with IndyErrorHandler("Exception building get-txn request", LedgerError): + txn_req_json = await indy.ledger.build_get_txn_request(None, None, seq_no) - txn_resp_json = await self._submit(txn_req_json) - txn_resp = json.loads(txn_resp_json) - txn_resp_data = txn_resp["result"]["data"] - if not txn_resp_data: - raise BadLedgerRequestError( - f"Bad or missing ledger NYM transaction for DID {public_did}" - ) - txn_data_data = txn_resp_data["txn"]["data"] - role_token = Role.get(txn_data_data.get("role")).token() - alias = txn_data_data.get("alias") - await self.register_nym(public_did, verkey, role_token, alias) + txn_resp_json = await self._submit(txn_req_json) + txn_resp = json.loads(txn_resp_json) + txn_resp_data = txn_resp["result"]["data"] + if not txn_resp_data: + raise BadLedgerRequestError( + f"Bad or missing ledger NYM transaction for DID {public_did}" + ) + txn_data_data = txn_resp_data["txn"]["data"] + role_token = Role.get(txn_data_data.get("role")).token() + alias = txn_data_data.get("alias") + await self.register_nym(public_did, verkey, role_token, alias) - # update wallet + # update wallet + async with self.profile.session() as session: + wallet = session.inject(BaseWallet) await wallet.rotate_did_keypair_apply(public_did) async def get_txn_author_agreement(self, reload: bool = False) -> dict: @@ -1162,9 +972,7 @@ async def fetch_txn_author_agreement(self) -> dict: async def get_indy_storage(self) -> IndySdkStorage: """Get an IndySdkStorage instance for the current wallet.""" - async with self.profile.session() as session: - wallet = session.inject(BaseWallet) - return IndySdkStorage(wallet.opened) + return IndySdkStorage(self.profile.wallet) def taa_rough_timestamp(self) -> int: """Get a timestamp accurate to the day. @@ -1193,33 +1001,27 @@ async def accept_txn_author_agreement( ) storage = await self.get_indy_storage() await storage.add_record(record) - async with self.profile.session() as session: - wallet = session.inject(BaseWallet) - if self.pool.cache: - cache_key = ( - TAA_ACCEPTED_RECORD_TYPE - + "::" - + wallet.opened.name - + "::" - + self.pool.name - + "::" - ) - await self.pool.cache.set( - cache_key, acceptance, self.pool.cache_duration - ) - - async def get_latest_txn_author_acceptance(self) -> dict: - """Look up the latest TAA acceptance.""" - async with self.profile.session() as session: - wallet = session.inject(BaseWallet) + if self.pool.cache: cache_key = ( TAA_ACCEPTED_RECORD_TYPE + "::" - + wallet.opened.name + + self.profile.name + "::" + self.pool.name + "::" ) + await self.pool.cache.set(cache_key, acceptance, self.pool.cache_duration) + + async def get_latest_txn_author_acceptance(self) -> dict: + """Look up the latest TAA acceptance.""" + cache_key = ( + TAA_ACCEPTED_RECORD_TYPE + + "::" + + self.profile.name + + "::" + + self.pool.name + + "::" + ) acceptance = self.pool.cache and await self.pool.cache.get(cache_key) if not acceptance: storage = await self.get_indy_storage() diff --git a/aries_cloudagent/ledger/indy_vdr.py b/aries_cloudagent/ledger/indy_vdr.py index 3043f16cc0..cbc350d748 100644 --- a/aries_cloudagent/ledger/indy_vdr.py +++ b/aries_cloudagent/ledger/indy_vdr.py @@ -12,13 +12,12 @@ from io import StringIO from pathlib import Path from time import time -from typing import Sequence, Tuple, Union, Optional +from typing import Tuple, Union, Optional from indy_vdr import ledger, open_pool, Pool, Request, VdrError from ..cache.base import BaseCache from ..core.profile import Profile -from ..indy.issuer import IndyIssuer, IndyIssuerError, DEFAULT_CRED_DEF_TAG from ..storage.base import BaseStorage, StorageRecord from ..utils import sentinel from ..utils.env import storage_path @@ -367,124 +366,23 @@ async def _submit( return request_result - async def create_and_send_schema( + async def _create_schema_request( self, - issuer: IndyIssuer, - schema_name: str, - schema_version: str, - attribute_names: Sequence[str], + public_info: DIDInfo, + schema_json: str, write_ledger: bool = True, endorser_did: str = None, - ) -> Tuple[str, dict]: - """ - Send schema to ledger. - - Args: - issuer: The issuer instance creating the schema - schema_name: The schema name - schema_version: The schema version - attribute_names: A list of schema attributes - - """ - - public_info = await self.get_wallet_public_did() - if not public_info: - raise BadLedgerRequestError("Cannot publish schema without a public DID") - - schema_info = await self.check_existing_schema( - public_info.did, schema_name, schema_version, attribute_names - ) - if schema_info: - LOGGER.warning("Schema already exists on ledger. Returning details.") - schema_id, schema_def = schema_info - else: - if self.read_only: - raise LedgerError( - "Error cannot write schema when ledger is in read only mode" - ) - - try: - schema_id, schema_json = await issuer.create_schema( - public_info.did, - schema_name, - schema_version, - attribute_names, - ) - except IndyIssuerError as err: - raise LedgerError(err.message) from err - schema_def = json.loads(schema_json) - - try: - schema_req = ledger.build_schema_request(public_info.did, schema_json) - except VdrError as err: - raise LedgerError("Exception when building schema request") from err - - if endorser_did and not write_ledger: - schema_req.set_endorser(endorser_did) - - try: - resp = await self._submit( - schema_req, - sign=True, - sign_did=public_info, - write_ledger=write_ledger, - ) - - if not write_ledger: - return schema_id, {"signed_txn": resp} - - try: - # parse sequence number out of response - seq_no = resp["txnMetadata"]["seqNo"] - schema_def["seqNo"] = seq_no - except KeyError as err: - raise LedgerError( - "Failed to parse schema sequence number from ledger response" - ) from err - except LedgerTransactionError as e: - # Identify possible duplicate schema errors on indy-node < 1.9 and > 1.9 - if ( - "can have one and only one SCHEMA with name" in e.message - or "UnauthorizedClientRequest" in e.message - ): - # handle potential race condition if multiple agents are publishing - # the same schema simultaneously - schema_info = await self.check_existing_schema( - public_info.did, schema_name, schema_version, attribute_names - ) - if schema_info: - LOGGER.warning( - "Schema already exists on ledger. Returning details." - " Error: %s", - e, - ) - schema_id, schema_def = schema_info - else: - raise + ): + """Create the ledger request for publishing a schema.""" + try: + schema_req = ledger.build_schema_request(public_info.did, schema_json) + except VdrError as err: + raise LedgerError("Exception when building schema request") from err - return schema_id, schema_def + if endorser_did and not write_ledger: + schema_req.set_endorser(endorser_did) - async def check_existing_schema( - self, - public_did: str, - schema_name: str, - schema_version: str, - attribute_names: Sequence[str], - ) -> Tuple[str, dict]: - """Check if a schema has already been published.""" - fetch_schema_id = f"{public_did}:2:{schema_name}:{schema_version}" - schema = await self.fetch_schema_by_id(fetch_schema_id) - if schema: - fetched_attrs = schema["attrNames"].copy() - fetched_attrs.sort() - cmp_attrs = list(attribute_names) - cmp_attrs.sort() - if fetched_attrs != cmp_attrs: - raise LedgerTransactionError( - "Schema already exists on ledger, but attributes do not match: " - + f"{schema_name}:{schema_version} {fetched_attrs} != {cmp_attrs}" - ) - return fetch_schema_id, schema + return schema_req async def get_schema(self, schema_id: str) -> dict: """ @@ -551,7 +449,7 @@ async def fetch_schema_by_id(self, schema_id: str) -> dict: return schema_data - async def fetch_schema_by_seq_no(self, seq_no: int): + async def fetch_schema_by_seq_no(self, seq_no: int) -> dict: """ Fetch a schema by its sequence number. @@ -585,122 +483,25 @@ async def fetch_schema_by_seq_no(self, seq_no: int): f"Could not get schema from ledger for seq no {seq_no}" ) - async def create_and_send_credential_definition( + async def _create_credential_definition_request( self, - issuer: IndyIssuer, - schema_id: str, - signature_type: str = None, - tag: str = None, - support_revocation: bool = False, + public_info: DIDInfo, + credential_definition_json: str, write_ledger: bool = True, endorser_did: str = None, - ) -> Tuple[str, dict, bool]: - """ - Send credential definition to ledger and store relevant key matter in wallet. - - Args: - issuer: The issuer instance to use for credential definition creation - schema_id: The schema id of the schema to create cred def for - signature_type: The signature type to use on the credential definition - tag: Optional tag to distinguish multiple credential definitions - support_revocation: Optional flag to enable revocation for this cred def - - Returns: - Tuple with cred def id, cred def structure, and whether it's novel - - """ - - public_info = await self.get_wallet_public_did() - if not public_info: - raise BadLedgerRequestError( - "Cannot publish credential definition without a public DID" - ) - - schema = await self.get_schema(schema_id) - if not schema: - raise LedgerError(f"Ledger {self.pool_name} has no schema {schema_id}") - - novel = False - - # check if cred def is on ledger already - for test_tag in [tag] if tag else ["tag", DEFAULT_CRED_DEF_TAG]: - credential_definition_id = issuer.make_credential_definition_id( - public_info.did, schema, signature_type, test_tag - ) - ledger_cred_def = await self.fetch_credential_definition( - credential_definition_id + ): + """Create the ledger request for publishing a credential definition.""" + try: + cred_def_req = ledger.build_cred_def_request( + public_info.did, credential_definition_json ) - if ledger_cred_def: - LOGGER.warning( - "Credential definition %s already exists on ledger %s", - credential_definition_id, - self.pool_name, - ) - - try: - if not await issuer.credential_definition_in_wallet( - credential_definition_id - ): - raise LedgerError( - f"Credential definition {credential_definition_id} is on " - f"ledger {self.pool_name} but not in wallet " - f"{self.profile.name}" - ) - except IndyIssuerError as err: - raise LedgerError(err.message) from err - - credential_definition_json = json.dumps(ledger_cred_def) - break - else: # no such cred def on ledger - try: - if await issuer.credential_definition_in_wallet( - credential_definition_id - ): - raise LedgerError( - f"Credential definition {credential_definition_id} is in " - f"wallet {self.profile.name} but not on ledger {self.pool_name}" - ) - except IndyIssuerError as err: - raise LedgerError(err.message) from err - - # Cred def is neither on ledger nor in wallet: create and send it - novel = True - try: - ( - credential_definition_id, - credential_definition_json, - ) = await issuer.create_and_store_credential_definition( - public_info.did, - schema, - signature_type, - tag, - support_revocation, - ) - except IndyIssuerError as err: - raise LedgerError(err.message) from err - - if self.read_only: - raise LedgerError( - "Error cannot write cred def when ledger is in read only mode" - ) - - try: - cred_def_req = ledger.build_cred_def_request( - public_info.did, credential_definition_json - ) - except VdrError as err: - raise LedgerError("Exception when building cred def request") from err - - if endorser_did and not write_ledger: - cred_def_req.set_endorser(endorser_did) + except VdrError as err: + raise LedgerError("Exception when building cred def request") from err - resp = await self._submit( - cred_def_req, True, sign_did=public_info, write_ledger=write_ledger - ) - if not write_ledger: - return (credential_definition_id, {"signed_txn": resp}, novel) + if endorser_did and not write_ledger: + cred_def_req.set_endorser(endorser_did) - return (credential_definition_id, json.loads(credential_definition_json), novel) + return cred_def_req async def get_credential_definition(self, credential_definition_id: str) -> dict: """ @@ -1368,13 +1169,19 @@ async def txn_submit( self, request_json: str, sign: bool, - taa_accept: bool, + taa_accept: bool = None, sign_did: DIDInfo = sentinel, + write_ledger: bool = True, ) -> str: """Write the provided (signed and possibly endorsed) transaction to the ledger.""" resp = await self._submit( - request_json, sign=sign, taa_accept=taa_accept, sign_did=sign_did + request_json, + sign=sign, + taa_accept=taa_accept, + sign_did=sign_did, + write_ledger=write_ledger, ) - # match the format returned by indy sdk - sdk_resp = {"op": "REPLY", "result": resp} - return json.dumps(sdk_resp) + if write_ledger: + # match the format returned by indy sdk + resp = {"op": "REPLY", "result": resp} + return json.dumps(resp) diff --git a/aries_cloudagent/ledger/tests/test_indy.py b/aries_cloudagent/ledger/tests/test_indy.py index effa67cac2..796ea1e887 100644 --- a/aries_cloudagent/ledger/tests/test_indy.py +++ b/aries_cloudagent/ledger/tests/test_indy.py @@ -593,8 +593,9 @@ async def test_send_schema( mock_submit.assert_called_once_with( mock_build_schema_req.return_value, - True, + sign=True, sign_did=mock_wallet_get_public_did.return_value, + taa_accept=None, write_ledger=True, ) diff --git a/aries_cloudagent/ledger/tests/test_indy_vdr.py b/aries_cloudagent/ledger/tests/test_indy_vdr.py index bf8375f1d6..f6ef98962a 100644 --- a/aries_cloudagent/ledger/tests/test_indy_vdr.py +++ b/aries_cloudagent/ledger/tests/test_indy_vdr.py @@ -237,8 +237,9 @@ async def test_send_schema( endorser_did=test_did.did, ) assert schema_id == issuer.create_schema.return_value[0] - assert signed_txn["signed_txn"].get("endorser") == test_did.did - assert signed_txn["signed_txn"].get("signature") + txn = json.loads(signed_txn["signed_txn"]) + assert txn.get("endorser") == test_did.did + assert txn.get("signature") @pytest.mark.asyncio async def test_send_schema_no_public_did( From 9a305dec4cc54659bc70b233a5b5a08eb2e2fa2e Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Thu, 21 Jul 2022 10:37:58 -0600 Subject: [PATCH 350/872] fix: Switch from using Raw public keys to using DID Keys for Mediation This is work for #1859 Signed-off-by: Colton Wolkins (Indicio work address) --- .../coordinate_mediation/v1_0/messages/inner/keylist_key.py | 4 ++-- .../v1_0/messages/inner/keylist_update_rule.py | 4 ++-- .../v1_0/messages/inner/keylist_updated.py | 4 ++-- .../coordinate_mediation/v1_0/models/mediation_record.py | 4 ++-- .../coordinate_mediation/v1_0/tests/test_mediation_manager.py | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_key.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_key.py index aa7701ff5a..45a1df48c3 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_key.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_key.py @@ -3,7 +3,7 @@ from marshmallow import EXCLUDE, fields from ......messaging.models.base import BaseModel, BaseModelSchema -from ......messaging.valid import INDY_RAW_PUBLIC_KEY +from ......messaging.valid import DID_KEY class KeylistKey(BaseModel): @@ -44,4 +44,4 @@ class Meta: model_class = KeylistKey unknown = EXCLUDE - recipient_key = fields.Str(required=True, **INDY_RAW_PUBLIC_KEY) + recipient_key = fields.Str(required=True, **DID_KEY) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_update_rule.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_update_rule.py index 6ece696537..c4dd529935 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_update_rule.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_update_rule.py @@ -8,7 +8,7 @@ from marshmallow.validate import OneOf from ......messaging.models.base import BaseModel, BaseModelSchema -from ......messaging.valid import INDY_RAW_PUBLIC_KEY +from ......messaging.valid import DID_KEY class KeylistUpdateRule(BaseModel): @@ -45,7 +45,7 @@ class Meta: model_class = KeylistUpdateRule recipient_key = fields.Str( - description="Key to remove or add", required=True, **INDY_RAW_PUBLIC_KEY + description="Key to remove or add", required=True, **DID_KEY ) action = fields.Str( required=True, diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_updated.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_updated.py index ac354e45d1..1fe36c8044 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_updated.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_updated.py @@ -6,7 +6,7 @@ from marshmallow import EXCLUDE, fields from ......messaging.models.base import BaseModel, BaseModelSchema -from ......messaging.valid import INDY_RAW_PUBLIC_KEY +from ......messaging.valid import DID_KEY class KeylistUpdated(BaseModel): @@ -54,6 +54,6 @@ class Meta: model_class = KeylistUpdated unknown = EXCLUDE - recipient_key = fields.Str(required=True, **INDY_RAW_PUBLIC_KEY) + recipient_key = fields.Str(required=True, **DID_KEY) action = fields.Str(required=True) result = fields.Str(required=True) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/models/mediation_record.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/models/mediation_record.py index 0bfda79c7f..f7801a45c5 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/models/mediation_record.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/models/mediation_record.py @@ -6,7 +6,7 @@ from .....core.profile import ProfileSession from .....messaging.models.base_record import BaseRecord, BaseRecordSchema -from .....messaging.valid import INDY_RAW_PUBLIC_KEY +from .....messaging.valid import DID_KEY from .....storage.base import StorageDuplicateError, StorageNotFoundError @@ -172,5 +172,5 @@ class Meta: connection_id = fields.Str(required=True) mediator_terms = fields.List(fields.Str(), required=False) recipient_terms = fields.List(fields.Str(), required=False) - routing_keys = fields.List(fields.Str(**INDY_RAW_PUBLIC_KEY), required=False) + routing_keys = fields.List(fields.Str(**DID_KEY), required=False) endpoint = fields.Str(required=False) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py index f3a03c0a30..788404feb8 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py @@ -28,8 +28,8 @@ TEST_CONN_ID = "conn-id" TEST_THREAD_ID = "thread-id" TEST_ENDPOINT = "https://example.com" -TEST_VERKEY = "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx" -TEST_ROUTE_VERKEY = "9WCgWKUaAJj3VWxxtzvvMQN3AoFxoBtBDo9ntwJnVVCC" +TEST_VERKEY = "did:key:z3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx" +TEST_ROUTE_VERKEY = "did:key:z9WCgWKUaAJj3VWxxtzvvMQN3AoFxoBtBDo9ntwJnVVCC" pytestmark = pytest.mark.asyncio From 495584eef87390746f06d20fac6c0824e637abe2 Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Thu, 21 Jul 2022 11:15:37 -0600 Subject: [PATCH 351/872] fix: Send keys back in did:key format during response Signed-off-by: Colton Wolkins (Indicio work address) --- .../coordinate_mediation/v1_0/messages/mediate_grant.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/mediate_grant.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/mediate_grant.py index b8616090dc..692f3892e8 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/mediate_grant.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/mediate_grant.py @@ -41,7 +41,7 @@ def __init__( """ super(MediationGrant, self).__init__(**kwargs) self.endpoint = endpoint - self.routing_keys = list(routing_keys) if routing_keys else [] + self.routing_keys = list(f"did:key:z{key}" for key in routing_keys) if routing_keys else [] class MediationGrantSchema(AgentMessageSchema): From 9f9e058f4bca178ae3d4f2dcdf8d427a6be4cecb Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Wed, 13 Jul 2022 10:27:47 -0700 Subject: [PATCH 352/872] unit test fixes for python 3.9 Signed-off-by: Andrew Whitehead --- .../admin/tests/test_admin_server.py | 66 ++++---- aries_cloudagent/core/plugin_registry.py | 4 +- aries_cloudagent/core/tests/test_conductor.py | 156 ++++++++---------- .../core/tests/test_dispatcher.py | 14 +- aries_cloudagent/ledger/tests/test_routes.py | 80 ++++----- .../dif/tests/test_pres_exch_handler.py | 60 +++---- aries_cloudagent/wallet/tests/test_routes.py | 90 +++++----- requirements.dev.txt | 2 + setup.cfg | 1 + 9 files changed, 233 insertions(+), 240 deletions(-) diff --git a/aries_cloudagent/admin/tests/test_admin_server.py b/aries_cloudagent/admin/tests/test_admin_server.py index b21c3f05b4..94f3642edc 100644 --- a/aries_cloudagent/admin/tests/test_admin_server.py +++ b/aries_cloudagent/admin/tests/test_admin_server.py @@ -1,19 +1,18 @@ import json + import pytest +import mock as async_mock +from async_case import IsolatedAsyncioTestCase from aiohttp import ClientSession, DummyCookieJar, TCPConnector, web from aiohttp.test_utils import unused_port -from asynctest import TestCase as AsyncTestCase -from asynctest import mock as async_mock - from ...config.default_context import DefaultContextBuilder from ...config.injection_context import InjectionContext from ...core.event_bus import Event from ...core.in_memory import InMemoryProfile from ...core.protocol_registry import ProtocolRegistry from ...core.goal_code_registry import GoalCodeRegistry -from ...transport.outbound.message import OutboundMessage from ...utils.stats import Collector from ...utils.task_queue import TaskQueue @@ -21,19 +20,18 @@ from ..server import AdminServer, AdminSetupError -class TestAdminServer(AsyncTestCase): - async def setUp(self): +class TestAdminServer(IsolatedAsyncioTestCase): + async def asyncSetUp(self): self.message_results = [] self.webhook_results = [] self.port = 0 self.connector = TCPConnector(limit=16, limit_per_host=4) - session_args = {"cookie_jar": DummyCookieJar(), "connector": self.connector} self.client_session = ClientSession( cookie_jar=DummyCookieJar(), connector=self.connector ) - async def tearDown(self): + async def asyncTearDown(self): if self.client_session: await self.client_session.close() self.client_session = None @@ -49,9 +47,9 @@ async def test_debug_middleware(self): method="GET", path_qs="/hello/world?a=1&b=2", match_info={"match": "info"}, - text=async_mock.CoroutineMock(return_value="abc123"), + text=async_mock.AsyncMock(return_value="abc123"), ) - handler = async_mock.CoroutineMock() + handler = async_mock.AsyncMock() await test_module.debug_middleware(request, handler) mock_logger.isEnabledFor.assert_called_once() @@ -69,7 +67,7 @@ async def test_ready_middleware(self): request = async_mock.MagicMock( rel_url="/", app=async_mock.MagicMock(_state={"ready": False}) ) - handler = async_mock.CoroutineMock(return_value="OK") + handler = async_mock.AsyncMock(return_value="OK") with self.assertRaises(test_module.web.HTTPServiceUnavailable): await test_module.ready_middleware(request, handler) @@ -77,28 +75,28 @@ async def test_ready_middleware(self): assert await test_module.ready_middleware(request, handler) == "OK" request.app._state["ready"] = True - handler = async_mock.CoroutineMock( + handler = async_mock.AsyncMock( side_effect=test_module.LedgerConfigError("Bad config") ) with self.assertRaises(test_module.LedgerConfigError): await test_module.ready_middleware(request, handler) request.app._state["ready"] = True - handler = async_mock.CoroutineMock( + handler = async_mock.AsyncMock( side_effect=test_module.web.HTTPFound(location="/api/doc") ) with self.assertRaises(test_module.web.HTTPFound): await test_module.ready_middleware(request, handler) request.app._state["ready"] = True - handler = async_mock.CoroutineMock( + handler = async_mock.AsyncMock( side_effect=test_module.asyncio.CancelledError("Cancelled") ) with self.assertRaises(test_module.asyncio.CancelledError): await test_module.ready_middleware(request, handler) request.app._state["ready"] = True - handler = async_mock.CoroutineMock(side_effect=KeyError("No such thing")) + handler = async_mock.AsyncMock(side_effect=KeyError("No such thing")) with self.assertRaises(KeyError): await test_module.ready_middleware(request, handler) @@ -132,10 +130,10 @@ def get_admin_server( profile, self.outbound_message_router, self.webhook_router, - conductor_stop=async_mock.CoroutineMock(), + conductor_stop=async_mock.AsyncMock(), task_queue=TaskQueue(max_active=4) if task_queue else None, conductor_stats=( - None if task_queue else async_mock.CoroutineMock(return_value={"a": 1}) + None if task_queue else async_mock.AsyncMock(return_value={"a": 1}) ), ) @@ -177,7 +175,7 @@ async def test_start_stop(self): await server.stop() with async_mock.patch.object( - web.TCPSite, "start", async_mock.CoroutineMock() + web.TCPSite, "start", async_mock.AsyncMock() ) as mock_start: mock_start.side_effect = OSError("Failure to launch") with self.assertRaises(AdminSetupError): @@ -227,7 +225,7 @@ async def test_import_routes_multitenant_middleware(self): method="GET", headers={"Authorization": "Bearer ..."}, path="/multitenancy/etc", - text=async_mock.CoroutineMock(return_value="abc123"), + text=async_mock.AsyncMock(return_value="abc123"), ) with self.assertRaises(test_module.web.HTTPUnauthorized): await mt_authz_middle(mock_request, None) @@ -236,7 +234,7 @@ async def test_import_routes_multitenant_middleware(self): method="GET", headers={}, path="/protected/non-multitenancy/non-server", - text=async_mock.CoroutineMock(return_value="abc123"), + text=async_mock.AsyncMock(return_value="abc123"), ) with self.assertRaises(test_module.web.HTTPUnauthorized): await mt_authz_middle(mock_request, None) @@ -245,9 +243,9 @@ async def test_import_routes_multitenant_middleware(self): method="GET", headers={"Authorization": "Bearer ..."}, path="/protected/non-multitenancy/non-server", - text=async_mock.CoroutineMock(return_value="abc123"), + text=async_mock.AsyncMock(return_value="abc123"), ) - mock_handler = async_mock.CoroutineMock() + mock_handler = async_mock.AsyncMock() await mt_authz_middle(mock_request, mock_handler) assert mock_handler.called_once_with(mock_request) @@ -255,9 +253,9 @@ async def test_import_routes_multitenant_middleware(self): method="GET", headers={"Authorization": "Non-bearer ..."}, path="/test", - text=async_mock.CoroutineMock(return_value="abc123"), + text=async_mock.AsyncMock(return_value="abc123"), ) - mock_handler = async_mock.CoroutineMock() + mock_handler = async_mock.AsyncMock() await mt_authz_middle(mock_request, mock_handler) assert mock_handler.called_once_with(mock_request) @@ -268,7 +266,7 @@ async def test_import_routes_multitenant_middleware(self): method="GET", headers={"Authorization": "Non-bearer ..."}, path="/protected/non-multitenancy/non-server", - text=async_mock.CoroutineMock(return_value="abc123"), + text=async_mock.AsyncMock(return_value="abc123"), ) with self.assertRaises(test_module.web.HTTPUnauthorized): await setup_ctx_middle(mock_request, None) @@ -277,12 +275,12 @@ async def test_import_routes_multitenant_middleware(self): method="GET", headers={"Authorization": "Bearer ..."}, path="/protected/non-multitenancy/non-server", - text=async_mock.CoroutineMock(return_value="abc123"), + text=async_mock.AsyncMock(return_value="abc123"), ) with async_mock.patch.object( server.multitenant_manager, "get_profile_for_token", - async_mock.CoroutineMock(), + async_mock.AsyncMock(), ) as mock_get_profile: mock_get_profile.side_effect = [ test_module.MultitenantManagerError("corrupt token"), @@ -295,11 +293,15 @@ async def test_import_routes_multitenant_middleware(self): async def test_register_external_plugin_x(self): context = InjectionContext() context.injector.bind_instance(ProtocolRegistry, ProtocolRegistry()) - with self.assertRaises(ValueError): + context.injector.bind_instance(GoalCodeRegistry, GoalCodeRegistry()) + with self.assertLogs(level="ERROR") as logs: builder = DefaultContextBuilder( - settings={"external_plugins": "aries_cloudagent.nosuchmodule"} + settings={"external_plugins": ["aries_cloudagent.nosuchmodule"]} ) await builder.load_plugins(context) + assert "Module doesn't exist: aries_cloudagent.nosuchmodule" in "\n".join( + logs.output + ) async def test_visit_insecure_mode(self): settings = {"admin.admin_insecure_mode": True, "task_queue": True} @@ -480,9 +482,9 @@ async def test_server_health_state(self): @pytest.fixture async def server(): test_class = TestAdminServer() - await test_class.setUp() + await test_class.asyncSetUp() yield test_class.get_admin_server() - await test_class.tearDown() + await test_class.asyncTearDown() @pytest.mark.asyncio @@ -493,7 +495,7 @@ async def server(): async def test_on_record_event(server, event_topic, webhook_topic): profile = InMemoryProfile.test_profile() with async_mock.patch.object( - server, "send_webhook", async_mock.CoroutineMock() + server, "send_webhook", async_mock.AsyncMock() ) as mock_send_webhook: await server._on_record_event(profile, Event(event_topic, None)) mock_send_webhook.assert_called_once_with(profile, webhook_topic, None) diff --git a/aries_cloudagent/core/plugin_registry.py b/aries_cloudagent/core/plugin_registry.py index d75dd103ed..40ea4fb83f 100644 --- a/aries_cloudagent/core/plugin_registry.py +++ b/aries_cloudagent/core/plugin_registry.py @@ -218,7 +218,7 @@ async def load_protocol_version( ): """Load a particular protocol version.""" protocol_registry = context.inject(ProtocolRegistry) - goal_code_resgistry = context.inject(GoalCodeRegistry) + goal_code_registry = context.inject(GoalCodeRegistry) if hasattr(mod, "MESSAGE_TYPES"): protocol_registry.register_message_types( mod.MESSAGE_TYPES, version_definition=version_definition @@ -227,7 +227,7 @@ async def load_protocol_version( protocol_registry.register_controllers( mod.CONTROLLERS, version_definition=version_definition ) - goal_code_resgistry.register_controllers(mod.CONTROLLERS) + goal_code_registry.register_controllers(mod.CONTROLLERS) async def load_protocols(self, context: InjectionContext, plugin: ModuleType): """For modules that don't implement setup, register protocols manually.""" diff --git a/aries_cloudagent/core/tests/test_conductor.py b/aries_cloudagent/core/tests/test_conductor.py index b65b0494d8..02fbe4ff49 100644 --- a/aries_cloudagent/core/tests/test_conductor.py +++ b/aries_cloudagent/core/tests/test_conductor.py @@ -1,8 +1,7 @@ from io import StringIO -import asynctest -from asynctest import TestCase as AsyncTestCase -from asynctest import mock as async_mock +import mock as async_mock +from async_case import IsolatedAsyncioTestCase from ...admin.base_server import BaseAdminServer from ...config.base_context import ContextBuilder @@ -21,7 +20,6 @@ from ...core.protocol_registry import ProtocolRegistry from ...protocols.coordinate_mediation.mediation_invite_store import ( MediationInviteRecord, - MediationInviteStore, ) from ...protocols.coordinate_mediation.v1_0.models.mediation_record import ( MediationRecord, @@ -106,7 +104,7 @@ async def build_context(self) -> InjectionContext: return context -class TestConductor(AsyncTestCase, Config, TestDIDs): +class TestConductor(IsolatedAsyncioTestCase, Config, TestDIDs): async def test_startup(self): builder: ContextBuilder = StubContextBuilder(self.test_settings) conductor = test_module.Conductor(builder) @@ -120,7 +118,7 @@ async def test_startup(self): ) as mock_logger, async_mock.patch.object( BaseStorage, "find_record", - async_mock.CoroutineMock( + async_mock.AsyncMock( return_value=async_mock.MagicMock(value=f"v{__version__}") ), ): @@ -188,14 +186,14 @@ async def test_startup_no_public_did(self): ) as mock_logger, async_mock.patch.object( BaseStorage, "find_record", - async_mock.CoroutineMock( + async_mock.AsyncMock( return_value=async_mock.MagicMock(value=f"v{__version__}") ), ): mock_outbound_mgr.return_value.registered_transports = {} - mock_outbound_mgr.return_value.enqueue_message = async_mock.CoroutineMock() - mock_outbound_mgr.return_value.start = async_mock.CoroutineMock() - mock_outbound_mgr.return_value.stop = async_mock.CoroutineMock() + mock_outbound_mgr.return_value.enqueue_message = async_mock.AsyncMock() + mock_outbound_mgr.return_value.start = async_mock.AsyncMock() + mock_outbound_mgr.return_value.stop = async_mock.AsyncMock() await conductor.setup() mock_inbound_mgr.return_value.setup.assert_awaited_once() @@ -464,8 +462,8 @@ async def test_handle_nots(self): test_module, "OutboundTransportManager", async_mock.MagicMock() ) as mock_outbound_mgr: mock_outbound_mgr.return_value = async_mock.MagicMock( - setup=async_mock.CoroutineMock(), - enqueue_message=async_mock.CoroutineMock(), + setup=async_mock.AsyncMock(), + enqueue_message=async_mock.AsyncMock(), ) payload = "{}" @@ -485,7 +483,7 @@ async def test_handle_nots(self): conductor.dispatcher, "run_task", async_mock.MagicMock() ) as mock_run_task: mock_conn_mgr.return_value.get_connection_targets = ( - async_mock.CoroutineMock() + async_mock.AsyncMock() ) mock_run_task.side_effect = test_module.ConnectionManagerError() await conductor.queue_outbound(conductor.root_profile, message) @@ -527,7 +525,7 @@ async def test_handle_not_returned_ledger_x(self): with async_mock.patch.object( conductor.dispatcher, "run_task", async_mock.MagicMock() ) as mock_dispatch_run, async_mock.patch.object( - conductor, "queue_outbound", async_mock.CoroutineMock() + conductor, "queue_outbound", async_mock.AsyncMock() ) as mock_queue, async_mock.patch.object( conductor.admin_server, "notify_fatal_error", async_mock.MagicMock() ) as mock_notify: @@ -567,7 +565,7 @@ async def test_queue_outbound_ledger_x(self): ) as mock_dispatch_run, async_mock.patch.object( conductor.admin_server, "notify_fatal_error", async_mock.MagicMock() ) as mock_notify: - conn_mgr.return_value.get_connection_targets = async_mock.CoroutineMock() + conn_mgr.return_value.get_connection_targets = async_mock.AsyncMock() mock_dispatch_run.side_effect = test_module.LedgerConfigError( "No such ledger" ) @@ -613,7 +611,7 @@ async def test_admin(self): ) as admin_stop, async_mock.patch.object( BaseStorage, "find_record", - async_mock.CoroutineMock( + async_mock.AsyncMock( return_value=async_mock.MagicMock(value=f"v{__version__}") ), ): @@ -661,15 +659,15 @@ async def test_admin_startx(self): ) as conn_mgr, async_mock.patch.object( BaseStorage, "find_record", - async_mock.CoroutineMock( + async_mock.AsyncMock( return_value=async_mock.MagicMock(value=f"v{__version__}") ), ): admin_start.side_effect = KeyError("trouble") - oob_mgr.return_value.create_invitation = async_mock.CoroutineMock( + oob_mgr.return_value.create_invitation = async_mock.AsyncMock( side_effect=KeyError("double trouble") ) - conn_mgr.return_value.create_invitation = async_mock.CoroutineMock( + conn_mgr.return_value.create_invitation = async_mock.AsyncMock( side_effect=KeyError("triple trouble") ) await conductor.start() @@ -704,7 +702,7 @@ async def test_start_static(self): ) as mock_mgr, async_mock.patch.object( BaseStorage, "find_record", - async_mock.CoroutineMock( + async_mock.AsyncMock( return_value=async_mock.MagicMock(value=f"v{__version__}") ), ), async_mock.patch.object( @@ -722,7 +720,7 @@ async def test_start_static(self): KeyType.ED25519, ) - mock_mgr.return_value.create_static_connection = async_mock.CoroutineMock() + mock_mgr.return_value.create_static_connection = async_mock.AsyncMock() await conductor.start() mock_mgr.return_value.create_static_connection.assert_awaited_once() @@ -739,14 +737,14 @@ async def test_start_x_in(self): test_module, "OutboundTransportManager", autospec=True ) as mock_outbound_mgr: mock_intx_mgr.return_value = async_mock.MagicMock( - setup=async_mock.CoroutineMock(), - start=async_mock.CoroutineMock(side_effect=KeyError("trouble")), + setup=async_mock.AsyncMock(), + start=async_mock.AsyncMock(side_effect=KeyError("trouble")), ) mock_outbound_mgr.return_value.registered_transports = { "test": async_mock.MagicMock(schemes=["http"]) } await conductor.setup() - mock_mgr.return_value.create_static_connection = async_mock.CoroutineMock() + mock_mgr.return_value.create_static_connection = async_mock.AsyncMock() with self.assertRaises(KeyError): await conductor.start() @@ -761,12 +759,12 @@ async def test_start_x_out_a(self): test_module, "OutboundTransportManager" ) as mock_outx_mgr: mock_outx_mgr.return_value = async_mock.MagicMock( - setup=async_mock.CoroutineMock(), - start=async_mock.CoroutineMock(side_effect=KeyError("trouble")), + setup=async_mock.AsyncMock(), + start=async_mock.AsyncMock(side_effect=KeyError("trouble")), registered_transports={"test": async_mock.MagicMock(schemes=["http"])}, ) await conductor.setup() - mock_mgr.return_value.create_static_connection = async_mock.CoroutineMock() + mock_mgr.return_value.create_static_connection = async_mock.AsyncMock() with self.assertRaises(KeyError): await conductor.start() @@ -781,14 +779,14 @@ async def test_start_x_out_b(self): test_module, "OutboundTransportManager" ) as mock_outx_mgr: mock_outx_mgr.return_value = async_mock.MagicMock( - setup=async_mock.CoroutineMock(), - start=async_mock.CoroutineMock(side_effect=KeyError("trouble")), - stop=async_mock.CoroutineMock(), + setup=async_mock.AsyncMock(), + start=async_mock.AsyncMock(side_effect=KeyError("trouble")), + stop=async_mock.AsyncMock(), registered_transports={}, - enqueue_message=async_mock.CoroutineMock(), + enqueue_message=async_mock.AsyncMock(), ) await conductor.setup() - mock_mgr.return_value.create_static_connection = async_mock.CoroutineMock() + mock_mgr.return_value.create_static_connection = async_mock.AsyncMock() with self.assertRaises(KeyError): await conductor.start() @@ -799,8 +797,9 @@ async def test_dispatch_complete_non_fatal_x(self): message_body = "{}" receipt = MessageReceipt(direct_response_mode="snail mail") message = InboundMessage(message_body, receipt) + exc = KeyError("sample exception") mock_task = async_mock.MagicMock( - exc_info=(KeyError, KeyError("sample exception"), "..."), + exc_info=(type(exc), exc, exc.__traceback__), ident="abc", timing={ "queued": 1234567890, @@ -831,12 +830,9 @@ async def test_dispatch_complete_fatal_x(self): message_body = "{}" receipt = MessageReceipt(direct_response_mode="snail mail") message = InboundMessage(message_body, receipt) + exc = test_module.LedgerTransactionError("Ledger is wobbly") mock_task = async_mock.MagicMock( - exc_info=( - test_module.LedgerTransactionError, - test_module.LedgerTransactionError("Ledger is wobbly"), - "...", - ), + exc_info=(type(exc), exc, exc.__traceback__), ident="abc", timing={ "queued": 1234567890, @@ -876,7 +872,7 @@ async def test_print_invite_connection(self): ) as captured, async_mock.patch.object( BaseStorage, "find_record", - async_mock.CoroutineMock( + async_mock.AsyncMock( return_value=async_mock.MagicMock(value=f"v{__version__}") ), ), async_mock.patch.object( @@ -917,12 +913,12 @@ async def test_clear_default_mediator(self): test_module, "MediationManager", return_value=async_mock.MagicMock( - clear_default_mediator=async_mock.CoroutineMock() + clear_default_mediator=async_mock.AsyncMock() ), ) as mock_mgr, async_mock.patch.object( BaseStorage, "find_record", - async_mock.CoroutineMock( + async_mock.AsyncMock( return_value=async_mock.MagicMock(value=f"v{__version__}") ), ): @@ -947,10 +943,10 @@ async def test_set_default_mediator(self): test_module, "MediationManager", return_value=async_mock.MagicMock( - set_default_mediator_by_id=async_mock.CoroutineMock() + set_default_mediator_by_id=async_mock.AsyncMock() ), ) as mock_mgr, async_mock.patch.object( - MediationRecord, "retrieve_by_id", async_mock.CoroutineMock() + MediationRecord, "retrieve_by_id", async_mock.AsyncMock() ), async_mock.patch.object( test_module, "LOGGER", @@ -962,7 +958,7 @@ async def test_set_default_mediator(self): ), async_mock.patch.object( BaseStorage, "find_record", - async_mock.CoroutineMock( + async_mock.AsyncMock( return_value=async_mock.MagicMock(value=f"v{__version__}") ), ): @@ -986,13 +982,13 @@ async def test_set_default_mediator_x(self): with async_mock.patch.object( MediationRecord, "retrieve_by_id", - async_mock.CoroutineMock(side_effect=Exception()), + async_mock.AsyncMock(side_effect=Exception()), ), async_mock.patch.object( test_module, "LOGGER" ) as mock_logger, async_mock.patch.object( BaseStorage, "find_record", - async_mock.CoroutineMock( + async_mock.AsyncMock( return_value=async_mock.MagicMock(value=f"v{__version__}") ), ): @@ -1064,11 +1060,11 @@ async def test_shutdown_multitenant_profiles(self): multitenant_mgr._profiles.put( "test1", - async_mock.MagicMock(close=async_mock.CoroutineMock()), + async_mock.MagicMock(close=async_mock.AsyncMock()), ) multitenant_mgr._profiles.put( "test2", - async_mock.MagicMock(close=async_mock.CoroutineMock()), + async_mock.MagicMock(close=async_mock.AsyncMock()), ) await conductor.stop() @@ -1084,14 +1080,12 @@ def get_invite_store_mock( used_invite = MediationInviteRecord(invite_string, used=True) return async_mock.MagicMock( - get_mediation_invite_record=async_mock.CoroutineMock( - return_value=unused_invite - ), - mark_default_invite_as_used=async_mock.CoroutineMock(return_value=used_invite), + get_mediation_invite_record=async_mock.AsyncMock(return_value=unused_invite), + mark_default_invite_as_used=async_mock.AsyncMock(return_value=used_invite), ) -class TestConductorMediationSetup(AsyncTestCase, Config): +class TestConductorMediationSetup(IsolatedAsyncioTestCase, Config): """ Test related with setting up mediation from given arguments or stored invitation. """ @@ -1106,12 +1100,12 @@ def __get_mediator_config( return builder - @asynctest.patch.object( + @async_mock.patch.object( test_module, "MediationInviteStore", return_value=get_invite_store_mock("test-invite"), ) - @asynctest.patch.object(test_module.ConnectionInvitation, "from_url") + @async_mock.patch.object(test_module.ConnectionInvitation, "from_url") async def test_mediator_invitation_0160(self, mock_from_url, _): conductor = test_module.Conductor( self.__get_mediator_config("test-invite", True) @@ -1131,17 +1125,17 @@ async def test_mediator_invitation_0160(self, mock_from_url, _): "ConnectionManager", async_mock.MagicMock( return_value=async_mock.MagicMock( - receive_invitation=async_mock.CoroutineMock( + receive_invitation=async_mock.AsyncMock( return_value=mock_conn_record ) ) ), ) as mock_mgr, async_mock.patch.object( - mock_conn_record, "metadata_set", async_mock.CoroutineMock() + mock_conn_record, "metadata_set", async_mock.AsyncMock() ), async_mock.patch.object( BaseStorage, "find_record", - async_mock.CoroutineMock( + async_mock.AsyncMock( return_value=async_mock.MagicMock(value=f"v{__version__}") ), ): @@ -1150,12 +1144,12 @@ async def test_mediator_invitation_0160(self, mock_from_url, _): mock_from_url.assert_called_once_with("test-invite") mock_mgr.return_value.receive_invitation.assert_called_once() - @asynctest.patch.object( + @async_mock.patch.object( test_module, "MediationInviteStore", return_value=get_invite_store_mock("test-invite"), ) - @asynctest.patch.object(test_module.InvitationMessage, "from_url") + @async_mock.patch.object(test_module.InvitationMessage, "from_url") async def test_mediator_invitation_0434(self, mock_from_url, _): conductor = test_module.Conductor( self.__get_mediator_config("test-invite", False) @@ -1181,15 +1175,13 @@ async def test_mediator_invitation_0434(self, mock_from_url, _): "OutOfBandManager", async_mock.MagicMock( return_value=async_mock.MagicMock( - receive_invitation=async_mock.CoroutineMock( - return_value=conn_record - ) + receive_invitation=async_mock.AsyncMock(return_value=conn_record) ) ), ) as mock_mgr, async_mock.patch.object( BaseStorage, "find_record", - async_mock.CoroutineMock( + async_mock.AsyncMock( return_value=async_mock.MagicMock(value=f"v{__version__}") ), ): @@ -1198,8 +1190,8 @@ async def test_mediator_invitation_0434(self, mock_from_url, _): mock_from_url.assert_called_once_with("test-invite") mock_mgr.return_value.receive_invitation.assert_called_once() - @asynctest.patch.object(test_module, "MediationInviteStore") - @asynctest.patch.object(test_module.ConnectionInvitation, "from_url") + @async_mock.patch.object(test_module, "MediationInviteStore") + @async_mock.patch.object(test_module.ConnectionInvitation, "from_url") async def test_mediation_invitation_should_use_stored_invitation( self, patched_from_url, patched_invite_store ): @@ -1227,23 +1219,23 @@ async def test_mediation_invitation_should_use_stored_invitation( patched_invite_store.return_value = mocked_store connection_manager_mock = async_mock.MagicMock( - receive_invitation=async_mock.CoroutineMock(return_value=mock_conn_record) + receive_invitation=async_mock.AsyncMock(return_value=mock_conn_record) ) mock_mediation_manager = async_mock.MagicMock( - clear_default_mediator=async_mock.CoroutineMock() + clear_default_mediator=async_mock.AsyncMock() ) # when with async_mock.patch.object( test_module, "ConnectionManager", return_value=connection_manager_mock ), async_mock.patch.object( - mock_conn_record, "metadata_set", async_mock.CoroutineMock() + mock_conn_record, "metadata_set", async_mock.AsyncMock() ), async_mock.patch.object( test_module, "MediationManager", return_value=mock_mediation_manager ), async_mock.patch.object( BaseStorage, "find_record", - async_mock.CoroutineMock( + async_mock.AsyncMock( return_value=async_mock.MagicMock(value=f"v{__version__}") ), ): @@ -1257,8 +1249,8 @@ async def test_mediation_invitation_should_use_stored_invitation( patched_from_url.assert_called_with(invite_string) mock_mediation_manager.clear_default_mediator.assert_called_once() - @asynctest.patch.object(test_module, "MediationInviteStore") - @asynctest.patch.object(test_module, "ConnectionManager") + @async_mock.patch.object(test_module, "MediationInviteStore") + @async_mock.patch.object(test_module, "ConnectionManager") async def test_mediation_invitation_should_not_create_connection_for_old_invitation( self, patched_connection_manager, patched_invite_store ): @@ -1280,13 +1272,13 @@ async def test_mediation_invitation_should_not_create_connection_for_old_invitat patched_invite_store.return_value = invite_store_mock connection_manager_mock = async_mock.MagicMock( - receive_invitation=async_mock.CoroutineMock() + receive_invitation=async_mock.AsyncMock() ) patched_connection_manager.return_value = connection_manager_mock with async_mock.patch.object( BaseStorage, "find_record", - async_mock.CoroutineMock( + async_mock.AsyncMock( return_value=async_mock.MagicMock(value=f"v{__version__}") ), ): @@ -1300,7 +1292,7 @@ async def test_mediation_invitation_should_not_create_connection_for_old_invitat ) connection_manager_mock.receive_invitation.assert_not_called() - @asynctest.patch.object( + @async_mock.patch.object( test_module, "MediationInviteStore", return_value=get_invite_store_mock("test-invite"), @@ -1326,7 +1318,7 @@ async def test_mediator_invitation_x(self, _): ) as mock_logger, async_mock.patch.object( BaseStorage, "find_record", - async_mock.CoroutineMock( + async_mock.AsyncMock( return_value=async_mock.MagicMock(value=f"v{__version__}") ), ): @@ -1344,9 +1336,9 @@ async def test_setup_ledger_both_multiple_and_base(self): with async_mock.patch.object( test_module, "load_multiple_genesis_transactions_from_config", - async_mock.CoroutineMock(), + async_mock.AsyncMock(), ) as mock_multiple_genesis_load, async_mock.patch.object( - test_module, "get_genesis_transactions", async_mock.CoroutineMock() + test_module, "get_genesis_transactions", async_mock.AsyncMock() ) as mock_genesis_load, async_mock.patch.object( test_module, "OutboundTransportManager", autospec=True ) as mock_outbound_mgr: @@ -1363,7 +1355,7 @@ async def test_setup_ledger_only_base(self): conductor = test_module.Conductor(builder) with async_mock.patch.object( - test_module, "get_genesis_transactions", async_mock.CoroutineMock() + test_module, "get_genesis_transactions", async_mock.AsyncMock() ) as mock_genesis_load, async_mock.patch.object( test_module, "OutboundTransportManager", autospec=True ) as mock_outbound_mgr: @@ -1386,9 +1378,7 @@ async def test_startup_x_version_mismatch(self): ) as mock_logger, async_mock.patch.object( BaseStorage, "find_record", - async_mock.CoroutineMock( - return_value=async_mock.MagicMock(value=f"v0.6.0") - ), + async_mock.AsyncMock(return_value=async_mock.MagicMock(value=f"v0.6.0")), ): mock_outbound_mgr.return_value.registered_transports = { "test": async_mock.MagicMock(schemes=["http"]) @@ -1425,7 +1415,7 @@ async def test_startup_x_no_storage_version(self): ) as mock_logger, async_mock.patch.object( BaseStorage, "find_record", - async_mock.CoroutineMock(side_effect=StorageNotFoundError()), + async_mock.AsyncMock(side_effect=StorageNotFoundError()), ): mock_outbound_mgr.return_value.registered_transports = { "test": async_mock.MagicMock(schemes=["http"]) diff --git a/aries_cloudagent/core/tests/test_dispatcher.py b/aries_cloudagent/core/tests/test_dispatcher.py index 5caef86516..4722ad9530 100644 --- a/aries_cloudagent/core/tests/test_dispatcher.py +++ b/aries_cloudagent/core/tests/test_dispatcher.py @@ -1,8 +1,9 @@ import json +from async_case import IsolatedAsyncioTestCase +import mock as async_mock import pytest -from asynctest import TestCase as AsyncTestCase, mock as async_mock from marshmallow import EXCLUDE from ...config.injection_context import InjectionContext @@ -89,7 +90,7 @@ async def handle(self, context, responder): pass -class TestDispatcher(AsyncTestCase): +class TestDispatcher(IsolatedAsyncioTestCase): async def test_dispatch(self): profile = make_profile() registry = profile.inject(ProtocolRegistry) @@ -112,7 +113,7 @@ async def test_dispatch(self): test_module, "ConnectionManager", autospec=True ) as conn_mgr_mock: conn_mgr_mock.return_value = async_mock.MagicMock( - find_inbound_connection=async_mock.CoroutineMock( + find_inbound_connection=async_mock.AsyncMock( return_value=async_mock.MagicMock(connection_id="dummy") ) ) @@ -364,8 +365,9 @@ async def test_dispatch_log(self): dispatcher = test_module.Dispatcher(profile) await dispatcher.setup() + exc = KeyError("sample exception") mock_task = async_mock.MagicMock( - exc_info=(KeyError, KeyError("sample exception"), "..."), + exc_info=(type(exc), exc, exc.__traceback__), ident="abc", timing={ "queued": 1234567890, @@ -385,7 +387,7 @@ async def test_create_send_outbound(self): message = StubAgentMessage() responder = test_module.DispatcherResponder(context, message, None) outbound_message = await responder.create_outbound(message) - with async_mock.patch.object(responder, "_send", async_mock.CoroutineMock()): + with async_mock.patch.object(responder, "_send", async_mock.AsyncMock()): await responder.send_outbound(outbound_message) async def test_create_send_webhook(self): @@ -402,7 +404,7 @@ async def test_create_enc_outbound(self): message = b"abc123xyz7890000" responder = test_module.DispatcherResponder(context, message, None) with async_mock.patch.object( - responder, "send_outbound", async_mock.CoroutineMock() + responder, "send_outbound", async_mock.AsyncMock() ) as mock_send_outbound: await responder.send(message) assert mock_send_outbound.called_once() diff --git a/aries_cloudagent/ledger/tests/test_routes.py b/aries_cloudagent/ledger/tests/test_routes.py index e3df880112..f4d0cd8ade 100644 --- a/aries_cloudagent/ledger/tests/test_routes.py +++ b/aries_cloudagent/ledger/tests/test_routes.py @@ -1,6 +1,8 @@ -from asynctest import mock as async_mock, TestCase as AsyncTestCase from typing import Tuple +from async_case import IsolatedAsyncioTestCase +import mock as async_mock + from ...core.in_memory import InMemoryProfile from ...ledger.base import BaseLedger from ...ledger.endpoint_type import EndpointType @@ -18,7 +20,7 @@ from ...connections.models.conn_record import ConnRecord -class TestLedgerRoutes(AsyncTestCase): +class TestLedgerRoutes(IsolatedAsyncioTestCase): def setUp(self): self.ledger = async_mock.create_autospec(BaseLedger) self.ledger.pool_name = "pool.0" @@ -28,7 +30,7 @@ def setUp(self): self.profile.context.injector.bind_instance(BaseLedger, self.ledger) self.request_dict = { "context": self.context, - "outbound_message_router": async_mock.CoroutineMock(), + "outbound_message_router": async_mock.AsyncMock(), } self.request = async_mock.MagicMock( app={}, @@ -47,7 +49,7 @@ async def test_missing_ledger(self): self.profile.context.injector.bind_instance( IndyLedgerRequestsExecutor, async_mock.MagicMock( - get_ledger_for_identifier=async_mock.CoroutineMock( + get_ledger_for_identifier=async_mock.AsyncMock( return_value=(None, None) ) ), @@ -82,7 +84,7 @@ async def test_get_verkey_a(self): self.profile.context.injector.bind_instance( IndyLedgerRequestsExecutor, async_mock.MagicMock( - get_ledger_for_identifier=async_mock.CoroutineMock( + get_ledger_for_identifier=async_mock.AsyncMock( return_value=(None, self.ledger) ) ), @@ -102,7 +104,7 @@ async def test_get_verkey_b(self): self.profile.context.injector.bind_instance( IndyLedgerRequestsExecutor, async_mock.MagicMock( - get_ledger_for_identifier=async_mock.CoroutineMock( + get_ledger_for_identifier=async_mock.AsyncMock( return_value=("test_ledger_id", self.ledger) ) ), @@ -130,7 +132,7 @@ async def test_get_verkey_multitenant(self): with async_mock.patch.object( IndyLedgerRequestsExecutor, "get_ledger_for_identifier", - async_mock.CoroutineMock(return_value=("test_ledger_id", self.ledger)), + async_mock.AsyncMock(return_value=("test_ledger_id", self.ledger)), ), async_mock.patch.object( test_module.web, "json_response", async_mock.Mock() ) as json_response: @@ -153,7 +155,7 @@ async def test_get_verkey_did_not_public(self): self.profile.context.injector.bind_instance( IndyLedgerRequestsExecutor, async_mock.MagicMock( - get_ledger_for_identifier=async_mock.CoroutineMock( + get_ledger_for_identifier=async_mock.AsyncMock( return_value=("test_ledger_id", self.ledger) ) ), @@ -167,7 +169,7 @@ async def test_get_verkey_x(self): self.profile.context.injector.bind_instance( IndyLedgerRequestsExecutor, async_mock.MagicMock( - get_ledger_for_identifier=async_mock.CoroutineMock( + get_ledger_for_identifier=async_mock.AsyncMock( return_value=(None, self.ledger) ) ), @@ -181,7 +183,7 @@ async def test_get_endpoint(self): self.profile.context.injector.bind_instance( IndyLedgerRequestsExecutor, async_mock.MagicMock( - get_ledger_for_identifier=async_mock.CoroutineMock( + get_ledger_for_identifier=async_mock.AsyncMock( return_value=(None, self.ledger) ) ), @@ -206,7 +208,7 @@ async def test_get_endpoint_multitenant(self): with async_mock.patch.object( IndyLedgerRequestsExecutor, "get_ledger_for_identifier", - async_mock.CoroutineMock(return_value=("test_ledger_id", self.ledger)), + async_mock.AsyncMock(return_value=("test_ledger_id", self.ledger)), ), async_mock.patch.object( test_module.web, "json_response", async_mock.Mock() ) as json_response: @@ -224,7 +226,7 @@ async def test_get_endpoint_of_type_profile(self): self.profile.context.injector.bind_instance( IndyLedgerRequestsExecutor, async_mock.MagicMock( - get_ledger_for_identifier=async_mock.CoroutineMock( + get_ledger_for_identifier=async_mock.AsyncMock( return_value=("test_ledger_id", self.ledger) ) ), @@ -257,7 +259,7 @@ async def test_get_endpoint_x(self): self.profile.context.injector.bind_instance( IndyLedgerRequestsExecutor, async_mock.MagicMock( - get_ledger_for_identifier=async_mock.CoroutineMock( + get_ledger_for_identifier=async_mock.AsyncMock( return_value=("test_ledger_id", self.ledger) ) ), @@ -325,21 +327,21 @@ async def test_register_nym_create_transaction_for_endorser(self): } with async_mock.patch.object( - ConnRecord, "retrieve_by_id", async_mock.CoroutineMock() + ConnRecord, "retrieve_by_id", async_mock.AsyncMock() ) as mock_conn_rec_retrieve, async_mock.patch.object( test_module, "TransactionManager", async_mock.MagicMock() ) as mock_txn_mgr, async_mock.patch.object( test_module.web, "json_response", async_mock.MagicMock() ) as mock_response: mock_txn_mgr.return_value = async_mock.MagicMock( - create_record=async_mock.CoroutineMock( + create_record=async_mock.AsyncMock( return_value=async_mock.MagicMock( serialize=async_mock.MagicMock(return_value={"...": "..."}) ) ) ) mock_conn_rec_retrieve.return_value = async_mock.MagicMock( - metadata_get=async_mock.CoroutineMock( + metadata_get=async_mock.AsyncMock( return_value={ "endorser_did": ("did"), "endorser_name": ("name"), @@ -368,18 +370,18 @@ async def test_register_nym_create_transaction_for_endorser_storage_x(self): } with async_mock.patch.object( - ConnRecord, "retrieve_by_id", async_mock.CoroutineMock() + ConnRecord, "retrieve_by_id", async_mock.AsyncMock() ) as mock_conn_rec_retrieve, async_mock.patch.object( test_module, "TransactionManager", async_mock.MagicMock() ) as mock_txn_mgr: mock_txn_mgr.return_value = async_mock.MagicMock( - create_record=async_mock.CoroutineMock( + create_record=async_mock.AsyncMock( side_effect=test_module.StorageError() ) ) mock_conn_rec_retrieve.return_value = async_mock.MagicMock( - metadata_get=async_mock.CoroutineMock( + metadata_get=async_mock.AsyncMock( return_value={ "endorser_did": ("did"), "endorser_name": ("name"), @@ -405,7 +407,7 @@ async def test_register_nym_create_transaction_for_endorser_not_found_x(self): } with async_mock.patch.object( - ConnRecord, "retrieve_by_id", async_mock.CoroutineMock() + ConnRecord, "retrieve_by_id", async_mock.AsyncMock() ) as mock_conn_rec_retrieve: mock_conn_rec_retrieve.side_effect = test_module.StorageNotFoundError() self.ledger.register_nym.return_value: Tuple[bool, dict] = ( @@ -427,7 +429,7 @@ async def test_register_nym_create_transaction_for_endorser_base_model_x(self): } with async_mock.patch.object( - ConnRecord, "retrieve_by_id", async_mock.CoroutineMock() + ConnRecord, "retrieve_by_id", async_mock.AsyncMock() ) as mock_conn_rec_retrieve: mock_conn_rec_retrieve.side_effect = test_module.BaseModelError() self.ledger.register_nym.return_value: Tuple[bool, dict] = ( @@ -451,10 +453,10 @@ async def test_register_nym_create_transaction_for_endorser_no_endorser_info_x( } with async_mock.patch.object( - ConnRecord, "retrieve_by_id", async_mock.CoroutineMock() + ConnRecord, "retrieve_by_id", async_mock.AsyncMock() ) as mock_conn_rec_retrieve: mock_conn_rec_retrieve.return_value = async_mock.MagicMock( - metadata_get=async_mock.CoroutineMock(return_value=None) + metadata_get=async_mock.AsyncMock(return_value=None) ) self.ledger.register_nym.return_value: Tuple[bool, dict] = ( True, @@ -475,10 +477,10 @@ async def test_register_nym_create_transaction_for_endorser_no_endorser_did_x(se } with async_mock.patch.object( - ConnRecord, "retrieve_by_id", async_mock.CoroutineMock() + ConnRecord, "retrieve_by_id", async_mock.AsyncMock() ) as mock_conn_rec_retrieve: mock_conn_rec_retrieve.return_value = async_mock.MagicMock( - metadata_get=async_mock.CoroutineMock( + metadata_get=async_mock.AsyncMock( return_value={ "endorser_name": ("name"), } @@ -496,7 +498,7 @@ async def test_get_nym_role_a(self): self.profile.context.injector.bind_instance( IndyLedgerRequestsExecutor, async_mock.MagicMock( - get_ledger_for_identifier=async_mock.CoroutineMock( + get_ledger_for_identifier=async_mock.AsyncMock( return_value=(None, self.ledger) ) ), @@ -515,7 +517,7 @@ async def test_get_nym_role_b(self): self.profile.context.injector.bind_instance( IndyLedgerRequestsExecutor, async_mock.MagicMock( - get_ledger_for_identifier=async_mock.CoroutineMock( + get_ledger_for_identifier=async_mock.AsyncMock( return_value=("test_ledger_id", self.ledger) ) ), @@ -542,7 +544,7 @@ async def test_get_nym_role_multitenant(self): with async_mock.patch.object( IndyLedgerRequestsExecutor, "get_ledger_for_identifier", - async_mock.CoroutineMock(return_value=("test_ledger_id", self.ledger)), + async_mock.AsyncMock(return_value=("test_ledger_id", self.ledger)), ), async_mock.patch.object( test_module.web, "json_response", async_mock.Mock() ) as json_response: @@ -562,7 +564,7 @@ async def test_get_nym_role_ledger_txn_error(self): self.profile.context.injector.bind_instance( IndyLedgerRequestsExecutor, async_mock.MagicMock( - get_ledger_for_identifier=async_mock.CoroutineMock( + get_ledger_for_identifier=async_mock.AsyncMock( return_value=("test_ledger_id", self.ledger) ) ), @@ -578,7 +580,7 @@ async def test_get_nym_role_bad_ledger_req(self): self.profile.context.injector.bind_instance( IndyLedgerRequestsExecutor, async_mock.MagicMock( - get_ledger_for_identifier=async_mock.CoroutineMock( + get_ledger_for_identifier=async_mock.AsyncMock( return_value=("test_ledger_id", self.ledger) ) ), @@ -594,7 +596,7 @@ async def test_get_nym_role_ledger_error(self): self.profile.context.injector.bind_instance( IndyLedgerRequestsExecutor, async_mock.MagicMock( - get_ledger_for_identifier=async_mock.CoroutineMock( + get_ledger_for_identifier=async_mock.AsyncMock( return_value=(None, self.ledger) ) ), @@ -608,7 +610,7 @@ async def test_rotate_public_did_keypair(self): with async_mock.patch.object( test_module.web, "json_response", async_mock.Mock() ) as json_response: - self.ledger.rotate_public_did_keypair = async_mock.CoroutineMock() + self.ledger.rotate_public_did_keypair = async_mock.AsyncMock() await test_module.rotate_public_did_keypair(self.request) json_response.assert_called_once_with({}) @@ -617,7 +619,7 @@ async def test_rotate_public_did_keypair_public_wallet_x(self): with async_mock.patch.object( test_module.web, "json_response", async_mock.Mock() ) as json_response: - self.ledger.rotate_public_did_keypair = async_mock.CoroutineMock( + self.ledger.rotate_public_did_keypair = async_mock.AsyncMock( side_effect=test_module.WalletError("Exception") ) @@ -660,7 +662,7 @@ async def test_get_taa_x(self): await test_module.ledger_get_taa(self.request) async def test_taa_accept_not_required(self): - self.request.json = async_mock.CoroutineMock( + self.request.json = async_mock.AsyncMock( return_value={ "version": "version", "text": "text", @@ -673,7 +675,7 @@ async def test_taa_accept_not_required(self): await test_module.ledger_accept_taa(self.request) async def test_accept_taa(self): - self.request.json = async_mock.CoroutineMock( + self.request.json = async_mock.AsyncMock( return_value={ "version": "version", "text": "text", @@ -701,7 +703,7 @@ async def test_accept_taa(self): assert result is json_response.return_value async def test_accept_taa_x(self): - self.request.json = async_mock.CoroutineMock( + self.request.json = async_mock.AsyncMock( return_value={ "version": "version", "text": "text", @@ -732,7 +734,7 @@ async def test_get_write_ledger(self): self.profile.context.injector.bind_instance( BaseMultipleLedgerManager, async_mock.MagicMock( - get_write_ledger=async_mock.CoroutineMock( + get_write_ledger=async_mock.AsyncMock( return_value=("test_ledger_id", self.ledger) ) ), @@ -757,14 +759,14 @@ async def test_get_ledger_config(self): self.profile.context.injector.bind_instance( BaseMultipleLedgerManager, async_mock.MagicMock( - get_prod_ledgers=async_mock.CoroutineMock( + get_prod_ledgers=async_mock.AsyncMock( return_value={ "test_1": async_mock.MagicMock(), "test_2": async_mock.MagicMock(), "test_5": async_mock.MagicMock(), } ), - get_nonprod_ledgers=async_mock.CoroutineMock( + get_nonprod_ledgers=async_mock.AsyncMock( return_value={ "test_3": async_mock.MagicMock(), "test_4": async_mock.MagicMock(), diff --git a/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py b/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py index e1e41b1513..509e87ba6f 100644 --- a/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py +++ b/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py @@ -1,11 +1,11 @@ import asyncio -from datetime import datetime -import pytest - -from asynctest import mock as async_mock from copy import deepcopy +from datetime import datetime from uuid import uuid4 +import mock as async_mock +import pytest + from .....core.in_memory import InMemoryProfile from .....did.did_key import DIDKey from .....resolver.did_resolver_registry import DIDResolverRegistry @@ -16,7 +16,6 @@ from .....wallet.did_method import DIDMethod from .....wallet.error import WalletNotFoundError from .....vc.ld_proofs import ( - BbsBlsSignatureProof2020, BbsBlsSignature2020, ) from .....vc.ld_proofs.document_loader import DocumentLoader @@ -25,7 +24,6 @@ from .....vc.tests.document_loader import custom_document_loader from .....vc.tests.data import ( BBS_SIGNED_VC_MATTR, - BBS_NESTED_VC_REVEAL_DOCUMENT_MATTR, ) from .. import pres_exch_handler as test_module @@ -60,7 +58,7 @@ ) -@pytest.yield_fixture(scope="class") +@pytest.fixture(scope="class") def event_loop(request): loop = asyncio.get_event_loop_policy().new_event_loop() yield loop @@ -1670,7 +1668,6 @@ def test_subject_is_issuer(self, setup_tuple, profile): tmp_cred.issuer_id = "19b823fb-55ef-49f4-8caf-2a26b8b9286f" assert dif_pres_exch_handler.subject_is_issuer(tmp_cred) is False - @pytest.mark.asyncio def test_is_numeric(self, profile): dif_pres_exch_handler = DIFPresExchHandler(profile) with pytest.raises(DIFPresExchError): @@ -1682,7 +1679,6 @@ def test_is_numeric(self, profile): with pytest.raises(DIFPresExchError): dif_pres_exch_handler.is_numeric(2 + 3j) - @pytest.mark.asyncio def test_filter_no_match(self, profile): dif_pres_exch_handler = DIFPresExchHandler(profile) tmp_filter_excl_min = Filter(exclusive_min=7) @@ -1700,7 +1696,6 @@ def test_filter_no_match(self, profile): tmp_filter_max = Filter(maximum=10) assert dif_pres_exch_handler.maximum_check("test", tmp_filter_max) is False - @pytest.mark.asyncio def test_filter_valueerror(self, profile): dif_pres_exch_handler = DIFPresExchHandler(profile) tmp_filter_excl_min = Filter(exclusive_min=7, fmt="date") @@ -1718,7 +1713,6 @@ def test_filter_valueerror(self, profile): tmp_filter_max = Filter(maximum=10, fmt="date") assert dif_pres_exch_handler.maximum_check("test", tmp_filter_max) is False - @pytest.mark.asyncio def test_filter_length_check(self, profile): dif_pres_exch_handler = DIFPresExchHandler(profile) tmp_filter_both = Filter(min_length=7, max_length=10) @@ -1729,7 +1723,6 @@ def test_filter_length_check(self, profile): assert dif_pres_exch_handler.length_check("test", tmp_filter_max) is True assert dif_pres_exch_handler.length_check("test12", tmp_filter_min) is False - @pytest.mark.asyncio def test_filter_pattern_check(self, profile): dif_pres_exch_handler = DIFPresExchHandler(profile) tmp_filter = Filter(pattern="test1|test2") @@ -1737,7 +1730,6 @@ def test_filter_pattern_check(self, profile): tmp_filter = Filter(const="test3") assert dif_pres_exch_handler.pattern_check("test3", tmp_filter) is False - @pytest.mark.asyncio def test_is_len_applicable(self, profile): dif_pres_exch_handler = DIFPresExchHandler(profile) tmp_req_a = Requirement(count=1) @@ -1748,7 +1740,6 @@ def test_is_len_applicable(self, profile): assert dif_pres_exch_handler.is_len_applicable(tmp_req_b, 2) is False assert dif_pres_exch_handler.is_len_applicable(tmp_req_c, 6) is False - @pytest.mark.asyncio def test_create_vcrecord(self, profile): dif_pres_exch_handler = DIFPresExchHandler(profile) test_cred_dict = { @@ -2037,7 +2028,7 @@ async def test_get_sign_key_credential_subject_id(self, profile): with async_mock.patch.object( DIFPresExchHandler, "_did_info_for_did", - async_mock.CoroutineMock(), + async_mock.AsyncMock(), ) as mock_did_info: did_info = DIDInfo( did="did:sov:LjgpST2rjsoxYegQDRm7EL", @@ -2102,7 +2093,7 @@ async def test_get_sign_key_credential_subject_id_error(self, profile): with async_mock.patch.object( DIFPresExchHandler, "_did_info_for_did", - async_mock.CoroutineMock(), + async_mock.AsyncMock(), ) as mock_did_info: did_info = DIDInfo( did="did:sov:LjgpST2rjsoxYegQDRm7EL", @@ -2170,7 +2161,7 @@ async def test_get_sign_key_credential_subject_id_bbsbls(self, profile): with async_mock.patch.object( DIFPresExchHandler, "_did_info_for_did", - async_mock.CoroutineMock(), + async_mock.AsyncMock(), ) as mock_did_info: did_info = DIDInfo( did="did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", @@ -2240,23 +2231,23 @@ async def test_create_vp_no_issuer(self, profile, setup_tuple): with async_mock.patch.object( DIFPresExchHandler, "_did_info_for_did", - async_mock.CoroutineMock(), + async_mock.AsyncMock(), ) as mock_did_info, async_mock.patch.object( DIFPresExchHandler, "make_requirement", - async_mock.CoroutineMock(), + async_mock.AsyncMock(), ) as mock_make_req, async_mock.patch.object( DIFPresExchHandler, "apply_requirements", - async_mock.CoroutineMock(), + async_mock.AsyncMock(), ) as mock_apply_req, async_mock.patch.object( DIFPresExchHandler, "merge", - async_mock.CoroutineMock(), + async_mock.AsyncMock(), ) as mock_merge, async_mock.patch.object( test_module, "create_presentation", - async_mock.CoroutineMock(), + async_mock.AsyncMock(), ) as mock_create_vp: mock_make_req.return_value = async_mock.MagicMock() mock_apply_req.return_value = async_mock.MagicMock() @@ -2292,27 +2283,27 @@ async def test_create_vp_with_bbs_suite(self, profile, setup_tuple): with async_mock.patch.object( DIFPresExchHandler, "_did_info_for_did", - async_mock.CoroutineMock(), + async_mock.AsyncMock(), ) as mock_did_info, async_mock.patch.object( DIFPresExchHandler, "make_requirement", - async_mock.CoroutineMock(), + async_mock.AsyncMock(), ) as mock_make_req, async_mock.patch.object( DIFPresExchHandler, "apply_requirements", - async_mock.CoroutineMock(), + async_mock.AsyncMock(), ) as mock_apply_req, async_mock.patch.object( DIFPresExchHandler, "merge", - async_mock.CoroutineMock(), + async_mock.AsyncMock(), ) as mock_merge, async_mock.patch.object( test_module, "create_presentation", - async_mock.CoroutineMock(), + async_mock.AsyncMock(), ) as mock_create_vp, async_mock.patch.object( test_module, "sign_presentation", - async_mock.CoroutineMock(), + async_mock.AsyncMock(), ) as mock_sign_vp: mock_make_req.return_value = async_mock.MagicMock() mock_apply_req.return_value = async_mock.MagicMock() @@ -2349,27 +2340,27 @@ async def test_create_vp_no_issuer_with_bbs_suite(self, profile, setup_tuple): with async_mock.patch.object( DIFPresExchHandler, "_did_info_for_did", - async_mock.CoroutineMock(), + async_mock.AsyncMock(), ) as mock_did_info, async_mock.patch.object( DIFPresExchHandler, "make_requirement", - async_mock.CoroutineMock(), + async_mock.AsyncMock(), ) as mock_make_req, async_mock.patch.object( DIFPresExchHandler, "apply_requirements", - async_mock.CoroutineMock(), + async_mock.AsyncMock(), ) as mock_apply_req, async_mock.patch.object( DIFPresExchHandler, "merge", - async_mock.CoroutineMock(), + async_mock.AsyncMock(), ) as mock_merge, async_mock.patch.object( test_module, "create_presentation", - async_mock.CoroutineMock(), + async_mock.AsyncMock(), ) as mock_create_vp, async_mock.patch.object( DIFPresExchHandler, "get_sign_key_credential_subject_id", - async_mock.CoroutineMock(), + async_mock.AsyncMock(), ) as mock_sign_key_cred_subject: mock_make_req.return_value = async_mock.MagicMock() mock_apply_req.return_value = async_mock.MagicMock() @@ -2993,6 +2984,7 @@ async def test_filter_creds_record_id(self, profile): assert filtered_cred_list[1].record_id in record_id_list @pytest.mark.asyncio + @pytest.mark.ursa_bbs_signatures async def test_create_vp_record_ids(self, profile): dif_pres_exch_handler = DIFPresExchHandler(profile) test_pd_filter_with_only_num_type = """ diff --git a/aries_cloudagent/wallet/tests/test_routes.py b/aries_cloudagent/wallet/tests/test_routes.py index 6d67d0f17f..efc9aba79d 100644 --- a/aries_cloudagent/wallet/tests/test_routes.py +++ b/aries_cloudagent/wallet/tests/test_routes.py @@ -1,5 +1,7 @@ +import mock as async_mock +from async_case import IsolatedAsyncioTestCase + from aiohttp.web import HTTPForbidden -from asynctest import TestCase as AsyncTestCase, mock as async_mock from .. import routes as test_module from ...admin.request_context import AdminRequestContext @@ -13,7 +15,7 @@ from ..did_posture import DIDPosture -class TestWalletRoutes(AsyncTestCase): +class TestWalletRoutes(IsolatedAsyncioTestCase): def setUp(self): self.wallet = async_mock.create_autospec(BaseWallet) self.session_inject = {BaseWallet: self.wallet} @@ -23,7 +25,7 @@ def setUp(self): ) self.request_dict = { "context": self.context, - "outbound_message_router": async_mock.CoroutineMock(), + "outbound_message_router": async_mock.AsyncMock(), } self.request = async_mock.MagicMock( app={}, @@ -53,7 +55,7 @@ async def test_missing_wallet(self): await test_module.wallet_set_public_did(self.request) with self.assertRaises(HTTPForbidden): - self.request.json = async_mock.CoroutineMock( + self.request.json = async_mock.AsyncMock( return_value={ "did": self.test_did, "endpoint": "https://my-endpoint.ca:8020", @@ -125,7 +127,7 @@ async def test_create_did(self): assert result is json_response.return_value async def test_create_did_unsupported_key_type(self): - self.request.json = async_mock.CoroutineMock( + self.request.json = async_mock.AsyncMock( return_value={"method": "sov", "options": {"key_type": "bls12381g2"}} ) with self.assertRaises(test_module.web.HTTPForbidden): @@ -374,12 +376,12 @@ async def test_set_public_did(self): Ledger = async_mock.MagicMock() ledger = Ledger() - ledger.get_key_for_did = async_mock.CoroutineMock() - ledger.update_endpoint_for_did = async_mock.CoroutineMock() - ledger.__aenter__ = async_mock.CoroutineMock(return_value=ledger) + ledger.get_key_for_did = async_mock.AsyncMock() + ledger.update_endpoint_for_did = async_mock.AsyncMock() + ledger.__aenter__ = async_mock.AsyncMock(return_value=ledger) self.profile.context.injector.bind_instance(BaseLedger, ledger) mock_route_manager = async_mock.MagicMock() - mock_route_manager.route_public_did = async_mock.CoroutineMock() + mock_route_manager.route_public_did = async_mock.AsyncMock() self.profile.context.injector.bind_instance(RouteManager, mock_route_manager) with async_mock.patch.object( @@ -422,8 +424,8 @@ async def test_set_public_did_not_public(self): Ledger = async_mock.MagicMock() ledger = Ledger() - ledger.get_key_for_did = async_mock.CoroutineMock(return_value=None) - ledger.__aenter__ = async_mock.CoroutineMock(return_value=ledger) + ledger.get_key_for_did = async_mock.AsyncMock(return_value=None) + ledger.__aenter__ = async_mock.AsyncMock(return_value=ledger) self.profile.context.injector.bind_instance(BaseLedger, ledger) with self.assertRaises(test_module.web.HTTPNotFound): @@ -434,8 +436,8 @@ async def test_set_public_did_not_found(self): Ledger = async_mock.MagicMock() ledger = Ledger() - ledger.get_key_for_did = async_mock.CoroutineMock(return_value=None) - ledger.__aenter__ = async_mock.CoroutineMock(return_value=ledger) + ledger.get_key_for_did = async_mock.AsyncMock(return_value=None) + ledger.__aenter__ = async_mock.AsyncMock(return_value=ledger) self.profile.context.injector.bind_instance(BaseLedger, ledger) self.wallet.get_local_did.side_effect = test_module.WalletNotFoundError() @@ -447,9 +449,9 @@ async def test_set_public_did_x(self): Ledger = async_mock.MagicMock() ledger = Ledger() - ledger.update_endpoint_for_did = async_mock.CoroutineMock() - ledger.get_key_for_did = async_mock.CoroutineMock() - ledger.__aenter__ = async_mock.CoroutineMock(return_value=ledger) + ledger.update_endpoint_for_did = async_mock.AsyncMock() + ledger.get_key_for_did = async_mock.AsyncMock() + ledger.__aenter__ = async_mock.AsyncMock(return_value=ledger) self.profile.context.injector.bind_instance(BaseLedger, ledger) with async_mock.patch.object( test_module.web, "json_response", async_mock.Mock() @@ -470,9 +472,9 @@ async def test_set_public_did_no_wallet_did(self): Ledger = async_mock.MagicMock() ledger = Ledger() - ledger.update_endpoint_for_did = async_mock.CoroutineMock() - ledger.get_key_for_did = async_mock.CoroutineMock() - ledger.__aenter__ = async_mock.CoroutineMock(return_value=ledger) + ledger.update_endpoint_for_did = async_mock.AsyncMock() + ledger.get_key_for_did = async_mock.AsyncMock() + ledger.__aenter__ = async_mock.AsyncMock(return_value=ledger) self.profile.context.injector.bind_instance(BaseLedger, ledger) with async_mock.patch.object( @@ -494,12 +496,12 @@ async def test_set_public_did_update_endpoint(self): Ledger = async_mock.MagicMock() ledger = Ledger() - ledger.update_endpoint_for_did = async_mock.CoroutineMock() - ledger.get_key_for_did = async_mock.CoroutineMock() - ledger.__aenter__ = async_mock.CoroutineMock(return_value=ledger) + ledger.update_endpoint_for_did = async_mock.AsyncMock() + ledger.get_key_for_did = async_mock.AsyncMock() + ledger.__aenter__ = async_mock.AsyncMock(return_value=ledger) self.profile.context.injector.bind_instance(BaseLedger, ledger) mock_route_manager = async_mock.MagicMock() - mock_route_manager.route_public_did = async_mock.CoroutineMock() + mock_route_manager.route_public_did = async_mock.AsyncMock() self.profile.context.injector.bind_instance(RouteManager, mock_route_manager) with async_mock.patch.object( @@ -535,12 +537,12 @@ async def test_set_public_did_update_endpoint_use_default_update_in_wallet(self) Ledger = async_mock.MagicMock() ledger = Ledger() - ledger.update_endpoint_for_did = async_mock.CoroutineMock() - ledger.get_key_for_did = async_mock.CoroutineMock() - ledger.__aenter__ = async_mock.CoroutineMock(return_value=ledger) + ledger.update_endpoint_for_did = async_mock.AsyncMock() + ledger.get_key_for_did = async_mock.AsyncMock() + ledger.__aenter__ = async_mock.AsyncMock(return_value=ledger) self.profile.context.injector.bind_instance(BaseLedger, ledger) mock_route_manager = async_mock.MagicMock() - mock_route_manager.route_public_did = async_mock.CoroutineMock() + mock_route_manager.route_public_did = async_mock.AsyncMock() self.profile.context.injector.bind_instance(RouteManager, mock_route_manager) with async_mock.patch.object( @@ -578,7 +580,7 @@ async def test_set_public_did_update_endpoint_use_default_update_in_wallet(self) assert result is json_response.return_value async def test_set_did_endpoint(self): - self.request.json = async_mock.CoroutineMock( + self.request.json = async_mock.AsyncMock( return_value={ "did": self.test_did, "endpoint": "https://my-endpoint.ca:8020", @@ -587,8 +589,8 @@ async def test_set_did_endpoint(self): Ledger = async_mock.MagicMock() ledger = Ledger() - ledger.update_endpoint_for_did = async_mock.CoroutineMock() - ledger.__aenter__ = async_mock.CoroutineMock(return_value=ledger) + ledger.update_endpoint_for_did = async_mock.AsyncMock() + ledger.__aenter__ = async_mock.AsyncMock(return_value=ledger) self.profile.context.injector.bind_instance(BaseLedger, ledger) self.wallet.get_local_did.return_value = DIDInfo( @@ -613,7 +615,7 @@ async def test_set_did_endpoint(self): json_response.assert_called_once_with({}) async def test_set_did_endpoint_public_did_no_ledger(self): - self.request.json = async_mock.CoroutineMock( + self.request.json = async_mock.AsyncMock( return_value={ "did": self.test_did, "endpoint": "https://my-endpoint.ca:8020", @@ -640,7 +642,7 @@ async def test_set_did_endpoint_public_did_no_ledger(self): await test_module.wallet_set_did_endpoint(self.request) async def test_set_did_endpoint_x(self): - self.request.json = async_mock.CoroutineMock( + self.request.json = async_mock.AsyncMock( return_value={ "did": self.test_did, "endpoint": "https://my-endpoint.ca:8020", @@ -649,8 +651,8 @@ async def test_set_did_endpoint_x(self): Ledger = async_mock.MagicMock() ledger = Ledger() - ledger.update_endpoint_for_did = async_mock.CoroutineMock() - ledger.__aenter__ = async_mock.CoroutineMock(return_value=ledger) + ledger.update_endpoint_for_did = async_mock.AsyncMock() + ledger.__aenter__ = async_mock.AsyncMock(return_value=ledger) self.profile.context.injector.bind_instance(BaseLedger, ledger) self.wallet.set_did_endpoint.side_effect = test_module.WalletError() @@ -659,7 +661,7 @@ async def test_set_did_endpoint_x(self): await test_module.wallet_set_did_endpoint(self.request) async def test_set_did_endpoint_no_wallet_did(self): - self.request.json = async_mock.CoroutineMock( + self.request.json = async_mock.AsyncMock( return_value={ "did": self.test_did, "endpoint": "https://my-endpoint.ca:8020", @@ -668,8 +670,8 @@ async def test_set_did_endpoint_no_wallet_did(self): Ledger = async_mock.MagicMock() ledger = Ledger() - ledger.update_endpoint_for_did = async_mock.CoroutineMock() - ledger.__aenter__ = async_mock.CoroutineMock(return_value=ledger) + ledger.update_endpoint_for_did = async_mock.AsyncMock() + ledger.__aenter__ = async_mock.AsyncMock(return_value=ledger) self.profile.context.injector.bind_instance(BaseLedger, ledger) self.wallet.set_did_endpoint.side_effect = test_module.WalletNotFoundError() @@ -727,7 +729,7 @@ async def test_rotate_did_keypair(self): with async_mock.patch.object( test_module.web, "json_response", async_mock.Mock() ) as json_response: - self.wallet.get_local_did = async_mock.CoroutineMock( + self.wallet.get_local_did = async_mock.AsyncMock( return_value=DIDInfo( "did", "verkey", @@ -736,8 +738,8 @@ async def test_rotate_did_keypair(self): KeyType.ED25519, ) ) - self.wallet.rotate_did_keypair_start = async_mock.CoroutineMock() - self.wallet.rotate_did_keypair_apply = async_mock.CoroutineMock() + self.wallet.rotate_did_keypair_start = async_mock.AsyncMock() + self.wallet.rotate_did_keypair_apply = async_mock.AsyncMock() await test_module.wallet_rotate_did_keypair(self.request) json_response.assert_called_once_with({}) @@ -756,13 +758,13 @@ async def test_rotate_did_keypair_no_query_did(self): async def test_rotate_did_keypair_did_not_local(self): self.request.query = {"did": "did"} - self.wallet.get_local_did = async_mock.CoroutineMock( + self.wallet.get_local_did = async_mock.AsyncMock( side_effect=test_module.WalletNotFoundError("Unknown DID") ) with self.assertRaises(test_module.web.HTTPNotFound): await test_module.wallet_rotate_did_keypair(self.request) - self.wallet.get_local_did = async_mock.CoroutineMock( + self.wallet.get_local_did = async_mock.AsyncMock( return_value=DIDInfo( "did", "verkey", @@ -777,7 +779,7 @@ async def test_rotate_did_keypair_did_not_local(self): async def test_rotate_did_keypair_x(self): self.request.query = {"did": "did"} - self.wallet.get_local_did = async_mock.CoroutineMock( + self.wallet.get_local_did = async_mock.AsyncMock( return_value=DIDInfo( "did", "verkey", @@ -786,7 +788,7 @@ async def test_rotate_did_keypair_x(self): KeyType.ED25519, ) ) - self.wallet.rotate_did_keypair_start = async_mock.CoroutineMock( + self.wallet.rotate_did_keypair_start = async_mock.AsyncMock( side_effect=test_module.WalletError() ) with self.assertRaises(test_module.web.HTTPBadRequest): diff --git a/requirements.dev.txt b/requirements.dev.txt index 22433dcadc..e9c5d55595 100644 --- a/requirements.dev.txt +++ b/requirements.dev.txt @@ -1,8 +1,10 @@ asynctest==0.13.0 +async-case~=10.1 pytest~=5.4.0 pytest-asyncio==0.14.0 pytest-cov==2.10.1 pytest-flake8==1.0.6 +mock~=4.0 flake8==3.9.0 # flake8-rst-docstrings==0.0.8 diff --git a/setup.cfg b/setup.cfg index c1942b7fd5..f96dbb91be 100644 --- a/setup.cfg +++ b/setup.cfg @@ -12,6 +12,7 @@ markers = ursa_bbs_signatures: Tests specificaly relating to BBS Signatures support postgres: Tests relating to the postgres storage plugin for Indy junit_family = xunit1 +asyncio_mode = auto [flake8] # https://github.com/ambv/black#line-length From d114a1862398ed1d24bd3cb24d499ab5fa2062f3 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Thu, 21 Jul 2022 17:21:14 -0400 Subject: [PATCH 353/872] test: universal resolver helpers and edge cases Signed-off-by: Daniel Bluhm --- .../config/tests/test_argparse.py | 44 +++++++ .../resolver/default/tests/test_universal.py | 115 ++++++++++++++++++ .../resolver/default/universal.py | 12 +- 3 files changed, 166 insertions(+), 5 deletions(-) diff --git a/aries_cloudagent/config/tests/test_argparse.py b/aries_cloudagent/config/tests/test_argparse.py index de680bb8f3..e9303b8987 100644 --- a/aries_cloudagent/config/tests/test_argparse.py +++ b/aries_cloudagent/config/tests/test_argparse.py @@ -469,3 +469,47 @@ async def test_discover_features_args(self): assert (["test_goal_code_1", "test_goal_code_2"]) == settings.get( "disclose_goal_code_list" ) + + def test_universal_resolver(self): + """Test universal resolver flags.""" + parser = argparse.create_argument_parser() + group = argparse.GeneralGroup() + group.add_arguments(parser) + + result = parser.parse_args(["-e", "test", "--universal-resolver"]) + settings = group.get_settings(result) + endpoint = settings.get("resolver.universal") + assert endpoint + assert endpoint == "DEFAULT" + + result = parser.parse_args( + ["-e", "test", "--universal-resolver", "https://example.com"] + ) + settings = group.get_settings(result) + endpoint = settings.get("resolver.universal") + assert endpoint + assert endpoint == "https://example.com" + + result = parser.parse_args( + [ + "-e", + "test", + "--universal-resolver", + "https://example.com", + "--universal-resolver-regex", + "regex", + ] + ) + settings = group.get_settings(result) + endpoint = settings.get("resolver.universal") + assert endpoint + assert endpoint == "https://example.com" + supported_regex = settings.get("resolver.universal.supported") + assert supported_regex + assert supported_regex == ["regex"] + + result = parser.parse_args( + ["-e", "test", "--universal-resolver-regex", "regex"] + ) + with self.assertRaises(argparse.ArgsParseError): + group.get_settings(result) diff --git a/aries_cloudagent/resolver/default/tests/test_universal.py b/aries_cloudagent/resolver/default/tests/test_universal.py index 5248e62521..381e9194e3 100644 --- a/aries_cloudagent/resolver/default/tests/test_universal.py +++ b/aries_cloudagent/resolver/default/tests/test_universal.py @@ -6,6 +6,8 @@ from asynctest import mock as async_mock import pytest +from aries_cloudagent.config.settings import Settings + from .. import universal as test_module from ...base import DIDNotFound, ResolverError from ..universal import UniversalResolver @@ -105,3 +107,116 @@ async def test_resolve_unexpeceted_status(profile, resolver, mock_client_session ) with pytest.raises(ResolverError): await resolver.resolve(profile, "did:sov:WRfXPg8dantKVubE3HX8pw") + + +@pytest.mark.asyncio +async def test_fetch_resolver_props(mock_client_session: MockClientSession): + mock_client_session.response = MockResponse(200, {"test": "json"}) + assert await test_module._fetch_resolver_props("test") == {"test": "json"} + mock_client_session.response = MockResponse(404, "Not found") + with pytest.raises(ResolverError): + await test_module._fetch_resolver_props("test") + + +@pytest.mark.asyncio +async def test_get_supported_did_regex(): + props = {"example": {"http": {"pattern": "match a test string"}}} + with async_mock.patch.object( + test_module, + "_fetch_resolver_props", + async_mock.CoroutineMock(return_value=props), + ): + pattern = await test_module._get_supported_did_regex("test") + assert pattern.fullmatch("match a test string") + + +def test_compile_supported_did_regex(): + patterns = ["one", "two", "three"] + compiled = test_module._compile_supported_did_regex(patterns) + assert compiled.match("one") + assert compiled.match("two") + assert compiled.match("three") + + +@pytest.mark.asyncio +async def test_setup_endpoint_regex_set(resolver: UniversalResolver): + settings = Settings( + { + "resolver.universal": "http://example.com", + "resolver.universal.supported": "test", + } + ) + context = async_mock.MagicMock() + context.settings = settings + with async_mock.patch.object( + test_module, + "_compile_supported_did_regex", + async_mock.MagicMock(return_value="pattern"), + ): + await resolver.setup(context) + + assert resolver._endpoint == "http://example.com" + assert resolver._supported_did_regex == "pattern" + + +@pytest.mark.asyncio +async def test_setup_endpoint_set(resolver: UniversalResolver): + settings = Settings( + { + "resolver.universal": "http://example.com", + } + ) + context = async_mock.MagicMock() + context.settings = settings + with async_mock.patch.object( + test_module, + "_get_supported_did_regex", + async_mock.CoroutineMock(return_value="pattern"), + ): + await resolver.setup(context) + + assert resolver._endpoint == "http://example.com" + assert resolver._supported_did_regex == "pattern" + + +@pytest.mark.asyncio +async def test_setup_endpoint_default(resolver: UniversalResolver): + settings = Settings( + { + "resolver.universal": "DEFAULT", + } + ) + context = async_mock.MagicMock() + context.settings = settings + with async_mock.patch.object( + test_module, + "_get_supported_did_regex", + async_mock.CoroutineMock(return_value="pattern"), + ): + await resolver.setup(context) + + assert resolver._endpoint == test_module.DEFAULT_ENDPOINT + assert resolver._supported_did_regex == "pattern" + + +@pytest.mark.asyncio +async def test_setup_endpoint_unset(resolver: UniversalResolver): + settings = Settings() + context = async_mock.MagicMock() + context.settings = settings + with async_mock.patch.object( + test_module, + "_get_supported_did_regex", + async_mock.CoroutineMock(return_value="pattern"), + ): + await resolver.setup(context) + + assert resolver._endpoint == test_module.DEFAULT_ENDPOINT + assert resolver._supported_did_regex == "pattern" + + +@pytest.mark.asyncio +async def test_supported_did_regex_not_setup(): + resolver = UniversalResolver() + with pytest.raises(ResolverError): + resolver.supported_did_regex diff --git a/aries_cloudagent/resolver/default/universal.py b/aries_cloudagent/resolver/default/universal.py index 8fe94fba30..6846d7f016 100644 --- a/aries_cloudagent/resolver/default/universal.py +++ b/aries_cloudagent/resolver/default/universal.py @@ -20,7 +20,9 @@ async def _fetch_resolver_props(endpoint: str) -> dict: async with session.get(f"{endpoint}/1.0/properties/") as resp: if resp.status >= 200 and resp.status < 400: return await resp.json() - raise ValueError(await resp.text()) + raise ResolverError( + "Failed to retrieve resolver properties: " + await resp.text() + ) async def _get_supported_did_regex(endpoint: str) -> Pattern: @@ -64,11 +66,11 @@ async def setup(self, context: InjectionContext): if endpoint == "DEFAULT" or not endpoint: endpoint = DEFAULT_ENDPOINT - supported = context.settings.get("resolver.universal.supported", []) - if supported: - supported_did_regex = _compile_supported_did_regex(supported) - else: + supported = context.settings.get("resolver.universal.supported") + if supported is None: supported_did_regex = await _get_supported_did_regex(endpoint) + else: + supported_did_regex = _compile_supported_did_regex(supported) self._endpoint = endpoint self._supported_did_regex = supported_did_regex From f8175657d3f790ebd0e3de59e7a0d387b904f0d0 Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Wed, 15 Jun 2022 16:47:28 -0700 Subject: [PATCH 354/872] initial updates to revocation creation procedure Signed-off-by: Andrew Whitehead --- aries_cloudagent/indy/sdk/tests/test_util.py | 23 +- aries_cloudagent/indy/util.py | 16 +- aries_cloudagent/ledger/base.py | 4 +- aries_cloudagent/ledger/indy.py | 4 +- aries_cloudagent/ledger/indy_vdr.py | 4 +- .../credential_definitions/routes.py | 11 +- .../tests/test_routes.py | 1 - .../endorse_transaction/v1_0/manager.py | 59 ++-- .../issue_credential/v1_0/manager.py | 186 ++++-------- .../v2_0/formats/indy/handler.py | 158 +++------- aries_cloudagent/revocation/indy.py | 89 +++++- .../models/issuer_rev_reg_record.py | 64 +++- .../tests/test_issuer_rev_reg_record.py | 2 +- aries_cloudagent/revocation/routes.py | 275 ++++++++---------- aries_cloudagent/revocation/util.py | 63 ++-- 15 files changed, 411 insertions(+), 548 deletions(-) diff --git a/aries_cloudagent/indy/sdk/tests/test_util.py b/aries_cloudagent/indy/sdk/tests/test_util.py index cb6a55ea50..be3f5ee36b 100644 --- a/aries_cloudagent/indy/sdk/tests/test_util.py +++ b/aries_cloudagent/indy/sdk/tests/test_util.py @@ -1,20 +1,15 @@ import pytest -from os import makedirs -from os.path import join -from pathlib import Path from shutil import rmtree import indy.blob_storage from asynctest import mock as async_mock, TestCase as AsyncTestCase -from ...util import indy_client_dir, generate_pr_nonce, tails_path +from ...util import indy_client_dir, generate_pr_nonce from ..util import create_tails_reader, create_tails_writer -from .. import util as test_module - @pytest.mark.indy class TestIndyUtils(AsyncTestCase): @@ -49,19 +44,3 @@ async def test_tails_writer(self): async def test_nonce(self): assert await generate_pr_nonce() - - async def test_tails_path(self): - tails_dir = indy_client_dir("tails", create=False) - rmtree(tails_dir, ignore_errors=True) - - tails_local_path = tails_path("rev-reg-id") - assert tails_local_path is None - - tails_rr_dir = indy_client_dir(join("tails", "rev-reg-id"), create=True) - tails_local_path = tails_path("rev-reg-id") - assert tails_local_path is None - - with open(join(tails_rr_dir, "tails-hash"), "w") as f: - f.write("content") - tails_local_path = tails_path("rev-reg-id") - assert tails_local_path diff --git a/aries_cloudagent/indy/util.py b/aries_cloudagent/indy/util.py index bb44f446b0..2c9a126c46 100644 --- a/aries_cloudagent/indy/util.py +++ b/aries_cloudagent/indy/util.py @@ -1,6 +1,6 @@ """Utilities for dealing with Indy conventions.""" -from os import getenv, listdir, makedirs, urandom +from os import getenv, makedirs, urandom from os.path import isdir, join from pathlib import Path from platform import system @@ -37,17 +37,3 @@ def indy_client_dir(subpath: str = None, create: bool = False) -> str: makedirs(target_dir, exist_ok=True) return target_dir - - -def tails_path(rev_reg_id: str) -> str: - """Return path to indy tails file for input rev reg id.""" - - tails_dir = indy_client_dir(join("tails", rev_reg_id), create=False) - if not isdir(tails_dir): - return None - - content = listdir(tails_dir) - if len(content) != 1: - return None - - return join(tails_dir, content[0]) diff --git a/aries_cloudagent/ledger/base.py b/aries_cloudagent/ledger/base.py index 451f0ffc41..bcbb8c54e1 100644 --- a/aries_cloudagent/ledger/base.py +++ b/aries_cloudagent/ledger/base.py @@ -352,7 +352,7 @@ async def send_revoc_reg_def( issuer_did: str = None, write_ledger: bool = True, endorser_did: str = None, - ): + ) -> dict: """Publish a revocation registry definition to the ledger.""" @abstractmethod @@ -364,7 +364,7 @@ async def send_revoc_reg_entry( issuer_did: str = None, write_ledger: bool = True, endorser_did: str = None, - ): + ) -> dict: """Publish a revocation registry entry to the ledger.""" async def create_and_send_credential_definition( diff --git a/aries_cloudagent/ledger/indy.py b/aries_cloudagent/ledger/indy.py index 36aaef00c7..3ac2b19363 100644 --- a/aries_cloudagent/ledger/indy.py +++ b/aries_cloudagent/ledger/indy.py @@ -1131,7 +1131,7 @@ async def send_revoc_reg_def( issuer_did: str = None, write_ledger: bool = True, endorser_did: str = None, - ): + ) -> dict: """Publish a revocation registry definition to the ledger.""" # NOTE - issuer DID could be extracted from the revoc_reg_def ID if issuer_did: @@ -1167,7 +1167,7 @@ async def send_revoc_reg_entry( issuer_did: str = None, write_ledger: bool = True, endorser_did: str = None, - ): + ) -> dict: """Publish a revocation registry entry to the ledger.""" if issuer_did: async with self.profile.session() as session: diff --git a/aries_cloudagent/ledger/indy_vdr.py b/aries_cloudagent/ledger/indy_vdr.py index cbc350d748..4dc98e75ba 100644 --- a/aries_cloudagent/ledger/indy_vdr.py +++ b/aries_cloudagent/ledger/indy_vdr.py @@ -1068,7 +1068,7 @@ async def send_revoc_reg_def( issuer_did: str = None, write_ledger: bool = True, endorser_did: str = None, - ): + ) -> dict: """Publish a revocation registry definition to the ledger.""" # NOTE - issuer DID could be extracted from the revoc_reg_def ID async with self.profile.session() as session: @@ -1105,7 +1105,7 @@ async def send_revoc_reg_entry( issuer_did: str = None, write_ledger: bool = True, endorser_did: str = None, - ): + ) -> dict: """Publish a revocation registry entry to the ledger.""" async with self.profile.session() as session: wallet = session.inject(BaseWallet) diff --git a/aries_cloudagent/messaging/credential_definitions/routes.py b/aries_cloudagent/messaging/credential_definitions/routes.py index 82ffa09fab..5724b8a979 100644 --- a/aries_cloudagent/messaging/credential_definitions/routes.py +++ b/aries_cloudagent/messaging/credential_definitions/routes.py @@ -41,7 +41,7 @@ get_endorser_connection_id, ) -from ...revocation.util import notify_revocation_reg_event +from ...revocation.indy import IndyRevocation from ...storage.base import BaseStorage, StorageRecord from ...storage.error import StorageError @@ -514,16 +514,15 @@ async def on_cred_def_event(profile: Profile, event: Event): if support_revocation and novel and auto_create_rev_reg: # this kicks off the revocation registry creation process, which is 3 steps: # 1 - create revocation registry (ledger transaction may require endorsement) - # 2 - create revocation entry (ledger transaction may require endorsement) - # 3 - upload tails file + # 2 - upload tails file + # 3 - create revocation entry (ledger transaction may require endorsement) # For a cred def we also automatically create a second "pending" revocation # registry, so when the first one fills up we can continue to issue credentials # without a delay - await notify_revocation_reg_event( - profile, + revoc = IndyRevocation(profile) + await revoc.init_issuer_registry( cred_def_id, rev_reg_size, - auto_create_rev_reg=auto_create_rev_reg, create_pending_rev_reg=create_pending_rev_reg, endorser_connection_id=endorser_connection_id, ) diff --git a/aries_cloudagent/messaging/credential_definitions/tests/test_routes.py b/aries_cloudagent/messaging/credential_definitions/tests/test_routes.py index 88164b437a..24b5a41bda 100644 --- a/aries_cloudagent/messaging/credential_definitions/tests/test_routes.py +++ b/aries_cloudagent/messaging/credential_definitions/tests/test_routes.py @@ -11,7 +11,6 @@ from ....multitenant.base import BaseMultitenantManager from ....multitenant.manager import MultitenantManager from ....storage.base import BaseStorage -from ....tails.base import BaseTailsServer from .. import routes as test_module from ....connections.models.conn_record import ConnRecord diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py index 4c8b7d0d21..da95091be4 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py @@ -16,7 +16,7 @@ from ....messaging.schemas.util import notify_schema_event from ....revocation.util import ( notify_revocation_entry_event, - notify_revocation_tails_file_event, + notify_revocation_reg_endorsed_event, ) from ....storage.error import StorageError, StorageNotFoundError from ....transport.inbound.receipt import MessageReceipt @@ -255,13 +255,12 @@ async def create_endorse_response( endorser_did = endorser_did_info.did endorser_verkey = endorser_did_info.verkey - async with self._profile.session() as session: - ledger = session.context.inject_or(BaseLedger) - if not ledger: - reason = "No ledger available" - if not session.context.settings.get_value("wallet.type"): - reason += ": missing wallet-type?" - raise LedgerError(reason=reason) + ledger = self._profile.context.inject_or(BaseLedger) + if not ledger: + reason = "No ledger available" + if not self._profile.context.settings.get_value("wallet.type"): + reason += ": missing wallet-type?" + raise LedgerError(reason=reason) async with ledger: endorsed_msg = await shield( @@ -371,23 +370,20 @@ async def complete_transaction(self, transaction: TransactionRecord): """ ledger_transaction = transaction.messages_attach[0]["data"]["json"] - async with self._profile.session() as session: - ledger = self._profile.inject(BaseLedger) - if not ledger: - reason = "No ledger available" - if not session.context.settings.get_value("wallet.type"): - reason += ": missing wallet-type?" - raise TransactionManagerError(reason) + ledger = self._profile.inject(BaseLedger) + if not ledger: + reason = "No ledger available" + if not self._profile.context.settings.get_value("wallet.type"): + reason += ": missing wallet-type?" + raise TransactionManagerError(reason) - async with ledger: - try: - ledger_response_json = await shield( - ledger.txn_submit( - ledger_transaction, sign=False, taa_accept=False - ) - ) - except (IndyIssuerError, LedgerError) as err: - raise TransactionManagerError(err.roll_up) from err + async with ledger: + try: + ledger_response_json = await shield( + ledger.txn_submit(ledger_transaction, sign=False, taa_accept=False) + ) + except (IndyIssuerError, LedgerError) as err: + raise TransactionManagerError(err.roll_up) from err ledger_response = json.loads(ledger_response_json) @@ -734,13 +730,12 @@ async def endorsed_txn_post_processing( would be stored in wallet. """ - async with self._profile.session() as session: - ledger = self._profile.inject(BaseLedger) - if not ledger: - reason = "No ledger available" - if not session.context.settings.get_value("wallet.type"): - reason += ": missing wallet-type?" - raise TransactionManagerError(reason) + ledger = self._profile.inject(BaseLedger) + if not ledger: + reason = "No ledger available" + if not self._profile.context.settings.get_value("wallet.type"): + reason += ": missing wallet-type?" + raise TransactionManagerError(reason) # setup meta_data to pass to future events, if necessary meta_data = transaction.meta_data @@ -802,7 +797,7 @@ async def endorsed_txn_post_processing( # If "auto_processing" is enabled, also upload tails file for this registry if auto_create_rev_reg: - await notify_revocation_tails_file_event( + await notify_revocation_reg_endorsed_event( self._profile, rev_reg_id, meta_data ) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/manager.py b/aries_cloudagent/protocols/issue_credential/v1_0/manager.py index 7aba66c330..88ceb3396d 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/manager.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/manager.py @@ -24,8 +24,6 @@ from ....multitenant.base import BaseMultitenantManager from ....revocation.indy import IndyRevocation from ....revocation.models.revocation_registry import RevocationRegistry -from ....revocation.models.issuer_rev_reg_record import IssuerRevRegRecord -from ....revocation.util import notify_revocation_reg_event from ....storage.base import BaseStorage from ....storage.error import StorageError, StorageNotFoundError from ....connections.models.conn_record import ConnRecord @@ -593,13 +591,25 @@ async def issue_credential( ) credential_ser = cred_ex_record._credential.ser - elif cred_ex_record.state == V10CredentialExchange.STATE_REQUEST_RECEIVED: - rev_reg = None - rev_reg_id = None - cred_rev_id = None + elif cred_ex_record.state != V10CredentialExchange.STATE_REQUEST_RECEIVED: + raise CredentialManagerError( + f"Credential exchange {cred_ex_record.credential_exchange_id} " + f"in {cred_ex_record.state} state " + f"(must be {V10CredentialExchange.STATE_REQUEST_RECEIVED})" + ) + + else: cred_offer_ser = cred_ex_record._credential_offer.ser cred_req_ser = cred_ex_record._credential_request.ser + cred_values = ( + cred_ex_record.credential_proposal_dict.credential_proposal.attr_dict( + decode=False + ) + ) schema_id = cred_ex_record.schema_id + cred_def_id = cred_ex_record.credential_definition_id + + issuer = self.profile.inject(IndyIssuer) multitenant_mgr = self.profile.inject_or(BaseMultitenantManager) if multitenant_mgr: ledger_exec_inst = IndyLedgerRequestsExecutor(self.profile) @@ -616,127 +626,59 @@ async def issue_credential( credential_definition = await ledger.get_credential_definition( cred_ex_record.credential_definition_id ) + revocable = credential_definition["value"].get("revocation") - tails_path = None - if credential_definition["value"].get("revocation"): - revoc = IndyRevocation(self._profile) - try: - active_rev_reg_rec = await revoc.get_active_issuer_rev_reg_record( - cred_ex_record.credential_definition_id - ) - rev_reg = await active_rev_reg_rec.get_registry() - rev_reg_id = rev_reg.registry_id - tails_path = rev_reg.tails_local_path - await rev_reg.get_or_fetch_local_tails_path() - - except StorageNotFoundError: - async with self._profile.session() as session: - posted_rev_reg_recs = ( - await IssuerRevRegRecord.query_by_cred_def_id( - session, - cred_ex_record.credential_definition_id, - state=IssuerRevRegRecord.STATE_POSTED, - ) - ) - if not posted_rev_reg_recs: - # Send next 2 rev regs, publish tails files in background - async with self._profile.session() as session: - old_rev_reg_recs = sorted( - await IssuerRevRegRecord.query_by_cred_def_id( - session, - cred_ex_record.credential_definition_id, - ) - ) # prefer to reuse prior rev reg size - cred_def_id = cred_ex_record.credential_definition_id - rev_reg_size = ( - old_rev_reg_recs[0].max_cred_num - if old_rev_reg_recs - else None - ) - for _ in range(2): - await notify_revocation_reg_event( - self.profile, - cred_def_id, - rev_reg_size, - auto_create_rev_reg=True, - ) - - if retries > 0: - LOGGER.info( - "Waiting 2s on posted rev reg for cred def %s, retrying", - cred_ex_record.credential_definition_id, - ) - await asyncio.sleep(2) - return await self.issue_credential( - cred_ex_record=cred_ex_record, - comment=comment, - retries=retries - 1, - ) - - raise CredentialManagerError( - f"Cred def id {cred_ex_record.credential_definition_id} " - "has no active revocation registry" - ) from None - del revoc - - credential_values = ( - cred_ex_record.credential_proposal_dict.credential_proposal.attr_dict( - decode=False - ) - ) - issuer = self._profile.inject(IndyIssuer) - try: - (credential_json, cred_rev_id) = await issuer.create_credential( - schema, - cred_offer_ser, - cred_req_ser, - credential_values, - cred_ex_record.credential_exchange_id, - rev_reg_id, - tails_path, - ) - credential_ser = json.loads(credential_json) - - # If the rev reg is now full - if rev_reg and rev_reg.max_creds == int(cred_rev_id): - async with self._profile.session() as session: - await active_rev_reg_rec.set_state( - session, - IssuerRevRegRecord.STATE_FULL, - ) - - # Send next 1 rev reg, publish tails file in background - cred_def_id = cred_ex_record.credential_definition_id - rev_reg_size = active_rev_reg_rec.max_cred_num - await notify_revocation_reg_event( - self.profile, + for attempt in range(max(retries, 1)): + if attempt > 0: + LOGGER.info( + "Waiting 2s before retrying credential issuance " + "for cred def '%s'", cred_def_id, - rev_reg_size, - auto_create_rev_reg=True, ) + await asyncio.sleep(2) - except IndyIssuerRevocationRegistryFullError: - # unlucky: duelling instance issued last cred near same time as us - async with self._profile.session() as session: - await active_rev_reg_rec.set_state( - session, - IssuerRevRegRecord.STATE_FULL, + if revocable: + revoc = IndyRevocation(self.profile) + registry_info = await revoc.get_or_create_active_registry( + cred_def_id ) + if not registry_info: + continue + del revoc + issuer_rev_reg, rev_reg = registry_info + rev_reg_id = issuer_rev_reg.revoc_reg_id + tails_path = rev_reg.tails_local_path + else: + rev_reg_id = None + tails_path = None - if retries > 0: - # use next rev reg; at worst, lucky instance is putting one up - LOGGER.info( - "Waiting 1s and retrying: revocation registry %s is full", - active_rev_reg_rec.revoc_reg_id, - ) - await asyncio.sleep(1) - return await self.issue_credential( - cred_ex_record=cred_ex_record, - comment=comment, - retries=retries - 1, + try: + (credential_json, cred_rev_id) = await issuer.create_credential( + schema, + cred_offer_ser, + cred_req_ser, + cred_values, + cred_ex_record.credential_exchange_id, + rev_reg_id, + tails_path, ) + except IndyIssuerRevocationRegistryFullError: + # unlucky, another instance filled the registry first + continue + + if rev_reg and rev_reg.max_creds <= int(cred_rev_id): + revoc = IndyRevocation(self.profile) + await revoc.handle_full_registry(rev_reg_id) + del revoc + + credential_ser = json.loads(credential_json) + break - raise + if not credential_ser: + raise CredentialManagerError( + f"Cred def id {cred_ex_record.credential_definition_id} " + "has no active revocation registry" + ) from None async with self._profile.transaction() as txn: cred_ex_record = await V10CredentialExchange.retrieve_by_id( @@ -754,12 +696,6 @@ async def issue_credential( cred_ex_record.revocation_id = cred_rev_id await cred_ex_record.save(txn, reason="issue credential") await txn.commit() - else: - raise CredentialManagerError( - f"Credential exchange {cred_ex_record.credential_exchange_id} " - f"in {cred_ex_record.state} state " - f"(must be {V10CredentialExchange.STATE_REQUEST_RECEIVED})" - ) credential_message = CredentialIssue( comment=comment, diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/formats/indy/handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/formats/indy/handler.py index 3561687065..c6f4c62cdf 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/formats/indy/handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/formats/indy/handler.py @@ -25,13 +25,9 @@ ) from ......messaging.decorators.attach_decorator import AttachDecorator from ......multitenant.base import BaseMultitenantManager -from ......revocation.models.issuer_rev_reg_record import IssuerRevRegRecord from ......revocation.models.revocation_registry import RevocationRegistry from ......revocation.indy import IndyRevocation -from ......revocation.util import notify_revocation_reg_event from ......storage.base import BaseStorage -from ......storage.error import StorageNotFoundError - from ...message_types import ( ATTACHMENT_FORMAT, @@ -328,18 +324,17 @@ async def issue_credential( self, cred_ex_record: V20CredExRecord, retries: int = 5 ) -> CredFormatAttachment: """Issue indy credential.""" - await self._check_uniqueness(cred_ex_record.cred_ex_id) - cred_offer = cred_ex_record.cred_offer.attachment(IndyCredFormatHandler.format) cred_request = cred_ex_record.cred_request.attachment( IndyCredFormatHandler.format ) - + cred_values = cred_ex_record.cred_offer.credential_preview.attr_dict( + decode=False + ) schema_id = cred_offer["schema_id"] cred_def_id = cred_offer["cred_def_id"] - rev_reg_id = None - rev_reg = None + issuer = self.profile.inject(IndyIssuer) multitenant_mgr = self.profile.inject_or(BaseMultitenantManager) if multitenant_mgr: ledger_exec_inst = IndyLedgerRequestsExecutor(self.profile) @@ -354,124 +349,63 @@ async def issue_credential( async with ledger: schema = await ledger.get_schema(schema_id) cred_def = await ledger.get_credential_definition(cred_def_id) + revocable = cred_def["value"].get("revocation") - tails_path = None - if cred_def["value"].get("revocation"): - revoc = IndyRevocation(self.profile) - try: - active_rev_reg_rec = await revoc.get_active_issuer_rev_reg_record( - cred_def_id + for attempt in range(max(retries, 1)): + if attempt > 0: + LOGGER.info( + "Waiting 2s before retrying credential issuance for cred def '%s'", + cred_def_id, ) - rev_reg = await active_rev_reg_rec.get_registry() - rev_reg_id = active_rev_reg_rec.revoc_reg_id - + await asyncio.sleep(2) + + await self._check_uniqueness(cred_ex_record.cred_ex_id) + + if revocable: + revoc = IndyRevocation(self.profile) + registry_info = await revoc.get_or_create_active_registry(cred_def_id) + if not registry_info: + continue + del revoc + issuer_rev_reg, rev_reg = registry_info + rev_reg_id = issuer_rev_reg.revoc_reg_id tails_path = rev_reg.tails_local_path - await rev_reg.get_or_fetch_local_tails_path() - - except StorageNotFoundError: - async with self.profile.session() as session: - posted_rev_reg_recs = await IssuerRevRegRecord.query_by_cred_def_id( - session, - cred_def_id, - state=IssuerRevRegRecord.STATE_POSTED, - ) - if not posted_rev_reg_recs: - # Send next 2 rev regs, publish tails files in background - async with self.profile.session() as session: - old_rev_reg_recs = sorted( - await IssuerRevRegRecord.query_by_cred_def_id( - session, - cred_def_id, - ) - ) # prefer to reuse prior rev reg size - rev_reg_size = ( - old_rev_reg_recs[0].max_cred_num if old_rev_reg_recs else None - ) - for _ in range(2): - await notify_revocation_reg_event( - self.profile, - cred_def_id, - rev_reg_size, - auto_create_rev_reg=True, - ) - - if retries > 0: - LOGGER.info( - ("Waiting 2s on posted rev reg " "for cred def %s, retrying"), - cred_def_id, - ) - await asyncio.sleep(2) - return await self.issue_credential( - cred_ex_record, - retries - 1, - ) + else: + rev_reg_id = None + tails_path = None - raise V20CredFormatError( - f"Cred def id {cred_def_id} " "has no active revocation registry" + try: + (cred_json, cred_rev_id) = await issuer.create_credential( + schema, + cred_offer, + cred_request, + cred_values, + cred_ex_record.cred_ex_id, + rev_reg_id, + tails_path, ) - del revoc - - cred_values = cred_ex_record.cred_offer.credential_preview.attr_dict( - decode=False - ) - issuer = self.profile.inject(IndyIssuer) - try: - (cred_json, cred_rev_id,) = await issuer.create_credential( - schema, - cred_offer, - cred_request, - cred_values, - cred_ex_record.cred_ex_id, - rev_reg_id, - tails_path, - ) + except IndyIssuerRevocationRegistryFullError: + # unlucky, another instance filled the registry first + continue detail_record = V20CredExRecordIndy( cred_ex_id=cred_ex_record.cred_ex_id, rev_reg_id=rev_reg_id, cred_rev_id=cred_rev_id, ) - - # If the rev reg is now full - if rev_reg and rev_reg.max_creds == int(cred_rev_id): - async with self.profile.session() as session: - await active_rev_reg_rec.set_state( - session, - IssuerRevRegRecord.STATE_FULL, - ) - - # Send next 1 rev reg, publish tails file in background - rev_reg_size = active_rev_reg_rec.max_cred_num - await notify_revocation_reg_event( - self.profile, cred_def_id, rev_reg_size, auto_create_rev_reg=True - ) - - async with self.profile.session() as session: + async with self._profile.session() as session: await detail_record.save(session, reason="v2.0 issue credential") - except IndyIssuerRevocationRegistryFullError: - # unlucky: duelling instance issued last cred near same time as us - async with self.profile.session() as session: - await active_rev_reg_rec.set_state( - session, - IssuerRevRegRecord.STATE_FULL, - ) - - if retries > 0: - # use next rev reg; at worst, lucky instance is putting one up - LOGGER.info( - "Waiting 1s and retrying: revocation registry %s is full", - active_rev_reg_rec.revoc_reg_id, - ) - await asyncio.sleep(1) - return await self.issue_credential( - cred_ex_record, - retries - 1, - ) + if rev_reg and rev_reg.max_creds <= int(cred_rev_id): + revoc = IndyRevocation(self.profile) + await revoc.handle_full_registry(rev_reg_id) + del revoc - raise + return self.get_format_data(CRED_20_ISSUE, json.loads(cred_json)) - return self.get_format_data(CRED_20_ISSUE, json.loads(cred_json)) + raise V20CredFormatError( + f"Cred def '{cred_def_id}' has no active revocation registry" + ) async def receive_credential( self, cred_ex_record: V20CredExRecord, cred_issue_message: V20CredIssue diff --git a/aries_cloudagent/revocation/indy.py b/aries_cloudagent/revocation/indy.py index 7d09ece62f..9e4562e88c 100644 --- a/aries_cloudagent/revocation/indy.py +++ b/aries_cloudagent/revocation/indy.py @@ -1,6 +1,6 @@ """Indy revocation registry management.""" -from typing import Sequence +from typing import Optional, Sequence, Tuple from ..core.profile import Profile from ..ledger.base import BaseLedger @@ -10,11 +10,20 @@ IndyLedgerRequestsExecutor, ) from ..multitenant.base import BaseMultitenantManager +from ..protocols.endorse_transaction.v1_0.util import ( + get_endorser_connection_id, + is_author_role, +) from ..storage.base import StorageNotFoundError -from .error import RevocationNotSupportedError, RevocationRegistryBadSizeError +from .error import ( + RevocationError, + RevocationNotSupportedError, + RevocationRegistryBadSizeError, +) from .models.issuer_rev_reg_record import IssuerRevRegRecord from .models.revocation_registry import RevocationRegistry +from .util import notify_revocation_reg_init_event class IndyRevocation: @@ -32,6 +41,8 @@ async def init_issuer_registry( max_cred_num: int = None, revoc_def_type: str = None, tag: str = None, + create_pending_rev_reg: bool = False, + endorser_connection_id: str = None, ) -> "IssuerRevRegRecord": """Create a new revocation registry record for a credential definition.""" multitenant_mgr = self._profile.inject_or(BaseMultitenantManager) @@ -69,11 +80,44 @@ async def init_issuer_registry( ) async with self._profile.session() as session: await record.save(session, reason="Init revocation registry") + + if endorser_connection_id is None and is_author_role(self._profile): + endorser_connection_id = await get_endorser_connection_id(self._profile) + if not endorser_connection_id: + raise RevocationError(reason="Endorser connection not found") + + await notify_revocation_reg_init_event( + self._profile, + record.record_id, + create_pending_rev_reg=create_pending_rev_reg, + endorser_connection_id=endorser_connection_id, + ) + return record + async def handle_full_registry(self, revoc_reg_id: str): + """Update the registry status and start the next registry generation.""" + async with self._profile.transaction() as txn: + registry = await IssuerRevRegRecord.retrieve_by_revoc_reg_id( + txn, revoc_reg_id, for_update=True + ) + if registry.state == IssuerRevRegRecord.STATE_FULL: + return + await registry.set_state( + txn, + IssuerRevRegRecord.STATE_FULL, + ) + await txn.commit() + + await self.init_issuer_registry( + registry.cred_def_id, + registry.max_cred_num, + registry.revoc_def_type, + ) + async def get_active_issuer_rev_reg_record( self, cred_def_id: str - ) -> "IssuerRevRegRecord": + ) -> IssuerRevRegRecord: """Return current active registry for issuing a given credential definition. Args: @@ -91,9 +135,7 @@ async def get_active_issuer_rev_reg_record( f"No active issuer revocation record found for cred def id {cred_def_id}" ) - async def get_issuer_rev_reg_record( - self, revoc_reg_id: str - ) -> "IssuerRevRegRecord": + async def get_issuer_rev_reg_record(self, revoc_reg_id: str) -> IssuerRevRegRecord: """Return a revocation registry record by identifier. Args: @@ -104,7 +146,7 @@ async def get_issuer_rev_reg_record( session, revoc_reg_id ) - async def list_issuer_registries(self) -> Sequence["IssuerRevRegRecord"]: + async def list_issuer_registries(self) -> Sequence[IssuerRevRegRecord]: """List the issuer's current revocation registries.""" async with self._profile.session() as session: return await IssuerRevRegRecord.query(session) @@ -129,7 +171,36 @@ async def get_issuer_rev_reg_delta( return rev_reg_delta - async def get_ledger_registry(self, revoc_reg_id: str) -> "RevocationRegistry": + async def get_or_create_active_registry( + self, cred_def_id: str, max_cred_num: int = None + ) -> Optional[Tuple[IssuerRevRegRecord, RevocationRegistry]]: + """Fetch the active revocation registry. + + If there is no active registry then creation of a new registry will be + triggered and the caller should retry after a delay. + """ + try: + active_rev_reg_rec = await self.get_active_issuer_rev_reg_record( + cred_def_id + ) + rev_reg = active_rev_reg_rec.get_registry() + await rev_reg.get_or_fetch_local_tails_path() + return active_rev_reg_rec, rev_reg + except StorageNotFoundError: + pass + + async with self._profile.session() as session: + rev_reg_recs = await IssuerRevRegRecord.query_by_cred_def_id( + session, cred_def_id, {"$neq": IssuerRevRegRecord.STATE_FULL} + ) + if not rev_reg_recs: + await self.init_issuer_registry( + cred_def_id, + max_cred_num=max_cred_num, + ) + return None + + async def get_ledger_registry(self, revoc_reg_id: str) -> RevocationRegistry: """Get a revocation registry from the ledger, fetching as necessary.""" if revoc_reg_id in IndyRevocation.REV_REG_CACHE: return IndyRevocation.REV_REG_CACHE[revoc_reg_id] @@ -143,7 +214,7 @@ async def get_ledger_registry(self, revoc_reg_id: str) -> "RevocationRegistry": IndyRevocation.REV_REG_CACHE[revoc_reg_id] = rev_reg return rev_reg - async def get_ledger_for_registry(self, revoc_reg_id: str) -> "BaseLedger": + async def get_ledger_for_registry(self, revoc_reg_id: str) -> BaseLedger: """Get the ledger for the given registry.""" multitenant_mgr = self._profile.inject_or(BaseMultitenantManager) if multitenant_mgr: diff --git a/aries_cloudagent/revocation/models/issuer_rev_reg_record.py b/aries_cloudagent/revocation/models/issuer_rev_reg_record.py index fbbaac9e43..d95a5f938b 100644 --- a/aries_cloudagent/revocation/models/issuer_rev_reg_record.py +++ b/aries_cloudagent/revocation/models/issuer_rev_reg_record.py @@ -5,6 +5,7 @@ import uuid from functools import total_ordering from os.path import join +from pathlib import Path from shutil import move from typing import Any, Mapping, Sequence, Union from urllib.parse import urlparse @@ -29,7 +30,10 @@ INDY_REV_REG_ID, UUIDFour, ) +from ...tails.base import BaseTailsServer + from ..error import RevocationError + from .revocation_registry import RevocationRegistry DEFAULT_REGISTRY_SIZE = 1000 @@ -62,7 +66,8 @@ class Meta: STATE_INIT = "init" STATE_GENERATED = "generated" - STATE_POSTED = "posted" # definition published: ephemeral, should last milliseconds + STATE_POSTED = "posted" # definition published + STATE_UPLOADED = "uploaded" # tails file uploaded STATE_ACTIVE = "active" # initial entry published, possibly subsequent entries STATE_FULL = "full" # includes corrupt @@ -227,7 +232,7 @@ async def send_def( profile: Profile, write_ledger: bool = True, endorser_did: str = None, - ): + ) -> dict: """Send the revocation registry definition to the ledger.""" if not (self.revoc_reg_def and self.issuer_did): raise RevocationError(f"Revocation registry {self.revoc_reg_id} undefined") @@ -261,7 +266,7 @@ async def send_entry( profile: Profile, write_ledger: bool = True, endorser_did: str = None, - ): + ) -> dict: """Send a registry entry to the ledger.""" if not ( self.revoc_reg_id @@ -274,7 +279,7 @@ async def send_entry( self._check_url(self.tails_public_uri) if self.state not in ( - IssuerRevRegRecord.STATE_POSTED, + IssuerRevRegRecord.STATE_UPLOADED, IssuerRevRegRecord.STATE_ACTIVE, IssuerRevRegRecord.STATE_FULL, # can still publish revocation deltas ): @@ -294,7 +299,7 @@ async def send_entry( write_ledger=write_ledger, endorser_did=endorser_did, ) - if self.state == IssuerRevRegRecord.STATE_POSTED: + if self.state == IssuerRevRegRecord.STATE_UPLOADED: self.state = IssuerRevRegRecord.STATE_ACTIVE # initial entry activates async with profile.session() as session: await self.save( @@ -303,6 +308,36 @@ async def send_entry( return rev_entry_res + @property + def has_local_tails_file(self) -> bool: + """Check if a local copy of the tails file is available.""" + return bool(self.tails_local_path) and Path(self.tails_local_path).is_file() + + async def upload_tails_file(self, profile: Profile): + """Upload the local tails file to the tails server.""" + tails_server = profile.inject_or(BaseTailsServer) + if not tails_server: + raise RevocationError("Tails server not configured") + if not self.has_local_tails_file: + raise RevocationError("Local tails file not found") + + (upload_success, reason) = await tails_server.upload_tails_file( + profile.context, + self.revoc_reg_id, + self.tails_local_path, + interval=0.8, + backoff=-0.5, + max_attempts=5, # heuristic: respect HTTP timeout + ) + if not upload_success: + raise RevocationError( + f"Tails file for rev reg {self.revoc_reg_id} failed to upload: {reason}" + ) + + self.state = IssuerRevRegRecord.STATE_UPLOADED + async with profile.session() as session: + await self.save(session, reason="Uploaded tails file") + async def mark_pending(self, session: ProfileSession, cred_rev_id: str) -> None: """Mark a credential revocation id as revoked pending publication to ledger. @@ -334,7 +369,7 @@ async def clear_pending( self.pending_pub.clear() await self.save(session, reason="Cleared pending revocations") - async def get_registry(self) -> RevocationRegistry: + def get_registry(self) -> RevocationRegistry: """Create a `RevocationRegistry` instance from this record.""" return RevocationRegistry( self.revoc_reg_id, @@ -359,10 +394,12 @@ async def query_by_cred_def_id( cred_def_id: The credential definition ID to filter by state: A state value to filter by """ - tag_filter = { - **{"cred_def_id": cred_def_id for _ in [""] if cred_def_id}, - **{"state": state for _ in [""] if state}, - } + tag_filter = dict( + filter( + lambda f: f[1] is not None, + (("cred_def_id", cred_def_id), ("state", state)), + ) + ) return await cls.query(session, tag_filter) @classmethod @@ -383,16 +420,19 @@ async def query_by_pending( @classmethod async def retrieve_by_revoc_reg_id( - cls, session: ProfileSession, revoc_reg_id: str + cls, session: ProfileSession, revoc_reg_id: str, for_update: bool = False ) -> "IssuerRevRegRecord": """Retrieve a revocation registry record by revocation registry ID. Args: session: The profile session to use revoc_reg_id: The revocation registry ID + for_update: Retrieve for update """ tag_filter = {"revoc_reg_id": revoc_reg_id} - return await cls.retrieve_by_tag_filter(session, tag_filter) + return await cls.retrieve_by_tag_filter( + session, tag_filter, for_update=for_update + ) async def set_state(self, session: ProfileSession, state: str = None): """Change the registry state (default full).""" diff --git a/aries_cloudagent/revocation/models/tests/test_issuer_rev_reg_record.py b/aries_cloudagent/revocation/models/tests/test_issuer_rev_reg_record.py index c023caa68b..34048f0423 100644 --- a/aries_cloudagent/revocation/models/tests/test_issuer_rev_reg_record.py +++ b/aries_cloudagent/revocation/models/tests/test_issuer_rev_reg_record.py @@ -128,7 +128,7 @@ async def test_generate_registry_etc(self): assert rec.state == IssuerRevRegRecord.STATE_ACTIVE self.ledger.send_revoc_reg_entry.assert_called_once() - rev_reg = await rec.get_registry() + rev_reg = rec.get_registry() assert type(rev_reg) == RevocationRegistry async with self.profile.session() as session: diff --git a/aries_cloudagent/revocation/routes.py b/aries_cloudagent/revocation/routes.py index f22ac1c0fd..b54547d63f 100644 --- a/aries_cloudagent/revocation/routes.py +++ b/aries_cloudagent/revocation/routes.py @@ -21,7 +21,6 @@ from ..core.event_bus import Event, EventBus from ..core.profile import Profile from ..indy.issuer import IndyIssuerError -from ..indy.util import tails_path from ..ledger.base import BaseLedger from ..ledger.multiple_ledger.base_manager import BaseMultipleLedgerManager from ..ledger.error import LedgerError @@ -51,7 +50,6 @@ ) from ..storage.base import BaseStorage from ..storage.error import StorageError, StorageNotFoundError -from ..tails.base import BaseTailsServer from .error import RevocationError, RevocationNotSupportedError from .indy import IndyRevocation @@ -64,12 +62,10 @@ from .recover import generate_ledger_rrrecovery_txn from .util import ( REVOCATION_EVENT_PREFIX, - REVOCATION_REG_EVENT, + REVOCATION_REG_INIT_EVENT, + REVOCATION_REG_ENDORSED_EVENT, REVOCATION_ENTRY_EVENT, - REVOCATION_TAILS_EVENT, - notify_revocation_reg_event, notify_revocation_entry_event, - notify_revocation_tails_file_event, ) @@ -958,24 +954,19 @@ async def upload_tails_file(request: web.BaseRequest): context: AdminRequestContext = request["context"] rev_reg_id = request.match_info["rev_reg_id"] + try: + revoc = IndyRevocation(context.profile) + rev_reg = await revoc.get_issuer_rev_reg_record(rev_reg_id) + except StorageNotFoundError as err: + raise web.HTTPNotFound(reason=err.roll_up) from err - tails_server = context.inject_or(BaseTailsServer) - if not tails_server: - raise web.HTTPForbidden(reason="No tails server configured") - - loc_tails_path = tails_path(rev_reg_id) - if not loc_tails_path: + if not rev_reg.has_local_tails_file: raise web.HTTPNotFound(reason=f"No local tails file for rev reg {rev_reg_id}") - (upload_success, reason) = await tails_server.upload_tails_file( - context, - rev_reg_id, - loc_tails_path, - interval=0.8, - backoff=-0.5, - max_attempts=16, - ) - if not upload_success: - raise web.HTTPInternalServerError(reason=reason) + + try: + await rev_reg.upload_tails_file(context.profile) + except RevocationError as e: + raise web.HTTPInternalServerError(reason=str(e)) return web.json_response({}) @@ -1175,7 +1166,6 @@ async def send_rev_reg_entry(request: web.BaseRequest): except StorageNotFoundError as err: raise web.HTTPNotFound(reason=err.roll_up) from err - except RevocationError as err: raise web.HTTPBadRequest(reason=err.roll_up) from err @@ -1287,162 +1277,145 @@ async def set_rev_reg_state(request: web.BaseRequest): def register_events(event_bus: EventBus): """Subscribe to any events we need to support.""" event_bus.subscribe( - re.compile(f"^{REVOCATION_EVENT_PREFIX}{REVOCATION_REG_EVENT}.*"), - on_revocation_registry_event, + re.compile(f"^{REVOCATION_EVENT_PREFIX}{REVOCATION_REG_INIT_EVENT}.*"), + on_revocation_registry_init_event, ) event_bus.subscribe( - re.compile(f"^{REVOCATION_EVENT_PREFIX}{REVOCATION_ENTRY_EVENT}.*"), - on_revocation_entry_event, + re.compile(f"^{REVOCATION_EVENT_PREFIX}{REVOCATION_REG_ENDORSED_EVENT}.*"), + on_revocation_registry_endorsed_event, ) event_bus.subscribe( - re.compile(f"^{REVOCATION_EVENT_PREFIX}{REVOCATION_TAILS_EVENT}.*"), - on_revocation_tails_file_event, + re.compile(f"^{REVOCATION_EVENT_PREFIX}{REVOCATION_ENTRY_EVENT}.*"), + on_revocation_entry_event, ) -async def on_revocation_registry_event(profile: Profile, event: Event): - """Handle revocation registry event.""" - if "endorser" in event.payload: +async def on_revocation_registry_init_event(profile: Profile, event: Event): + """Handle revocation registry initiation event.""" + meta_data = event.payload + if "endorser" in meta_data: # TODO error handling - for now just let exceptions get raised async with profile.session() as session: connection = await ConnRecord.retrieve_by_id( - session, event.payload["endorser"]["connection_id"] + session, meta_data["endorser"]["connection_id"] ) endorser_info = await connection.metadata_get(session, "endorser_info") endorser_did = endorser_info["endorser_did"] write_ledger = False - create_transaction_for_endorser = True else: endorser_did = None write_ledger = True - create_transaction_for_endorser = False - cred_def_id = event.payload["context"]["cred_def_id"] - rev_reg_size = event.payload["context"]["rev_reg_size"] - try: - tails_base_url = profile.settings.get("tails_server_base_url") - if not tails_base_url: - raise RevocationError("tails_server_base_url not configured") - - # Create registry - revoc = IndyRevocation(profile) - registry_record = await revoc.init_issuer_registry( - cred_def_id, - max_cred_num=rev_reg_size, - ) - - await shield(registry_record.generate_registry(profile)) + tails_base_url = profile.settings.get("tails_server_base_url") + if not tails_base_url: + raise RevocationError("tails_server_base_url not configured") - await registry_record.set_tails_file_public_uri( + # Generate the registry and upload the tails file + async def generate(rr_record: IssuerRevRegRecord) -> dict: + await rr_record.generate_registry(profile) + await rr_record.set_tails_file_public_uri( profile, f"{tails_base_url}/{registry_record.revoc_reg_id}", ) - rev_reg_resp = await registry_record.send_def( + rev_reg_resp = await rr_record.send_def( profile, write_ledger=write_ledger, endorser_did=endorser_did, ) - except RevocationError: - raise + if write_ledger: + # Upload the tails file + await rr_record.upload_tails_file(profile) - if not create_transaction_for_endorser: - meta_data = event.payload - rev_reg_id = registry_record.revoc_reg_id - meta_data["context"]["rev_reg_id"] = rev_reg_id - auto_create_rev_reg = meta_data["processing"].get("auto_create_rev_reg", False) - - # Notify event - if auto_create_rev_reg: - await notify_revocation_entry_event(profile, rev_reg_id, meta_data) - - else: - transaction_manager = TransactionManager(profile) - try: - revo_transaction = await transaction_manager.create_record( - messages_attach=rev_reg_resp["result"], - connection_id=connection.connection_id, - meta_data=event.payload, - ) - except StorageError as err: - raise TransactionManagerError(reason=err.roll_up) from err - - # if auto-request, send the request to the endorser - if profile.settings.get_value("endorser.auto_request"): + # Post the initial revocation entry + await notify_revocation_entry_event(profile, record_id, meta_data) + else: + transaction_manager = TransactionManager(profile) try: - ( - revo_transaction, - revo_transaction_request, - ) = await transaction_manager.create_request( - transaction=revo_transaction, - # TODO see if we need to parameterize these params - # expires_time=expires_time, - # endorser_write_txn=endorser_write_txn, + revo_transaction = await transaction_manager.create_record( + messages_attach=rev_reg_resp["result"], + connection_id=connection.connection_id, + meta_data=event.payload, ) - except (StorageError, TransactionManagerError) as err: + except StorageError as err: raise TransactionManagerError(reason=err.roll_up) from err - responder = profile.inject_or(BaseResponder) - if responder: - await responder.send( - revo_transaction_request, - connection_id=connection.connection_id, - ) - else: - LOGGER.warning( - "Configuration has no BaseResponder: cannot update " - "revocation on cred def %s", - cred_def_id, - ) + # if auto-request, send the request to the endorser + if profile.settings.get_value("endorser.auto_request"): + try: + ( + revo_transaction, + revo_transaction_request, + ) = await transaction_manager.create_request( + transaction=revo_transaction, + # TODO see if we need to parameterize these params + # expires_time=expires_time, + # endorser_write_txn=endorser_write_txn, + ) + except (StorageError, TransactionManagerError) as err: + raise TransactionManagerError(reason=err.roll_up) from err + + responder = profile.inject_or(BaseResponder) + if responder: + await responder.send( + revo_transaction_request, + connection_id=connection.connection_id, + ) + else: + LOGGER.warning( + "Configuration has no BaseResponder: cannot update " + "revocation on registry ID: %s", + record_id, + ) + + record_id = meta_data["context"]["issuer_rev_id"] + async with profile.session() as session: + registry_record = await IssuerRevRegRecord.retrieve_by_id(session, record_id) + await shield(generate(registry_record)) + + create_pending_rev_reg = meta_data["processing"].get( + "create_pending_rev_reg", False + ) + if write_ledger and create_pending_rev_reg: + revoc = IndyRevocation(profile) + await revoc.init_issuer_registry( + registry_record.cred_def_id, + registry_record.max_cred_num, + registry_record.revoc_def_type, + ) async def on_revocation_entry_event(profile: Profile, event: Event): """Handle revocation entry event.""" - if "endorser" in event.payload: + meta_data = event.payload + if "endorser" in meta_data: # TODO error handling - for now just let exceptions get raised async with profile.session() as session: connection = await ConnRecord.retrieve_by_id( - session, event.payload["endorser"]["connection_id"] + session, meta_data["endorser"]["connection_id"] ) endorser_info = await connection.metadata_get(session, "endorser_info") endorser_did = endorser_info["endorser_did"] write_ledger = False - create_transaction_for_endorser = True else: endorser_did = None write_ledger = True - create_transaction_for_endorser = False - rev_reg_id = event.payload["context"]["rev_reg_id"] - try: - tails_base_url = profile.settings.get("tails_server_base_url") - if not tails_base_url: - raise RevocationError("tails_server_base_url not configured") - - revoc = IndyRevocation(profile) - registry_record = await revoc.get_issuer_rev_reg_record(rev_reg_id) - rev_entry_resp = await registry_record.send_entry( - profile, - write_ledger=write_ledger, - endorser_did=endorser_did, - ) - except RevocationError: - raise - - if not create_transaction_for_endorser: - meta_data = event.payload - auto_create_rev_reg = meta_data["processing"].get("auto_create_rev_reg", False) - - # Notify event - if auto_create_rev_reg: - await notify_revocation_tails_file_event(profile, rev_reg_id, meta_data) + record_id = meta_data["context"]["issuer_rev_id"] + async with profile.session() as session: + registry_record = await IssuerRevRegRecord.retrieve_by_id(session, record_id) + rev_entry_resp = await registry_record.send_entry( + profile, + write_ledger=write_ledger, + endorser_did=endorser_did, + ) - else: + if not write_ledger: transaction_manager = TransactionManager(profile) try: revo_transaction = await transaction_manager.create_record( messages_attach=rev_entry_resp["result"], connection_id=connection.connection_id, - meta_data=event.payload, + meta_data=meta_data, ) except StorageError as err: raise RevocationError(err.roll_up) from err @@ -1472,57 +1445,37 @@ async def on_revocation_entry_event(profile: Profile, event: Event): LOGGER.warning( "Configuration has no BaseResponder: cannot update " "revocation on cred def %s", - event.payload["endorser"]["cred_def_id"], + meta_data["endorser"]["cred_def_id"], ) -async def on_revocation_tails_file_event(profile: Profile, event: Event): +async def on_revocation_registry_endorsed_event(profile: Profile, event: Event): """Handle revocation tails file event.""" - tails_base_url = profile.settings.get("tails_server_base_url") - if not tails_base_url: - raise RevocationError("tails_server_base_url not configured") - - tails_server = profile.inject(BaseTailsServer) - revoc_reg_id = event.payload["context"]["rev_reg_id"] - tails_local_path = tails_path(revoc_reg_id) - (upload_success, reason) = await tails_server.upload_tails_file( - profile.context, - revoc_reg_id, - tails_local_path, - interval=0.8, - backoff=-0.5, - max_attempts=5, # heuristic: respect HTTP timeout - ) - if not upload_success: - raise RevocationError( - f"Tails file for rev reg {revoc_reg_id} failed to upload: {reason}" - ) + meta_data = event.payload + rev_reg_id = meta_data["context"]["rev_reg_id"] + revoc = IndyRevocation(profile) + registry_record = await revoc.get_issuer_rev_reg_record(rev_reg_id) + # NOTE: if there are multiple pods, then the one processing this + # event may not be the one that generated the tails file. + await registry_record.upload_tails_file(profile) # create a "pending" registry if one is requested # (this is done automatically when creating a credential definition, so that when a # revocation registry fills up, we ca continue to issue credentials without a # delay) - create_pending_rev_reg = event.payload["processing"].get( + create_pending_rev_reg = meta_data["processing"].get( "create_pending_rev_reg", False ) if create_pending_rev_reg: - meta_data = event.payload - del meta_data["context"]["rev_reg_id"] - del meta_data["processing"]["create_pending_rev_reg"] - cred_def_id = meta_data["context"]["cred_def_id"] - rev_reg_size = meta_data["context"].get("rev_reg_size", None) - auto_create_rev_reg = meta_data["processing"].get("auto_create_rev_reg", False) endorser_connection_id = ( meta_data["endorser"].get("connection_id", None) if "endorser" in meta_data else None ) - - await notify_revocation_reg_event( - profile, - cred_def_id, - rev_reg_size, - auto_create_rev_reg=auto_create_rev_reg, + await revoc.init_issuer_registry( + registry_record.cred_def_id, + registry_record.max_cred_num, + registry_record.revoc_def_type, endorser_connection_id=endorser_connection_id, ) diff --git a/aries_cloudagent/revocation/util.py b/aries_cloudagent/revocation/util.py index 50dd08a50c..f81e26dc4a 100644 --- a/aries_cloudagent/revocation/util.py +++ b/aries_cloudagent/revocation/util.py @@ -4,79 +4,50 @@ from typing import Sequence from ..core.profile import Profile -from ..protocols.endorse_transaction.v1_0.util import ( - get_endorser_connection_id, - is_author_role, -) REVOCATION_EVENT_PREFIX = "acapy::REVOCATION::" EVENT_LISTENER_PATTERN = re.compile(f"^{REVOCATION_EVENT_PREFIX}(.*)?$") -REVOCATION_REG_EVENT = "REGISTRY" +REVOCATION_REG_INIT_EVENT = "REGISTRY_INIT" +REVOCATION_REG_ENDORSED_EVENT = "REGISTRY_ENDORSED" REVOCATION_ENTRY_EVENT = "ENTRY" -REVOCATION_TAILS_EVENT = "TAILS" REVOCATION_PUBLISHED_EVENT = "published" REVOCATION_CLEAR_PENDING_EVENT = "clear-pending" -async def notify_revocation_reg_event( +async def notify_revocation_reg_init_event( profile: Profile, - cred_def_id: str, - rev_reg_size: int, - auto_create_rev_reg: bool = False, + issuer_rev_id: str, create_pending_rev_reg: bool = False, endorser_connection_id: str = None, ): - """Send notification for a revocation registry event.""" + """Send notification for a revocation registry init event.""" meta_data = { "context": { - "cred_def_id": cred_def_id, - "support_revocation": True, - "rev_reg_size": rev_reg_size, - }, - "processing": { - "auto_create_rev_reg": auto_create_rev_reg, + "issuer_rev_id": issuer_rev_id, }, + "processing": {"create_pending_rev_reg": create_pending_rev_reg}, } - if ( - (not endorser_connection_id) - and is_author_role(profile) - and "endorser" not in meta_data - ): - endorser_connection_id = await get_endorser_connection_id(profile) - if not endorser_connection_id: - raise Exception(reason="No endorser connection found") - if create_pending_rev_reg: - meta_data["processing"]["create_pending_rev_reg"] = create_pending_rev_reg if endorser_connection_id: meta_data["endorser"] = {"connection_id": endorser_connection_id} - event_id = REVOCATION_EVENT_PREFIX + REVOCATION_REG_EVENT + "::" + cred_def_id - await profile.notify( - event_id, - meta_data, - ) + topic = f"{REVOCATION_EVENT_PREFIX}{REVOCATION_REG_INIT_EVENT}::{issuer_rev_id}" + await profile.notify(topic, meta_data) async def notify_revocation_entry_event( - profile: Profile, rev_reg_id: str, meta_data: dict + profile: Profile, issuer_rev_id: str, meta_data: dict ): - """Send notification for a revocation registry event.""" - event_id = REVOCATION_EVENT_PREFIX + REVOCATION_ENTRY_EVENT + "::" + rev_reg_id - await profile.notify( - event_id, - meta_data, - ) + """Send notification for a revocation registry entry event.""" + topic = f"{REVOCATION_EVENT_PREFIX}{REVOCATION_ENTRY_EVENT}::{issuer_rev_id}" + await profile.notify(topic, meta_data) -async def notify_revocation_tails_file_event( +async def notify_revocation_reg_endorsed_event( profile: Profile, rev_reg_id: str, meta_data: dict ): - """Send notification for a revocation tails file event.""" - event_id = REVOCATION_EVENT_PREFIX + REVOCATION_TAILS_EVENT + "::" + rev_reg_id - await profile.notify( - event_id, - meta_data, - ) + """Send notification for a revocation registry endorsement event.""" + topic = f"{REVOCATION_EVENT_PREFIX}{REVOCATION_REG_ENDORSED_EVENT}::{rev_reg_id}" + await profile.notify(topic, meta_data) async def notify_revocation_published_event( From 86624306a1c6741070d654a2ccb99fe237513409 Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Wed, 15 Jun 2022 20:18:23 -0700 Subject: [PATCH 355/872] fixing unit tests Signed-off-by: Andrew Whitehead --- .../issue_credential/v1_0/manager.py | 2 +- .../v1_0/tests/test_manager.py | 182 +++++----------- .../v2_0/formats/indy/handler.py | 6 +- .../v2_0/formats/indy/tests/test_handler.py | 195 ++++-------------- .../tests/test_issuer_rev_reg_record.py | 7 +- .../revocation/tests/test_routes.py | 75 +++---- 6 files changed, 133 insertions(+), 334 deletions(-) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/manager.py b/aries_cloudagent/protocols/issue_credential/v1_0/manager.py index 88ceb3396d..2a1c37dd38 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/manager.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/manager.py @@ -666,7 +666,7 @@ async def issue_credential( # unlucky, another instance filled the registry first continue - if rev_reg and rev_reg.max_creds <= int(cred_rev_id): + if revocable and rev_reg.max_creds <= int(cred_rev_id): revoc = IndyRevocation(self.profile) await revoc.handle_full_registry(rev_reg_id) del revoc diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py index e889aae662..ba0e65f924 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py @@ -5,6 +5,7 @@ from time import time from asynctest import mock as async_mock, TestCase as AsyncTestCase +from more_itertools import side_effect from .....core.in_memory import InMemoryProfile from .....cache.base import BaseCache @@ -880,7 +881,7 @@ async def test_receive_request_no_cred_ex_with_offer_found(self): for_update=True, ) - async def test_issue_credential(self): + async def test_issue_credential_revocable(self): connection_id = "test_conn_id" comment = "comment" cred_values = {"attr": "value"} @@ -918,18 +919,18 @@ async def test_issue_credential(self): with async_mock.patch.object( test_module, "IndyRevocation", autospec=True ) as revoc, async_mock.patch.object( - asyncio, "ensure_future", autospec=True - ) as asyncio_mock, async_mock.patch.object( V10CredentialExchange, "save", autospec=True ) as save_ex: - revoc.return_value.get_active_issuer_rev_reg_record = async_mock.CoroutineMock( - return_value=async_mock.MagicMock( # active_rev_reg_rec - get_registry=async_mock.CoroutineMock( - return_value=async_mock.MagicMock( # rev_reg - registry_id=REV_REG_ID, - tails_local_path="dummy-path", - get_or_fetch_local_tails_path=async_mock.CoroutineMock(), - ) + revoc.return_value.get_or_create_active_registry = async_mock.CoroutineMock( + return_value=( + async_mock.MagicMock( # active_rev_reg_rec + revoc_reg_id=REV_REG_ID, + ), + async_mock.MagicMock( # rev_reg + registry_id=REV_REG_ID, + tails_local_path="dummy-path", + get_or_fetch_local_tails_path=async_mock.CoroutineMock(), + max_creds=10, ), ) ) @@ -1078,33 +1079,28 @@ async def test_issue_credential_fills_rr(self): with async_mock.patch.object( test_module, "IndyRevocation", autospec=True ) as revoc, async_mock.patch.object( - asyncio, "ensure_future", autospec=True - ) as asyncio_mock, async_mock.patch.object( V10CredentialExchange, "save", autospec=True ) as save_ex: revoc.return_value = async_mock.MagicMock( - get_active_issuer_rev_reg_record=( + get_or_create_active_registry=( async_mock.CoroutineMock( - return_value=async_mock.MagicMock( # active_rev_reg_rec - get_registry=async_mock.CoroutineMock( - return_value=async_mock.MagicMock( # rev_reg - registry_id=REV_REG_ID, - tails_local_path="dummy-path", - max_creds=1000, - get_or_fetch_local_tails_path=( - async_mock.CoroutineMock() - ), - ) + return_value=( + async_mock.MagicMock( # active_rev_reg_rec + revoc_reg_id=REV_REG_ID, + set_state=async_mock.CoroutineMock(), + ), + async_mock.MagicMock( # rev_reg + registry_id=REV_REG_ID, + tails_local_path="dummy-path", + max_creds=1000, + get_or_fetch_local_tails_path=( + async_mock.CoroutineMock() + ), ), - set_state=async_mock.CoroutineMock(), ) ) ), - init_issuer_registry=async_mock.CoroutineMock( - return_value=async_mock.MagicMock( # pending_rev_reg_rec - stage_pending_registry=async_mock.CoroutineMock() - ) - ), + handle_full_registry=async_mock.CoroutineMock(), ) (ret_exchange, ret_cred_issue) = await self.manager.issue_credential( stored_exchange, comment=comment, retries=0 @@ -1122,6 +1118,8 @@ async def test_issue_credential_fills_rr(self): "dummy-path", ) + revoc.return_value.handle_full_registry.assert_awaited_once_with(REV_REG_ID) + assert ret_exchange._credential.ser == cred assert ret_cred_issue.indy_credential() == cred assert ret_exchange.state == V10CredentialExchange.STATE_ISSUED @@ -1191,28 +1189,28 @@ async def test_issue_credential_no_active_rr_no_retries(self): ), ) with async_mock.patch.object( - test_module, "IssuerRevRegRecord", autospec=True - ) as issuer_rr_rec, async_mock.patch.object( test_module, "IndyRevocation", autospec=True - ) as revoc, async_mock.patch.object( - V10CredentialExchange, "save", autospec=True - ) as save_ex: - revoc.return_value.get_active_issuer_rev_reg_record = ( - async_mock.CoroutineMock(side_effect=test_module.StorageNotFoundError()) - ) - revoc.return_value.init_issuer_registry = async_mock.CoroutineMock( - return_value=async_mock.MagicMock( # pending_rev_reg_rec - stage_pending_registry=async_mock.CoroutineMock() - ) - ) - issuer_rr_rec.query_by_cred_def_id = async_mock.CoroutineMock( - return_value=[] + ) as revoc: + revoc.return_value.get_or_create_active_registry = async_mock.CoroutineMock( + side_effect=[ + None, + ( + async_mock.MagicMock( # active_rev_reg_rec + revoc_reg_id=REV_REG_ID, + set_state=async_mock.CoroutineMock(), + ), + async_mock.MagicMock( # rev_reg + tails_local_path="dummy-path", + get_or_fetch_local_tails_path=(async_mock.CoroutineMock()), + ), + ), + ] ) - with self.assertRaises(CredentialManagerError) as x_cred_mgr: + with self.assertRaises(CredentialManagerError) as context: await self.manager.issue_credential( stored_exchange, comment=comment, retries=0 ) - assert "has no active revocation registry" in x_cred_mgr.message + assert "has no active revocation registry" in context.message async def test_issue_credential_no_active_rr_retry(self): connection_id = "test_conn_id" @@ -1256,99 +1254,17 @@ async def test_issue_credential_no_active_rr_retry(self): ) ), ) - with async_mock.patch.object( - test_module, "IssuerRevRegRecord", autospec=True - ) as issuer_rr_rec, async_mock.patch.object( - test_module, "IndyRevocation", autospec=True - ) as revoc, async_mock.patch.object( - V10CredentialExchange, "save", autospec=True - ) as save_ex: - revoc.return_value.get_active_issuer_rev_reg_record = ( - async_mock.CoroutineMock(side_effect=test_module.StorageNotFoundError()) - ) - issuer_rr_rec.query_by_cred_def_id = async_mock.CoroutineMock( - side_effect=[ - [], # posted_rev_reg_recs - [async_mock.MagicMock(max_cred_num=1000)], # old_rev_reg_recs - ] - * 2 - ) - revoc.return_value.init_issuer_registry = async_mock.CoroutineMock( - return_value=async_mock.MagicMock( # pending_rev_reg_rec - stage_pending_registry=async_mock.CoroutineMock() - ) - ) - with self.assertRaises(CredentialManagerError) as x_cred_mgr: - await self.manager.issue_credential( - stored_exchange, comment=comment, retries=1 - ) - assert "has no active revocation registry" in x_cred_mgr.message - - async def test_issue_credential_rr_full(self): - connection_id = "test_conn_id" - comment = "comment" - cred_values = {"attr": "value"} - thread_id = "thread-id" - - stored_exchange = V10CredentialExchange( - credential_exchange_id="dummy-cxid", - connection_id=connection_id, - credential_definition_id=CRED_DEF_ID, - credential_offer=INDY_OFFER, - credential_request=INDY_CRED_REQ, - credential_proposal_dict=CredentialProposal( - credential_proposal=CredentialPreview.deserialize( - {"attributes": [{"name": "attr", "value": "value"}]} - ), - cred_def_id=CRED_DEF_ID, - schema_id=SCHEMA_ID, - ).serialize(), - initiator=V10CredentialExchange.INITIATOR_SELF, - role=V10CredentialExchange.ROLE_ISSUER, - state=V10CredentialExchange.STATE_REQUEST_RECEIVED, - thread_id=thread_id, - new_with_id=True, - ) - await stored_exchange.save(self.session) - - issuer = async_mock.MagicMock() - cred = {"indy": "credential"} - issuer.create_credential = async_mock.CoroutineMock( - side_effect=test_module.IndyIssuerRevocationRegistryFullError("Nope") - ) - self.context.injector.bind_instance(IndyIssuer, issuer) - self.context.injector.bind_instance( - IndyLedgerRequestsExecutor, - async_mock.MagicMock( - get_ledger_for_identifier=async_mock.CoroutineMock( - return_value=("test_ledger_id", self.ledger) - ) - ), - ) with async_mock.patch.object( test_module, "IndyRevocation", autospec=True ) as revoc: - revoc.return_value.get_active_issuer_rev_reg_record = ( - async_mock.CoroutineMock( - return_value=async_mock.MagicMock( # active_rev_reg_rec - revoc_reg_id=REV_REG_ID, - set_state=async_mock.CoroutineMock(), - get_registry=async_mock.CoroutineMock( - return_value=async_mock.MagicMock( # rev_reg - tails_local_path="dummy-path", - get_or_fetch_local_tails_path=( - async_mock.CoroutineMock() - ), - ) - ), - ) - ) + revoc.return_value.get_or_create_active_registry = async_mock.CoroutineMock( + return_value=None ) - - with self.assertRaises(test_module.IndyIssuerRevocationRegistryFullError): + with self.assertRaises(CredentialManagerError) as context: await self.manager.issue_credential( stored_exchange, comment=comment, retries=1 ) + assert "has no active revocation registry" in context.message async def test_receive_credential(self): connection_id = "test_conn_id" diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/formats/indy/handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/formats/indy/handler.py index c6f4c62cdf..fbd2882b7d 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/formats/indy/handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/formats/indy/handler.py @@ -324,6 +324,8 @@ async def issue_credential( self, cred_ex_record: V20CredExRecord, retries: int = 5 ) -> CredFormatAttachment: """Issue indy credential.""" + await self._check_uniqueness(cred_ex_record.cred_ex_id) + cred_offer = cred_ex_record.cred_offer.attachment(IndyCredFormatHandler.format) cred_request = cred_ex_record.cred_request.attachment( IndyCredFormatHandler.format @@ -359,8 +361,6 @@ async def issue_credential( ) await asyncio.sleep(2) - await self._check_uniqueness(cred_ex_record.cred_ex_id) - if revocable: revoc = IndyRevocation(self.profile) registry_info = await revoc.get_or_create_active_registry(cred_def_id) @@ -396,7 +396,7 @@ async def issue_credential( async with self._profile.session() as session: await detail_record.save(session, reason="v2.0 issue credential") - if rev_reg and rev_reg.max_creds <= int(cred_rev_id): + if revocable and rev_reg.max_creds <= int(cred_rev_id): revoc = IndyRevocation(self.profile) await revoc.handle_full_registry(rev_reg_id) del revoc diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/formats/indy/tests/test_handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/formats/indy/tests/test_handler.py index c759e37e17..e97fd6d457 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/formats/indy/tests/test_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/formats/indy/tests/test_handler.py @@ -5,6 +5,7 @@ from asynctest import TestCase as AsyncTestCase from asynctest import mock as async_mock from marshmallow import ValidationError +from more_itertools import side_effect from .. import handler as test_module @@ -678,7 +679,7 @@ async def test_receive_request_no_offer(self): in str(context.exception) ) - async def test_issue_credential(self): + async def test_issue_credential_revocable(self): attr_values = { "legalName": "value", "jurisdictionId": "value", @@ -729,22 +730,17 @@ async def test_issue_credential(self): with async_mock.patch.object( test_module, "IndyRevocation", autospec=True - ) as revoc, async_mock.patch.object( - asyncio, "ensure_future", autospec=True - ) as asyncio_mock: - revoc.return_value.get_active_issuer_rev_reg_record = ( - async_mock.CoroutineMock( - return_value=async_mock.MagicMock( # active_rev_reg_rec + ) as revoc: + revoc.return_value.get_or_create_active_registry = async_mock.CoroutineMock( + return_value=( + async_mock.MagicMock( # active_rev_reg_rec revoc_reg_id=REV_REG_ID, - get_registry=async_mock.CoroutineMock( - return_value=async_mock.MagicMock( # rev_reg - tails_local_path="dummy-path", - get_or_fetch_local_tails_path=( - async_mock.CoroutineMock() - ), - ) - ), - ) + ), + async_mock.MagicMock( # rev_reg + tails_local_path="dummy-path", + get_or_fetch_local_tails_path=(async_mock.CoroutineMock()), + max_creds=10, + ), ) ) @@ -870,109 +866,6 @@ async def test_issue_credential_not_unique_x(self): assert "indy detail record already exists" in str(context.exception) - async def test_issue_credential_fills_revocation_registry(self): - attr_values = { - "legalName": "value", - "jurisdictionId": "value", - "incorporationDate": "value", - } - cred_rev_id = "1000" - - cred_preview = V20CredPreview( - attributes=[ - V20CredAttrSpec(name=k, value=v) for (k, v) in attr_values.items() - ] - ) - cred_offer = V20CredOffer( - credential_preview=cred_preview, - formats=[ - V20CredFormat( - attach_id="0", - format_=ATTACHMENT_FORMAT[CRED_20_OFFER][ - V20CredFormat.Format.INDY.api - ], - ) - ], - offers_attach=[AttachDecorator.data_base64(INDY_OFFER, ident="0")], - ) - cred_request = V20CredRequest( - formats=[ - V20CredFormat( - attach_id="0", - format_=ATTACHMENT_FORMAT[CRED_20_REQUEST][ - V20CredFormat.Format.INDY.api - ], - ) - ], - requests_attach=[AttachDecorator.data_base64(INDY_CRED_REQ, ident="0")], - ) - - cred_ex_record = V20CredExRecord( - cred_ex_id="dummy-cxid", - cred_offer=cred_offer.serialize(), - cred_request=cred_request.serialize(), - initiator=V20CredExRecord.INITIATOR_SELF, - role=V20CredExRecord.ROLE_ISSUER, - state=V20CredExRecord.STATE_REQUEST_RECEIVED, - ) - - self.issuer.create_credential = async_mock.CoroutineMock( - return_value=(json.dumps(INDY_CRED), cred_rev_id) - ) - - with async_mock.patch.object( - test_module, "IndyRevocation", autospec=True - ) as revoc, async_mock.patch.object( - asyncio, "ensure_future", autospec=True - ) as asyncio_mock: - revoc.return_value = async_mock.MagicMock( - get_active_issuer_rev_reg_record=( - async_mock.CoroutineMock( - return_value=async_mock.MagicMock( # active_rev_reg_rec - revoc_reg_id=REV_REG_ID, - get_registry=async_mock.CoroutineMock( - return_value=async_mock.MagicMock( # rev_reg - tails_local_path="dummy-path", - max_creds=1000, - get_or_fetch_local_tails_path=( - async_mock.CoroutineMock() - ), - ) - ), - set_state=async_mock.CoroutineMock(), - ) - ) - ), - init_issuer_registry=async_mock.CoroutineMock( - return_value=async_mock.MagicMock( # pending_rev_reg_rec - stage_pending_registry=async_mock.CoroutineMock() - ) - ), - ) - - (cred_format, attachment) = await self.handler.issue_credential( - cred_ex_record, retries=0 - ) - - self.issuer.create_credential.assert_called_once_with( - SCHEMA, - INDY_OFFER, - INDY_CRED_REQ, - attr_values, - cred_ex_record.cred_ex_id, - REV_REG_ID, - "dummy-path", - ) - - # assert identifier match - assert cred_format.attach_id == self.handler.format.api == attachment.ident - - # assert content of attachment is proposal data - assert attachment.content == INDY_CRED - - # assert data is encoded as base64 - assert attachment.data.base64 - async def test_issue_credential_no_active_rr_no_retries(self): attr_values = { "legalName": "value", @@ -1024,22 +917,11 @@ async def test_issue_credential_no_active_rr_no_retries(self): ) with async_mock.patch.object( - test_module, "IssuerRevRegRecord", autospec=True - ) as issuer_rr_rec, async_mock.patch.object( test_module, "IndyRevocation", autospec=True ) as revoc: - revoc.return_value.get_active_issuer_rev_reg_record = ( - async_mock.CoroutineMock(side_effect=StorageNotFoundError()) - ) - revoc.return_value.init_issuer_registry = async_mock.CoroutineMock( - return_value=async_mock.MagicMock( # pending_rev_reg_rec - stage_pending_registry=async_mock.CoroutineMock() - ) - ) - issuer_rr_rec.query_by_cred_def_id = async_mock.CoroutineMock( - return_value=[] + revoc.return_value.get_or_create_active_registry = async_mock.CoroutineMock( + return_value=() ) - with self.assertRaises(V20CredFormatError) as context: await self.handler.issue_credential(cred_ex_record, retries=0) assert "has no active revocation registry" in str(context.exception) @@ -1095,24 +977,22 @@ async def test_issue_credential_no_active_rr_retry(self): ) with async_mock.patch.object( - test_module, "IssuerRevRegRecord", autospec=True - ) as issuer_rr_rec, async_mock.patch.object( test_module, "IndyRevocation", autospec=True ) as revoc: - revoc.return_value.get_active_issuer_rev_reg_record = ( - async_mock.CoroutineMock(side_effect=StorageNotFoundError()) - ) - issuer_rr_rec.query_by_cred_def_id = async_mock.CoroutineMock( + revoc.return_value.get_or_create_active_registry = async_mock.CoroutineMock( side_effect=[ - [], # posted_rev_reg_recs - [async_mock.MagicMock(max_cred_num=1000)], # old_rev_reg_recs + None, + ( + async_mock.MagicMock( # active_rev_reg_rec + revoc_reg_id=REV_REG_ID, + set_state=async_mock.CoroutineMock(), + ), + async_mock.MagicMock( # rev_reg + tails_local_path="dummy-path", + get_or_fetch_local_tails_path=(async_mock.CoroutineMock()), + ), + ), ] - * 2 - ) - revoc.return_value.init_issuer_registry = async_mock.CoroutineMock( - return_value=async_mock.MagicMock( # pending_rev_reg_rec - stage_pending_registry=async_mock.CoroutineMock() - ) ) with self.assertRaises(V20CredFormatError) as context: @@ -1171,25 +1051,22 @@ async def test_issue_credential_rr_full(self): with async_mock.patch.object( test_module, "IndyRevocation", autospec=True ) as revoc: - revoc.return_value.get_active_issuer_rev_reg_record = ( - async_mock.CoroutineMock( - return_value=async_mock.MagicMock( # active_rev_reg_rec + revoc.return_value.get_or_create_active_registry = async_mock.CoroutineMock( + return_value=( + async_mock.MagicMock( # active_rev_reg_rec revoc_reg_id=REV_REG_ID, set_state=async_mock.CoroutineMock(), - get_registry=async_mock.CoroutineMock( - return_value=async_mock.MagicMock( # rev_reg - tails_local_path="dummy-path", - get_or_fetch_local_tails_path=( - async_mock.CoroutineMock() - ), - ) - ), - ) + ), + async_mock.MagicMock( # rev_reg + tails_local_path="dummy-path", + get_or_fetch_local_tails_path=(async_mock.CoroutineMock()), + ), ) ) - with self.assertRaises(test_module.IndyIssuerRevocationRegistryFullError): + with self.assertRaises(V20CredFormatError) as context: await self.handler.issue_credential(cred_ex_record, retries=1) + assert "has no active revocation registry" in str(context.exception) async def test_receive_credential(self): cred_ex_record = async_mock.MagicMock() diff --git a/aries_cloudagent/revocation/models/tests/test_issuer_rev_reg_record.py b/aries_cloudagent/revocation/models/tests/test_issuer_rev_reg_record.py index 34048f0423..f0347939e4 100644 --- a/aries_cloudagent/revocation/models/tests/test_issuer_rev_reg_record.py +++ b/aries_cloudagent/revocation/models/tests/test_issuer_rev_reg_record.py @@ -65,7 +65,7 @@ async def setUp(self): TailsServer = async_mock.MagicMock(BaseTailsServer, autospec=True) self.tails_server = TailsServer() self.tails_server.upload_tails_file = async_mock.CoroutineMock( - return_value=(False, "Internal Server Error") + return_value=(True, None) ) self.profile.context.injector.bind_instance(BaseTailsServer, self.tails_server) @@ -124,6 +124,11 @@ async def test_generate_registry_etc(self): assert rec.state == IssuerRevRegRecord.STATE_POSTED self.ledger.send_revoc_reg_def.assert_called_once() + with async_mock.patch.object(test_module.Path, "is_file", lambda _: True): + await rec.upload_tails_file(self.profile) + assert rec.state == IssuerRevRegRecord.STATE_UPLOADED + self.tails_server.upload_tails_file.assert_called_once() + await rec.send_entry(self.profile) assert rec.state == IssuerRevRegRecord.STATE_ACTIVE self.ledger.send_revoc_reg_entry.assert_called_once() diff --git a/aries_cloudagent/revocation/tests/test_routes.py b/aries_cloudagent/revocation/tests/test_routes.py index f004d0c90b..ace6f54e9d 100644 --- a/aries_cloudagent/revocation/tests/test_routes.py +++ b/aries_cloudagent/revocation/tests/test_routes.py @@ -3,25 +3,18 @@ from asynctest import mock as async_mock from aries_cloudagent.core.in_memory import InMemoryProfile +from aries_cloudagent.revocation.error import RevocationError -from ...admin.request_context import AdminRequestContext from ...storage.in_memory import InMemoryStorage -from ...tails.base import BaseTailsServer from .. import routes as test_module class TestRevocationRoutes(AsyncTestCase): def setUp(self): - TailsServer = async_mock.MagicMock(BaseTailsServer, autospec=True) - self.tails_server = TailsServer() - self.tails_server.upload_tails_file = async_mock.CoroutineMock( - return_value=(True, None) - ) self.profile = InMemoryProfile.test_profile() self.context = self.profile.context setattr(self.context, "profile", self.profile) - self.context.injector.bind_instance(BaseTailsServer, self.tails_server) self.request_dict = { "context": self.context, "outbound_message_router": async_mock.CoroutineMock(), @@ -539,34 +532,32 @@ async def test_get_tails_file_not_found(self): result = await test_module.get_tails_file(self.request) mock_file_response.assert_not_called() - async def test_upload_tails_file(self): + async def test_upload_tails_file_basic(self): REV_REG_ID = "{}:4:{}:3:CL:1234:default:CL_ACCUM:default".format( self.test_did, self.test_did ) self.request.match_info = {"rev_reg_id": REV_REG_ID} with async_mock.patch.object( - test_module, "tails_path", async_mock.MagicMock() - ) as mock_tails_path, async_mock.patch.object( + test_module, "IndyRevocation", autospec=True + ) as mock_indy_revoc, async_mock.patch.object( test_module.web, "json_response", async_mock.Mock() ) as mock_json_response: - mock_tails_path.return_value = f"/tmp/tails/{REV_REG_ID}" - + mock_upload = async_mock.CoroutineMock() + mock_indy_revoc.return_value = async_mock.MagicMock( + get_issuer_rev_reg_record=async_mock.CoroutineMock( + return_value=async_mock.MagicMock( + tails_local_path=f"/tmp/tails/{REV_REG_ID}", + has_local_tails_file=True, + upload_tails_file=mock_upload, + ) + ) + ) result = await test_module.upload_tails_file(self.request) + mock_upload.assert_awaited_once() mock_json_response.assert_called_once_with({}) assert result is mock_json_response.return_value - async def test_upload_tails_file_no_tails_server(self): - REV_REG_ID = "{}:4:{}:3:CL:1234:default:CL_ACCUM:default".format( - self.test_did, self.test_did - ) - self.request.match_info = {"rev_reg_id": REV_REG_ID} - - self.context.injector.clear_binding(BaseTailsServer) - - with self.assertRaises(test_module.web.HTTPForbidden): - await test_module.upload_tails_file(self.request) - async def test_upload_tails_file_no_local_tails_file(self): REV_REG_ID = "{}:4:{}:3:CL:1234:default:CL_ACCUM:default".format( self.test_did, self.test_did @@ -574,9 +565,16 @@ async def test_upload_tails_file_no_local_tails_file(self): self.request.match_info = {"rev_reg_id": REV_REG_ID} with async_mock.patch.object( - test_module, "tails_path", async_mock.MagicMock() - ) as mock_tails_path: - mock_tails_path.return_value = None + test_module, "IndyRevocation", autospec=True + ) as mock_indy_revoc: + mock_indy_revoc.return_value = async_mock.MagicMock( + get_issuer_rev_reg_record=async_mock.CoroutineMock( + return_value=async_mock.MagicMock( + tails_local_path=f"/tmp/tails/{REV_REG_ID}", + has_local_tails_file=False, + ) + ) + ) with self.assertRaises(test_module.web.HTTPNotFound): await test_module.upload_tails_file(self.request) @@ -587,17 +585,20 @@ async def test_upload_tails_file_fail(self): ) self.request.match_info = {"rev_reg_id": REV_REG_ID} - TailsServer = async_mock.MagicMock(BaseTailsServer, autospec=True) - self.tails_server = TailsServer() - self.tails_server.upload_tails_file = async_mock.CoroutineMock( - return_value=(False, "Internal Server Error") - ) - self.context.injector.clear_binding(BaseTailsServer) - self.context.injector.bind_instance(BaseTailsServer, self.tails_server) - with async_mock.patch.object( - test_module, "tails_path", async_mock.MagicMock() - ) as mock_tails_path: + test_module, "IndyRevocation", autospec=True + ) as mock_indy_revoc: + mock_upload = async_mock.CoroutineMock(side_effect=RevocationError("test")) + mock_indy_revoc.return_value = async_mock.MagicMock( + get_issuer_rev_reg_record=async_mock.CoroutineMock( + return_value=async_mock.MagicMock( + tails_local_path=f"/tmp/tails/{REV_REG_ID}", + has_local_tails_file=True, + upload_tails_file=mock_upload, + ) + ) + ) + with self.assertRaises(test_module.web.HTTPInternalServerError): await test_module.upload_tails_file(self.request) From 252a9b3fe048a237266874d16b833c3c9e22e50c Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Fri, 17 Jun 2022 17:10:06 -0700 Subject: [PATCH 356/872] detect when send_entry has been called Signed-off-by: Andrew Whitehead --- demo/features/steps/0586-sign-transaction.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/demo/features/steps/0586-sign-transaction.py b/demo/features/steps/0586-sign-transaction.py index 5f3868f830..04b6249598 100644 --- a/demo/features/steps/0586-sign-transaction.py +++ b/demo/features/steps/0586-sign-transaction.py @@ -366,8 +366,20 @@ def step_impl(context, agent_name): def step_impl(context, agent_name): agent = context.active_agents[agent_name] - # TODO not sure what to check here, let's just do a short pause - async_sleep(2.0) + # a registry is promoted to active when its initial entry is sent + i = 5 + while i > 0: + async_sleep(1.0) + reg_info = agent_container_GET( + agent["agent"], + f"/revocation/registry/{context.rev_reg_id}", + ) + state = reg_info["result"]["state"] + if state == "active": + return + i = i - 1 + + assert False @when( From 5cd0a4e4921baf3abdaa17ea6a90fdd70c214a59 Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Fri, 17 Jun 2022 17:10:56 -0700 Subject: [PATCH 357/872] revert uploaded state for IssuerRevRegRecord; fix interaction with transaction endorsement Signed-off-by: Andrew Whitehead --- .../endorse_transaction/v1_0/manager.py | 22 +++------- aries_cloudagent/revocation/indy.py | 14 ++++--- .../models/issuer_rev_reg_record.py | 14 +++---- .../tests/test_issuer_rev_reg_record.py | 7 +++- aries_cloudagent/revocation/routes.py | 42 +++++++++++-------- aries_cloudagent/revocation/util.py | 11 ++++- aries_cloudagent/tails/indy_tails_server.py | 24 ++++++----- aries_cloudagent/tails/tests/test_indy.py | 12 ++++-- 8 files changed, 80 insertions(+), 66 deletions(-) diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py index da95091be4..8ce93c45a8 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py @@ -15,8 +15,8 @@ from ....messaging.credential_definitions.util import notify_cred_def_event from ....messaging.schemas.util import notify_schema_event from ....revocation.util import ( - notify_revocation_entry_event, notify_revocation_reg_endorsed_event, + notify_revocation_entry_endorsed_event, ) from ....storage.error import StorageError, StorageNotFoundError from ....transport.inbound.receipt import MessageReceipt @@ -777,30 +777,18 @@ async def endorsed_txn_post_processing( # revocation registry transaction rev_reg_id = ledger_response["result"]["txnMetadata"]["txnId"] meta_data["context"]["rev_reg_id"] = rev_reg_id - auto_create_rev_reg = meta_data["processing"].get( - "auto_create_rev_reg", False + await notify_revocation_reg_endorsed_event( + self._profile, rev_reg_id, meta_data ) - # If "auto_processing" is enabled, also create the revocation entry record - if auto_create_rev_reg: - await notify_revocation_entry_event( - self._profile, rev_reg_id, meta_data - ) - elif ledger_response["result"]["txn"]["type"] == "114": # revocation entry transaction rev_reg_id = ledger_response["result"]["txn"]["data"]["revocRegDefId"] meta_data["context"]["rev_reg_id"] = rev_reg_id - auto_create_rev_reg = meta_data["processing"].get( - "auto_create_rev_reg", False + await notify_revocation_entry_endorsed_event( + self._profile, rev_reg_id, meta_data ) - # If "auto_processing" is enabled, also upload tails file for this registry - if auto_create_rev_reg: - await notify_revocation_reg_endorsed_event( - self._profile, rev_reg_id, meta_data - ) - elif ledger_response["result"]["txn"]["type"] == "1": # write DID to ledger did = ledger_response["result"]["txn"]["data"]["dest"] diff --git a/aries_cloudagent/revocation/indy.py b/aries_cloudagent/revocation/indy.py index 9e4562e88c..e010fe4534 100644 --- a/aries_cloudagent/revocation/indy.py +++ b/aries_cloudagent/revocation/indy.py @@ -43,6 +43,7 @@ async def init_issuer_registry( tag: str = None, create_pending_rev_reg: bool = False, endorser_connection_id: str = None, + notify: bool = True, ) -> "IssuerRevRegRecord": """Create a new revocation registry record for a credential definition.""" multitenant_mgr = self._profile.inject_or(BaseMultitenantManager) @@ -86,12 +87,13 @@ async def init_issuer_registry( if not endorser_connection_id: raise RevocationError(reason="Endorser connection not found") - await notify_revocation_reg_init_event( - self._profile, - record.record_id, - create_pending_rev_reg=create_pending_rev_reg, - endorser_connection_id=endorser_connection_id, - ) + if notify: + await notify_revocation_reg_init_event( + self._profile, + record.record_id, + create_pending_rev_reg=create_pending_rev_reg, + endorser_connection_id=endorser_connection_id, + ) return record diff --git a/aries_cloudagent/revocation/models/issuer_rev_reg_record.py b/aries_cloudagent/revocation/models/issuer_rev_reg_record.py index d95a5f938b..98451dcb3d 100644 --- a/aries_cloudagent/revocation/models/issuer_rev_reg_record.py +++ b/aries_cloudagent/revocation/models/issuer_rev_reg_record.py @@ -67,7 +67,6 @@ class Meta: STATE_INIT = "init" STATE_GENERATED = "generated" STATE_POSTED = "posted" # definition published - STATE_UPLOADED = "uploaded" # tails file uploaded STATE_ACTIVE = "active" # initial entry published, possibly subsequent entries STATE_FULL = "full" # includes corrupt @@ -279,7 +278,7 @@ async def send_entry( self._check_url(self.tails_public_uri) if self.state not in ( - IssuerRevRegRecord.STATE_UPLOADED, + IssuerRevRegRecord.STATE_POSTED, IssuerRevRegRecord.STATE_ACTIVE, IssuerRevRegRecord.STATE_FULL, # can still publish revocation deltas ): @@ -299,7 +298,7 @@ async def send_entry( write_ledger=write_ledger, endorser_did=endorser_did, ) - if self.state == IssuerRevRegRecord.STATE_UPLOADED: + if self.state == IssuerRevRegRecord.STATE_POSTED: self.state = IssuerRevRegRecord.STATE_ACTIVE # initial entry activates async with profile.session() as session: await self.save( @@ -321,7 +320,7 @@ async def upload_tails_file(self, profile: Profile): if not self.has_local_tails_file: raise RevocationError("Local tails file not found") - (upload_success, reason) = await tails_server.upload_tails_file( + (upload_success, result) = await tails_server.upload_tails_file( profile.context, self.revoc_reg_id, self.tails_local_path, @@ -331,12 +330,9 @@ async def upload_tails_file(self, profile: Profile): ) if not upload_success: raise RevocationError( - f"Tails file for rev reg {self.revoc_reg_id} failed to upload: {reason}" + f"Tails file for rev reg {self.revoc_reg_id} failed to upload: {result}" ) - - self.state = IssuerRevRegRecord.STATE_UPLOADED - async with profile.session() as session: - await self.save(session, reason="Uploaded tails file") + await self.set_tails_file_public_uri(profile, result) async def mark_pending(self, session: ProfileSession, cred_rev_id: str) -> None: """Mark a credential revocation id as revoked pending publication to ledger. diff --git a/aries_cloudagent/revocation/models/tests/test_issuer_rev_reg_record.py b/aries_cloudagent/revocation/models/tests/test_issuer_rev_reg_record.py index f0347939e4..8154884aeb 100644 --- a/aries_cloudagent/revocation/models/tests/test_issuer_rev_reg_record.py +++ b/aries_cloudagent/revocation/models/tests/test_issuer_rev_reg_record.py @@ -65,7 +65,7 @@ async def setUp(self): TailsServer = async_mock.MagicMock(BaseTailsServer, autospec=True) self.tails_server = TailsServer() self.tails_server.upload_tails_file = async_mock.CoroutineMock( - return_value=(True, None) + return_value=(True, "http://1.2.3.4:8088/rev-reg-id") ) self.profile.context.injector.bind_instance(BaseTailsServer, self.tails_server) @@ -126,7 +126,10 @@ async def test_generate_registry_etc(self): with async_mock.patch.object(test_module.Path, "is_file", lambda _: True): await rec.upload_tails_file(self.profile) - assert rec.state == IssuerRevRegRecord.STATE_UPLOADED + assert ( + rec.tails_public_uri + and rec.revoc_reg_def.value.tails_location == rec.tails_public_uri + ) self.tails_server.upload_tails_file.assert_called_once() await rec.send_entry(self.profile) diff --git a/aries_cloudagent/revocation/routes.py b/aries_cloudagent/revocation/routes.py index b54547d63f..637332d55a 100644 --- a/aries_cloudagent/revocation/routes.py +++ b/aries_cloudagent/revocation/routes.py @@ -550,6 +550,7 @@ async def create_rev_reg(request: web.BaseRequest): issuer_rev_reg_rec = await revoc.init_issuer_registry( credential_definition_id, max_cred_num=max_cred_num, + notify=False, ) except RevocationNotSupportedError as e: raise web.HTTPBadRequest(reason=e.message) from e @@ -1128,17 +1129,16 @@ async def send_rev_reg_entry(request: web.BaseRequest): raise web.HTTPBadRequest(reason="No endorser connection found") if not write_ledger: - try: - async with profile.session() as session: + async with profile.session() as session: + try: connection_record = await ConnRecord.retrieve_by_id( session, connection_id ) - except StorageNotFoundError as err: - raise web.HTTPNotFound(reason=err.roll_up) from err - except BaseModelError as err: - raise web.HTTPBadRequest(reason=err.roll_up) from err + except StorageNotFoundError as err: + raise web.HTTPNotFound(reason=err.roll_up) from err + except BaseModelError as err: + raise web.HTTPBadRequest(reason=err.roll_up) from err - async with profile.session() as session: endorser_info = await connection_record.metadata_get( session, "endorser_info" ) @@ -1295,14 +1295,16 @@ async def on_revocation_registry_init_event(profile: Profile, event: Event): meta_data = event.payload if "endorser" in meta_data: # TODO error handling - for now just let exceptions get raised + endorser_connection_id = meta_data["endorser"]["connection_id"] async with profile.session() as session: connection = await ConnRecord.retrieve_by_id( - session, meta_data["endorser"]["connection_id"] + session, endorser_connection_id ) endorser_info = await connection.metadata_get(session, "endorser_info") endorser_did = endorser_info["endorser_did"] write_ledger = False else: + endorser_connection_id = None endorser_did = None write_ledger = True @@ -1313,10 +1315,8 @@ async def on_revocation_registry_init_event(profile: Profile, event: Event): # Generate the registry and upload the tails file async def generate(rr_record: IssuerRevRegRecord) -> dict: await rr_record.generate_registry(profile) - await rr_record.set_tails_file_public_uri( - profile, - f"{tails_base_url}/{registry_record.revoc_reg_id}", - ) + public_uri = tails_base_url.rstrip("/") + f"/{registry_record.revoc_reg_id}" + await rr_record.set_tails_file_public_uri(profile, public_uri) rev_reg_resp = await rr_record.send_def( profile, write_ledger=write_ledger, @@ -1381,6 +1381,7 @@ async def generate(rr_record: IssuerRevRegRecord) -> dict: registry_record.cred_def_id, registry_record.max_cred_num, registry_record.revoc_def_type, + endorser_connection_id=endorser_connection_id, ) @@ -1450,18 +1451,25 @@ async def on_revocation_entry_event(profile: Profile, event: Event): async def on_revocation_registry_endorsed_event(profile: Profile, event: Event): - """Handle revocation tails file event.""" + """Handle revocation registry endorsement event.""" meta_data = event.payload rev_reg_id = meta_data["context"]["rev_reg_id"] revoc = IndyRevocation(profile) registry_record = await revoc.get_issuer_rev_reg_record(rev_reg_id) - # NOTE: if there are multiple pods, then the one processing this - # event may not be the one that generated the tails file. - await registry_record.upload_tails_file(profile) + + if profile.settings.get_value("endorser.auto_request"): + # NOTE: if there are multiple pods, then the one processing this + # event may not be the one that generated the tails file. + await registry_record.upload_tails_file(profile) + + # Post the initial revocation entry + await notify_revocation_entry_event( + profile, registry_record.record_id, meta_data + ) # create a "pending" registry if one is requested # (this is done automatically when creating a credential definition, so that when a - # revocation registry fills up, we ca continue to issue credentials without a + # revocation registry fills up, we can continue to issue credentials without a # delay) create_pending_rev_reg = meta_data["processing"].get( "create_pending_rev_reg", False diff --git a/aries_cloudagent/revocation/util.py b/aries_cloudagent/revocation/util.py index f81e26dc4a..df40a17630 100644 --- a/aries_cloudagent/revocation/util.py +++ b/aries_cloudagent/revocation/util.py @@ -10,7 +10,8 @@ EVENT_LISTENER_PATTERN = re.compile(f"^{REVOCATION_EVENT_PREFIX}(.*)?$") REVOCATION_REG_INIT_EVENT = "REGISTRY_INIT" REVOCATION_REG_ENDORSED_EVENT = "REGISTRY_ENDORSED" -REVOCATION_ENTRY_EVENT = "ENTRY" +REVOCATION_ENTRY_ENDORSED_EVENT = "ENTRY_ENDORSED" +REVOCATION_ENTRY_EVENT = "SEND_ENTRY" REVOCATION_PUBLISHED_EVENT = "published" REVOCATION_CLEAR_PENDING_EVENT = "clear-pending" @@ -50,6 +51,14 @@ async def notify_revocation_reg_endorsed_event( await profile.notify(topic, meta_data) +async def notify_revocation_entry_endorsed_event( + profile: Profile, rev_reg_id: str, meta_data: dict +): + """Send notification for a revocation registry entry endorsement event.""" + topic = f"{REVOCATION_EVENT_PREFIX}{REVOCATION_ENTRY_ENDORSED_EVENT}::{rev_reg_id}" + await profile.notify(topic, meta_data) + + async def notify_revocation_published_event( profile: Profile, rev_reg_id: str, diff --git a/aries_cloudagent/tails/indy_tails_server.py b/aries_cloudagent/tails/indy_tails_server.py index 9f07970a42..0c5ebb6ab4 100644 --- a/aries_cloudagent/tails/indy_tails_server.py +++ b/aries_cloudagent/tails/indy_tails_server.py @@ -1,8 +1,9 @@ """Indy tails server interface class.""" -from typing import Tuple import logging +from typing import Tuple + from ..config.injection_context import InjectionContext from ..ledger.multiple_ledger.base_manager import BaseMultipleLedgerManager from ..utils.http import put_file, PutError @@ -58,17 +59,18 @@ async def upload_tails_file( "tails_server_upload_url setting is not set" ) + upload_url = tails_server_upload_url.rstrip("/") + f"/{rev_reg_id}" + try: - return ( - True, - await put_file( - f"{tails_server_upload_url}/{rev_reg_id}", - {"tails": tails_file_path}, - {"genesis": genesis_transactions}, - interval=interval, - backoff=backoff, - max_attempts=max_attempts, - ), + await put_file( + upload_url, + {"tails": tails_file_path}, + {"genesis": genesis_transactions}, + interval=interval, + backoff=backoff, + max_attempts=max_attempts, ) except PutError as x_put: return (False, x_put.message) + + return True, upload_url diff --git a/aries_cloudagent/tails/tests/test_indy.py b/aries_cloudagent/tails/tests/test_indy.py index ca9d1d6729..65d026a59e 100644 --- a/aries_cloudagent/tails/tests/test_indy.py +++ b/aries_cloudagent/tails/tests/test_indy.py @@ -38,7 +38,9 @@ async def test_upload(self): "/tmp/dummy/path", ) assert ok - assert text == "tails-hash" + assert ( + text == context.settings["tails_server_upload_url"] + "/" + REV_REG_ID + ) async def test_upload_indy_sdk(self): profile = InMemoryProfile.test_profile() @@ -68,7 +70,9 @@ async def test_upload_indy_sdk(self): "/tmp/dummy/path", ) assert ok - assert text == "tails-hash" + assert ( + text == profile.settings["tails_server_upload_url"] + "/" + REV_REG_ID + ) async def test_upload_indy_vdr(self): profile = InMemoryProfile.test_profile() @@ -98,7 +102,9 @@ async def test_upload_indy_vdr(self): "/tmp/dummy/path", ) assert ok - assert text == "tails-hash" + assert ( + text == profile.settings["tails_server_upload_url"] + "/" + REV_REG_ID + ) async def test_upload_x(self): context = InjectionContext( From 8bfa60bd2082a23bd7140a072dffd270519df2c1 Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Tue, 21 Jun 2022 11:37:00 -0700 Subject: [PATCH 358/872] move IssuerCredRevRecord creation to issue-credential manager Signed-off-by: Andrew Whitehead --- aries_cloudagent/indy/credx/issuer.py | 18 -- .../indy/credx/tests/test_cred_issuance.py | 2 - aries_cloudagent/indy/issuer.py | 2 - aries_cloudagent/indy/sdk/issuer.py | 19 -- .../indy/sdk/tests/test_issuer.py | 234 +++++++----------- .../issue_credential/v1_0/manager.py | 24 +- .../v1_0/tests/test_manager.py | 3 - .../v2_0/formats/indy/handler.py | 50 ++-- .../v2_0/formats/indy/tests/test_handler.py | 2 - aries_cloudagent/revocation/manager.py | 53 ++-- .../models/issuer_cred_rev_record.py | 10 + .../revocation/tests/test_manager.py | 42 +++- aries_cloudagent/storage/in_memory.py | 3 +- 13 files changed, 228 insertions(+), 234 deletions(-) diff --git a/aries_cloudagent/indy/credx/issuer.py b/aries_cloudagent/indy/credx/issuer.py index cc996ae3f6..b4a4524948 100644 --- a/aries_cloudagent/indy/credx/issuer.py +++ b/aries_cloudagent/indy/credx/issuer.py @@ -28,8 +28,6 @@ DEFAULT_CRED_DEF_TAG, DEFAULT_SIGNATURE_TYPE, ) -from ...revocation.models.issuer_cred_rev_record import IssuerCredRevRecord - LOGGER = logging.getLogger(__name__) @@ -225,7 +223,6 @@ async def create_credential( credential_offer: dict, credential_request: dict, credential_values: dict, - cred_ex_id: str, revoc_reg_id: str = None, tails_file_path: str = None, ) -> Tuple[str, str]: @@ -237,7 +234,6 @@ async def create_credential( credential_offer: Credential Offer to create credential for credential_request: Credential request to create credential for credential_values: Values to go in credential - cred_ex_id: credential exchange identifier to use in issuer cred rev rec revoc_reg_id: ID of the revocation registry tails_file_path: The location of the tails file @@ -324,20 +320,6 @@ async def create_credential( await txn.handle.replace( CATEGORY_REV_REG_INFO, revoc_reg_id, value_json=rev_info ) - - issuer_cr_rec = IssuerCredRevRecord( - state=IssuerCredRevRecord.STATE_ISSUED, - cred_ex_id=cred_ex_id, - rev_reg_id=revoc_reg_id, - cred_rev_id=str(rev_reg_index), - ) - await issuer_cr_rec.save( - txn, - reason=( - "Created issuer cred rev record for " - f"rev reg id {revoc_reg_id}, {rev_reg_index}" - ), - ) await txn.commit() except AskarError as err: raise IndyIssuerError( diff --git a/aries_cloudagent/indy/credx/tests/test_cred_issuance.py b/aries_cloudagent/indy/credx/tests/test_cred_issuance.py index 0c23eda904..027c16b94d 100644 --- a/aries_cloudagent/indy/credx/tests/test_cred_issuance.py +++ b/aries_cloudagent/indy/credx/tests/test_cred_issuance.py @@ -137,7 +137,6 @@ async def test_issue_store_non_rev(self): cred_offer, cred_req, {"name": "NAME", "moniker": "MONIKER"}, - cred_ex_id="cred_ex_id", revoc_reg_id=None, tails_file_path=None, ) @@ -255,7 +254,6 @@ async def test_issue_store_rev(self): cred_offer, cred_req, {"name": "NAME", "moniker": "MONIKER"}, - cred_ex_id="cred_ex_id", revoc_reg_id=reg_id, tails_file_path=tails_path, ) diff --git a/aries_cloudagent/indy/issuer.py b/aries_cloudagent/indy/issuer.py index 3503626f72..d889746d41 100644 --- a/aries_cloudagent/indy/issuer.py +++ b/aries_cloudagent/indy/issuer.py @@ -122,7 +122,6 @@ async def create_credential( credential_offer: dict, credential_request: dict, credential_values: dict, - cred_ex_id: str, revoc_reg_id: str = None, tails_file_path: str = None, ) -> Tuple[str, str]: @@ -134,7 +133,6 @@ async def create_credential( credential_offer: Credential Offer to create credential for credential_request: Credential request to create credential for credential_values: Values to go in credential - cred_ex_id: credential exchange identifier to use in issuer cred rev rec revoc_reg_id: ID of the revocation registry tails_file_path: The location of the tails file diff --git a/aries_cloudagent/indy/sdk/issuer.py b/aries_cloudagent/indy/sdk/issuer.py index 72f569f2ee..3d84473c93 100644 --- a/aries_cloudagent/indy/sdk/issuer.py +++ b/aries_cloudagent/indy/sdk/issuer.py @@ -10,7 +10,6 @@ from ...indy.sdk.profile import IndySdkProfile from ...messaging.util import encode -from ...revocation.models.issuer_cred_rev_record import IssuerCredRevRecord from ...storage.error import StorageError from ..issuer import ( @@ -162,7 +161,6 @@ async def create_credential( credential_offer: dict, credential_request: dict, credential_values: dict, - cred_ex_id: str, rev_reg_id: str = None, tails_file_path: str = None, ) -> Tuple[str, str]: @@ -174,7 +172,6 @@ async def create_credential( credential_offer: Credential Offer to create credential for credential_request: Credential request to create credential for credential_values: Values to go in credential - cred_ex_id: credential exchange identifier to use in issuer cred rev rec rev_reg_id: ID of the revocation registry tails_file_path: Path to the local tails file @@ -219,22 +216,6 @@ async def create_credential( rev_reg_id, tails_reader_handle, ) - - if cred_rev_id: - issuer_cr_rec = IssuerCredRevRecord( - state=IssuerCredRevRecord.STATE_ISSUED, - cred_ex_id=cred_ex_id, - rev_reg_id=rev_reg_id, - cred_rev_id=cred_rev_id, - ) - async with self.profile.session() as session: - await issuer_cr_rec.save( - session, - reason=( - "Created issuer cred rev record for " - f"rev reg id {rev_reg_id}, {cred_rev_id}" - ), - ) except AnoncredsRevocationRegistryFullError: LOGGER.warning( "Revocation registry %s is full: cannot create credential", diff --git a/aries_cloudagent/indy/sdk/tests/test_issuer.py b/aries_cloudagent/indy/sdk/tests/test_issuer.py index a1b76509a0..3b007e788e 100644 --- a/aries_cloudagent/indy/sdk/tests/test_issuer.py +++ b/aries_cloudagent/indy/sdk/tests/test_issuer.py @@ -167,58 +167,46 @@ async def test_create_revoke_credentials( for cr_id in test_cred_rev_ids ] - with async_mock.patch.object( - test_module, "IssuerCredRevRecord", async_mock.MagicMock() - ) as mock_issuer_cr_rec: - mock_issuer_cr_rec.return_value.save = async_mock.CoroutineMock() - mock_issuer_cr_rec.retrieve_by_ids = async_mock.CoroutineMock( - return_value=async_mock.MagicMock( - set_state=async_mock.CoroutineMock(), - ) - ) - - with self.assertRaises(test_module.IndyIssuerError): # missing attribute - cred_json, revoc_id = await self.issuer.create_credential( - test_schema, - test_offer, - test_request, - {}, - "dummy-cxid", - ) - - (cred_json, cred_rev_id) = await self.issuer.create_credential( # main line + with self.assertRaises(test_module.IndyIssuerError): # missing attribute + cred_json, revoc_id = await self.issuer.create_credential( test_schema, test_offer, test_request, - test_values, - "dummy-cxid", - REV_REG_ID, - "/tmp/tails/path/dummy", - ) - mock_indy_create_credential.assert_called_once() - ( - call_wallet, - call_offer, - call_request, - call_values, - call_etc1, - call_etc2, - ) = mock_indy_create_credential.call_args[0] - assert call_wallet is self.wallet.handle - assert json.loads(call_offer) == test_offer - assert json.loads(call_request) == test_request - values = json.loads(call_values) - assert "attr1" in values - - mock_indy_revoke_credential.return_value = json.dumps(TEST_RR_DELTA) - mock_indy_merge_rr_deltas.return_value = json.dumps(TEST_RR_DELTA) - (result, failed) = await self.issuer.revoke_credentials( - REV_REG_ID, tails_file_path="dummy", cred_rev_ids=test_cred_rev_ids + {}, ) - assert json.loads(result) == TEST_RR_DELTA - assert not failed - assert mock_indy_revoke_credential.call_count == 2 - mock_indy_merge_rr_deltas.assert_called_once() + + (cred_json, cred_rev_id) = await self.issuer.create_credential( # main line + test_schema, + test_offer, + test_request, + test_values, + REV_REG_ID, + "/tmp/tails/path/dummy", + ) + mock_indy_create_credential.assert_called_once() + ( + call_wallet, + call_offer, + call_request, + call_values, + call_etc1, + call_etc2, + ) = mock_indy_create_credential.call_args[0] + assert call_wallet is self.wallet.handle + assert json.loads(call_offer) == test_offer + assert json.loads(call_request) == test_request + values = json.loads(call_values) + assert "attr1" in values + + mock_indy_revoke_credential.return_value = json.dumps(TEST_RR_DELTA) + mock_indy_merge_rr_deltas.return_value = json.dumps(TEST_RR_DELTA) + (result, failed) = await self.issuer.revoke_credentials( + REV_REG_ID, tails_file_path="dummy", cred_rev_ids=test_cred_rev_ids + ) + assert json.loads(result) == TEST_RR_DELTA + assert not failed + assert mock_indy_revoke_credential.call_count == 2 + mock_indy_merge_rr_deltas.assert_called_once() @async_mock.patch("indy.anoncreds.issuer_create_credential") @async_mock.patch.object(test_module, "create_tails_reader", autospec=True) @@ -267,73 +255,53 @@ async def test_create_revoke_credentials_x( test_offer, test_request, {}, - "dummy-cxid", ) - with async_mock.patch.object( - test_module, "IssuerCredRevRecord", async_mock.MagicMock() - ) as mock_issuer_cr_rec: - mock_issuer_cr_rec.return_value.save = async_mock.CoroutineMock( - side_effect=test_module.StorageError( - "could not store" # not fatal; maximize coverage - ) - ) - mock_issuer_cr_rec.retrieve_by_ids = async_mock.CoroutineMock( - return_value=async_mock.MagicMock( - set_state=async_mock.CoroutineMock( - side_effect=test_module.StorageError( - "could not store" # not fatal; maximize coverage - ) - ), - ) - ) - - (cred_json, cred_rev_id) = await self.issuer.create_credential( # main line - test_schema, - test_offer, - test_request, - test_values, - "dummy-cxid", - REV_REG_ID, - "/tmp/tails/path/dummy", - ) - mock_indy_create_credential.assert_called_once() - ( - call_wallet, - call_offer, - call_request, - call_values, - call_etc1, - call_etc2, - ) = mock_indy_create_credential.call_args[0] - assert call_wallet is self.wallet.handle - assert json.loads(call_offer) == test_offer - assert json.loads(call_request) == test_request - values = json.loads(call_values) - assert "attr1" in values - - def mock_revoke(_h, _t, _r, cred_rev_id): - if cred_rev_id == "42": - return json.dumps(TEST_RR_DELTA) - if cred_rev_id == "54": - raise IndyError( - error_code=ErrorCode.AnoncredsInvalidUserRevocId, - error_details={"message": "already revoked"}, - ) + (cred_json, cred_rev_id) = await self.issuer.create_credential( # main line + test_schema, + test_offer, + test_request, + test_values, + REV_REG_ID, + "/tmp/tails/path/dummy", + ) + mock_indy_create_credential.assert_called_once() + ( + call_wallet, + call_offer, + call_request, + call_values, + call_etc1, + call_etc2, + ) = mock_indy_create_credential.call_args[0] + assert call_wallet is self.wallet.handle + assert json.loads(call_offer) == test_offer + assert json.loads(call_request) == test_request + values = json.loads(call_values) + assert "attr1" in values + + def mock_revoke(_h, _t, _r, cred_rev_id): + if cred_rev_id == "42": + return json.dumps(TEST_RR_DELTA) + if cred_rev_id == "54": raise IndyError( - error_code=ErrorCode.UnknownCryptoTypeError, - error_details={"message": "truly an outlier"}, + error_code=ErrorCode.AnoncredsInvalidUserRevocId, + error_details={"message": "already revoked"}, ) - - mock_indy_revoke_credential.side_effect = mock_revoke - mock_indy_merge_rr_deltas.return_value = json.dumps(TEST_RR_DELTA) - (result, failed) = await self.issuer.revoke_credentials( - REV_REG_ID, tails_file_path="dummy", cred_rev_ids=test_cred_rev_ids + raise IndyError( + error_code=ErrorCode.UnknownCryptoTypeError, + error_details={"message": "truly an outlier"}, ) - assert json.loads(result) == TEST_RR_DELTA - assert failed == ["54", "103"] - assert mock_indy_revoke_credential.call_count == 3 - mock_indy_merge_rr_deltas.assert_not_called() + + mock_indy_revoke_credential.side_effect = mock_revoke + mock_indy_merge_rr_deltas.return_value = json.dumps(TEST_RR_DELTA) + (result, failed) = await self.issuer.revoke_credentials( + REV_REG_ID, tails_file_path="dummy", cred_rev_ids=test_cred_rev_ids + ) + assert json.loads(result) == TEST_RR_DELTA + assert failed == ["54", "103"] + assert mock_indy_revoke_credential.call_count == 3 + mock_indy_merge_rr_deltas.assert_not_called() @async_mock.patch("indy.anoncreds.issuer_create_credential") @async_mock.patch.object(test_module, "create_tails_reader", autospec=True) @@ -358,25 +326,14 @@ async def test_create_credential_rr_full( error_code=ErrorCode.AnoncredsRevocationRegistryFullError ) - with async_mock.patch.object( - test_module, "IssuerCredRevRecord", async_mock.MagicMock() - ) as mock_issuer_cr_rec: - mock_issuer_cr_rec.return_value.save = async_mock.CoroutineMock() - mock_issuer_cr_rec.retrieve_by_ids = async_mock.CoroutineMock( - return_value=async_mock.MagicMock( - set_state=async_mock.CoroutineMock(), - ) + with self.assertRaises(IndyIssuerRevocationRegistryFullError): + await self.issuer.create_credential( + test_schema, + test_offer, + test_request, + test_values, ) - with self.assertRaises(IndyIssuerRevocationRegistryFullError): - await self.issuer.create_credential( - test_schema, - test_offer, - test_request, - test_values, - "dummy-cxid", - ) - @async_mock.patch("indy.anoncreds.issuer_create_credential") @async_mock.patch.object(test_module, "create_tails_reader", autospec=True) async def test_create_credential_x_indy( @@ -401,25 +358,14 @@ async def test_create_credential_x_indy( error_code=ErrorCode.WalletInvalidHandle ) - with async_mock.patch.object( - test_module, "IssuerCredRevRecord", async_mock.MagicMock() - ) as mock_issuer_cr_rec: - mock_issuer_cr_rec.return_value.save = async_mock.CoroutineMock() - mock_issuer_cr_rec.retrieve_by_ids = async_mock.CoroutineMock( - return_value=async_mock.MagicMock( - set_state=async_mock.CoroutineMock(), - ) + with self.assertRaises(test_module.IndyIssuerError): + await self.issuer.create_credential( + test_schema, + test_offer, + test_request, + test_values, ) - with self.assertRaises(test_module.IndyIssuerError): - await self.issuer.create_credential( - test_schema, - test_offer, - test_request, - test_values, - "dummy-cxid", - ) - @async_mock.patch("indy.anoncreds.issuer_create_and_store_revoc_reg") @async_mock.patch.object(test_module, "create_tails_writer", autospec=True) async def test_create_and_store_revocation_registry( diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/manager.py b/aries_cloudagent/protocols/issue_credential/v1_0/manager.py index 2a1c37dd38..90d1a15217 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/manager.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/manager.py @@ -7,6 +7,7 @@ from typing import Mapping, Optional, Tuple from ....cache.base import BaseCache +from ....connections.models.conn_record import ConnRecord from ....core.error import BaseError from ....core.profile import Profile from ....indy.holder import IndyHolder, IndyHolderError @@ -23,10 +24,10 @@ from ....messaging.responder import BaseResponder from ....multitenant.base import BaseMultitenantManager from ....revocation.indy import IndyRevocation +from ....revocation.models.issuer_cred_rev_record import IssuerCredRevRecord from ....revocation.models.revocation_registry import RevocationRegistry from ....storage.base import BaseStorage from ....storage.error import StorageError, StorageNotFoundError -from ....connections.models.conn_record import ConnRecord from ...out_of_band.v1_0.models.oob_record import OobRecord from .messages.credential_ack import CredentialAck @@ -638,7 +639,7 @@ async def issue_credential( await asyncio.sleep(2) if revocable: - revoc = IndyRevocation(self.profile) + revoc = IndyRevocation(self._profile) registry_info = await revoc.get_or_create_active_registry( cred_def_id ) @@ -658,7 +659,6 @@ async def issue_credential( cred_offer_ser, cred_req_ser, cred_values, - cred_ex_record.credential_exchange_id, rev_reg_id, tails_path, ) @@ -667,7 +667,7 @@ async def issue_credential( continue if revocable and rev_reg.max_creds <= int(cred_rev_id): - revoc = IndyRevocation(self.profile) + revoc = IndyRevocation(self._profile) await revoc.handle_full_registry(rev_reg_id) del revoc @@ -681,6 +681,22 @@ async def issue_credential( ) from None async with self._profile.transaction() as txn: + if revocable and cred_rev_id: + issuer_cr_rec = IssuerCredRevRecord( + state=IssuerCredRevRecord.STATE_ISSUED, + cred_ex_id=cred_ex_record.credential_exchange_id, + cred_ex_version=IssuerCredRevRecord.VERSION_1, + rev_reg_id=rev_reg_id, + cred_rev_id=cred_rev_id, + ) + await issuer_cr_rec.save( + txn, + reason=( + "Created issuer cred rev record for " + f"rev reg id {rev_reg_id}, index {cred_rev_id}" + ), + ) + cred_ex_record = await V10CredentialExchange.retrieve_by_id( txn, cred_ex_record.credential_exchange_id, for_update=True ) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py index ba0e65f924..94ec08f0f6 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py @@ -945,7 +945,6 @@ async def test_issue_credential_revocable(self): INDY_OFFER, INDY_CRED_REQ, cred_values, - stored_exchange.credential_exchange_id, REV_REG_ID, "dummy-path", ) @@ -1031,7 +1030,6 @@ async def test_issue_credential_non_revocable(self): INDY_OFFER, INDY_CRED_REQ, cred_values, - stored_exchange.credential_exchange_id, None, None, ) @@ -1113,7 +1111,6 @@ async def test_issue_credential_fills_rr(self): INDY_OFFER, INDY_CRED_REQ, cred_values, - stored_exchange.credential_exchange_id, REV_REG_ID, "dummy-path", ) diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/formats/indy/handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/formats/indy/handler.py index fbd2882b7d..7ef901260f 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/formats/indy/handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/formats/indy/handler.py @@ -25,8 +25,9 @@ ) from ......messaging.decorators.attach_decorator import AttachDecorator from ......multitenant.base import BaseMultitenantManager -from ......revocation.models.revocation_registry import RevocationRegistry from ......revocation.indy import IndyRevocation +from ......revocation.models.issuer_cred_rev_record import IssuerCredRevRecord +from ......revocation.models.revocation_registry import RevocationRegistry from ......storage.base import BaseStorage from ...message_types import ( @@ -352,6 +353,7 @@ async def issue_credential( schema = await ledger.get_schema(schema_id) cred_def = await ledger.get_credential_definition(cred_def_id) revocable = cred_def["value"].get("revocation") + result = None for attempt in range(max(retries, 1)): if attempt > 0: @@ -380,7 +382,6 @@ async def issue_credential( cred_offer, cred_request, cred_values, - cred_ex_record.cred_ex_id, rev_reg_id, tails_path, ) @@ -388,24 +389,45 @@ async def issue_credential( # unlucky, another instance filled the registry first continue - detail_record = V20CredExRecordIndy( - cred_ex_id=cred_ex_record.cred_ex_id, - rev_reg_id=rev_reg_id, - cred_rev_id=cred_rev_id, - ) - async with self._profile.session() as session: - await detail_record.save(session, reason="v2.0 issue credential") - if revocable and rev_reg.max_creds <= int(cred_rev_id): revoc = IndyRevocation(self.profile) await revoc.handle_full_registry(rev_reg_id) del revoc - return self.get_format_data(CRED_20_ISSUE, json.loads(cred_json)) + result = self.get_format_data(CRED_20_ISSUE, json.loads(cred_json)) + break - raise V20CredFormatError( - f"Cred def '{cred_def_id}' has no active revocation registry" - ) + if not result: + raise V20CredFormatError( + f"Cred def '{cred_def_id}' has no active revocation registry" + ) + + async with self._profile.transaction() as txn: + detail_record = V20CredExRecordIndy( + cred_ex_id=cred_ex_record.cred_ex_id, + rev_reg_id=rev_reg_id, + cred_rev_id=cred_rev_id, + ) + await detail_record.save(txn, reason="v2.0 issue credential") + + if revocable and cred_rev_id: + issuer_cr_rec = IssuerCredRevRecord( + state=IssuerCredRevRecord.STATE_ISSUED, + cred_ex_id=cred_ex_record.cred_ex_id, + cred_ex_version=IssuerCredRevRecord.VERSION_2, + rev_reg_id=rev_reg_id, + cred_rev_id=cred_rev_id, + ) + await issuer_cr_rec.save( + txn, + reason=( + "Created issuer cred rev record for " + f"rev reg id {rev_reg_id}, index {cred_rev_id}" + ), + ) + await txn.commit() + + return result async def receive_credential( self, cred_ex_record: V20CredExRecord, cred_issue_message: V20CredIssue diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/formats/indy/tests/test_handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/formats/indy/tests/test_handler.py index e97fd6d457..06028fa00e 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/formats/indy/tests/test_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/formats/indy/tests/test_handler.py @@ -753,7 +753,6 @@ async def test_issue_credential_revocable(self): INDY_OFFER, INDY_CRED_REQ, attr_values, - cred_ex_record.cred_ex_id, REV_REG_ID, "dummy-path", ) @@ -837,7 +836,6 @@ async def test_issue_credential_non_revocable(self): INDY_OFFER, INDY_CRED_REQ, attr_values, - cred_ex_record.cred_ex_id, None, None, ) diff --git a/aries_cloudagent/revocation/manager.py b/aries_cloudagent/revocation/manager.py index 4057d7a799..10db676f70 100644 --- a/aries_cloudagent/revocation/manager.py +++ b/aries_cloudagent/revocation/manager.py @@ -297,6 +297,7 @@ async def set_cred_revoked_state( txn, rev_reg_id, cred_rev_id, for_update=True ) cred_ex_id = rev_rec.cred_ex_id + cred_ex_version = rev_rec.cred_ex_version rev_rec.state = IssuerCredRevRecord.STATE_REVOKED await rev_rec.save(txn, reason="revoke credential") await txn.commit() @@ -304,25 +305,33 @@ async def set_cred_revoked_state( continue async with self._profile.transaction() as txn: - try: - cred_ex_record = await V10CredentialExchange.retrieve_by_id( - txn, cred_ex_id, for_update=True - ) - cred_ex_record.state = ( - V10CredentialExchange.STATE_CREDENTIAL_REVOKED - ) - await cred_ex_record.save(txn, reason="revoke credential") - await txn.commit() - continue # skip 2.0 record check - except StorageNotFoundError: - pass - - try: - cred_ex_record = await V20CredExRecord.retrieve_by_id( - txn, cred_ex_id, for_update=True - ) - cred_ex_record.state = V20CredExRecord.STATE_CREDENTIAL_REVOKED - await cred_ex_record.save(txn, reason="revoke credential") - await txn.commit() - except StorageNotFoundError: - pass + if ( + not cred_ex_version + or cred_ex_version == IssuerCredRevRecord.VERSION_1 + ): + try: + cred_ex_record = await V10CredentialExchange.retrieve_by_id( + txn, cred_ex_id, for_update=True + ) + cred_ex_record.state = ( + V10CredentialExchange.STATE_CREDENTIAL_REVOKED + ) + await cred_ex_record.save(txn, reason="revoke credential") + await txn.commit() + continue # skip 2.0 record check + except StorageNotFoundError: + pass + + if ( + not cred_ex_version + or cred_ex_version == IssuerCredRevRecord.VERSION_2 + ): + try: + cred_ex_record = await V20CredExRecord.retrieve_by_id( + txn, cred_ex_id, for_update=True + ) + cred_ex_record.state = V20CredExRecord.STATE_CREDENTIAL_REVOKED + await cred_ex_record.save(txn, reason="revoke credential") + await txn.commit() + except StorageNotFoundError: + pass diff --git a/aries_cloudagent/revocation/models/issuer_cred_rev_record.py b/aries_cloudagent/revocation/models/issuer_cred_rev_record.py index 5ab8d3ba09..10ba69ef53 100644 --- a/aries_cloudagent/revocation/models/issuer_cred_rev_record.py +++ b/aries_cloudagent/revocation/models/issuer_cred_rev_record.py @@ -27,6 +27,7 @@ class Meta: RECORD_TOPIC = "issuer_cred_rev" TAG_NAMES = { "cred_ex_id", + "cred_ex_version", "cred_def_id", "rev_reg_id", "cred_rev_id", @@ -36,6 +37,9 @@ class Meta: STATE_ISSUED = "issued" STATE_REVOKED = "revoked" + VERSION_1 = "1" + VERSION_2 = "2" + def __init__( self, *, @@ -45,6 +49,7 @@ def __init__( rev_reg_id: str = None, cred_rev_id: str = None, cred_def_id: str = None, # Marshmallow formalism: leave None + cred_ex_version: str = None, **kwargs, ): """Initialize a new IssuerCredRevRecord.""" @@ -53,6 +58,7 @@ def __init__( self.rev_reg_id = rev_reg_id self.cred_rev_id = cred_rev_id self.cred_def_id = ":".join(rev_reg_id.split(":")[-7:-2]) + self.cred_ex_version = cred_ex_version @property def record_id(self) -> str: @@ -158,3 +164,7 @@ class Meta: description="Credential revocation identifier", **INDY_CRED_REV_ID, ) + cred_ex_version = fields.Str( + required=False, + description="Credential exchange version", + ) diff --git a/aries_cloudagent/revocation/tests/test_manager.py b/aries_cloudagent/revocation/tests/test_manager.py index 113c4850d1..ec026b9afd 100644 --- a/aries_cloudagent/revocation/tests/test_manager.py +++ b/aries_cloudagent/revocation/tests/test_manager.py @@ -2,7 +2,6 @@ from asynctest import mock as async_mock from asynctest import TestCase as AsyncTestCase -from more_itertools import side_effect from aries_cloudagent.revocation.models.issuer_cred_rev_record import ( IssuerCredRevRecord, @@ -13,6 +12,8 @@ from ...protocols.issue_credential.v1_0.models.credential_exchange import ( V10CredentialExchange, ) +from ...protocols.issue_credential.v2_0.models.cred_ex_record import V20CredExRecord + from ..manager import RevocationManager, RevocationManagerError @@ -427,7 +428,7 @@ async def test_retrieve_records(self): assert ret_ex.connection_id == str(index) assert ret_ex.thread_id == str(1000 + index) - async def test_set_revoked_state(self): + async def test_set_revoked_state_v1(self): CRED_REV_ID = "1" async with self.profile.session() as session: @@ -466,3 +467,40 @@ async def test_set_revoked_state(self): session, crev_record.record_id ) assert check_crev_record.state == IssuerCredRevRecord.STATE_REVOKED + + async def test_set_revoked_state_v2(self): + CRED_REV_ID = "1" + + async with self.profile.session() as session: + exchange_record = V20CredExRecord( + connection_id="mark-revoked-cid", + thread_id="mark-revoked-tid", + initiator=V20CredExRecord.INITIATOR_SELF, + role=V20CredExRecord.ROLE_ISSUER, + state=V20CredExRecord.STATE_ISSUED, + ) + await exchange_record.save(session) + + crev_record = IssuerCredRevRecord( + cred_ex_id=exchange_record.cred_ex_id, + cred_def_id=CRED_DEF_ID, + rev_reg_id=REV_REG_ID, + cred_rev_id=CRED_REV_ID, + state=IssuerCredRevRecord.STATE_ISSUED, + ) + await crev_record.save(session) + + await self.manager.set_cred_revoked_state(REV_REG_ID, [CRED_REV_ID]) + + async with self.profile.session() as session: + check_exchange_record = await V20CredExRecord.retrieve_by_id( + session, exchange_record.cred_ex_id + ) + assert ( + check_exchange_record.state == V20CredExRecord.STATE_CREDENTIAL_REVOKED + ) + + check_crev_record = await IssuerCredRevRecord.retrieve_by_id( + session, crev_record.record_id + ) + assert check_crev_record.state == IssuerCredRevRecord.STATE_REVOKED diff --git a/aries_cloudagent/storage/in_memory.py b/aries_cloudagent/storage/in_memory.py index c94a9d6f93..296858f1d2 100644 --- a/aries_cloudagent/storage/in_memory.py +++ b/aries_cloudagent/storage/in_memory.py @@ -70,8 +70,7 @@ async def get_record( row = self.profile.records.get(record_id) if row and row.type == record_type: return row - if not row: - raise StorageNotFoundError("Record not found: {}".format(record_id)) + raise StorageNotFoundError("Record not found: {}".format(record_id)) async def update_record(self, record: StorageRecord, value: str, tags: Mapping): """ From 0975af6091a2e38625c91ef89fd5082ecbddec0c Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Tue, 26 Jul 2022 09:21:36 -0600 Subject: [PATCH 359/872] fix: Fix the way routing DIDs are being created/sent Signed-off-by: Colton Wolkins (Indicio work address) --- .../protocols/coordinate_mediation/v1_0/manager.py | 2 +- .../coordinate_mediation/v1_0/messages/mediate_grant.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py index 195dd5fd5a..6a5af46957 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py @@ -183,7 +183,7 @@ async def grant_request( await mediation_record.save(session, reason="Mediation request granted") grant = MediationGrant( endpoint=session.settings.get("default_endpoint"), - routing_keys=[routing_did.verkey], + routing_keys=[routing_did.did], ) return mediation_record, grant diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/mediate_grant.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/mediate_grant.py index 692f3892e8..c18a2d132d 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/mediate_grant.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/mediate_grant.py @@ -8,6 +8,8 @@ from marshmallow import fields from .....messaging.agent_message import AgentMessage, AgentMessageSchema +from .....did.did_key import DIDKey +from .....wallet.key_type import KeyType from ..message_types import MEDIATE_GRANT, PROTOCOL_PACKAGE HANDLER_CLASS = ( @@ -41,7 +43,7 @@ def __init__( """ super(MediationGrant, self).__init__(**kwargs) self.endpoint = endpoint - self.routing_keys = list(f"did:key:z{key}" for key in routing_keys) if routing_keys else [] + self.routing_keys = list(f"did:key:z{DIDKey.from_public_key_b58(key, KeyType.ED25519).did}" for key in routing_keys) if routing_keys else [] class MediationGrantSchema(AgentMessageSchema): From d0b19d9ad6715e671940d0008c958ecbf4e7289e Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Tue, 26 Jul 2022 10:04:02 -0600 Subject: [PATCH 360/872] fix: Reformat did during grant Signed-off-by: Colton Wolkins (Indicio work address) --- .../protocols/coordinate_mediation/v1_0/manager.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py index 6a5af46957..c96358b6a1 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py @@ -13,6 +13,7 @@ from ....wallet.did_method import DIDMethod from ....wallet.base import BaseWallet from ....wallet.did_info import DIDInfo +from ....did.did_key import DIDKey from ...routing.v1_0.manager import RoutingManager from ...routing.v1_0.models.route_record import RouteRecord @@ -180,6 +181,8 @@ async def grant_request( mediation_record.state = MediationRecord.STATE_GRANTED + routing_did = DIDKey.from_public_key_b58(routing_did.verkey, routing_did.key_type) + await mediation_record.save(session, reason="Mediation request granted") grant = MediationGrant( endpoint=session.settings.get("default_endpoint"), From 7ff2c0cc63d27c25d76a7c7dfa28973f1e56a309 Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Tue, 26 Jul 2022 10:27:51 -0600 Subject: [PATCH 361/872] Revert "fix: Reformat did during grant" This reverts commit d0b19d9ad6715e671940d0008c958ecbf4e7289e. Signed-off-by: Colton Wolkins (Indicio work address) --- .../protocols/coordinate_mediation/v1_0/manager.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py index c96358b6a1..6a5af46957 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py @@ -13,7 +13,6 @@ from ....wallet.did_method import DIDMethod from ....wallet.base import BaseWallet from ....wallet.did_info import DIDInfo -from ....did.did_key import DIDKey from ...routing.v1_0.manager import RoutingManager from ...routing.v1_0.models.route_record import RouteRecord @@ -181,8 +180,6 @@ async def grant_request( mediation_record.state = MediationRecord.STATE_GRANTED - routing_did = DIDKey.from_public_key_b58(routing_did.verkey, routing_did.key_type) - await mediation_record.save(session, reason="Mediation request granted") grant = MediationGrant( endpoint=session.settings.get("default_endpoint"), From 1ad866da13794980ff060377349281b9c9378ced Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Tue, 26 Jul 2022 23:57:48 -0600 Subject: [PATCH 362/872] fix: Fix how Mediation Grant returns routing keys Signed-off-by: Colton Wolkins (Indicio work address) --- .../protocols/coordinate_mediation/v1_0/manager.py | 2 +- .../coordinate_mediation/v1_0/messages/mediate_grant.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py index 6a5af46957..195dd5fd5a 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py @@ -183,7 +183,7 @@ async def grant_request( await mediation_record.save(session, reason="Mediation request granted") grant = MediationGrant( endpoint=session.settings.get("default_endpoint"), - routing_keys=[routing_did.did], + routing_keys=[routing_did.verkey], ) return mediation_record, grant diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/mediate_grant.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/mediate_grant.py index c18a2d132d..3b9e80fc74 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/mediate_grant.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/mediate_grant.py @@ -43,7 +43,10 @@ def __init__( """ super(MediationGrant, self).__init__(**kwargs) self.endpoint = endpoint - self.routing_keys = list(f"did:key:z{DIDKey.from_public_key_b58(key, KeyType.ED25519).did}" for key in routing_keys) if routing_keys else [] + self.routing_keys = list( + (key if key.startswith("did:key:z") + else DIDKey.from_public_key_b58(key, KeyType.ED25519).did) + for key in routing_keys) if routing_keys else [] class MediationGrantSchema(AgentMessageSchema): From 4a0be009528ddbda35d55ccfba504dbd9fc05357 Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Wed, 27 Jul 2022 00:54:21 -0600 Subject: [PATCH 363/872] chore: Fix unit tests Signed-off-by: Colton Wolkins (Indicio work address) --- .../v1_0/handlers/tests/test_mediation_grant_handler.py | 2 +- .../coordinate_mediation/v1_0/messages/tests/test_keylist.py | 2 +- .../v1_0/messages/tests/test_keylist_update.py | 2 +- .../v1_0/messages/tests/test_keylist_update_response.py | 2 +- .../v1_0/messages/tests/test_mediate_grant.py | 2 +- .../v1_0/tests/test_mediation_manager.py | 5 ++++- 6 files changed, 9 insertions(+), 6 deletions(-) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/tests/test_mediation_grant_handler.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/tests/test_mediation_grant_handler.py index c4c5d1a074..fd75c1a0d6 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/tests/test_mediation_grant_handler.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/tests/test_mediation_grant_handler.py @@ -18,7 +18,7 @@ from .. import mediation_grant_handler as test_module TEST_CONN_ID = "conn-id" -TEST_VERKEY = "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx" +TEST_VERKEY = "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL" TEST_ENDPOINT = "https://example.com" diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/tests/test_keylist.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/tests/test_keylist.py index a65512d026..a955580966 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/tests/test_keylist.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/tests/test_keylist.py @@ -18,7 +18,7 @@ class TestKeylist(MessageTest, TestCase): "pagination": KeylistQueryPaginate(10, 10), "keys": [ KeylistKey( - recipient_key="3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx", + recipient_key="did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", action="added", result="success", ) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/tests/test_keylist_update.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/tests/test_keylist_update.py index 650105425e..c22a75ba9e 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/tests/test_keylist_update.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/tests/test_keylist_update.py @@ -15,6 +15,6 @@ class TestKeylistUpdate(MessageTest, TestCase): SCHEMA = KeylistUpdateSchema VALUES = { "updates": [ - KeylistUpdateRule("3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx", "add") + KeylistUpdateRule("did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", "add") ] } diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/tests/test_keylist_update_response.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/tests/test_keylist_update_response.py index b7f70e25af..e59ea63511 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/tests/test_keylist_update_response.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/tests/test_keylist_update_response.py @@ -16,7 +16,7 @@ class TestKeylistUpdateResponse(MessageTest, TestCase): VALUES = { "updated": [ KeylistUpdated( - recipient_key="3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx", + recipient_key="did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", action="added", result="success", ) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/tests/test_mediate_grant.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/tests/test_mediate_grant.py index 2f73b32afe..5d24ca330b 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/tests/test_mediate_grant.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/tests/test_mediate_grant.py @@ -12,4 +12,4 @@ class TestMediateGrant(MessageTest, TestCase): TYPE = MEDIATE_GRANT CLASS = MediationGrant SCHEMA = MediationGrantSchema - VALUES = {"endpoint": "http://localhost:3000", "routing_keys": ["test_routing_key"]} + VALUES = {"endpoint": "http://localhost:3000", "routing_keys": ["did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL"]} diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py index 788404feb8..3e9a1175fa 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py @@ -9,6 +9,7 @@ from .....core.event_bus import EventBus, MockEventBus from .....core.in_memory import InMemoryProfile from .....core.profile import Profile, ProfileSession +from .....did.did_key import DIDKey from .....storage.error import StorageNotFoundError from ....routing.v1_0.models.route_record import RouteRecord from ..manager import ( @@ -113,8 +114,10 @@ async def test_grant_request(self, session, manager): assert record.connection_id == TEST_CONN_ID record, grant = await manager.grant_request(record.mediation_id) assert grant.endpoint == session.settings.get("default_endpoint") + routing_key = (await manager._retrieve_routing_did(session)) + routing_key = DIDKey.from_public_key_b58(routing_key.verkey, routing_key.key_type).did assert grant.routing_keys == [ - (await manager._retrieve_routing_did(session)).verkey + routing_key ] async def test_deny_request(self, manager): From 72921d1181355975b1242aa34d36366352bb8e65 Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Wed, 27 Jul 2022 08:13:08 -0600 Subject: [PATCH 364/872] feat: Add did:key conversions throughout mediation Also attempt to revert key formats before forwarding messages Signed-off-by: Colton Wolkins (Indicio work address) --- .../v1_0/messages/inner/keylist_key.py | 9 +++++++-- .../v1_0/messages/inner/keylist_update_rule.py | 7 ++++++- .../v1_0/messages/inner/keylist_updated.py | 7 ++++++- .../v1_0/messages/mediate_grant.py | 2 +- aries_cloudagent/transport/pack_format.py | 13 ++++++++++++- 5 files changed, 32 insertions(+), 6 deletions(-) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_key.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_key.py index 45a1df48c3..eb7819819f 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_key.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_key.py @@ -3,7 +3,9 @@ from marshmallow import EXCLUDE, fields from ......messaging.models.base import BaseModel, BaseModelSchema -from ......messaging.valid import DID_KEY +from ......messaging.valid import INDY_RAW_PUBLIC_KEY, DID_KEY +from ......did.did_key import DIDKey +from ......wallet.key_type import KeyType class KeylistKey(BaseModel): @@ -32,7 +34,10 @@ def __init__( """ super().__init__(**kwargs) - self.recipient_key = recipient_key + if key.startswith("did:key:"): + self.recipient_key = recipient_key + else: + self.recipient_key = DIDKey.from_public_key_b58(key, KeyType.ED25519).did class KeylistKeySchema(BaseModelSchema): diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_update_rule.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_update_rule.py index c4dd529935..cf79bd92db 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_update_rule.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_update_rule.py @@ -9,6 +9,8 @@ from ......messaging.models.base import BaseModel, BaseModelSchema from ......messaging.valid import DID_KEY +from ......did.did_key import DIDKey +from ......wallet.key_type import KeyType class KeylistUpdateRule(BaseModel): @@ -32,7 +34,10 @@ def __init__(self, recipient_key: str, action: str, **kwargs): """ super().__init__(**kwargs) - self.recipient_key = recipient_key + if key.startswith("did:key:"): + self.recipient_key = recipient_key + else: + self.recipient_key = DIDKey.from_public_key_b58(key, KeyType.ED25519).did self.action = action diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_updated.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_updated.py index 1fe36c8044..942ef0c030 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_updated.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_updated.py @@ -7,6 +7,8 @@ from ......messaging.models.base import BaseModel, BaseModelSchema from ......messaging.valid import DID_KEY +from ......did.did_key import DIDKey +from ......wallet.key_type import KeyType class KeylistUpdated(BaseModel): @@ -40,7 +42,10 @@ def __init__( """ super().__init__(**kwargs) - self.recipient_key = recipient_key + if key.startswith("did:key:"): + self.recipient_key = recipient_key + else: + self.recipient_key = DIDKey.from_public_key_b58(key, KeyType.ED25519).did self.action = action self.result = result diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/mediate_grant.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/mediate_grant.py index 3b9e80fc74..f15ec524d6 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/mediate_grant.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/mediate_grant.py @@ -44,7 +44,7 @@ def __init__( super(MediationGrant, self).__init__(**kwargs) self.endpoint = endpoint self.routing_keys = list( - (key if key.startswith("did:key:z") + (key if key.startswith("did:key:") else DIDKey.from_public_key_b58(key, KeyType.ED25519).did) for key in routing_keys) if routing_keys else [] diff --git a/aries_cloudagent/transport/pack_format.py b/aries_cloudagent/transport/pack_format.py index f982748098..a16fbb118b 100644 --- a/aries_cloudagent/transport/pack_format.py +++ b/aries_cloudagent/transport/pack_format.py @@ -7,6 +7,8 @@ from ..core.profile import ProfileSession from ..protocols.routing.v1_0.messages.forward import Forward +from ..did.did_key import DIDKey +from ..wallet.key_type import KeyType from ..messaging.util import time_now from ..utils.task_queue import TaskQueue @@ -183,12 +185,21 @@ async def pack( raise WireFormatEncodeError("Message pack failed") from e if routing_keys: - recip_keys = recipient_keys + recip_keys = [] + for key in recipient_keys: + if key.startswith("did:key:"): + recip_keys.append(DIDKey.from_did(key).public_key_b58) + else: + recip_keys.append(recipient_key) for router_key in routing_keys: message = json.loads(message.decode("utf-8")) fwd_msg = Forward(to=recip_keys[0], msg=message) # Forwards are anon packed recip_keys = [router_key] + if router_key.startswith("did:key:"): + recip_keys = [DIDKey.from_did(router_key).public_key_b58] + else: + recip_keys = [recipient_key] try: message = await wallet.pack_message(fwd_msg.to_json(), recip_keys) except WalletError as e: From f22af92705c36aa05b247bf2d15e5329045ecbe6 Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Wed, 27 Jul 2022 08:27:13 -0600 Subject: [PATCH 365/872] fix: Typos in variable names Signed-off-by: Colton Wolkins (Indicio work address) --- .../coordinate_mediation/v1_0/messages/inner/keylist_key.py | 4 ++-- .../v1_0/messages/inner/keylist_update_rule.py | 4 ++-- .../v1_0/messages/inner/keylist_updated.py | 4 ++-- aries_cloudagent/transport/pack_format.py | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_key.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_key.py index eb7819819f..dbc9583893 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_key.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_key.py @@ -34,10 +34,10 @@ def __init__( """ super().__init__(**kwargs) - if key.startswith("did:key:"): + if recipient_key.startswith("did:key:"): self.recipient_key = recipient_key else: - self.recipient_key = DIDKey.from_public_key_b58(key, KeyType.ED25519).did + self.recipient_key = DIDKey.from_public_key_b58(recipient_key, KeyType.ED25519).did class KeylistKeySchema(BaseModelSchema): diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_update_rule.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_update_rule.py index cf79bd92db..eb7c843810 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_update_rule.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_update_rule.py @@ -34,10 +34,10 @@ def __init__(self, recipient_key: str, action: str, **kwargs): """ super().__init__(**kwargs) - if key.startswith("did:key:"): + if recipient_key.startswith("did:key:"): self.recipient_key = recipient_key else: - self.recipient_key = DIDKey.from_public_key_b58(key, KeyType.ED25519).did + self.recipient_key = DIDKey.from_public_key_b58(recipient_key, KeyType.ED25519).did self.action = action diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_updated.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_updated.py index 942ef0c030..8f17e5d52e 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_updated.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_updated.py @@ -42,10 +42,10 @@ def __init__( """ super().__init__(**kwargs) - if key.startswith("did:key:"): + if recipient_key.startswith("did:key:"): self.recipient_key = recipient_key else: - self.recipient_key = DIDKey.from_public_key_b58(key, KeyType.ED25519).did + self.recipient_key = DIDKey.from_public_key_b58(recipient_key, KeyType.ED25519).did self.action = action self.result = result diff --git a/aries_cloudagent/transport/pack_format.py b/aries_cloudagent/transport/pack_format.py index a16fbb118b..967ec8322f 100644 --- a/aries_cloudagent/transport/pack_format.py +++ b/aries_cloudagent/transport/pack_format.py @@ -190,7 +190,7 @@ async def pack( if key.startswith("did:key:"): recip_keys.append(DIDKey.from_did(key).public_key_b58) else: - recip_keys.append(recipient_key) + recip_keys.append(key) for router_key in routing_keys: message = json.loads(message.decode("utf-8")) fwd_msg = Forward(to=recip_keys[0], msg=message) From 4e5d589626d8f2a8e3557266b5adb6312ea12c89 Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Wed, 27 Jul 2022 12:11:24 -0600 Subject: [PATCH 366/872] fix: Convert from did:key to verkey when retrieving recipient Signed-off-by: Colton Wolkins (Indicio work address) --- aries_cloudagent/protocols/routing/v1_0/manager.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/aries_cloudagent/protocols/routing/v1_0/manager.py b/aries_cloudagent/protocols/routing/v1_0/manager.py index 44c1b7b7c6..ad5a9f23b7 100644 --- a/aries_cloudagent/protocols/routing/v1_0/manager.py +++ b/aries_cloudagent/protocols/routing/v1_0/manager.py @@ -4,6 +4,8 @@ from ....core.error import BaseError from ....core.profile import Profile +from ....did.did_key import DIDKey +from ....wallet.key_type import KeyType from ....storage.error import ( StorageError, StorageDuplicateError, @@ -56,6 +58,8 @@ async def get_recipient(self, recip_verkey: str) -> RouteRecord: try: async with self._profile.session() as session: + if recip_verkey.startswith("did:key:"): + recip_verkey = DIDKey.from_did(recip_verkey).public_key_b58 record = await RouteRecord.retrieve_by_recipient_key( session, recip_verkey ) From a66091a316df7d465cb770ef033c8c08dbf7a446 Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Wed, 27 Jul 2022 12:33:50 -0600 Subject: [PATCH 367/872] chore: test Signed-off-by: Colton Wolkins (Indicio work address) --- aries_cloudagent/protocols/routing/v1_0/manager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/protocols/routing/v1_0/manager.py b/aries_cloudagent/protocols/routing/v1_0/manager.py index ad5a9f23b7..bd3c0b49f4 100644 --- a/aries_cloudagent/protocols/routing/v1_0/manager.py +++ b/aries_cloudagent/protocols/routing/v1_0/manager.py @@ -58,8 +58,8 @@ async def get_recipient(self, recip_verkey: str) -> RouteRecord: try: async with self._profile.session() as session: - if recip_verkey.startswith("did:key:"): - recip_verkey = DIDKey.from_did(recip_verkey).public_key_b58 + if not recip_verkey.startswith("did:key:"): + recip_verkey = DIDKey.from_public_key_b58(key, KeyType.ED25519).did record = await RouteRecord.retrieve_by_recipient_key( session, recip_verkey ) From 6d0c27baecd916293c681df73e880f1e4f8c3db8 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Wed, 27 Jul 2022 16:38:10 -0400 Subject: [PATCH 368/872] fix: unable to use askar with in memory db Signed-off-by: Daniel Bluhm --- aries_cloudagent/askar/store.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aries_cloudagent/askar/store.py b/aries_cloudagent/askar/store.py index f96112e4a2..6c81d6696d 100644 --- a/aries_cloudagent/askar/store.py +++ b/aries_cloudagent/askar/store.py @@ -146,7 +146,7 @@ async def open_store(self, provision: bool = False) -> "AskarOpenStore": """ try: - if provision: + if provision or self.in_memory: store = await Store.provision( self.get_uri(create=True), self.key_derivation_method, From 13648d31877642006b1c57f868f2e8dfc518d60f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Humbert?= Date: Thu, 28 Jul 2022 12:09:57 +0200 Subject: [PATCH 369/872] Allow fully qualified class names for profile managers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix a small oversight that prevents specifying a qualified class name as wallet type. Signed-off-by: Clément Humbert --- aries_cloudagent/core/profile.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/aries_cloudagent/core/profile.py b/aries_cloudagent/core/profile.py index e5de864d6e..c78ed8d94f 100644 --- a/aries_cloudagent/core/profile.py +++ b/aries_cloudagent/core/profile.py @@ -315,14 +315,14 @@ def __init__(self): def provide(self, settings: BaseSettings, injector: BaseInjector): """Create the profile manager instance.""" + mgr_type = settings.get_value("wallet.type", default="in_memory") - mgr_type = settings.get_value("wallet.type", default="in_memory").lower() - if mgr_type == "basic": + if mgr_type.lower() == "basic": # map previous value mgr_type = "in_memory" # mgr_type may be a fully qualified class name - mgr_class = self.MANAGER_TYPES.get(mgr_type, mgr_type) + mgr_class = self.MANAGER_TYPES.get(mgr_type.lower(), mgr_type) if mgr_class not in self._inst: LOGGER.info("Create profile manager: %s", mgr_type) From 051e62dc40cf2d0f49611cbfe16812c953c3de8a Mon Sep 17 00:00:00 2001 From: Roman Reinert Date: Mon, 1 Aug 2022 14:37:18 +0000 Subject: [PATCH 370/872] fix incorrect response schema in receive_invitation Signed-off-by: Roman Reinert --- aries_cloudagent/protocols/out_of_band/v1_0/routes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/routes.py b/aries_cloudagent/protocols/out_of_band/v1_0/routes.py index f96f859f86..3f7384c164 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/routes.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/routes.py @@ -9,7 +9,6 @@ from marshmallow.exceptions import ValidationError from ....admin.request_context import AdminRequestContext -from ....connections.models.conn_record import ConnRecordSchema from ....messaging.models.base import BaseModelError from ....messaging.models.openapi import OpenAPISchema from ....messaging.valid import UUID4 @@ -22,6 +21,7 @@ from .messages.invitation import HSProto, InvitationMessage, InvitationMessageSchema from .message_types import SPEC_URI from .models.invitation import InvitationRecordSchema +from .models.oob_record import OobRecordSchema LOGGER = logging.getLogger(__name__) @@ -188,7 +188,7 @@ async def invitation_create(request: web.BaseRequest): ) @querystring_schema(InvitationReceiveQueryStringSchema()) @request_schema(InvitationMessageSchema()) -@response_schema(ConnRecordSchema(), 200, description="") +@response_schema(OobRecordSchema(), 200, description="") async def invitation_receive(request: web.BaseRequest): """ Request handler for receiving a new connection invitation. From 72111e6c24c3ea7814d16d194fe3c8bad0c295d9 Mon Sep 17 00:00:00 2001 From: Roman Reinert Date: Mon, 1 Aug 2022 15:40:49 +0000 Subject: [PATCH 371/872] save connection state after receiving request Signed-off-by: Roman Reinert --- aries_cloudagent/protocols/didexchange/v1_0/manager.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/protocols/didexchange/v1_0/manager.py b/aries_cloudagent/protocols/didexchange/v1_0/manager.py index ec80923e9a..587e6b56b5 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/manager.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/manager.py @@ -477,7 +477,10 @@ async def receive_request( conn_rec.their_did = request.did conn_rec.state = ConnRecord.State.REQUEST.rfc23 conn_rec.request_id = request._id - + async with self.profile.session() as session: + await conn_rec.save( + session, reason="Received connection request from invitation" + ) else: # request is against implicit invitation on public DID async with self.profile.session() as session: From 6460304d849efd5d6cee2eeb1817c8b00cbf49bb Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Tue, 2 Aug 2022 08:49:31 -0600 Subject: [PATCH 372/872] fix: Add RoutingKey validator Add the RoutingKey Validator that supports both DIDKey and Raw Indy Public Keys for backwards compatibility reasons Signed-off-by: Colton Wolkins (Indicio work address) --- aries_cloudagent/messaging/valid.py | 16 ++++++++++++++++ .../v1_0/messages/inner/keylist_update_rule.py | 4 ++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/messaging/valid.py b/aries_cloudagent/messaging/valid.py index acaf9a27f8..cde3449f7f 100644 --- a/aries_cloudagent/messaging/valid.py +++ b/aries_cloudagent/messaging/valid.py @@ -349,6 +349,21 @@ def __init__(self): ) +class RoutingKey(Regexp): + """Validate value against indy (Ed25519VerificationKey2018) raw public key or DID key specification.""" + + EXAMPLE = DIDKey.EXAMPLE + PATTERN = re.compile(DIDKey.PATTERN.pattern + "|" + IndyRawPublicKey.PATTERN) + + def __init__(self): + """Initializer.""" + + super().__init__( + RoutingKey.PATTERN, + error="Value {input} is not in W3C did:key or Ed25519VerificationKey2018 key format", + ) + + class IndyCredDefId(Regexp): """Validate value against indy credential definition identifier specification.""" @@ -788,6 +803,7 @@ def __init__( JWT = {"validate": JSONWebToken(), "example": JSONWebToken.EXAMPLE} DID_KEY = {"validate": DIDKey(), "example": DIDKey.EXAMPLE} DID_POSTURE = {"validate": DIDPosture(), "example": DIDPosture.EXAMPLE} +ROUTING_KEY = {"validate": RoutingKey(), "example": RoutingKey.EXAMPLE} INDY_DID = {"validate": IndyDID(), "example": IndyDID.EXAMPLE} GENERIC_DID = {"validate": MaybeIndyDID(), "example": MaybeIndyDID.EXAMPLE} INDY_RAW_PUBLIC_KEY = { diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_update_rule.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_update_rule.py index eb7c843810..96406b54ed 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_update_rule.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_update_rule.py @@ -8,7 +8,7 @@ from marshmallow.validate import OneOf from ......messaging.models.base import BaseModel, BaseModelSchema -from ......messaging.valid import DID_KEY +from ......messaging.valid import ROUTING_KEY from ......did.did_key import DIDKey from ......wallet.key_type import KeyType @@ -50,7 +50,7 @@ class Meta: model_class = KeylistUpdateRule recipient_key = fields.Str( - description="Key to remove or add", required=True, **DID_KEY + description="Key to remove or add", required=True, **ROUTING_KEY ) action = fields.Str( required=True, From 27cb777576be34d6c5c3ba6213465525fc101a12 Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Tue, 2 Aug 2022 10:22:28 -0600 Subject: [PATCH 373/872] fix: Decode recipient keys before packing message Signed-off-by: Colton Wolkins (Indicio work address) --- aries_cloudagent/transport/pack_format.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/aries_cloudagent/transport/pack_format.py b/aries_cloudagent/transport/pack_format.py index 967ec8322f..c1172a3156 100644 --- a/aries_cloudagent/transport/pack_format.py +++ b/aries_cloudagent/transport/pack_format.py @@ -177,20 +177,21 @@ async def pack( if not wallet: raise WireFormatEncodeError("No wallet instance") + recip_keys = [] + for key in recipient_keys: + if key.startswith("did:key:"): + recip_keys.append(DIDKey.from_did(key).public_key_b58) + else: + recip_keys.append(key) + try: message = await wallet.pack_message( - message_json, recipient_keys, sender_key + message_json, recip_keys, sender_key ) except WalletError as e: raise WireFormatEncodeError("Message pack failed") from e if routing_keys: - recip_keys = [] - for key in recipient_keys: - if key.startswith("did:key:"): - recip_keys.append(DIDKey.from_did(key).public_key_b58) - else: - recip_keys.append(key) for router_key in routing_keys: message = json.loads(message.decode("utf-8")) fwd_msg = Forward(to=recip_keys[0], msg=message) @@ -198,8 +199,6 @@ async def pack( recip_keys = [router_key] if router_key.startswith("did:key:"): recip_keys = [DIDKey.from_did(router_key).public_key_b58] - else: - recip_keys = [recipient_key] try: message = await wallet.pack_message(fwd_msg.to_json(), recip_keys) except WalletError as e: From 707dad21bcd317d872b6deef52b33adfff5f3fea Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Tue, 2 Aug 2022 11:01:57 -0600 Subject: [PATCH 374/872] fix: Decode routing keys when making DIDDoc Signed-off-by: Colton Wolkins (Indicio work address) --- aries_cloudagent/connections/base_manager.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/connections/base_manager.py b/aries_cloudagent/connections/base_manager.py index bd5e281694..78011f2c7c 100644 --- a/aries_cloudagent/connections/base_manager.py +++ b/aries_cloudagent/connections/base_manager.py @@ -18,6 +18,7 @@ from ..core.error import BaseError from ..core.profile import Profile from ..did.did_key import DIDKey +from ..wallet.key_type import KeyType from ..protocols.connections.v1_0.messages.connection_invitation import ( ConnectionInvitation, ) @@ -116,10 +117,12 @@ async def create_did_document( raise BaseConnectionManagerError( "Routing DIDDoc service has no recipient key(s)" ) + key = service.recip_keys[0].value + key = DIDKey.from_did(key).public_key_b58 if key.startswith("did:key:") else key rk = PublicKey( did_info.did, f"routing-{router_idx}", - service.recip_keys[0].value, + key, PublicKeyType.ED25519_SIG_2018, did_controller, True, @@ -135,7 +138,7 @@ async def create_did_document( PublicKey( did_info.did, f"routing-{idx}", - key, + DIDKey.from_did(key).public_key_b58 if key.startswith("did:key:") else key, PublicKeyType.ED25519_SIG_2018, did_controller, # TODO: get correct controller did_info True, # TODO: should this be true? From 5ed52473c4f1249338f08eb4d4aeb31229ed0481 Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Tue, 2 Aug 2022 11:31:32 -0600 Subject: [PATCH 375/872] fix: Fix typo in variable names Signed-off-by: Colton Wolkins (Indicio work address) --- aries_cloudagent/protocols/routing/v1_0/manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aries_cloudagent/protocols/routing/v1_0/manager.py b/aries_cloudagent/protocols/routing/v1_0/manager.py index bd3c0b49f4..48556fe347 100644 --- a/aries_cloudagent/protocols/routing/v1_0/manager.py +++ b/aries_cloudagent/protocols/routing/v1_0/manager.py @@ -59,7 +59,7 @@ async def get_recipient(self, recip_verkey: str) -> RouteRecord: try: async with self._profile.session() as session: if not recip_verkey.startswith("did:key:"): - recip_verkey = DIDKey.from_public_key_b58(key, KeyType.ED25519).did + recip_verkey = DIDKey.from_public_key_b58(recip_verkey, KeyType.ED25519).did record = await RouteRecord.retrieve_by_recipient_key( session, recip_verkey ) From 3ee7f433ca7a39d3fcc600aea0f8e771fa1bdc0c Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Wed, 3 Aug 2022 13:18:49 -0600 Subject: [PATCH 376/872] fix: Fix unit tests after did:key work Signed-off-by: Colton Wolkins (Indicio work address) --- aries_cloudagent/connections/base_manager.py | 9 ++++--- aries_cloudagent/messaging/valid.py | 10 ++++++-- .../multitenant/tests/test_base.py | 24 ++++++++++--------- .../connections/v1_0/tests/test_manager.py | 5 +++- .../tests/test_keylist_query_handler.py | 3 ++- .../v1_0/messages/inner/keylist_key.py | 6 +++-- .../messages/inner/keylist_update_rule.py | 4 +++- .../v1_0/messages/inner/keylist_updated.py | 4 +++- .../v1_0/tests/test_routes.py | 20 ++++++++++------ .../didexchange/v1_0/tests/test_manager.py | 5 +++- .../protocols/routing/v1_0/manager.py | 7 ++++-- aries_cloudagent/transport/pack_format.py | 1 - 12 files changed, 65 insertions(+), 33 deletions(-) diff --git a/aries_cloudagent/connections/base_manager.py b/aries_cloudagent/connections/base_manager.py index 78011f2c7c..6421a47c9c 100644 --- a/aries_cloudagent/connections/base_manager.py +++ b/aries_cloudagent/connections/base_manager.py @@ -18,7 +18,6 @@ from ..core.error import BaseError from ..core.profile import Profile from ..did.did_key import DIDKey -from ..wallet.key_type import KeyType from ..protocols.connections.v1_0.messages.connection_invitation import ( ConnectionInvitation, ) @@ -118,7 +117,10 @@ async def create_did_document( "Routing DIDDoc service has no recipient key(s)" ) key = service.recip_keys[0].value - key = DIDKey.from_did(key).public_key_b58 if key.startswith("did:key:") else key + key = ( + DIDKey.from_did(key).public_key_b58 + if key.startswith("did:key:") else key + ) rk = PublicKey( did_info.did, f"routing-{router_idx}", @@ -138,7 +140,8 @@ async def create_did_document( PublicKey( did_info.did, f"routing-{idx}", - DIDKey.from_did(key).public_key_b58 if key.startswith("did:key:") else key, + DIDKey.from_did(key).public_key_b58 + if key.startswith("did:key:") else key, PublicKeyType.ED25519_SIG_2018, did_controller, # TODO: get correct controller did_info True, # TODO: should this be true? diff --git a/aries_cloudagent/messaging/valid.py b/aries_cloudagent/messaging/valid.py index cde3449f7f..a62d587fff 100644 --- a/aries_cloudagent/messaging/valid.py +++ b/aries_cloudagent/messaging/valid.py @@ -350,7 +350,12 @@ def __init__(self): class RoutingKey(Regexp): - """Validate value against indy (Ed25519VerificationKey2018) raw public key or DID key specification.""" + """ + Validate between indy or did key. + + Validate value against indy (Ed25519VerificationKey2018) + raw public key or DID key specification. + """ EXAMPLE = DIDKey.EXAMPLE PATTERN = re.compile(DIDKey.PATTERN.pattern + "|" + IndyRawPublicKey.PATTERN) @@ -360,7 +365,8 @@ def __init__(self): super().__init__( RoutingKey.PATTERN, - error="Value {input} is not in W3C did:key or Ed25519VerificationKey2018 key format", + error=("Value {input} is not in W3C did:key" + " or Ed25519VerificationKey2018 key format"), ) diff --git a/aries_cloudagent/multitenant/tests/test_base.py b/aries_cloudagent/multitenant/tests/test_base.py index 5d37297b9d..f5a67249a4 100644 --- a/aries_cloudagent/multitenant/tests/test_base.py +++ b/aries_cloudagent/multitenant/tests/test_base.py @@ -25,6 +25,8 @@ from ..error import WalletKeyMissingError from .. import base as test_module +RECIPIENT_KEY = "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV" + class MockMultitenantManager(BaseMultitenantManager): async def get_wallet_profile( @@ -141,7 +143,7 @@ async def test_wallet_exists_false(self): assert wallet_name_exists is False async def test_get_wallet_by_key_routing_record_does_not_exist(self): - recipient_key = "test" + recipient_key = RECIPIENT_KEY with async_mock.patch.object(WalletRecord, "retrieve_by_id") as retrieve_by_id: wallet = await self.manager._get_wallet_by_key(recipient_key) @@ -152,7 +154,7 @@ async def test_get_wallet_by_key_routing_record_does_not_exist(self): await self.manager._get_wallet_by_key(recipient_key) async def test_get_wallet_by_key_wallet_record_does_not_exist(self): - recipient_key = "test-recipient-key" + recipient_key = RECIPIENT_KEY wallet_id = "test-wallet-id" route_record = RouteRecord(wallet_id=wallet_id, recipient_key=recipient_key) @@ -163,7 +165,7 @@ async def test_get_wallet_by_key_wallet_record_does_not_exist(self): await self.manager._get_wallet_by_key(recipient_key) async def test_get_wallet_by_key(self): - recipient_key = "test-recipient-key" + recipient_key = RECIPIENT_KEY wallet_record = WalletRecord(settings={}) async with self.profile.session() as session: @@ -356,10 +358,10 @@ async def test_add_key_no_mediation(self): ) as create_route_record, async_mock.patch.object( MediationManager, "add_key" ) as mediation_add_key: - await self.manager.add_key("wallet_id", "recipient_key") + await self.manager.add_key("wallet_id", RECIPIENT_KEY) create_route_record.assert_called_once_with( - recipient_key="recipient_key", internal_wallet_id="wallet_id" + recipient_key=RECIPIENT_KEY, internal_wallet_id="wallet_id" ) mediation_add_key.assert_not_called() @@ -372,11 +374,11 @@ async def test_add_key_skip_if_exists_does_not_exist(self): retrieve_by_recipient_key.side_effect = StorageNotFoundError() await self.manager.add_key( - "wallet_id", "recipient_key", skip_if_exists=True + "wallet_id", RECIPIENT_KEY, skip_if_exists=True ) create_route_record.assert_called_once_with( - recipient_key="recipient_key", internal_wallet_id="wallet_id" + recipient_key=RECIPIENT_KEY, internal_wallet_id="wallet_id" ) async def test_add_key_skip_if_exists_does_exist(self): @@ -386,7 +388,7 @@ async def test_add_key_skip_if_exists_does_exist(self): RouteRecord, "retrieve_by_recipient_key" ) as retrieve_by_recipient_key: await self.manager.add_key( - "wallet_id", "recipient_key", skip_if_exists=True + "wallet_id", RECIPIENT_KEY, skip_if_exists=True ) create_route_record.assert_not_called() @@ -405,14 +407,14 @@ async def test_add_key_mediation(self): get_default_mediator.return_value = default_mediator mediation_add_key.return_value = keylist_updates - await self.manager.add_key("wallet_id", "recipient_key") + await self.manager.add_key("wallet_id", RECIPIENT_KEY) create_route_record.assert_called_once_with( - recipient_key="recipient_key", internal_wallet_id="wallet_id" + recipient_key=RECIPIENT_KEY, internal_wallet_id="wallet_id" ) get_default_mediator.assert_called_once() - mediation_add_key.assert_called_once_with("recipient_key") + mediation_add_key.assert_called_once_with(RECIPIENT_KEY) self.responder.send.assert_called_once_with( keylist_updates, connection_id=default_mediator.connection_id ) diff --git a/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py index c1b1bb0ba1..a0a8ca5829 100644 --- a/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py @@ -1052,7 +1052,10 @@ async def test_receive_request_mediation_id(self): assert len(message.updates) == 1 (remove,) = message.updates assert remove.action == KeylistUpdateRule.RULE_REMOVE - assert remove.recipient_key == record.invitation_key + did_key = DIDKey.from_public_key_b58( + record.invitation_key, KeyType.ED25519 + ).did + assert remove.recipient_key == did_key async def test_receive_request_bad_mediation(self): mock_request = async_mock.MagicMock() diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/tests/test_keylist_query_handler.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/tests/test_keylist_query_handler.py index 6d6d7d9152..fd216be4a7 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/tests/test_keylist_query_handler.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/tests/test_keylist_query_handler.py @@ -18,6 +18,7 @@ TEST_CONN_ID = "conn-id" TEST_VERKEY = "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx" +TEST_VERKEY_DIDKEY = "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL" class TestKeylistQueryHandler(AsyncTestCase): @@ -77,4 +78,4 @@ async def test_handler(self): result, _target = responder.messages[0] assert isinstance(result, Keylist) assert len(result.keys) == 1 - assert result.keys[0].recipient_key == TEST_VERKEY + assert result.keys[0].recipient_key == TEST_VERKEY_DIDKEY diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_key.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_key.py index dbc9583893..c320ea62aa 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_key.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_key.py @@ -3,7 +3,7 @@ from marshmallow import EXCLUDE, fields from ......messaging.models.base import BaseModel, BaseModelSchema -from ......messaging.valid import INDY_RAW_PUBLIC_KEY, DID_KEY +from ......messaging.valid import DID_KEY from ......did.did_key import DIDKey from ......wallet.key_type import KeyType @@ -37,7 +37,9 @@ def __init__( if recipient_key.startswith("did:key:"): self.recipient_key = recipient_key else: - self.recipient_key = DIDKey.from_public_key_b58(recipient_key, KeyType.ED25519).did + self.recipient_key = ( + DIDKey.from_public_key_b58(recipient_key, KeyType.ED25519).did + ) class KeylistKeySchema(BaseModelSchema): diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_update_rule.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_update_rule.py index 96406b54ed..1ad0dc5d71 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_update_rule.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_update_rule.py @@ -37,7 +37,9 @@ def __init__(self, recipient_key: str, action: str, **kwargs): if recipient_key.startswith("did:key:"): self.recipient_key = recipient_key else: - self.recipient_key = DIDKey.from_public_key_b58(recipient_key, KeyType.ED25519).did + self.recipient_key = ( + DIDKey.from_public_key_b58(recipient_key, KeyType.ED25519).did + ) self.action = action diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_updated.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_updated.py index 8f17e5d52e..a2e4a11cd2 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_updated.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_updated.py @@ -45,7 +45,9 @@ def __init__( if recipient_key.startswith("did:key:"): self.recipient_key = recipient_key else: - self.recipient_key = DIDKey.from_public_key_b58(recipient_key, KeyType.ED25519).did + self.recipient_key = ( + DIDKey.from_public_key_b58(recipient_key, KeyType.ED25519).did + ) self.action = action self.result = result diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_routes.py index a19070a6e6..c63bca7c8b 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_routes.py @@ -454,8 +454,14 @@ async def test_get_keylist_storage_error(self): async def test_send_keylist_update(self): body = { "updates": [ - {"recipient_key": "test-key0", "action": "add"}, - {"recipient_key": "test-key1", "action": "remove"}, + {"recipient_key": "EwUKjVLboiLSuoWSEtDvrgrd41EUxG5bLecQrkHB63Up", "action": "add"}, + {"recipient_key": "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx", "action": "remove"}, + ] + } + body_with_didkey = { + "updates": [ + {"recipient_key": "did:key:z6MktPjNKjb39Fpv2JM8vTBmhnQcsaWLN9Kx2fXLh2FC1GGC", "action": "add"}, + {"recipient_key": "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", "action": "remove"}, ] } @@ -477,13 +483,13 @@ async def test_send_keylist_update(self): ), ) as mock_response: results, status = await test_module.send_keylist_update(self.request) - assert results["updates"] == body["updates"] + assert results["updates"] == body_with_didkey["updates"] assert status == 201 async def test_send_keylist_update_bad_action(self): self.request.json.return_value = { "updates": [ - {"recipient_key": "test-key0", "action": "wrong"}, + {"recipient_key": "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx", "action": "wrong"}, ] } @@ -493,7 +499,7 @@ async def test_send_keylist_update_bad_action(self): async def test_send_keylist_update_bad_mediation_state(self): self.request.json.return_value = { "updates": [ - {"recipient_key": "test-key0", "action": "add"}, + {"recipient_key": "EwUKjVLboiLSuoWSEtDvrgrd41EUxG5bLecQrkHB63Up", "action": "add"}, ] } @@ -516,7 +522,7 @@ async def test_send_keylist_update_bad_updates(self): async def test_send_keylist_update_x_no_mediation_rec(self): self.request.json.return_value = { "updates": [ - {"recipient_key": "test-key0", "action": "add"}, + {"recipient_key": "EwUKjVLboiLSuoWSEtDvrgrd41EUxG5bLecQrkHB63Up", "action": "add"}, ] } with async_mock.patch.object( @@ -529,7 +535,7 @@ async def test_send_keylist_update_x_no_mediation_rec(self): async def test_send_keylist_update_x_storage_error(self): self.request.json.return_value = { "updates": [ - {"recipient_key": "test-key0", "action": "add"}, + {"recipient_key": "EwUKjVLboiLSuoWSEtDvrgrd41EUxG5bLecQrkHB63Up", "action": "add"}, ] } diff --git a/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py index 3ffd26a181..2eb10cd20d 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py @@ -717,7 +717,10 @@ async def test_receive_request_with_mediator_without_multi_use_multitenant(self) assert len(message.updates) == 1 (remove,) = message.updates assert remove.action == KeylistUpdateRule.RULE_REMOVE - assert remove.recipient_key == record.invitation_key + did_key = DIDKey.from_public_key_b58( + record.invitation_key, KeyType.ED25519 + ).did + assert remove.recipient_key == did_key async def test_receive_request_with_mediator_without_multi_use_multitenant_mismatch( self, diff --git a/aries_cloudagent/protocols/routing/v1_0/manager.py b/aries_cloudagent/protocols/routing/v1_0/manager.py index 48556fe347..44db54cc50 100644 --- a/aries_cloudagent/protocols/routing/v1_0/manager.py +++ b/aries_cloudagent/protocols/routing/v1_0/manager.py @@ -58,8 +58,11 @@ async def get_recipient(self, recip_verkey: str) -> RouteRecord: try: async with self._profile.session() as session: - if not recip_verkey.startswith("did:key:"): - recip_verkey = DIDKey.from_public_key_b58(recip_verkey, KeyType.ED25519).did + if recip_verkey.startswith("did:key:"): + recip_verkey = ( + DIDKey.from_did(recip_verkey, KeyType.ED25519) + ) + recip_verkey = recip_verkey.public_key_b58 record = await RouteRecord.retrieve_by_recipient_key( session, recip_verkey ) diff --git a/aries_cloudagent/transport/pack_format.py b/aries_cloudagent/transport/pack_format.py index c1172a3156..2c078868e1 100644 --- a/aries_cloudagent/transport/pack_format.py +++ b/aries_cloudagent/transport/pack_format.py @@ -8,7 +8,6 @@ from ..protocols.routing.v1_0.messages.forward import Forward from ..did.did_key import DIDKey -from ..wallet.key_type import KeyType from ..messaging.util import time_now from ..utils.task_queue import TaskQueue From e49c3c3d7b15bf8df7d958be17a189ddc5798706 Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Thu, 4 Aug 2022 08:06:33 -0600 Subject: [PATCH 377/872] feat: Search for both route keys when getting recipients Signed-off-by: Colton Wolkins (Indicio work address) --- .../protocols/routing/v1_0/manager.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/aries_cloudagent/protocols/routing/v1_0/manager.py b/aries_cloudagent/protocols/routing/v1_0/manager.py index 44db54cc50..3d596cfa4b 100644 --- a/aries_cloudagent/protocols/routing/v1_0/manager.py +++ b/aries_cloudagent/protocols/routing/v1_0/manager.py @@ -62,9 +62,21 @@ async def get_recipient(self, recip_verkey: str) -> RouteRecord: recip_verkey = ( DIDKey.from_did(recip_verkey, KeyType.ED25519) ) - recip_verkey = recip_verkey.public_key_b58 - record = await RouteRecord.retrieve_by_recipient_key( - session, recip_verkey + # recip_verkey = recip_verkey.public_key_b58 + else: + recip_verkey = ( + DIDKey.from_public_key_b58( + recip_verkey, + KeyType.ED25519, + ) + ) + # record = await RouteRecord.retrieve_by_recipient_key( + tag_filter = {"$or":[ + {"recipient_key": recip_verkey.did}, + {"recipient_key": recip_verkey.public_key_b58} + ]} + record = await RouteRecord.retrieve_by_tag_filter( + session, tag_filter ) except StorageDuplicateError: raise RouteNotFoundError( From b6c55dcbde2c106989414935ddbf7f0960cb88f2 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Thu, 4 Aug 2022 12:19:50 -0400 Subject: [PATCH 378/872] fix: schema class can set Meta.unknown Signed-off-by: Daniel Bluhm --- aries_cloudagent/messaging/models/base.py | 119 ++++++++++++++++++---- aries_cloudagent/utils/classloader.py | 7 +- 2 files changed, 102 insertions(+), 24 deletions(-) diff --git a/aries_cloudagent/messaging/models/base.py b/aries_cloudagent/messaging/models/base.py index fd00c7d68d..9ce1f98485 100644 --- a/aries_cloudagent/messaging/models/base.py +++ b/aries_cloudagent/messaging/models/base.py @@ -5,7 +5,7 @@ from abc import ABC from collections import namedtuple -from typing import Mapping, Union +from typing import Literal, Mapping, Optional, Type, TypeVar, Union, cast, overload from marshmallow import Schema, post_dump, pre_load, post_load, ValidationError, EXCLUDE @@ -17,7 +17,7 @@ SerDe = namedtuple("SerDe", "ser de") -def resolve_class(the_cls, relative_cls: type = None): +def resolve_class(the_cls, relative_cls: Optional[type] = None) -> type: """ Resolve a class. @@ -38,6 +38,10 @@ def resolve_class(the_cls, relative_cls: type = None): elif isinstance(the_cls, str): default_module = relative_cls and relative_cls.__module__ resolved = ClassLoader.load_class(the_cls, default_module) + else: + raise TypeError( + f"Could not resolve class from {the_cls}; incorrect type {type(the_cls)}" + ) return resolved @@ -70,6 +74,9 @@ class BaseModelError(BaseError): """Base exception class for base model errors.""" +ModelType = TypeVar("ModelType", bound="BaseModel") + + class BaseModel(ABC): """Base model that provides convenience methods.""" @@ -94,7 +101,7 @@ def __init__(self): ) @classmethod - def _get_schema_class(cls): + def _get_schema_class(cls) -> Type["BaseModelSchema"]: """ Get the schema class. @@ -102,10 +109,16 @@ def _get_schema_class(cls): The resolved schema class """ - return resolve_class(cls.Meta.schema_class, cls) + resolved = resolve_class(cls.Meta.schema_class, cls) + if issubclass(resolved, BaseModelSchema): + return resolved + + raise TypeError( + f"Resolved class is not a subclass of BaseModelSchema: {resolved}" + ) @property - def Schema(self) -> type: + def Schema(self) -> Type["BaseModelSchema"]: """ Accessor for the model's schema class. @@ -115,8 +128,46 @@ def Schema(self) -> type: """ return self._get_schema_class() + @overload @classmethod - def deserialize(cls, obj, unknown: str = None, none2none: str = False): + def deserialize( + cls: Type[ModelType], + obj, + *, + unknown: Optional[str] = None, + ) -> ModelType: + ... + + @overload + @classmethod + def deserialize( + cls: Type[ModelType], + obj, + *, + none2none: Literal[False], + unknown: Optional[str] = None, + ) -> ModelType: + ... + + @overload + @classmethod + def deserialize( + cls: Type[ModelType], + obj, + *, + none2none: Literal[True], + unknown: Optional[str] = None, + ) -> Optional[ModelType]: + ... + + @classmethod + def deserialize( + cls: Type[ModelType], + obj, + *, + unknown: Optional[str] = None, + none2none: bool = False, + ) -> Optional[ModelType]: """ Convert from JSON representation to a model instance. @@ -132,18 +183,41 @@ def deserialize(cls, obj, unknown: str = None, none2none: str = False): if obj is None and none2none: return None - schema = cls._get_schema_class()(unknown=unknown or EXCLUDE) + schema_cls = cls._get_schema_class() + schema = schema_cls(unknown=unknown or schema_cls.Meta.unknown) + try: - return schema.loads(obj) if isinstance(obj, str) else schema.load(obj) + return cast( + ModelType, + schema.loads(obj) if isinstance(obj, str) else schema.load(obj), + ) except (AttributeError, ValidationError) as err: LOGGER.exception(f"{cls.__name__} message validation error:") raise BaseModelError(f"{cls.__name__} schema validation failed") from err + @overload def serialize( self, - as_string=False, - unknown: str = None, + *, + as_string: Literal[True], + unknown: Optional[str] = None, + ) -> str: + ... + + @overload + def serialize( + self, + *, + unknown: Optional[str] = None, ) -> dict: + ... + + def serialize( + self, + *, + as_string: bool = False, + unknown: Optional[str] = None, + ) -> Union[str, dict]: """ Create a JSON-compatible dict representation of the model instance. @@ -154,7 +228,8 @@ def serialize( A dict representation of this model, or a JSON string if as_string is True """ - schema = self.Schema(unknown=unknown or EXCLUDE) + schema_cls = self._get_schema_class() + schema = schema_cls(unknown=unknown or schema_cls.Meta.unknown) try: return ( schema.dumps(self, separators=(",", ":")) @@ -168,18 +243,17 @@ def serialize( ) from err @classmethod - def serde(cls, obj: Union["BaseModel", Mapping]) -> SerDe: + def serde(cls, obj: Union["BaseModel", Mapping]) -> Optional[SerDe]: """Return serialized, deserialized representations of input object.""" + if obj is None: + return None - return ( - SerDe(obj.serialize(), obj) - if isinstance(obj, BaseModel) - else None - if obj is None - else SerDe(obj, cls.deserialize(obj)) - ) + if isinstance(obj, BaseModel): + return SerDe(obj.serialize(), obj) + + return SerDe(obj, cls.deserialize(obj)) - def validate(self, unknown: str = None): + def validate(self, unknown: Optional[str] = None): """Validate a constructed model.""" schema = self.Schema(unknown=unknown) errors = schema.validate(self.serialize()) @@ -191,7 +265,7 @@ def validate(self, unknown: str = None): def from_json( cls, json_repr: Union[str, bytes], - unknown: str = None, + unknown: Optional[str] = None, ): """ Parse a JSON string into a model instance. @@ -218,7 +292,7 @@ def to_json(self, unknown: str = None) -> str: A JSON representation of this message """ - return json.dumps(self.serialize(unknown=unknown or EXCLUDE)) + return json.dumps(self.serialize(unknown=unknown)) def __repr__(self) -> str: """ @@ -246,6 +320,7 @@ class Meta: model_class = None skip_values = [None] ordered = True + unknown = EXCLUDE def __init__(self, *args, **kwargs): """ diff --git a/aries_cloudagent/utils/classloader.py b/aries_cloudagent/utils/classloader.py index 7c429b0ea9..2b4de2a207 100644 --- a/aries_cloudagent/utils/classloader.py +++ b/aries_cloudagent/utils/classloader.py @@ -7,7 +7,7 @@ from importlib import import_module from importlib.util import find_spec, resolve_name from types import ModuleType -from typing import Sequence, Type +from typing import Optional, Sequence, Type from ..core.error import BaseError @@ -75,7 +75,10 @@ def load_module(cls, mod_path: str, package: str = None) -> ModuleType: @classmethod def load_class( - cls, class_name: str, default_module: str = None, package: str = None + cls, + class_name: str, + default_module: Optional[str] = None, + package: Optional[str] = None, ): """ Resolve a complete class path (ie. typing.Dict) to the class itself. From e55ae7d7cdc52e2f0eba87b24359b5e2f5948395 Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Thu, 4 Aug 2022 10:41:51 -0600 Subject: [PATCH 379/872] fix: Move backwards compat code to proper location & fix tests Signed-off-by: Colton Wolkins (Indicio work address) --- .../v1_0/tests/test_mediation_manager.py | 6 +++-- .../protocols/routing/v1_0/manager.py | 23 ++----------------- .../routing/v1_0/models/route_record.py | 21 ++++++++++++++++- 3 files changed, 26 insertions(+), 24 deletions(-) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py index 3e9a1175fa..4f86b8eee4 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py @@ -29,8 +29,10 @@ TEST_CONN_ID = "conn-id" TEST_THREAD_ID = "thread-id" TEST_ENDPOINT = "https://example.com" -TEST_VERKEY = "did:key:z3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx" -TEST_ROUTE_VERKEY = "did:key:z9WCgWKUaAJj3VWxxtzvvMQN3AoFxoBtBDo9ntwJnVVCC" +# TEST_VERKEY = "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx" +TEST_VERKEY = "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL" +# TEST_ROUTE_VERKEY = "9WCgWKUaAJj3VWxxtzvvMQN3AoFxoBtBDo9ntwJnVVCC" +TEST_ROUTE_VERKEY = "did:key:z6MknxTj6Zj1VrDWc1ofaZtmCVv2zNXpD58Xup4ijDGoQhya" pytestmark = pytest.mark.asyncio diff --git a/aries_cloudagent/protocols/routing/v1_0/manager.py b/aries_cloudagent/protocols/routing/v1_0/manager.py index 3d596cfa4b..44c1b7b7c6 100644 --- a/aries_cloudagent/protocols/routing/v1_0/manager.py +++ b/aries_cloudagent/protocols/routing/v1_0/manager.py @@ -4,8 +4,6 @@ from ....core.error import BaseError from ....core.profile import Profile -from ....did.did_key import DIDKey -from ....wallet.key_type import KeyType from ....storage.error import ( StorageError, StorageDuplicateError, @@ -58,25 +56,8 @@ async def get_recipient(self, recip_verkey: str) -> RouteRecord: try: async with self._profile.session() as session: - if recip_verkey.startswith("did:key:"): - recip_verkey = ( - DIDKey.from_did(recip_verkey, KeyType.ED25519) - ) - # recip_verkey = recip_verkey.public_key_b58 - else: - recip_verkey = ( - DIDKey.from_public_key_b58( - recip_verkey, - KeyType.ED25519, - ) - ) - # record = await RouteRecord.retrieve_by_recipient_key( - tag_filter = {"$or":[ - {"recipient_key": recip_verkey.did}, - {"recipient_key": recip_verkey.public_key_b58} - ]} - record = await RouteRecord.retrieve_by_tag_filter( - session, tag_filter + record = await RouteRecord.retrieve_by_recipient_key( + session, recip_verkey ) except StorageDuplicateError: raise RouteNotFoundError( diff --git a/aries_cloudagent/protocols/routing/v1_0/models/route_record.py b/aries_cloudagent/protocols/routing/v1_0/models/route_record.py index e750cd15eb..f0016a979c 100644 --- a/aries_cloudagent/protocols/routing/v1_0/models/route_record.py +++ b/aries_cloudagent/protocols/routing/v1_0/models/route_record.py @@ -4,6 +4,8 @@ from .....core.profile import ProfileSession from .....messaging.models.base_record import BaseRecord, BaseRecordSchema +from .....did.did_key import DIDKey +from .....wallet.key_type import KeyType class RouteRecord(BaseRecord): @@ -75,7 +77,24 @@ async def retrieve_by_recipient_key( RouteRecord: retrieved route record """ - tag_filter = {"recipient_key": recipient_key} + if recipient_key.startswith("did:key:"): + recipient_key = ( + DIDKey.from_did(recipient_key) + ) + # recipient_key = recipient_key.public_key_b58 + else: + recipient_key = ( + DIDKey.from_public_key_b58( + recipient_key, + KeyType.ED25519, + ) + ) + # record = await RouteRecord.retrieve_by_recipient_key( + tag_filter = {"recipient_key": {"$in": [ + recipient_key.did, + recipient_key.public_key_b58, + ]}} + # tag_filter = {"recipient_key": recipient_key} return await cls.retrieve_by_tag_filter(session, tag_filter) @classmethod From 0de5904c356d05a8f7ffb4ee0d14859dd552f1b7 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Thu, 4 Aug 2022 12:53:18 -0400 Subject: [PATCH 380/872] test: schema meta unknown respected Signed-off-by: Daniel Bluhm --- aries_cloudagent/messaging/models/base.py | 17 +++-- .../messaging/models/tests/test_base.py | 74 ++++++++++++++++--- 2 files changed, 76 insertions(+), 15 deletions(-) diff --git a/aries_cloudagent/messaging/models/base.py b/aries_cloudagent/messaging/models/base.py index 9ce1f98485..77f1c8ce40 100644 --- a/aries_cloudagent/messaging/models/base.py +++ b/aries_cloudagent/messaging/models/base.py @@ -5,7 +5,8 @@ from abc import ABC from collections import namedtuple -from typing import Literal, Mapping, Optional, Type, TypeVar, Union, cast, overload +from typing import Mapping, Optional, Type, TypeVar, Union, cast, overload +from typing_extensions import Literal from marshmallow import Schema, post_dump, pre_load, post_load, ValidationError, EXCLUDE @@ -57,7 +58,10 @@ def resolve_meta_property(obj, prop_name: str, defval=None): The meta property """ - cls = obj.__class__ + if isinstance(obj, type): + cls = obj + else: + cls = obj.__class__ found = defval while cls: Meta = getattr(cls, "Meta", None) @@ -184,7 +188,9 @@ def deserialize( return None schema_cls = cls._get_schema_class() - schema = schema_cls(unknown=unknown or schema_cls.Meta.unknown) + schema = schema_cls( + unknown=unknown or resolve_meta_property(schema_cls, "unknown", EXCLUDE) + ) try: return cast( @@ -229,7 +235,9 @@ def serialize( """ schema_cls = self._get_schema_class() - schema = schema_cls(unknown=unknown or schema_cls.Meta.unknown) + schema = schema_cls( + unknown=unknown or resolve_meta_property(schema_cls, "unknown", EXCLUDE) + ) try: return ( schema.dumps(self, separators=(",", ":")) @@ -320,7 +328,6 @@ class Meta: model_class = None skip_values = [None] ordered = True - unknown = EXCLUDE def __init__(self, *args, **kwargs): """ diff --git a/aries_cloudagent/messaging/models/tests/test_base.py b/aries_cloudagent/messaging/models/tests/test_base.py index 62c327b7a1..9cc6eaf868 100644 --- a/aries_cloudagent/messaging/models/tests/test_base.py +++ b/aries_cloudagent/messaging/models/tests/test_base.py @@ -1,15 +1,6 @@ -import json - from asynctest import TestCase as AsyncTestCase, mock as async_mock -from marshmallow import EXCLUDE, fields, validates_schema, ValidationError - -from ....cache.base import BaseCache -from ....config.injection_context import InjectionContext -from ....storage.base import BaseStorage, StorageRecord - -from ...responder import BaseResponder, MockResponder -from ...util import time_now +from marshmallow import EXCLUDE, INCLUDE, fields, validates_schema, ValidationError from ..base import BaseModel, BaseModelError, BaseModelSchema @@ -35,6 +26,48 @@ def validate_fields(self, data, **kwargs): raise ValidationError("") +class ModelImplWithUnknown(BaseModel): + class Meta: + schema_class = "SchemaImplWithUnknown" + + def __init__(self, *, attr=None, **kwargs): + self.attr = attr + self.extra = kwargs + + +class SchemaImplWithUnknown(BaseModelSchema): + class Meta: + model_class = ModelImplWithUnknown + unknown = INCLUDE + + attr = fields.String(required=True) + + @validates_schema + def validate_fields(self, data, **kwargs): + if data["attr"] != "succeeds": + raise ValidationError("") + + +class ModelImplWithoutUnknown(BaseModel): + class Meta: + schema_class = "SchemaImplWithoutUnknown" + + def __init__(self, *, attr=None): + self.attr = attr + + +class SchemaImplWithoutUnknown(BaseModelSchema): + class Meta: + model_class = ModelImplWithoutUnknown + + attr = fields.String(required=True) + + @validates_schema + def validate_fields(self, data, **kwargs): + if data["attr"] != "succeeds": + raise ValidationError("") + + class TestBase(AsyncTestCase): def test_model_validate_fails(self): model = ModelImpl(attr="string") @@ -63,3 +96,24 @@ def test_from_json_x(self): data = "{}{}" with self.assertRaises(BaseModelError): ModelImpl.from_json(data) + + def test_model_with_unknown(self): + model = ModelImplWithUnknown(attr="succeeds") + model = model.validate() + assert model.attr == "succeeds" + + model = ModelImplWithUnknown.deserialize( + {"attr": "succeeds", "another": "value"} + ) + assert model.extra + assert model.extra["another"] == "value" + assert model.attr == "succeeds" + + def test_model_without_unknown_default_exclude(self): + model = ModelImplWithoutUnknown(attr="succeeds") + model = model.validate() + assert model.attr == "succeeds" + + assert ModelImplWithoutUnknown.deserialize( + {"attr": "succeeds", "another": "value"} + ) From a3ba4bfcc1c5ff1f9c1d29ed9b5a7fb3b4e56879 Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Thu, 4 Aug 2022 11:27:39 -0600 Subject: [PATCH 381/872] fix: Fix invited not getting created/retrieved Signed-off-by: Colton Wolkins (Indicio work address) --- .../v1_0/messages/connection_invitation.py | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/aries_cloudagent/protocols/connections/v1_0/messages/connection_invitation.py b/aries_cloudagent/protocols/connections/v1_0/messages/connection_invitation.py index 48ca887989..4f0d7d68e6 100644 --- a/aries_cloudagent/protocols/connections/v1_0/messages/connection_invitation.py +++ b/aries_cloudagent/protocols/connections/v1_0/messages/connection_invitation.py @@ -5,8 +5,9 @@ from marshmallow import EXCLUDE, fields, validates_schema, ValidationError +from .....did.did_key import DIDKey from .....messaging.agent_message import AgentMessage, AgentMessageSchema -from .....messaging.valid import INDY_DID, INDY_RAW_PUBLIC_KEY +from .....messaging.valid import INDY_DID, ROUTING_KEY from .....wallet.util import b64_to_bytes, bytes_to_b64 from ..message_types import CONNECTION_INVITATION, PROTOCOL_PACKAGE @@ -53,11 +54,26 @@ def __init__( super().__init__(**kwargs) self.label = label self.did = did - self.recipient_keys = list(recipient_keys) if recipient_keys else None self.endpoint = endpoint - self.routing_keys = list(routing_keys) if routing_keys else None self.image_url = image_url + if recipient_keys: + public_keys = list() + for key in recipient_keys: + public_keys.append( + DIDKey.from_did(key).public_key_b58 + if key.startswith("did:key:") else key + ) + self.recipient_keys = public_keys if recipient_keys else None + if routing_keys: + public_keys = list() + for key in routing_keys: + public_keys.append( + DIDKey.from_did(key).public_key_b58 + if key.startswith("did:key:") else key + ) + self.routing_keys = public_keys if routing_keys else None + def to_url(self, base_url: str = None) -> str: """ Convert an invitation to URL format for sharing. @@ -109,7 +125,7 @@ class Meta: required=False, description="DID for connection invitation", **INDY_DID ) recipient_keys = fields.List( - fields.Str(description="Recipient public key", **INDY_RAW_PUBLIC_KEY), + fields.Str(description="Recipient public key", **ROUTING_KEY), data_key="recipientKeys", required=False, description="List of recipient keys", @@ -121,7 +137,7 @@ class Meta: example="http://192.168.56.101:8020", ) routing_keys = fields.List( - fields.Str(description="Routing key", **INDY_RAW_PUBLIC_KEY), + fields.Str(description="Routing key", **ROUTING_KEY), data_key="routingKeys", required=False, description="List of routing keys", From 87a7c205e76dd078789e2a60631c8d9f0eef5856 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Thu, 4 Aug 2022 21:15:40 -0400 Subject: [PATCH 382/872] style: appease flake8 docstring requirements Signed-off-by: Daniel Bluhm --- aries_cloudagent/messaging/models/base.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/aries_cloudagent/messaging/models/base.py b/aries_cloudagent/messaging/models/base.py index 77f1c8ce40..6a85e9f993 100644 --- a/aries_cloudagent/messaging/models/base.py +++ b/aries_cloudagent/messaging/models/base.py @@ -140,6 +140,7 @@ def deserialize( *, unknown: Optional[str] = None, ) -> ModelType: + """Convert from JSON representation to a model instance.""" ... @overload @@ -151,6 +152,7 @@ def deserialize( none2none: Literal[False], unknown: Optional[str] = None, ) -> ModelType: + """Convert from JSON representation to a model instance.""" ... @overload @@ -162,6 +164,7 @@ def deserialize( none2none: Literal[True], unknown: Optional[str] = None, ) -> Optional[ModelType]: + """Convert from JSON representation to a model instance.""" ... @classmethod @@ -208,6 +211,7 @@ def serialize( as_string: Literal[True], unknown: Optional[str] = None, ) -> str: + """Create a JSON-compatible dict representation of the model instance.""" ... @overload @@ -216,6 +220,7 @@ def serialize( *, unknown: Optional[str] = None, ) -> dict: + """Create a JSON-compatible dict representation of the model instance.""" ... def serialize( From 10d99eb873a1347b505cce581929354013549b88 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Sat, 6 Aug 2022 17:31:33 -0400 Subject: [PATCH 383/872] feat: add dockerfiles for building images from repo Signed-off-by: Daniel Bluhm --- docker/Dockerfile | 83 ++++++++++++++++++ docker/Dockerfile.indy | 43 +++++++++ docker/Dockerfile.indy-base | 170 ++++++++++++++++++++++++++++++++++++ docker/Dockerfile.test-indy | 2 +- docker/Makefile | 51 +++++++++++ 5 files changed, 348 insertions(+), 1 deletion(-) create mode 100644 docker/Dockerfile create mode 100644 docker/Dockerfile.indy create mode 100644 docker/Dockerfile.indy-base create mode 100644 docker/Makefile diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000000..3fc1899b04 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,83 @@ +ARG python_version=3.6.13 +FROM python:${python_version}-slim-buster + +ARG uid=1001 +ARG user=aries +ARG acapy_version +ARG acapy_reqs +ARG git_egg_ref + +ENV HOME="/home/$user" \ + APP_ROOT="$HOME" \ + LC_ALL=C.UTF-8 \ + LANG=C.UTF-8 \ + PIP_NO_CACHE_DIR=off \ + PYTHONUNBUFFERED=1 \ + PYTHONIOENCODING=UTF-8 \ + RUST_LOG=warning \ + SHELL=/bin/bash \ + SUMMARY="aries-cloudagent image" \ + DESCRIPTION="aries-cloudagent provides a base image for running Hyperledger Aries agents in Docker. \ + This image layers the python implementation of aries-cloudagent $acapy_version. Based on Debian Buster." + +LABEL summary="$SUMMARY" \ + description="$DESCRIPTION" \ + io.k8s.description="$DESCRIPTION" \ + io.k8s.display-name="aries-cloudagent $acapy_version" \ + name="aries-cloudagent" \ + version="$acapy_version" \ + maintainer="" + +# Add aries user +RUN useradd -U -ms /bin/bash -u $uid $user + +# Install environment +RUN apt-get update -y && \ + apt-get install -y --no-install-recommends \ + apt-transport-https \ + ca-certificates \ + bzip2 \ + curl \ + git \ + less \ + libffi6 \ + libgmp10 \ + liblzma5 \ + libncurses5 \ + libncursesw5 \ + libsecp256k1-0 \ + libzmq5 \ + net-tools \ + openssl \ + sqlite3 \ + vim-tiny \ + zlib1g && \ + rm -rf /var/lib/apt/lists/* /usr/share/doc/* + +WORKDIR $HOME + +# Add local binaries and aliases to path +ENV PATH="$HOME/.local/bin:$PATH" + +# - In order to drop the root user, we have to make some directories writable +# to the root group as OpenShift default security model is to run the container +# under random UID. +RUN usermod -a -G 0 $user + +# Create standard directories to allow volume mounting and set permissions +# Note: PIP_NO_CACHE_DIR environment variable should be cleared to allow caching +RUN mkdir -p \ + $HOME/.aries_cloudagent \ + $HOME/.cache/pip/http \ + $HOME/ledger/sandbox/data \ + $HOME/log + +# The root group needs access the directories under $HOME for the container to function in OpenShift. +# Also ensure the permissions on the python 'site-packages' folder are set correctly. +RUN chmod -R ug+rw $HOME/log $HOME/ledger $HOME/.aries_cloudagent $HOME/.cache + +RUN pip install --no-cache-dir ${git_egg_ref}aries-cloudagent${acapy_reqs}==${acapy_version} + +USER $user + +ENTRYPOINT ["aca-py"] diff --git a/docker/Dockerfile.indy b/docker/Dockerfile.indy new file mode 100644 index 0000000000..681921f8ce --- /dev/null +++ b/docker/Dockerfile.indy @@ -0,0 +1,43 @@ +ARG python_version +ARG indy_version +FROM ghcr.io/hyperledger/indy-python:py${python_version}-${indy_version} + +ARG uid=1001 +ARG user=indy +ARG acapy_version +ARG acapy_reqs +ARG git_egg_ref + +ENV HOME="/home/$user" \ + APP_ROOT="$HOME" \ + LC_ALL=C.UTF-8 \ + LANG=C.UTF-8 \ + PIP_NO_CACHE_DIR=off \ + PYTHONUNBUFFERED=1 \ + PYTHONIOENCODING=UTF-8 \ + RUST_LOG=warning \ + SHELL=/bin/bash \ + SUMMARY="aries-cloudagent image" \ + DESCRIPTION="aries-cloudagent provides a base image for running Hyperledger Aries agents in Docker. \ + This image layers the python implementation of aries-cloudagent $acapy_version. Based on indy-python, \ + this image includes indy-sdk and supporting libraries." + +LABEL summary="$SUMMARY" \ + description="$DESCRIPTION" \ + io.k8s.description="$DESCRIPTION" \ + io.k8s.display-name="aries-cloudagent $acapy_version" \ + name="aries-cloudagent" \ + version="$acapy_version" \ + maintainer="" + +# Create standard directories to allow volume mounting and set permissions +# Note: PIP_NO_CACHE_DIR environment variable should be cleared to allow caching +RUN mkdir -p $HOME/.aries_cloudagent + +# The root group needs access the directories under $HOME/.indy_client for the container to function in OpenShift. +# Also ensure the permissions on the python 'site-packages' folder are set correctly. +RUN chmod -R ug+rw $HOME/.aries_cloudagent + +RUN pip install --no-cache-dir ${git_egg_ref}aries-cloudagent${acapy_reqs}==${acapy_version} + +ENTRYPOINT ["aca-py"] diff --git a/docker/Dockerfile.indy-base b/docker/Dockerfile.indy-base new file mode 100644 index 0000000000..fb53a59b81 --- /dev/null +++ b/docker/Dockerfile.indy-base @@ -0,0 +1,170 @@ +ARG python_version=3.6.13 +ARG rust_version=1.46 +FROM rust:${rust_version}-slim-buster as builder + +ARG user=indy +ENV HOME="/home/$user" +WORKDIR $HOME +RUN mkdir -p .local/bin .local/etc .local/lib + +# Install environment +RUN apt-get update -y && \ + apt-get install -y --no-install-recommends \ + automake \ + build-essential \ + ca-certificates \ + cmake \ + curl \ + git \ + libbz2-dev \ + libffi-dev \ + libgmp-dev \ + liblzma-dev \ + libncurses5-dev \ + libncursesw5-dev \ + libsecp256k1-dev \ + libsodium-dev \ + libsqlite3-dev \ + libssl-dev \ + libtool \ + libzmq3-dev \ + pkg-config \ + zlib1g-dev && \ + rm -rf /var/lib/apt/lists/* + +# set to --release for smaller, optimized library +ARG indy_build_flags=--release + +ARG indy_sdk_url + +# make local libs and binaries accessible +ENV PATH="$HOME/.local/bin:$PATH" +ENV LIBRARY_PATH="$HOME/.local/lib:$LIBRARY_PATH" + +# Download and extract indy-sdk +RUN mkdir indy-sdk && \ + curl "${indy_sdk_url}" | tar -xz -C indy-sdk + +# Build and install indy-sdk +WORKDIR $HOME/indy-sdk +RUN cd indy-sdk*/libindy && \ + cargo build ${indy_build_flags} && \ + cp target/*/libindy.so "$HOME/.local/lib" && \ + cargo clean + +# Package python3-indy +RUN tar czvf ../python3-indy.tgz -C indy-sdk*/wrappers/python . + +# grab the latest sdk code for the postgres plug-in +WORKDIR $HOME +ARG indy_postgres_url=${indy_sdk_url} +RUN mkdir indy-postgres && \ + curl "${indy_postgres_url}" | tar -xz -C indy-postgres + +# Build and install postgres_storage plugin +WORKDIR $HOME/indy-postgres +RUN cd indy-sdk*/experimental/plugins/postgres_storage && \ + cargo build ${indy_build_flags} && \ + cp target/*/libindystrgpostgres.so "$HOME/.local/lib" && \ + cargo clean + +# Clean up SDK +WORKDIR $HOME +RUN rm -rf indy-sdk indy-postgres + +## Start fresh (new image) ## +FROM python:${python_version}-slim-buster + + +ARG uid=1001 +ARG user=indy +ARG indy_version + +ENV HOME="/home/$user" \ + APP_ROOT="$HOME" \ + LC_ALL=C.UTF-8 \ + LANG=C.UTF-8 \ + PIP_NO_CACHE_DIR=off \ + PYTHONUNBUFFERED=1 \ + PYTHONIOENCODING=UTF-8 \ + RUST_LOG=warning \ + SHELL=/bin/bash \ + SUMMARY="indy-python base image" \ + DESCRIPTION="aries-cloudagent provides a base image for running Hyperledger Aries agents in Docker. \ + This image provides all the necessary dependencies to use the indy-sdk in python. Based on Debian Buster." + +LABEL summary="$SUMMARY" \ + description="$DESCRIPTION" \ + io.k8s.description="$DESCRIPTION" \ + io.k8s.display-name="indy-python $indy_version" \ + name="indy-python" \ + version="$indy_version" \ + maintainer="" + +# Add indy user +RUN useradd -U -ms /bin/bash -u $uid $user + +# Install environment +RUN apt-get update -y && \ + apt-get install -y --no-install-recommends \ + apt-transport-https \ + ca-certificates \ + bzip2 \ + curl \ + git \ + less \ + libffi6 \ + libgmp10 \ + liblzma5 \ + libncurses5 \ + libncursesw5 \ + libsecp256k1-0 \ + libzmq5 \ + net-tools \ + openssl \ + sqlite3 \ + vim-tiny \ + zlib1g && \ + rm -rf /var/lib/apt/lists/* /usr/share/doc/* + +WORKDIR $HOME + +# Copy build results +COPY --from=builder --chown=$user:$user $HOME . + +RUN mkdir -p $HOME/.local/bin + +# Add local binaries and aliases to path +ENV PATH="$HOME/.local/bin:$PATH" + +# Make libraries resolvable by python +ENV LD_LIBRARY_PATH="$HOME/.local/lib:$LD_LIBRARY_PATH" +RUN echo "$HOME/.local/lib" > /etc/ld.so.conf.d/local.conf && ldconfig + +# Install python3-indy +RUN pip install --no-cache-dir python3-indy.tgz && rm python3-indy.tgz + +# - In order to drop the root user, we have to make some directories writable +# to the root group as OpenShift default security model is to run the container +# under random UID. +RUN usermod -a -G 0 $user + +# Create standard directories to allow volume mounting and set permissions +# Note: PIP_NO_CACHE_DIR environment variable should be cleared to allow caching +RUN mkdir -p \ + $HOME/.cache/pip/http \ + $HOME/.indy-cli/networks \ + $HOME/.indy_client/wallet \ + $HOME/.indy_client/pool \ + $HOME/.indy_client/ledger-cache \ + $HOME/ledger/sandbox/data \ + $HOME/log + +# The root group needs access the directories under $HOME/.indy_client for the container to function in OpenShift. +# Also ensure the permissions on the python 'site-packages' folder are set correctly. +RUN chown -R $user:root $HOME/.indy_client \ + && chmod -R ug+rw $HOME/log $HOME/ledger $HOME/.cache $HOME/.indy_client + +USER $user + +CMD ["bash"] diff --git a/docker/Dockerfile.test-indy b/docker/Dockerfile.test-indy index 047b19187e..1acafb7d56 100644 --- a/docker/Dockerfile.test-indy +++ b/docker/Dockerfile.test-indy @@ -1,4 +1,4 @@ -FROM bcgovimages/von-image:py36-1.15-1 +FROM ghcr.io/hyperledger/indy-python:py3.6-1.16.0 USER indy diff --git a/docker/Makefile b/docker/Makefile new file mode 100644 index 0000000000..c5ecf7c398 --- /dev/null +++ b/docker/Makefile @@ -0,0 +1,51 @@ +# A simple makefile purely to demonstrate building the new docker images + +CONTAINER_RUNTIME ?= docker +IMAGE_NAME=ghcr.io/hyperledger/aries-cloudagent-python +PYTHON_VERSION=3.6.13 +PYTHON_VERSION_MAJ_MIN=3.6 +RUST_VERSION=1.46 +ACAPY_VERSION=0.7.4 +ACAPY_REQS=[askar,bbs] +INDY_VERSION=1.16.0 +INDY_SDK_URL=https://codeload.github.com/hyperledger/indy-sdk/tar.gz/refs/tags/v$(INDY_VERSION) +INDY_IMAGE_NAME=ghcr.io/hyperledger/indy-python + +all: indy-python indy standard + +indy-python: + $(CONTAINER_RUNTIME) build -t $(INDY_IMAGE_NAME):latest \ + --build-arg python_version=$(PYTHON_VERSION) \ + --build-arg rust_version=$(RUST_VERSION) \ + --build-arg indy_version=$(INDY_VERSION) \ + --build-arg indy_sdk_url=$(INDY_SDK_URL) \ + -f Dockerfile.indy-base . + $(CONTAINER_RUNTIME) tag $(INDY_IMAGE_NAME):latest \ + $(INDY_IMAGE_NAME):py$(PYTHON_VERSION)-$(INDY_VERSION) + $(CONTAINER_RUNTIME) tag $(INDY_IMAGE_NAME):latest \ + $(INDY_IMAGE_NAME):py$(PYTHON_VERSION_MAJ_MIN)-$(INDY_VERSION) + +indy: + $(CONTAINER_RUNTIME) build -t $(IMAGE_NAME):indy-latest \ + --build-arg python_version=$(PYTHON_VERSION) \ + --build-arg indy_version=$(INDY_VERSION) \ + --build-arg acapy_version=$(ACAPY_VERSION) \ + --build-arg acapy_reqs=$(ACAPY_REQS) \ + -f Dockerfile.indy . + $(CONTAINER_RUNTIME) tag $(IMAGE_NAME):indy-latest \ + $(IMAGE_NAME):py$(PYTHON_VERSION)-indy-$(INDY_VERSION)-$(ACAPY_VERSION) + $(CONTAINER_RUNTIME) tag $(IMAGE_NAME):indy-latest \ + $(IMAGE_NAME):py$(PYTHON_VERSION_MAJ_MIN)-indy-$(INDY_VERSION)-$(ACAPY_VERSION) + +standard: + $(CONTAINER_RUNTIME) build -t $(IMAGE_NAME):latest \ + --build-arg python_version=$(PYTHON_VERSION) \ + --build-arg acapy_version=$(ACAPY_VERSION) \ + --build-arg acapy_reqs=$(ACAPY_REQS) \ + -f Dockerfile . + $(CONTAINER_RUNTIME) tag $(IMAGE_NAME):latest \ + $(IMAGE_NAME):py$(PYTHON_VERSION)-$(ACAPY_VERSION) + $(CONTAINER_RUNTIME) tag $(IMAGE_NAME):latest \ + $(IMAGE_NAME):py$(PYTHON_VERSION_MAJ_MIN)-$(ACAPY_VERSION) + +.PHONY: all indy-python indy standard From c480cae4824fc7a46822920bd64e9e2bc2c6afd0 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Mon, 8 Aug 2022 09:26:41 -0700 Subject: [PATCH 384/872] Update integration tests to replicate LSBC issue Signed-off-by: Ian Costanzo --- demo/features/steps/0453-issue-credential.py | 9 +- .../taa-txn-author-acceptance.feature | 106 ++++++++++++++++++ 2 files changed, 114 insertions(+), 1 deletion(-) diff --git a/demo/features/steps/0453-issue-credential.py b/demo/features/steps/0453-issue-credential.py index e59f668b15..dffb380c0a 100644 --- a/demo/features/steps/0453-issue-credential.py +++ b/demo/features/steps/0453-issue-credential.py @@ -155,9 +155,16 @@ def step_impl(context, holder): # get the required revocation info from the last credential exchange cred_exchange = context.cred_exchange + print("cred_exchange:", json.dumps(cred_exchange)) + + cred_ex_id = ( + cred_exchange["cred_ex_id"] + if "cred_ex_id" in cred_exchange + else cred_exchange["cred_ex_record"]["cred_ex_id"] + ) cred_exchange = agent_container_GET( - agent["agent"], "/issue-credential-2.0/records/" + cred_exchange["cred_ex_id"] + agent["agent"], "/issue-credential-2.0/records/" + cred_ex_id ) context.cred_exchange = cred_exchange print("rev_reg_id:", cred_exchange["indy"]["rev_reg_id"]) diff --git a/demo/features/taa-txn-author-acceptance.feature b/demo/features/taa-txn-author-acceptance.feature index bcaaa3dc32..e6c99a44fe 100644 --- a/demo/features/taa-txn-author-acceptance.feature +++ b/demo/features/taa-txn-author-acceptance.feature @@ -90,3 +90,109 @@ Feature: TAA Transaction Author Agreement related tests Examples: | issuer | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Proof_request | | Faber | --taa-accept --revocation --public-did | | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | + + @T004-TAA @taa_required + Scenario Outline: Fail to publish revoked credential using a ledger with TAA required, and fix the ledger + Given we have "2" agents + | name | role | capabilities | + | Faber | verifier | | + | Bob | prover | | + And "Faber" connects to a ledger that requires acceptance of the TAA + And "Faber" accepts the TAA + And "Faber" and "Bob" have an existing connection + And "Bob" has an issued credential from "" + And "Faber" revokes the credential + And "Faber" successfully revoked the credential + When "Faber" rejects the TAA + And "Bob" has an issued credential from "" + And "Faber" attempts to revoke the credential + And "Faber" fails to publish the credential revocation + And "Faber" attempts to revoke the credential + And "Faber" fails to publish the credential revocation + And "Faber" accepts the TAA + And "Bob" has an issued credential from "" + And "Faber" attempts to revoke the credential + And "Faber" fails to publish the credential revocation + And "Faber" attempts to revoke the credential + And "Faber" fails to publish the credential revocation + Then "Faber" posts a revocation correction to the ledger + And "Faber" successfully revoked the credential + And "Bob" has an issued credential from "" + And "Faber" revokes the credential + And "Faber" successfully revoked the credential + + Examples: + | issuer | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Proof_request | + | Faber | --taa-accept --revocation --public-did | | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | + + @T004.1-TAA @taa_required + Scenario Outline: Fail to publish revoked credential using a ledger with TAA required, and fix the ledger + Given we have "2" agents + | name | role | capabilities | + | Faber | verifier | | + | Bob | prover | | + And "Faber" connects to a ledger that requires acceptance of the TAA + And "Faber" accepts the TAA + And "Faber" and "Bob" have an existing connection + And "Bob" has an issued credential from "" + And "Faber" revokes the credential + And "Faber" successfully revoked the credential + When "Faber" rejects the TAA + And "Bob" has an issued credential from "" + And "Faber" attempts to revoke the credential + And "Faber" fails to publish the credential revocation + And "Faber" attempts to revoke the credential + And "Faber" fails to publish the credential revocation + And "Bob" has an issued credential from "" + And "Faber" attempts to revoke the credential + And "Faber" fails to publish the credential revocation + And "Faber" attempts to revoke the credential + And "Faber" fails to publish the credential revocation + And "Faber" accepts the TAA + Then "Faber" posts a revocation correction to the ledger + And "Faber" successfully revoked the credential + And "Bob" has an issued credential from "" + And "Faber" revokes the credential + And "Faber" successfully revoked the credential + + Examples: + | issuer | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Proof_request | + | Faber | --taa-accept --revocation --public-did | | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | + + @T004.2-TAA @taa_required + Scenario Outline: Fail to publish revoked credential using a ledger with TAA required, and fix the ledger + Given we have "2" agents + | name | role | capabilities | + | Faber | verifier | | + | Bob | prover | | + And "Faber" connects to a ledger that requires acceptance of the TAA + And "Faber" accepts the TAA + And "Faber" and "Bob" have an existing connection + And "Bob" has an issued credential from "" + And "Faber" revokes the credential + And "Faber" successfully revoked the credential + When "Faber" rejects the TAA + And "Bob" has an issued credential from "" + And "Faber" attempts to revoke the credential + And "Faber" fails to publish the credential revocation + And "Bob" has an issued credential from "" + And "Faber" attempts to revoke the credential + And "Faber" fails to publish the credential revocation + And "Bob" has an issued credential from "" + And "Faber" attempts to revoke the credential + And "Faber" fails to publish the credential revocation + And "Faber" accepts the TAA + And "Bob" has an issued credential from "" + And "Faber" attempts to revoke the credential + And "Faber" fails to publish the credential revocation + And "Faber" attempts to revoke the credential + And "Faber" fails to publish the credential revocation + Then "Faber" posts a revocation correction to the ledger + And "Faber" successfully revoked the credential + And "Bob" has an issued credential from "" + And "Faber" revokes the credential + And "Faber" successfully revoked the credential + + Examples: + | issuer | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Proof_request | + | Faber | --taa-accept --revocation --public-did | | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | From 89c436decd8bd2d3bfaca239a62b0bc4ac4f6a75 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Mon, 8 Aug 2022 15:06:26 -0700 Subject: [PATCH 385/872] Refactor ledger-correcting code Signed-off-by: Ian Costanzo --- aries_cloudagent/revocation/manager.py | 113 ++++++++++++++++++++++++- aries_cloudagent/revocation/routes.py | 110 ++++++++---------------- 2 files changed, 147 insertions(+), 76 deletions(-) diff --git a/aries_cloudagent/revocation/manager.py b/aries_cloudagent/revocation/manager.py index 10db676f70..f5402337cf 100644 --- a/aries_cloudagent/revocation/manager.py +++ b/aries_cloudagent/revocation/manager.py @@ -10,10 +10,13 @@ from ..core.error import BaseError from ..core.profile import Profile from ..indy.issuer import IndyIssuer +from ..ledger.base import BaseLedger +from ..ledger.error import LedgerError, LedgerTransactionError from ..storage.error import StorageNotFoundError from .indy import IndyRevocation from .models.issuer_cred_rev_record import IssuerCredRevRecord from .models.issuer_rev_reg_record import IssuerRevRegRecord +from .recover import generate_ledger_rrrecovery_txn from .util import notify_pending_cleared_event, notify_revocation_published_event from ..protocols.issue_credential.v1_0.models.credential_exchange import ( V10CredentialExchange, @@ -147,7 +150,25 @@ async def revoke_credential( await txn.commit() await self.set_cred_revoked_state(rev_reg_id, crids) if delta_json: - await issuer_rr_upd.send_entry(self._profile) + try: + await issuer_rr_upd.send_entry(self._profile) + except LedgerTransactionError as err: + if "InvalidClientTaaAcceptanceError" in err.roll_up: + # ... if the ledger write fails (with "InvalidClientRequest") + # e.g. aries_cloudagent.ledger.error.LedgerTransactionError: + # Ledger rejected transaction request: client request invalid: + # InvalidClientRequest(...) + # TODO in this scenario we try to post a correction + raise err + elif "InvalidClientRequest" in err.roll_up: + # if no write access (with "InvalidClientTaaAcceptanceError") + # e.g. aries_cloudagent.ledger.error.LedgerTransactionError: + # Ledger rejected transaction request: client request invalid: + # InvalidClientTaaAcceptanceError(...) + raise err + else: + # not sure what happened, raise an error + raise err await notify_revocation_published_event( self._profile, rev_reg_id, [cred_rev_id] ) @@ -157,6 +178,96 @@ async def revoke_credential( await issuer_rr_rec.mark_pending(txn, cred_rev_id) await txn.commit() + async def update_rev_reg_revoked_state( + self, + rev_reg_id: str, + apply_ledger_update: bool, + rev_reg_record: IssuerRevRegRecord, + genesis_transactions: dict, + ): + """ + Request handler to fix ledger entry of credentials revoked against registry. + + Args: + rev_reg_id: revocation registry id + apply_ledger_update: whether to apply an update to the ledger + + Returns: + Number of credentials posted to ledger + + """ + # get rev reg delta (revocations published to ledger) + revoc = IndyRevocation(self._profile) + rev_reg_delta = await revoc.get_issuer_rev_reg_delta(rev_reg_id) + + # get rev reg records from wallet (revocations and status) + recs = [] + rec_count = 0 + accum_count = 0 + recovery_txn = {} + applied_txn = {} + async with self._profile.session() as session: + # rev_reg_record = await IssuerRevRegRecord.retrieve_by_revoc_reg_id( + # session, rev_reg_id + # ) + recs = await IssuerCredRevRecord.query_by_ids( + session, rev_reg_id=rev_reg_id + ) + + revoked_ids = [] + for rec in recs: + if rec.state == IssuerCredRevRecord.STATE_REVOKED: + revoked_ids.append(int(rec.cred_rev_id)) + if int(rec.cred_rev_id) not in rev_reg_delta["value"]["revoked"]: + # await rec.set_state(session, IssuerCredRevRecord.STATE_ISSUED) + rec_count += 1 + + self._logger.debug(">>> fixed entry recs count = %s", rec_count) + self._logger.debug( + ">>> rev_reg_record.revoc_reg_entry.value: %s", + rev_reg_record.revoc_reg_entry.value, + ) + self._logger.debug( + '>>> rev_reg_delta.get("value"): %s', rev_reg_delta.get("value") + ) + + # if we had any revocation discrepencies, check the accumulator value + if rec_count > 0: + if ( + rev_reg_record.revoc_reg_entry.value and rev_reg_delta.get("value") + ) and not ( + rev_reg_record.revoc_reg_entry.value.accum + == rev_reg_delta["value"]["accum"] + ): + # rev_reg_record.revoc_reg_entry = rev_reg_delta["value"] + # await rev_reg_record.save(session) + accum_count += 1 + + calculated_txn = await generate_ledger_rrrecovery_txn( + genesis_transactions, + rev_reg_id, + revoked_ids, + ) + recovery_txn = json.loads(calculated_txn.to_json()) + + self._logger.debug(">>> apply_ledger_update = %s", apply_ledger_update) + if apply_ledger_update: + ledger = session.inject_or(BaseLedger) + if not ledger: + reason = "No ledger available" + if not session.context.settings.get_value("wallet.type"): + reason += ": missing wallet-type?" + raise LedgerError(reason=reason) + + async with ledger: + ledger_response = await ledger.send_revoc_reg_entry( + rev_reg_id, "CL_ACCUM", recovery_txn + ) + + applied_txn = ledger_response["result"] + + return (rev_reg_delta, recovery_txn, applied_txn) + async def publish_pending_revocations( self, rrid2crid: Mapping[Text, Sequence[Text]] = None, diff --git a/aries_cloudagent/revocation/routes.py b/aries_cloudagent/revocation/routes.py index 637332d55a..bfca31c560 100644 --- a/aries_cloudagent/revocation/routes.py +++ b/aries_cloudagent/revocation/routes.py @@ -59,7 +59,6 @@ IssuerCredRevRecordSchema, ) from .models.issuer_rev_reg_record import IssuerRevRegRecord, IssuerRevRegRecordSchema -from .recover import generate_ledger_rrrecovery_txn from .util import ( REVOCATION_EVENT_PREFIX, REVOCATION_REG_INIT_EVENT, @@ -728,13 +727,13 @@ async def get_rev_reg_indy_recs(request: web.BaseRequest): @response_schema(RevRegWalletUpdatedResultSchema(), 200, description="") async def update_rev_reg_revoked_state(request: web.BaseRequest): """ - Request handler to get number of credentials issued against revocation registry. + Request handler to fix ledger entry of credentials revoked against registry. Args: request: aiohttp request object Returns: - Number of credentials updated in wallet + Number of credentials posted to ledger """ context: AdminRequestContext = request["context"] @@ -745,16 +744,8 @@ async def update_rev_reg_revoked_state(request: web.BaseRequest): LOGGER.debug(">>> apply_ledger_update_json = %s", apply_ledger_update_json) apply_ledger_update = json.loads(request.query.get("apply_ledger_update", "false")) - # get rev reg delta (revocations published to ledger) - revoc = IndyRevocation(context.profile) - rev_reg_delta = await revoc.get_issuer_rev_reg_delta(rev_reg_id) - - # get rev reg records from wallet (revocations and status) - recs = [] - rec_count = 0 - accum_count = 0 - recovery_txn = {} - applied_txn = {} + rev_reg_record = None + genesis_transactions = None async with context.profile.session() as session: try: rev_reg_record = await IssuerRevRegRecord.retrieve_by_revoc_reg_id( @@ -762,72 +753,41 @@ async def update_rev_reg_revoked_state(request: web.BaseRequest): ) except StorageNotFoundError as err: raise web.HTTPNotFound(reason=err.roll_up) from err - recs = await IssuerCredRevRecord.query_by_ids(session, rev_reg_id=rev_reg_id) - revoked_ids = [] - for rec in recs: - if rec.state == IssuerCredRevRecord.STATE_REVOKED: - revoked_ids.append(int(rec.cred_rev_id)) - if int(rec.cred_rev_id) not in rev_reg_delta["value"]["revoked"]: - # await rec.set_state(session, IssuerCredRevRecord.STATE_ISSUED) - rec_count += 1 - - LOGGER.debug(">>> fixed entry recs count = %s", rec_count) - LOGGER.debug( - ">>> rev_reg_record.revoc_reg_entry.value: %s", - rev_reg_record.revoc_reg_entry.value, - ) - LOGGER.debug('>>> rev_reg_delta.get("value"): %s', rev_reg_delta.get("value")) - - # if we had any revocation discrepencies, check the accumulator value - if rec_count > 0: - if ( - rev_reg_record.revoc_reg_entry.value and rev_reg_delta.get("value") - ) and not ( - rev_reg_record.revoc_reg_entry.value.accum - == rev_reg_delta["value"]["accum"] - ): - # rev_reg_record.revoc_reg_entry = rev_reg_delta["value"] - # await rev_reg_record.save(session) - accum_count += 1 - - genesis_transactions = context.settings.get("ledger.genesis_transactions") - if not genesis_transactions: - ledger_manager = context.injector.inject(BaseMultipleLedgerManager) - write_ledgers = await ledger_manager.get_write_ledger() - LOGGER.debug(f"write_ledgers = {write_ledgers}") - pool = write_ledgers[1].pool - LOGGER.debug(f"write_ledger pool = {pool}") - - genesis_transactions = pool.genesis_txns - - if not genesis_transactions: - raise web.HTTPInternalServerError( - reason="no genesis_transactions for writable ledger" - ) + genesis_transactions = context.settings.get("ledger.genesis_transactions") + if not genesis_transactions: + ledger_manager = context.injector.inject(BaseMultipleLedgerManager) + write_ledgers = await ledger_manager.get_write_ledger() + LOGGER.debug(f"write_ledgers = {write_ledgers}") + pool = write_ledgers[1].pool + LOGGER.debug(f"write_ledger pool = {pool}") + + genesis_transactions = pool.genesis_txns - calculated_txn = await generate_ledger_rrrecovery_txn( - genesis_transactions, - rev_reg_id, - revoked_ids, + if not genesis_transactions: + raise web.HTTPInternalServerError( + reason="no genesis_transactions for writable ledger" ) - recovery_txn = json.loads(calculated_txn.to_json()) - - LOGGER.debug(">>> apply_ledger_update = %s", apply_ledger_update) - if apply_ledger_update: - ledger = session.inject_or(BaseLedger) - if not ledger: - reason = "No ledger available" - if not session.context.settings.get_value("wallet.type"): - reason += ": missing wallet-type?" - raise web.HTTPInternalServerError(reason=reason) - - async with ledger: - ledger_response = await ledger.send_revoc_reg_entry( - rev_reg_id, "CL_ACCUM", recovery_txn - ) - applied_txn = ledger_response["result"] + if apply_ledger_update: + ledger = session.inject_or(BaseLedger) + if not ledger: + reason = "No ledger available" + if not session.context.settings.get_value("wallet.type"): + reason += ": missing wallet-type?" + raise web.HTTPInternalServerError(reason=reason) + + rev_manager = RevocationManager(context.profile) + try: + ( + rev_reg_delta, + recovery_txn, + applied_txn, + ) = await rev_manager.update_rev_reg_revoked_state( + rev_reg_id, apply_ledger_update, rev_reg_record, genesis_transactions + ) + except Exception as err: + raise web.HTTPBadRequest(reason=err.roll_up) return web.json_response( { From a655913b7728e3fd7295adba3fff3747199daa94 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Tue, 9 Aug 2022 15:03:32 -0700 Subject: [PATCH 386/872] Refactor ledger correction code and insert into revocation error handling Signed-off-by: Ian Costanzo --- aries_cloudagent/revocation/manager.py | 39 ++++++++++-- demo/features/steps/0453-issue-credential.py | 16 ++++- .../taa-txn-author-acceptance.feature | 60 +++++++++++++++++-- 3 files changed, 104 insertions(+), 11 deletions(-) diff --git a/aries_cloudagent/revocation/manager.py b/aries_cloudagent/revocation/manager.py index f5402337cf..0bb64a1b36 100644 --- a/aries_cloudagent/revocation/manager.py +++ b/aries_cloudagent/revocation/manager.py @@ -12,6 +12,7 @@ from ..indy.issuer import IndyIssuer from ..ledger.base import BaseLedger from ..ledger.error import LedgerError, LedgerTransactionError +from ..ledger.multiple_ledger.base_manager import BaseMultipleLedgerManager from ..storage.error import StorageNotFoundError from .indy import IndyRevocation from .models.issuer_cred_rev_record import IssuerCredRevRecord @@ -153,21 +154,49 @@ async def revoke_credential( try: await issuer_rr_upd.send_entry(self._profile) except LedgerTransactionError as err: - if "InvalidClientTaaAcceptanceError" in err.roll_up: + if "InvalidClientRequest" in err.roll_up: # ... if the ledger write fails (with "InvalidClientRequest") # e.g. aries_cloudagent.ledger.error.LedgerTransactionError: # Ledger rejected transaction request: client request invalid: # InvalidClientRequest(...) - # TODO in this scenario we try to post a correction - raise err - elif "InvalidClientRequest" in err.roll_up: + # In this scenario we try to post a correction + self._logger.warn("Retry ledger update/fix due to error") + self._logger.warn(err) + + async with self._profile.session() as session: + genesis_transactions = session.context.settings.get( + "ledger.genesis_transactions" + ) + if not genesis_transactions: + ledger_manager = session.context.injector.inject( + BaseMultipleLedgerManager + ) + write_ledgers = await ledger_manager.get_write_ledger() + self._logger.debug(f"write_ledgers = {write_ledgers}") + pool = write_ledgers[1].pool + self._logger.debug(f"write_ledger pool = {pool}") + + genesis_transactions = pool.genesis_txns + + (_, _, _) = await self.update_rev_reg_revoked_state( + rev_reg_id, + True, + issuer_rr_upd, + genesis_transactions, + ) + self._logger.warn("Ledger update/fix applied") + elif "InvalidClientTaaAcceptanceError" in err.roll_up: # if no write access (with "InvalidClientTaaAcceptanceError") # e.g. aries_cloudagent.ledger.error.LedgerTransactionError: # Ledger rejected transaction request: client request invalid: # InvalidClientTaaAcceptanceError(...) + self._logger.error("Ledger update failed due to TAA issue") + self._logger.error(err) raise err else: # not sure what happened, raise an error + self._logger.error("Ledger update failed due to unknown issue") + self._logger.error(err) raise err await notify_revocation_published_event( self._profile, rev_reg_id, [cred_rev_id] @@ -184,7 +213,7 @@ async def update_rev_reg_revoked_state( apply_ledger_update: bool, rev_reg_record: IssuerRevRegRecord, genesis_transactions: dict, - ): + ) -> (dict, dict, dict): """ Request handler to fix ledger entry of credentials revoked against registry. diff --git a/demo/features/steps/0453-issue-credential.py b/demo/features/steps/0453-issue-credential.py index dffb380c0a..b5fac909fc 100644 --- a/demo/features/steps/0453-issue-credential.py +++ b/demo/features/steps/0453-issue-credential.py @@ -83,8 +83,14 @@ def step_impl(context, holder): # get the required revocation info from the last credential exchange cred_exchange = context.cred_exchange + cred_ex_id = ( + cred_exchange["cred_ex_id"] + if "cred_ex_id" in cred_exchange + else cred_exchange["cred_ex_record"]["cred_ex_id"] + ) + cred_exchange = agent_container_GET( - agent["agent"], "/issue-credential-2.0/records/" + cred_exchange["cred_ex_id"] + agent["agent"], "/issue-credential-2.0/records/" + cred_ex_id ) context.cred_exchange = cred_exchange print("rev_reg_id:", cred_exchange["indy"]["rev_reg_id"]) @@ -141,6 +147,13 @@ def step_impl(context, holder): + cred_exchange["indy"]["rev_reg_id"] + "/issued/indy_recs", ) + print("ledger_revoked_creds:", ledger_revoked_creds) + print( + "assert", + cred_exchange["indy"]["cred_rev_id"], + "in", + ledger_revoked_creds["rev_reg_delta"]["value"]["revoked"], + ) assert ( int(cred_exchange["indy"]["cred_rev_id"]) in ledger_revoked_creds["rev_reg_delta"]["value"]["revoked"] @@ -224,6 +237,7 @@ def step_impl(context, holder): + cred_exchange["indy"]["rev_reg_id"] + "/issued/indy_recs", ) + print("ledger_revoked_creds:", ledger_revoked_creds) assert ( int(cred_exchange["indy"]["cred_rev_id"]) not in ledger_revoked_creds["rev_reg_delta"]["value"]["revoked"] diff --git a/demo/features/taa-txn-author-acceptance.feature b/demo/features/taa-txn-author-acceptance.feature index e6c99a44fe..55d5790227 100644 --- a/demo/features/taa-txn-author-acceptance.feature +++ b/demo/features/taa-txn-author-acceptance.feature @@ -92,7 +92,7 @@ Feature: TAA Transaction Author Agreement related tests | Faber | --taa-accept --revocation --public-did | | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | @T004-TAA @taa_required - Scenario Outline: Fail to publish revoked credential using a ledger with TAA required, and fix the ledger + Scenario Outline: Fail to publish revoked credential using a ledger with TAA required, and fix the ledger authomatically with the next revoked credential Given we have "2" agents | name | role | capabilities | | Faber | verifier | | @@ -111,10 +111,35 @@ Feature: TAA Transaction Author Agreement related tests And "Faber" fails to publish the credential revocation And "Faber" accepts the TAA And "Bob" has an issued credential from "" + And "Faber" revokes the credential + And "Faber" successfully revoked the credential + And "Bob" has an issued credential from "" + And "Faber" revokes the credential + And "Faber" successfully revoked the credential + + Examples: + | issuer | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Proof_request | + | Faber | --taa-accept --revocation --public-did | | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | + + @T004.0-TAA @taa_required + Scenario Outline: Fail to publish revoked credential using a ledger with TAA required, and fix the ledger manually before revoking more credentials + Given we have "2" agents + | name | role | capabilities | + | Faber | verifier | | + | Bob | prover | | + And "Faber" connects to a ledger that requires acceptance of the TAA + And "Faber" accepts the TAA + And "Faber" and "Bob" have an existing connection + And "Bob" has an issued credential from "" + And "Faber" revokes the credential + And "Faber" successfully revoked the credential + When "Faber" rejects the TAA + And "Bob" has an issued credential from "" And "Faber" attempts to revoke the credential And "Faber" fails to publish the credential revocation And "Faber" attempts to revoke the credential And "Faber" fails to publish the credential revocation + And "Faber" accepts the TAA Then "Faber" posts a revocation correction to the ledger And "Faber" successfully revoked the credential And "Bob" has an issued credential from "" @@ -126,7 +151,7 @@ Feature: TAA Transaction Author Agreement related tests | Faber | --taa-accept --revocation --public-did | | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | @T004.1-TAA @taa_required - Scenario Outline: Fail to publish revoked credential using a ledger with TAA required, and fix the ledger + Scenario Outline: Fail to publish revoked credential using a ledger with TAA required, and fix the ledger by manually applying a correction Given we have "2" agents | name | role | capabilities | | Faber | verifier | | @@ -160,7 +185,7 @@ Feature: TAA Transaction Author Agreement related tests | Faber | --taa-accept --revocation --public-did | | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | @T004.2-TAA @taa_required - Scenario Outline: Fail to publish revoked credential using a ledger with TAA required, and fix the ledger + Scenario Outline: Fail to publish revoked credential using a ledger with TAA required, and fix the ledger automatically with the next revocation Given we have "2" agents | name | role | capabilities | | Faber | verifier | | @@ -183,12 +208,37 @@ Feature: TAA Transaction Author Agreement related tests And "Faber" fails to publish the credential revocation And "Faber" accepts the TAA And "Bob" has an issued credential from "" + And "Faber" revokes the credential + And "Faber" successfully revoked the credential + And "Bob" has an issued credential from "" + And "Faber" revokes the credential + And "Faber" successfully revoked the credential + + Examples: + | issuer | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Proof_request | + | Faber | --taa-accept --revocation --public-did | | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | + + @T004.5-TAA @taa_required + Scenario Outline: Fail to publish revoked credential using a ledger with TAA required, and fix the ledger authomatically by revoking the last credential + Given we have "2" agents + | name | role | capabilities | + | Faber | verifier | | + | Bob | prover | | + And "Faber" connects to a ledger that requires acceptance of the TAA + And "Faber" accepts the TAA + And "Faber" and "Bob" have an existing connection + And "Bob" has an issued credential from "" + And "Faber" revokes the credential + And "Faber" successfully revoked the credential + When "Faber" rejects the TAA + And "Bob" has an issued credential from "" And "Faber" attempts to revoke the credential And "Faber" fails to publish the credential revocation And "Faber" attempts to revoke the credential And "Faber" fails to publish the credential revocation - Then "Faber" posts a revocation correction to the ledger - And "Faber" successfully revoked the credential + And "Faber" accepts the TAA + And "Faber" attempts to revoke the credential + And "Faber" fails to publish the credential revocation And "Bob" has an issued credential from "" And "Faber" revokes the credential And "Faber" successfully revoked the credential From 82fa762ae929cd60fd12d5fd28aac9defc1facad Mon Sep 17 00:00:00 2001 From: Roman Reinert Date: Wed, 10 Aug 2022 10:38:20 +0200 Subject: [PATCH 387/872] fix SchemasInputDescriptorFilter pre-load Signed-off-by: Roman Reinert --- aries_cloudagent/protocols/present_proof/dif/pres_exch.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/protocols/present_proof/dif/pres_exch.py b/aries_cloudagent/protocols/present_proof/dif/pres_exch.py index dafe0b8857..3155d28860 100644 --- a/aries_cloudagent/protocols/present_proof/dif/pres_exch.py +++ b/aries_cloudagent/protocols/present_proof/dif/pres_exch.py @@ -237,7 +237,9 @@ def extract_info(self, data, **kwargs): """deserialize.""" new_data = {} if isinstance(data, dict): - if "oneof_filter" in data: + if "uri_groups" in data: + return data + elif "oneof_filter" in data and isinstance(data["oneof_filter"], list): new_data["oneof_filter"] = True uri_group_list_of_list = [] uri_group_list = data.get("oneof_filter") From e14ee18a79e5e429e1cc2c5cca8a86eb648d52da Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Wed, 10 Aug 2022 13:53:14 -0700 Subject: [PATCH 388/872] Refactoring and handle the publish separately use case Signed-off-by: Ian Costanzo --- aries_cloudagent/revocation/manager.py | 129 +----------------- .../models/issuer_rev_reg_record.py | 123 +++++++++++++++-- aries_cloudagent/revocation/routes.py | 12 +- 3 files changed, 131 insertions(+), 133 deletions(-) diff --git a/aries_cloudagent/revocation/manager.py b/aries_cloudagent/revocation/manager.py index 0bb64a1b36..592a879c7a 100644 --- a/aries_cloudagent/revocation/manager.py +++ b/aries_cloudagent/revocation/manager.py @@ -10,14 +10,10 @@ from ..core.error import BaseError from ..core.profile import Profile from ..indy.issuer import IndyIssuer -from ..ledger.base import BaseLedger -from ..ledger.error import LedgerError, LedgerTransactionError -from ..ledger.multiple_ledger.base_manager import BaseMultipleLedgerManager from ..storage.error import StorageNotFoundError from .indy import IndyRevocation from .models.issuer_cred_rev_record import IssuerCredRevRecord from .models.issuer_rev_reg_record import IssuerRevRegRecord -from .recover import generate_ledger_rrrecovery_txn from .util import notify_pending_cleared_event, notify_revocation_published_event from ..protocols.issue_credential.v1_0.models.credential_exchange import ( V10CredentialExchange, @@ -151,53 +147,7 @@ async def revoke_credential( await txn.commit() await self.set_cred_revoked_state(rev_reg_id, crids) if delta_json: - try: - await issuer_rr_upd.send_entry(self._profile) - except LedgerTransactionError as err: - if "InvalidClientRequest" in err.roll_up: - # ... if the ledger write fails (with "InvalidClientRequest") - # e.g. aries_cloudagent.ledger.error.LedgerTransactionError: - # Ledger rejected transaction request: client request invalid: - # InvalidClientRequest(...) - # In this scenario we try to post a correction - self._logger.warn("Retry ledger update/fix due to error") - self._logger.warn(err) - - async with self._profile.session() as session: - genesis_transactions = session.context.settings.get( - "ledger.genesis_transactions" - ) - if not genesis_transactions: - ledger_manager = session.context.injector.inject( - BaseMultipleLedgerManager - ) - write_ledgers = await ledger_manager.get_write_ledger() - self._logger.debug(f"write_ledgers = {write_ledgers}") - pool = write_ledgers[1].pool - self._logger.debug(f"write_ledger pool = {pool}") - - genesis_transactions = pool.genesis_txns - - (_, _, _) = await self.update_rev_reg_revoked_state( - rev_reg_id, - True, - issuer_rr_upd, - genesis_transactions, - ) - self._logger.warn("Ledger update/fix applied") - elif "InvalidClientTaaAcceptanceError" in err.roll_up: - # if no write access (with "InvalidClientTaaAcceptanceError") - # e.g. aries_cloudagent.ledger.error.LedgerTransactionError: - # Ledger rejected transaction request: client request invalid: - # InvalidClientTaaAcceptanceError(...) - self._logger.error("Ledger update failed due to TAA issue") - self._logger.error(err) - raise err - else: - # not sure what happened, raise an error - self._logger.error("Ledger update failed due to unknown issue") - self._logger.error(err) - raise err + await issuer_rr_upd.send_entry(self._profile) await notify_revocation_published_event( self._profile, rev_reg_id, [cred_rev_id] ) @@ -209,7 +159,6 @@ async def revoke_credential( async def update_rev_reg_revoked_state( self, - rev_reg_id: str, apply_ledger_update: bool, rev_reg_record: IssuerRevRegRecord, genesis_transactions: dict, @@ -225,77 +174,11 @@ async def update_rev_reg_revoked_state( Number of credentials posted to ledger """ - # get rev reg delta (revocations published to ledger) - revoc = IndyRevocation(self._profile) - rev_reg_delta = await revoc.get_issuer_rev_reg_delta(rev_reg_id) - - # get rev reg records from wallet (revocations and status) - recs = [] - rec_count = 0 - accum_count = 0 - recovery_txn = {} - applied_txn = {} - async with self._profile.session() as session: - # rev_reg_record = await IssuerRevRegRecord.retrieve_by_revoc_reg_id( - # session, rev_reg_id - # ) - recs = await IssuerCredRevRecord.query_by_ids( - session, rev_reg_id=rev_reg_id - ) - - revoked_ids = [] - for rec in recs: - if rec.state == IssuerCredRevRecord.STATE_REVOKED: - revoked_ids.append(int(rec.cred_rev_id)) - if int(rec.cred_rev_id) not in rev_reg_delta["value"]["revoked"]: - # await rec.set_state(session, IssuerCredRevRecord.STATE_ISSUED) - rec_count += 1 - - self._logger.debug(">>> fixed entry recs count = %s", rec_count) - self._logger.debug( - ">>> rev_reg_record.revoc_reg_entry.value: %s", - rev_reg_record.revoc_reg_entry.value, - ) - self._logger.debug( - '>>> rev_reg_delta.get("value"): %s', rev_reg_delta.get("value") - ) - - # if we had any revocation discrepencies, check the accumulator value - if rec_count > 0: - if ( - rev_reg_record.revoc_reg_entry.value and rev_reg_delta.get("value") - ) and not ( - rev_reg_record.revoc_reg_entry.value.accum - == rev_reg_delta["value"]["accum"] - ): - # rev_reg_record.revoc_reg_entry = rev_reg_delta["value"] - # await rev_reg_record.save(session) - accum_count += 1 - - calculated_txn = await generate_ledger_rrrecovery_txn( - genesis_transactions, - rev_reg_id, - revoked_ids, - ) - recovery_txn = json.loads(calculated_txn.to_json()) - - self._logger.debug(">>> apply_ledger_update = %s", apply_ledger_update) - if apply_ledger_update: - ledger = session.inject_or(BaseLedger) - if not ledger: - reason = "No ledger available" - if not session.context.settings.get_value("wallet.type"): - reason += ": missing wallet-type?" - raise LedgerError(reason=reason) - - async with ledger: - ledger_response = await ledger.send_revoc_reg_entry( - rev_reg_id, "CL_ACCUM", recovery_txn - ) - - applied_txn = ledger_response["result"] - - return (rev_reg_delta, recovery_txn, applied_txn) + return await rev_reg_record.fix_ledger_entry( + self._profile, + apply_ledger_update, + genesis_transactions, + ) async def publish_pending_revocations( self, diff --git a/aries_cloudagent/revocation/models/issuer_rev_reg_record.py b/aries_cloudagent/revocation/models/issuer_rev_reg_record.py index 98451dcb3d..b9c12d8065 100644 --- a/aries_cloudagent/revocation/models/issuer_rev_reg_record.py +++ b/aries_cloudagent/revocation/models/issuer_rev_reg_record.py @@ -22,6 +22,7 @@ ) from ...indy.util import indy_client_dir from ...ledger.base import BaseLedger +from ...ledger.error import LedgerError, LedgerTransactionError from ...messaging.models.base_record import BaseRecord, BaseRecordSchema from ...messaging.valid import ( BASE58_SHA256_HASH, @@ -33,7 +34,9 @@ from ...tails.base import BaseTailsServer from ..error import RevocationError +from ..recover import generate_ledger_rrrecovery_txn +from .issuer_cred_rev_record import IssuerCredRevRecord from .revocation_registry import RevocationRegistry DEFAULT_REGISTRY_SIZE = 1000 @@ -290,14 +293,44 @@ async def send_entry( ledger = profile.inject(BaseLedger) async with ledger: - rev_entry_res = await ledger.send_revoc_reg_entry( - self.revoc_reg_id, - self.revoc_def_type, - self._revoc_reg_entry.ser, - self.issuer_did, - write_ledger=write_ledger, - endorser_did=endorser_did, - ) + try: + rev_entry_res = await ledger.send_revoc_reg_entry( + self.revoc_reg_id, + self.revoc_def_type, + self._revoc_reg_entry.ser, + self.issuer_did, + write_ledger=write_ledger, + endorser_did=endorser_did, + ) + except LedgerTransactionError as err: + if "InvalidClientRequest" in err.roll_up: + # ... if the ledger write fails (with "InvalidClientRequest") + # e.g. aries_cloudagent.ledger.error.LedgerTransactionError: + # Ledger rejected transaction request: client request invalid: + # InvalidClientRequest(...) + # In this scenario we try to post a correction + LOGGER.warn("Retry ledger update/fix due to error") + LOGGER.warn(err) + (_, _, res) = await self.fix_ledger_entry( + profile, + True, + ledger.pool.genesis_txns, + ) + rev_entry_res = {"result": res} + LOGGER.warn("Ledger update/fix applied") + elif "InvalidClientTaaAcceptanceError" in err.roll_up: + # if no write access (with "InvalidClientTaaAcceptanceError") + # e.g. aries_cloudagent.ledger.error.LedgerTransactionError: + # Ledger rejected transaction request: client request invalid: + # InvalidClientTaaAcceptanceError(...) + LOGGER.error("Ledger update failed due to TAA issue") + LOGGER.error(err) + raise err + else: + # not sure what happened, raise an error + LOGGER.error("Ledger update failed due to unknown issue") + LOGGER.error(err) + raise err if self.state == IssuerRevRegRecord.STATE_POSTED: self.state = IssuerRevRegRecord.STATE_ACTIVE # initial entry activates async with profile.session() as session: @@ -307,6 +340,80 @@ async def send_entry( return rev_entry_res + async def fix_ledger_entry( + self, + profile: Profile, + apply_ledger_update: bool, + genesis_transactions: str, + ) -> (dict, dict, dict): + """Fix the ledger entry to match wallet-recorded credentials.""" + # get rev reg delta (revocations published to ledger) + ledger = profile.inject(BaseLedger) + async with ledger: + (rev_reg_delta, _) = await ledger.get_revoc_reg_delta(self.revoc_reg_id) + + # get rev reg records from wallet (revocations and status) + recs = [] + rec_count = 0 + accum_count = 0 + recovery_txn = {} + applied_txn = {} + async with profile.session() as session: + recs = await IssuerCredRevRecord.query_by_ids( + session, rev_reg_id=self.revoc_reg_id + ) + + revoked_ids = [] + for rec in recs: + if rec.state == IssuerCredRevRecord.STATE_REVOKED: + revoked_ids.append(int(rec.cred_rev_id)) + if int(rec.cred_rev_id) not in rev_reg_delta["value"]["revoked"]: + # await rec.set_state(session, IssuerCredRevRecord.STATE_ISSUED) + rec_count += 1 + + LOGGER.debug(">>> fixed entry recs count = %s", rec_count) + LOGGER.debug( + ">>> rev_reg_record.revoc_reg_entry.value: %s", + self.revoc_reg_entry.value, + ) + LOGGER.debug( + '>>> rev_reg_delta.get("value"): %s', rev_reg_delta.get("value") + ) + + # if we had any revocation discrepencies, check the accumulator value + if rec_count > 0: + if (self.revoc_reg_entry.value and rev_reg_delta.get("value")) and not ( + self.revoc_reg_entry.value.accum == rev_reg_delta["value"]["accum"] + ): + # self.revoc_reg_entry = rev_reg_delta["value"] + # await self.save(session) + accum_count += 1 + + calculated_txn = await generate_ledger_rrrecovery_txn( + genesis_transactions, + self.revoc_reg_id, + revoked_ids, + ) + recovery_txn = json.loads(calculated_txn.to_json()) + + LOGGER.debug(">>> apply_ledger_update = %s", apply_ledger_update) + if apply_ledger_update: + ledger = session.inject_or(BaseLedger) + if not ledger: + reason = "No ledger available" + if not session.context.settings.get_value("wallet.type"): + reason += ": missing wallet-type?" + raise LedgerError(reason=reason) + + async with ledger: + ledger_response = await ledger.send_revoc_reg_entry( + self.revoc_reg_id, "CL_ACCUM", recovery_txn + ) + + applied_txn = ledger_response["result"] + + return (rev_reg_delta, recovery_txn, applied_txn) + @property def has_local_tails_file(self) -> bool: """Check if a local copy of the tails file is available.""" diff --git a/aries_cloudagent/revocation/routes.py b/aries_cloudagent/revocation/routes.py index bfca31c560..e768a00049 100644 --- a/aries_cloudagent/revocation/routes.py +++ b/aries_cloudagent/revocation/routes.py @@ -784,10 +784,18 @@ async def update_rev_reg_revoked_state(request: web.BaseRequest): recovery_txn, applied_txn, ) = await rev_manager.update_rev_reg_revoked_state( - rev_reg_id, apply_ledger_update, rev_reg_record, genesis_transactions + apply_ledger_update, rev_reg_record, genesis_transactions ) - except Exception as err: + except ( + RevocationManagerError, + RevocationError, + StorageError, + IndyIssuerError, + LedgerError, + ) as err: raise web.HTTPBadRequest(reason=err.roll_up) + except Exception as err: + raise web.HTTPBadRequest(reason=str(err)) return web.json_response( { From c76027d2d7467d3633fc01d46c9c38fc1e9da7a7 Mon Sep 17 00:00:00 2001 From: Roman Reinert Date: Thu, 11 Aug 2022 10:21:51 +0000 Subject: [PATCH 389/872] extend tests Signed-off-by: Roman Reinert --- .../present_proof/dif/tests/test_pres_exch.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch.py b/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch.py index 34638ef764..2709024263 100644 --- a/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch.py +++ b/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch.py @@ -395,6 +395,10 @@ def test_schemas_input_desc_filter(self): deser_schema_filter = SchemasInputDescriptorFilter.deserialize( test_schemas_filter ) + ser_schema_filter = deser_schema_filter.serialize() + deser_schema_filter = SchemasInputDescriptorFilter.deserialize( + ser_schema_filter + ) assert deser_schema_filter.oneof_filter assert deser_schema_filter.uri_groups[0][0].uri == test_schema_list[0][0].get( "uri" @@ -418,6 +422,10 @@ def test_schemas_input_desc_filter(self): deser_schema_filter = SchemasInputDescriptorFilter.deserialize( test_schemas_filter ) + ser_schema_filter = deser_schema_filter.serialize() + deser_schema_filter = SchemasInputDescriptorFilter.deserialize( + ser_schema_filter + ) assert deser_schema_filter.oneof_filter assert deser_schema_filter.uri_groups[0][0].uri == test_schema_list[0].get( "uri" @@ -428,6 +436,10 @@ def test_schemas_input_desc_filter(self): assert isinstance(deser_schema_filter, SchemasInputDescriptorFilter) deser_schema_filter = SchemasInputDescriptorFilter.deserialize(test_schema_list) + ser_schema_filter = deser_schema_filter.serialize() + deser_schema_filter = SchemasInputDescriptorFilter.deserialize( + ser_schema_filter + ) assert not deser_schema_filter.oneof_filter assert deser_schema_filter.uri_groups[0][0].uri == test_schema_list[0].get( "uri" From ae29733a7e88d0b4d03d9dfcd9d1057d07c3cf6c Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Thu, 11 Aug 2022 10:21:52 -0600 Subject: [PATCH 390/872] feat: Send webhook event upon record deletion Signed-off-by: Colton Wolkins (Indicio work address) --- aries_cloudagent/holder/routes.py | 4 ++++ aries_cloudagent/messaging/models/base_record.py | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/holder/routes.py b/aries_cloudagent/holder/routes.py index 7d52bdfc7d..40257dd0b1 100644 --- a/aries_cloudagent/holder/routes.py +++ b/aries_cloudagent/holder/routes.py @@ -273,6 +273,8 @@ async def credentials_remove(request: web.BaseRequest): try: async with context.profile.session() as session: holder = session.inject(IndyHolder) + topic = f"acapy::record::credentials::deleted" + await session.profile.notify(topic, {"id": credential_id, "state": "deleted"}) await holder.delete_credential(credential_id) except WalletNotFoundError as err: raise web.HTTPNotFound(reason=err.roll_up) from err @@ -375,6 +377,8 @@ async def w3c_cred_remove(request: web.BaseRequest): holder = session.inject(VCHolder) try: vc_record = await holder.retrieve_credential_by_id(credential_id) + topic = f"acapy::record::credentials::deleted" + await session.profile.notify(topic, {"id": credential_id, "state": "deleted"}) await holder.delete_credential(vc_record) except StorageNotFoundError as err: raise web.HTTPNotFound(reason=err.roll_up) from err diff --git a/aries_cloudagent/messaging/models/base_record.py b/aries_cloudagent/messaging/models/base_record.py index 35f77baf8e..1f0d5b4009 100644 --- a/aries_cloudagent/messaging/models/base_record.py +++ b/aries_cloudagent/messaging/models/base_record.py @@ -418,8 +418,10 @@ async def delete_record(self, session: ProfileSession): if self._id: storage = session.inject(BaseStorage) + if self.state: + self.state = "deleted" + await self.emit_event(session, self.serialize()) await storage.delete_record(self.storage_record) - # FIXME - update state and send webhook? async def emit_event(self, session: ProfileSession, payload: Any = None): """ From da81c69d8ecfe5be541ec02e59958f2e30e64b51 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Fri, 12 Aug 2022 09:20:52 -0700 Subject: [PATCH 391/872] Null check (to fix AATH errors) Signed-off-by: Ian Costanzo --- aries_cloudagent/protocols/out_of_band/v1_0/manager.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py index 7b02b328f7..4bdfcc39a6 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py @@ -359,9 +359,10 @@ async def create_invitation( async with self.profile.session() as session: await oob_record.save(session, reason="Created new oob invitation") - await self._route_manager.route_invitation( - self.profile, conn_rec, mediation_record - ) + if conn_rec: + await self._route_manager.route_invitation( + self.profile, conn_rec, mediation_record + ) return InvitationRecord( # for return via admin API, not storage oob_id=oob_record.oob_id, From f1f5086af5cc4b75bcf90eb242c041018c381ee2 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Fri, 12 Aug 2022 10:43:54 -0700 Subject: [PATCH 392/872] Fix AATH issues Signed-off-by: Ian Costanzo --- .../coordinate_mediation/v1_0/route_manager.py | 15 ++++++++------- .../v1_0/tests/test_route_manager.py | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py index 89fbf92081..07da03fd51 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py @@ -66,13 +66,14 @@ async def mediation_record_for_connection( or_default: bool = False, ): """Return relevant mediator for connection.""" - async with profile.session() as session: - mediation_metadata = await conn_record.metadata_get( - session, MediationManager.METADATA_KEY, {} - ) - mediation_id = ( - mediation_metadata.get(MediationManager.METADATA_ID) or mediation_id - ) + if conn_record.connection_id: + async with profile.session() as session: + mediation_metadata = await conn_record.metadata_get( + session, MediationManager.METADATA_KEY, {} + ) + mediation_id = ( + mediation_metadata.get(MediationManager.METADATA_ID) or mediation_id + ) mediation_record = await self.mediation_record_if_id( profile, mediation_id, or_default diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_route_manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_route_manager.py index dcb315337b..3482a1a545 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_route_manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_route_manager.py @@ -53,7 +53,7 @@ def mediation_route_manager(): @pytest.fixture def conn_record(): - record = ConnRecord() + record = ConnRecord(connection_id="12345") record.metadata_get = mock.CoroutineMock(return_value={}) record.metadata_set = mock.CoroutineMock() yield record From f13fc187246b3c922284c3faeb50bb0865ebdb85 Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Mon, 15 Aug 2022 10:37:43 -0600 Subject: [PATCH 393/872] chore: Tidy up webhook names Signed-off-by: Colton Wolkins (Indicio work address) --- aries_cloudagent/holder/routes.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/aries_cloudagent/holder/routes.py b/aries_cloudagent/holder/routes.py index 40257dd0b1..dc97b479e6 100644 --- a/aries_cloudagent/holder/routes.py +++ b/aries_cloudagent/holder/routes.py @@ -273,9 +273,9 @@ async def credentials_remove(request: web.BaseRequest): try: async with context.profile.session() as session: holder = session.inject(IndyHolder) - topic = f"acapy::record::credentials::deleted" - await session.profile.notify(topic, {"id": credential_id, "state": "deleted"}) await holder.delete_credential(credential_id) + topic = f"acapy::record::credential::delete" + await session.profile.notify(topic, {"id": credential_id, "state": "deleted"}) except WalletNotFoundError as err: raise web.HTTPNotFound(reason=err.roll_up) from err @@ -377,9 +377,9 @@ async def w3c_cred_remove(request: web.BaseRequest): holder = session.inject(VCHolder) try: vc_record = await holder.retrieve_credential_by_id(credential_id) - topic = f"acapy::record::credentials::deleted" - await session.profile.notify(topic, {"id": credential_id, "state": "deleted"}) await holder.delete_credential(vc_record) + topic = f"acapy::record::credential::delete" + await session.profile.notify(topic, {"id": credential_id, "state": "deleted"}) except StorageNotFoundError as err: raise web.HTTPNotFound(reason=err.roll_up) from err except StorageError as err: From 578833c8035ff638cbd700dfaf981f4ced00c4da Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Mon, 15 Aug 2022 11:08:37 -0600 Subject: [PATCH 394/872] revert: Changes to non-coordinate mediation related files Signed-off-by: Colton Wolkins (Indicio work address) --- aries_cloudagent/connections/base_manager.py | 10 ++----- .../multitenant/tests/test_base.py | 24 ++++++++--------- .../v1_0/messages/connection_invitation.py | 26 ++++--------------- .../connections/v1_0/tests/test_manager.py | 5 +--- .../didexchange/v1_0/tests/test_manager.py | 5 +--- .../routing/v1_0/models/route_record.py | 21 +-------------- aries_cloudagent/transport/pack_format.py | 13 ++-------- 7 files changed, 23 insertions(+), 81 deletions(-) diff --git a/aries_cloudagent/connections/base_manager.py b/aries_cloudagent/connections/base_manager.py index 6421a47c9c..bd5e281694 100644 --- a/aries_cloudagent/connections/base_manager.py +++ b/aries_cloudagent/connections/base_manager.py @@ -116,15 +116,10 @@ async def create_did_document( raise BaseConnectionManagerError( "Routing DIDDoc service has no recipient key(s)" ) - key = service.recip_keys[0].value - key = ( - DIDKey.from_did(key).public_key_b58 - if key.startswith("did:key:") else key - ) rk = PublicKey( did_info.did, f"routing-{router_idx}", - key, + service.recip_keys[0].value, PublicKeyType.ED25519_SIG_2018, did_controller, True, @@ -140,8 +135,7 @@ async def create_did_document( PublicKey( did_info.did, f"routing-{idx}", - DIDKey.from_did(key).public_key_b58 - if key.startswith("did:key:") else key, + key, PublicKeyType.ED25519_SIG_2018, did_controller, # TODO: get correct controller did_info True, # TODO: should this be true? diff --git a/aries_cloudagent/multitenant/tests/test_base.py b/aries_cloudagent/multitenant/tests/test_base.py index f5a67249a4..5d37297b9d 100644 --- a/aries_cloudagent/multitenant/tests/test_base.py +++ b/aries_cloudagent/multitenant/tests/test_base.py @@ -25,8 +25,6 @@ from ..error import WalletKeyMissingError from .. import base as test_module -RECIPIENT_KEY = "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV" - class MockMultitenantManager(BaseMultitenantManager): async def get_wallet_profile( @@ -143,7 +141,7 @@ async def test_wallet_exists_false(self): assert wallet_name_exists is False async def test_get_wallet_by_key_routing_record_does_not_exist(self): - recipient_key = RECIPIENT_KEY + recipient_key = "test" with async_mock.patch.object(WalletRecord, "retrieve_by_id") as retrieve_by_id: wallet = await self.manager._get_wallet_by_key(recipient_key) @@ -154,7 +152,7 @@ async def test_get_wallet_by_key_routing_record_does_not_exist(self): await self.manager._get_wallet_by_key(recipient_key) async def test_get_wallet_by_key_wallet_record_does_not_exist(self): - recipient_key = RECIPIENT_KEY + recipient_key = "test-recipient-key" wallet_id = "test-wallet-id" route_record = RouteRecord(wallet_id=wallet_id, recipient_key=recipient_key) @@ -165,7 +163,7 @@ async def test_get_wallet_by_key_wallet_record_does_not_exist(self): await self.manager._get_wallet_by_key(recipient_key) async def test_get_wallet_by_key(self): - recipient_key = RECIPIENT_KEY + recipient_key = "test-recipient-key" wallet_record = WalletRecord(settings={}) async with self.profile.session() as session: @@ -358,10 +356,10 @@ async def test_add_key_no_mediation(self): ) as create_route_record, async_mock.patch.object( MediationManager, "add_key" ) as mediation_add_key: - await self.manager.add_key("wallet_id", RECIPIENT_KEY) + await self.manager.add_key("wallet_id", "recipient_key") create_route_record.assert_called_once_with( - recipient_key=RECIPIENT_KEY, internal_wallet_id="wallet_id" + recipient_key="recipient_key", internal_wallet_id="wallet_id" ) mediation_add_key.assert_not_called() @@ -374,11 +372,11 @@ async def test_add_key_skip_if_exists_does_not_exist(self): retrieve_by_recipient_key.side_effect = StorageNotFoundError() await self.manager.add_key( - "wallet_id", RECIPIENT_KEY, skip_if_exists=True + "wallet_id", "recipient_key", skip_if_exists=True ) create_route_record.assert_called_once_with( - recipient_key=RECIPIENT_KEY, internal_wallet_id="wallet_id" + recipient_key="recipient_key", internal_wallet_id="wallet_id" ) async def test_add_key_skip_if_exists_does_exist(self): @@ -388,7 +386,7 @@ async def test_add_key_skip_if_exists_does_exist(self): RouteRecord, "retrieve_by_recipient_key" ) as retrieve_by_recipient_key: await self.manager.add_key( - "wallet_id", RECIPIENT_KEY, skip_if_exists=True + "wallet_id", "recipient_key", skip_if_exists=True ) create_route_record.assert_not_called() @@ -407,14 +405,14 @@ async def test_add_key_mediation(self): get_default_mediator.return_value = default_mediator mediation_add_key.return_value = keylist_updates - await self.manager.add_key("wallet_id", RECIPIENT_KEY) + await self.manager.add_key("wallet_id", "recipient_key") create_route_record.assert_called_once_with( - recipient_key=RECIPIENT_KEY, internal_wallet_id="wallet_id" + recipient_key="recipient_key", internal_wallet_id="wallet_id" ) get_default_mediator.assert_called_once() - mediation_add_key.assert_called_once_with(RECIPIENT_KEY) + mediation_add_key.assert_called_once_with("recipient_key") self.responder.send.assert_called_once_with( keylist_updates, connection_id=default_mediator.connection_id ) diff --git a/aries_cloudagent/protocols/connections/v1_0/messages/connection_invitation.py b/aries_cloudagent/protocols/connections/v1_0/messages/connection_invitation.py index 4f0d7d68e6..48ca887989 100644 --- a/aries_cloudagent/protocols/connections/v1_0/messages/connection_invitation.py +++ b/aries_cloudagent/protocols/connections/v1_0/messages/connection_invitation.py @@ -5,9 +5,8 @@ from marshmallow import EXCLUDE, fields, validates_schema, ValidationError -from .....did.did_key import DIDKey from .....messaging.agent_message import AgentMessage, AgentMessageSchema -from .....messaging.valid import INDY_DID, ROUTING_KEY +from .....messaging.valid import INDY_DID, INDY_RAW_PUBLIC_KEY from .....wallet.util import b64_to_bytes, bytes_to_b64 from ..message_types import CONNECTION_INVITATION, PROTOCOL_PACKAGE @@ -54,26 +53,11 @@ def __init__( super().__init__(**kwargs) self.label = label self.did = did + self.recipient_keys = list(recipient_keys) if recipient_keys else None self.endpoint = endpoint + self.routing_keys = list(routing_keys) if routing_keys else None self.image_url = image_url - if recipient_keys: - public_keys = list() - for key in recipient_keys: - public_keys.append( - DIDKey.from_did(key).public_key_b58 - if key.startswith("did:key:") else key - ) - self.recipient_keys = public_keys if recipient_keys else None - if routing_keys: - public_keys = list() - for key in routing_keys: - public_keys.append( - DIDKey.from_did(key).public_key_b58 - if key.startswith("did:key:") else key - ) - self.routing_keys = public_keys if routing_keys else None - def to_url(self, base_url: str = None) -> str: """ Convert an invitation to URL format for sharing. @@ -125,7 +109,7 @@ class Meta: required=False, description="DID for connection invitation", **INDY_DID ) recipient_keys = fields.List( - fields.Str(description="Recipient public key", **ROUTING_KEY), + fields.Str(description="Recipient public key", **INDY_RAW_PUBLIC_KEY), data_key="recipientKeys", required=False, description="List of recipient keys", @@ -137,7 +121,7 @@ class Meta: example="http://192.168.56.101:8020", ) routing_keys = fields.List( - fields.Str(description="Routing key", **ROUTING_KEY), + fields.Str(description="Routing key", **INDY_RAW_PUBLIC_KEY), data_key="routingKeys", required=False, description="List of routing keys", diff --git a/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py index a0a8ca5829..c1b1bb0ba1 100644 --- a/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py @@ -1052,10 +1052,7 @@ async def test_receive_request_mediation_id(self): assert len(message.updates) == 1 (remove,) = message.updates assert remove.action == KeylistUpdateRule.RULE_REMOVE - did_key = DIDKey.from_public_key_b58( - record.invitation_key, KeyType.ED25519 - ).did - assert remove.recipient_key == did_key + assert remove.recipient_key == record.invitation_key async def test_receive_request_bad_mediation(self): mock_request = async_mock.MagicMock() diff --git a/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py index 2eb10cd20d..3ffd26a181 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py @@ -717,10 +717,7 @@ async def test_receive_request_with_mediator_without_multi_use_multitenant(self) assert len(message.updates) == 1 (remove,) = message.updates assert remove.action == KeylistUpdateRule.RULE_REMOVE - did_key = DIDKey.from_public_key_b58( - record.invitation_key, KeyType.ED25519 - ).did - assert remove.recipient_key == did_key + assert remove.recipient_key == record.invitation_key async def test_receive_request_with_mediator_without_multi_use_multitenant_mismatch( self, diff --git a/aries_cloudagent/protocols/routing/v1_0/models/route_record.py b/aries_cloudagent/protocols/routing/v1_0/models/route_record.py index f0016a979c..e750cd15eb 100644 --- a/aries_cloudagent/protocols/routing/v1_0/models/route_record.py +++ b/aries_cloudagent/protocols/routing/v1_0/models/route_record.py @@ -4,8 +4,6 @@ from .....core.profile import ProfileSession from .....messaging.models.base_record import BaseRecord, BaseRecordSchema -from .....did.did_key import DIDKey -from .....wallet.key_type import KeyType class RouteRecord(BaseRecord): @@ -77,24 +75,7 @@ async def retrieve_by_recipient_key( RouteRecord: retrieved route record """ - if recipient_key.startswith("did:key:"): - recipient_key = ( - DIDKey.from_did(recipient_key) - ) - # recipient_key = recipient_key.public_key_b58 - else: - recipient_key = ( - DIDKey.from_public_key_b58( - recipient_key, - KeyType.ED25519, - ) - ) - # record = await RouteRecord.retrieve_by_recipient_key( - tag_filter = {"recipient_key": {"$in": [ - recipient_key.did, - recipient_key.public_key_b58, - ]}} - # tag_filter = {"recipient_key": recipient_key} + tag_filter = {"recipient_key": recipient_key} return await cls.retrieve_by_tag_filter(session, tag_filter) @classmethod diff --git a/aries_cloudagent/transport/pack_format.py b/aries_cloudagent/transport/pack_format.py index 2c078868e1..f982748098 100644 --- a/aries_cloudagent/transport/pack_format.py +++ b/aries_cloudagent/transport/pack_format.py @@ -7,7 +7,6 @@ from ..core.profile import ProfileSession from ..protocols.routing.v1_0.messages.forward import Forward -from ..did.did_key import DIDKey from ..messaging.util import time_now from ..utils.task_queue import TaskQueue @@ -176,28 +175,20 @@ async def pack( if not wallet: raise WireFormatEncodeError("No wallet instance") - recip_keys = [] - for key in recipient_keys: - if key.startswith("did:key:"): - recip_keys.append(DIDKey.from_did(key).public_key_b58) - else: - recip_keys.append(key) - try: message = await wallet.pack_message( - message_json, recip_keys, sender_key + message_json, recipient_keys, sender_key ) except WalletError as e: raise WireFormatEncodeError("Message pack failed") from e if routing_keys: + recip_keys = recipient_keys for router_key in routing_keys: message = json.loads(message.decode("utf-8")) fwd_msg = Forward(to=recip_keys[0], msg=message) # Forwards are anon packed recip_keys = [router_key] - if router_key.startswith("did:key:"): - recip_keys = [DIDKey.from_did(router_key).public_key_b58] try: message = await wallet.pack_message(fwd_msg.to_json(), recip_keys) except WalletError as e: From 2ec73fe20d37107cc392e44c8ceb4406fde754ff Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Mon, 15 Aug 2022 10:21:50 -0700 Subject: [PATCH 395/872] Revocation fix Signed-off-by: Ian Costanzo --- aries_cloudagent/revocation/routes.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/revocation/routes.py b/aries_cloudagent/revocation/routes.py index 637332d55a..964afe20b3 100644 --- a/aries_cloudagent/revocation/routes.py +++ b/aries_cloudagent/revocation/routes.py @@ -424,7 +424,8 @@ async def revoke(request: web.BaseRequest): body["notify"] = body.get("notify", context.settings.get("revocation.notify")) notify = body.get("notify") connection_id = body.get("connection_id") - notify_version = body.get("notify_version", "v1_0") + body["notify_version"] = body.get("notify_version", "v1_0") + notify_version = body["notify_version"] if notify and not connection_id: raise web.HTTPBadRequest(reason="connection_id must be set when notify is true") From f69632385c98e7ea9a81bcc15f499a55c84ce141 Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Mon, 15 Aug 2022 13:48:11 -0600 Subject: [PATCH 396/872] fix: Limit changes to the coordinate mediation protocol Signed-off-by: Colton Wolkins (Indicio work address) --- .../handlers/tests/test_mediation_grant_handler.py | 3 ++- .../protocols/coordinate_mediation/v1_0/manager.py | 10 +++++++++- .../v1_0/tests/test_mediation_manager.py | 6 +++--- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/tests/test_mediation_grant_handler.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/tests/test_mediation_grant_handler.py index fd75c1a0d6..e8924dbb83 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/tests/test_mediation_grant_handler.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/tests/test_mediation_grant_handler.py @@ -18,6 +18,7 @@ from .. import mediation_grant_handler as test_module TEST_CONN_ID = "conn-id" +TEST_RECORD_VERKEY = "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx" TEST_VERKEY = "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL" TEST_ENDPOINT = "https://example.com" @@ -58,7 +59,7 @@ async def test_handler(self): assert record assert record.state == MediationRecord.STATE_GRANTED assert record.endpoint == TEST_ENDPOINT - assert record.routing_keys == [TEST_VERKEY] + assert record.routing_keys == [TEST_RECORD_VERKEY] async def test_handler_connection_has_set_to_default_meta(self): handler, responder = MediationGrantHandler(), MockResponder() diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py index 195dd5fd5a..0cd5744b92 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py @@ -6,6 +6,7 @@ from ....core.error import BaseError from ....core.profile import Profile, ProfileSession +from ....did.did_key import DIDKey from ....storage.base import BaseStorage from ....storage.error import StorageNotFoundError from ....storage.record import StorageRecord @@ -447,7 +448,14 @@ async def request_granted(self, record: MediationRecord, grant: MediationGrant): """ record.state = MediationRecord.STATE_GRANTED record.endpoint = grant.endpoint - record.routing_keys = grant.routing_keys + # record.routing_keys = grant.routing_keys + routing_keys = [] + for key in grant.routing_keys: + routing_keys.append( + DIDKey.from_did(key).public_key_b58 + if key.startswith("did:key:") else key + ) + record.routing_keys = routing_keys async with self._profile.session() as session: await record.save(session, reason="Mediation request granted.") diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py index 4f86b8eee4..dc0c42766c 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py @@ -29,9 +29,9 @@ TEST_CONN_ID = "conn-id" TEST_THREAD_ID = "thread-id" TEST_ENDPOINT = "https://example.com" -# TEST_VERKEY = "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx" +# TEST_RECORD_VERKEY = "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx" TEST_VERKEY = "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL" -# TEST_ROUTE_VERKEY = "9WCgWKUaAJj3VWxxtzvvMQN3AoFxoBtBDo9ntwJnVVCC" +TEST_ROUTE_RECORD_VERKEY = "9WCgWKUaAJj3VWxxtzvvMQN3AoFxoBtBDo9ntwJnVVCC" TEST_ROUTE_VERKEY = "did:key:z6MknxTj6Zj1VrDWc1ofaZtmCVv2zNXpD58Xup4ijDGoQhya" pytestmark = pytest.mark.asyncio @@ -294,7 +294,7 @@ async def test_request_granted(self, manager): await manager.request_granted(record, grant) assert record.state == MediationRecord.STATE_GRANTED assert record.endpoint == TEST_ENDPOINT - assert record.routing_keys == [TEST_ROUTE_VERKEY] + assert record.routing_keys == [TEST_ROUTE_RECORD_VERKEY] async def test_request_denied(self, manager): """test_request_denied.""" From 834bfb19a9631e7a6c26d04506b5d8ec4bd7253c Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Tue, 16 Aug 2022 09:33:00 -0600 Subject: [PATCH 397/872] fix: Fix unit tests Signed-off-by: Colton Wolkins (Indicio work address) --- .../multitenant/tests/test_route_manager.py | 41 +++++++++++-------- .../v1_0/tests/test_route_manager.py | 26 +++++++----- 2 files changed, 38 insertions(+), 29 deletions(-) diff --git a/aries_cloudagent/multitenant/tests/test_route_manager.py b/aries_cloudagent/multitenant/tests/test_route_manager.py index 87bfef7129..75f4547a0a 100644 --- a/aries_cloudagent/multitenant/tests/test_route_manager.py +++ b/aries_cloudagent/multitenant/tests/test_route_manager.py @@ -13,6 +13,11 @@ from ...storage.error import StorageNotFoundError from ..route_manager import MultitenantRouteManager +TEST_RECORD_VERKEY = "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx" +TEST_VERKEY = "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL" +TEST_ROUTE_RECORD_VERKEY = "9WCgWKUaAJj3VWxxtzvvMQN3AoFxoBtBDo9ntwJnVVCC" +TEST_ROUTE_VERKEY = "did:key:z6MknxTj6Zj1VrDWc1ofaZtmCVv2zNXpD58Xup4ijDGoQhya" + @pytest.fixture def wallet_id(): @@ -68,18 +73,18 @@ async def test_route_for_key_sub_mediator_no_base_mediator( ) as mock_create_route_record: keylist_update = await route_manager._route_for_key( sub_profile, - "test-recipient-key", + TEST_VERKEY, mediation_record, skip_if_exists=False, replace_key=None, ) mock_create_route_record.assert_called_once_with( - recipient_key="test-recipient-key", internal_wallet_id=wallet_id + recipient_key=TEST_VERKEY, internal_wallet_id=wallet_id ) assert keylist_update assert keylist_update.serialize()["updates"] == [ - {"action": "add", "recipient_key": "test-recipient-key"} + {"action": "add", "recipient_key": TEST_VERKEY} ] assert mock_responder.messages assert ( @@ -112,18 +117,18 @@ async def test_route_for_key_sub_mediator_and_base_mediator( ) as mock_create_route_record: keylist_update = await route_manager._route_for_key( sub_profile, - "test-recipient-key", + TEST_VERKEY, mediation_record, skip_if_exists=False, replace_key=None, ) mock_create_route_record.assert_called_once_with( - recipient_key="test-recipient-key", internal_wallet_id=wallet_id + recipient_key=TEST_VERKEY, internal_wallet_id=wallet_id ) assert keylist_update assert keylist_update.serialize()["updates"] == [ - {"action": "add", "recipient_key": "test-recipient-key"} + {"action": "add", "recipient_key": TEST_VERKEY} ] assert mock_responder.messages assert ( @@ -153,18 +158,18 @@ async def test_route_for_key_base_mediator_no_sub_mediator( ) as mock_create_route_record: keylist_update = await route_manager._route_for_key( sub_profile, - "test-recipient-key", + TEST_VERKEY, None, skip_if_exists=False, replace_key=None, ) mock_create_route_record.assert_called_once_with( - recipient_key="test-recipient-key", internal_wallet_id=wallet_id + recipient_key=TEST_VERKEY, internal_wallet_id=wallet_id ) assert keylist_update assert keylist_update.serialize()["updates"] == [ - {"action": "add", "recipient_key": "test-recipient-key"} + {"action": "add", "recipient_key": TEST_VERKEY} ] assert mock_responder.messages assert ( @@ -187,7 +192,7 @@ async def test_route_for_key_skip_if_exists_and_exists( ): keylist_update = await route_manager._route_for_key( sub_profile, - "test-recipient-key", + TEST_VERKEY, mediation_record, skip_if_exists=True, replace_key=None, @@ -212,14 +217,14 @@ async def test_route_for_key_skip_if_exists_and_absent( ): keylist_update = await route_manager._route_for_key( sub_profile, - "test-recipient-key", + TEST_VERKEY, mediation_record, skip_if_exists=True, replace_key=None, ) assert keylist_update assert keylist_update.serialize()["updates"] == [ - {"action": "add", "recipient_key": "test-recipient-key"} + {"action": "add", "recipient_key": TEST_VERKEY} ] assert mock_responder.messages assert ( @@ -239,15 +244,15 @@ async def test_route_for_key_replace_key( ) keylist_update = await route_manager._route_for_key( sub_profile, - "test-recipient-key", + TEST_VERKEY, mediation_record, skip_if_exists=False, - replace_key="test-replace-key", + replace_key=TEST_ROUTE_VERKEY, ) assert keylist_update assert keylist_update.serialize()["updates"] == [ - {"action": "add", "recipient_key": "test-recipient-key"}, - {"action": "remove", "recipient_key": "test-replace-key"}, + {"action": "add", "recipient_key": TEST_VERKEY}, + {"action": "remove", "recipient_key": TEST_ROUTE_VERKEY}, ] assert mock_responder.messages assert ( @@ -264,10 +269,10 @@ async def test_route_for_key_no_mediator( assert ( await route_manager._route_for_key( sub_profile, - "test-recipient-key", + TEST_VERKEY, None, skip_if_exists=True, - replace_key="test-replace-key", + replace_key=TEST_ROUTE_VERKEY, ) is None ) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_route_manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_route_manager.py index dcb315337b..cd56195317 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_route_manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_route_manager.py @@ -18,6 +18,10 @@ RouteManagerError, ) +TEST_RECORD_VERKEY = "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx" +TEST_VERKEY = "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL" +TEST_ROUTE_RECORD_VERKEY = "9WCgWKUaAJj3VWxxtzvvMQN3AoFxoBtBDo9ntwJnVVCC" +TEST_ROUTE_VERKEY = "did:key:z6MknxTj6Zj1VrDWc1ofaZtmCVv2zNXpD58Xup4ijDGoQhya" class MockRouteManager(RouteManager): """Concretion of RouteManager for testing.""" @@ -486,14 +490,14 @@ async def test_mediation_route_for_key( ) keylist_update = await mediation_route_manager._route_for_key( profile, - "test-recipient-key", + TEST_VERKEY, mediation_record, skip_if_exists=False, replace_key=None, ) assert keylist_update assert keylist_update.serialize()["updates"] == [ - {"action": "add", "recipient_key": "test-recipient-key"} + {"action": "add", "recipient_key": TEST_VERKEY} ] assert mock_responder.messages assert ( @@ -516,7 +520,7 @@ async def test_mediation_route_for_key_skip_if_exists_and_exists( ): keylist_update = await mediation_route_manager._route_for_key( profile, - "test-recipient-key", + TEST_VERKEY, mediation_record, skip_if_exists=True, replace_key=None, @@ -541,14 +545,14 @@ async def test_mediation_route_for_key_skip_if_exists_and_absent( ): keylist_update = await mediation_route_manager._route_for_key( profile, - "test-recipient-key", + TEST_VERKEY, mediation_record, skip_if_exists=True, replace_key=None, ) assert keylist_update assert keylist_update.serialize()["updates"] == [ - {"action": "add", "recipient_key": "test-recipient-key"} + {"action": "add", "recipient_key": TEST_VERKEY} ] assert mock_responder.messages assert ( @@ -568,15 +572,15 @@ async def test_mediation_route_for_key_replace_key( ) keylist_update = await mediation_route_manager._route_for_key( profile, - "test-recipient-key", + TEST_VERKEY, mediation_record, skip_if_exists=False, - replace_key="test-replace-key", + replace_key=TEST_ROUTE_VERKEY, ) assert keylist_update assert keylist_update.serialize()["updates"] == [ - {"action": "add", "recipient_key": "test-recipient-key"}, - {"action": "remove", "recipient_key": "test-replace-key"}, + {"action": "add", "recipient_key": TEST_VERKEY}, + {"action": "remove", "recipient_key": TEST_ROUTE_VERKEY}, ] assert mock_responder.messages assert ( @@ -593,10 +597,10 @@ async def test_mediation_route_for_key_no_mediator( assert ( await mediation_route_manager._route_for_key( profile, - "test-recipient-key", + TEST_VERKEY, None, skip_if_exists=True, - replace_key="test-replace-key", + replace_key=TEST_ROUTE_VERKEY, ) is None ) From d86f5869dd728c2244c7c597da14bda726429a2a Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Tue, 16 Aug 2022 10:29:24 -0600 Subject: [PATCH 398/872] fix: Normalize recipient keys when updating routes Signed-off-by: Colton Wolkins (Indicio work address) --- .../protocols/coordinate_mediation/v1_0/manager.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py index 72c2f496b8..ec15019ec9 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py @@ -249,9 +249,16 @@ async def update_keylist( RouteUpdate.ACTION_CREATE: KeylistUpdateRule.RULE_ADD, } + def normalize_public_key(key: str): + if key.startswith("did:key:"): + return DIDKey.from_did(key).public_key_b58 + + return key + def rule_to_update(rule: KeylistUpdateRule): + recipient_key = normalize_public_key(rule.recipient_key) return RouteUpdate( - recipient_key=rule.recipient_key, action=action_map[rule.action] + recipient_key=recipient_key, action=action_map[rule.action] ) def updated_to_keylist_updated(updated: RouteUpdated): From 637e7f26313dd745bcd8124b8b72c3f7b4a9830e Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Tue, 16 Aug 2022 09:56:16 -0700 Subject: [PATCH 399/872] Include a TAA check when checking if ledger is read only Signed-off-by: Ian Costanzo --- aries_cloudagent/ledger/base.py | 8 ++- aries_cloudagent/ledger/indy.py | 16 ++++- aries_cloudagent/ledger/indy_vdr.py | 12 ++++ aries_cloudagent/ledger/tests/test_indy.py | 68 +++++++++++++++++-- .../ledger/tests/test_indy_vdr.py | 7 +- 5 files changed, 102 insertions(+), 9 deletions(-) diff --git a/aries_cloudagent/ledger/base.py b/aries_cloudagent/ledger/base.py index bcbb8c54e1..f12b91015e 100644 --- a/aries_cloudagent/ledger/base.py +++ b/aries_cloudagent/ledger/base.py @@ -48,6 +48,10 @@ def backend(self) -> str: def read_only(self) -> bool: """Accessor for the ledger read-only flag.""" + @abstractmethod + async def is_ledger_read_only(self) -> bool: + """Check if ledger is read-only including TAA.""" + @abstractmethod async def get_key_for_did(self, did: str) -> str: """Fetch the verkey for a ledger DID. @@ -266,7 +270,7 @@ async def create_and_send_schema( LOGGER.warning("Schema already exists on ledger. Returning details.") schema_id, schema_def = schema_info else: - if self.read_only: + if await self.is_ledger_read_only(): raise LedgerError( "Error cannot write schema when ledger is in read only mode" ) @@ -461,7 +465,7 @@ async def create_and_send_credential_definition( except IndyIssuerError as err: raise LedgerError(err.message) from err - if self.read_only: + if await self.is_ledger_read_only(): raise LedgerError( "Error cannot write cred def when ledger is in read only mode" ) diff --git a/aries_cloudagent/ledger/indy.py b/aries_cloudagent/ledger/indy.py index 3ac2b19363..ed7ff43a7b 100644 --- a/aries_cloudagent/ledger/indy.py +++ b/aries_cloudagent/ledger/indy.py @@ -294,6 +294,18 @@ def read_only(self) -> bool: """Accessor for the ledger read-only flag.""" return self.pool.read_only + async def is_ledger_read_only(self) -> bool: + """Check if ledger is read-only including TAA.""" + if self.read_only: + return self.read_only + # if TAA is required and not accepted we should be in read-only mode + taa = await self.get_txn_author_agreement() + if taa["taa_required"]: + taa_acceptance = await self.get_latest_txn_author_acceptance() + if "mechanism" not in taa_acceptance: + return True + return self.read_only + async def __aenter__(self) -> "IndySdkLedger": """ Context manager entry. @@ -758,7 +770,7 @@ async def update_endpoint_for_did( ) if exist_endpoint_of_type != endpoint: - if self.pool.read_only: + if await self.is_ledger_read_only(): raise LedgerError( "Error cannot update endpoint when ledger is in read only mode" ) @@ -811,7 +823,7 @@ async def register_nym( alias: Human-friendly alias to assign to the DID. role: For permissioned ledgers, what role should the new DID have. """ - if self.pool.read_only: + if await self.is_ledger_read_only(): raise LedgerError( "Error cannot register nym when ledger is in read only mode" ) diff --git a/aries_cloudagent/ledger/indy_vdr.py b/aries_cloudagent/ledger/indy_vdr.py index 4dc98e75ba..061606a64e 100644 --- a/aries_cloudagent/ledger/indy_vdr.py +++ b/aries_cloudagent/ledger/indy_vdr.py @@ -283,6 +283,18 @@ def read_only(self) -> bool: """Accessor for the ledger read-only flag.""" return self.pool.read_only + async def is_ledger_read_only(self) -> bool: + """Check if ledger is read-only including TAA.""" + if self.read_only: + return self.read_only + # if TAA is required and not accepted we should be in read-only mode + taa = await self.get_txn_author_agreement() + if taa["taa_required"]: + taa_acceptance = await self.get_latest_txn_author_acceptance() + if "mechanism" not in taa_acceptance: + return True + return self.read_only + async def __aenter__(self) -> "IndyVdrLedger": """ Context manager entry. diff --git a/aries_cloudagent/ledger/tests/test_indy.py b/aries_cloudagent/ledger/tests/test_indy.py index 796ea1e887..83dedb9936 100644 --- a/aries_cloudagent/ledger/tests/test_indy.py +++ b/aries_cloudagent/ledger/tests/test_indy.py @@ -537,8 +537,10 @@ async def test_txn_endorse( @async_mock.patch("aries_cloudagent.storage.indy.IndySdkStorage.add_record") @async_mock.patch("indy.ledger.build_schema_request") @async_mock.patch("indy.ledger.append_request_endorser") + @async_mock.patch("aries_cloudagent.ledger.indy.IndySdkLedger.is_ledger_read_only") async def test_send_schema( self, + mock_is_ledger_read_only, mock_append_request_endorser, mock_build_schema_req, mock_add_record, @@ -550,6 +552,7 @@ async def test_send_schema( ): mock_wallet = async_mock.MagicMock() self.session.context.injector.bind_provider(BaseWallet, mock_wallet) + mock_is_ledger_read_only.return_value = False issuer = async_mock.MagicMock(IndyIssuer) ledger = IndySdkLedger(IndySdkLedgerPool("name", checked=True), self.profile) @@ -677,8 +680,10 @@ async def test_send_schema_already_exists( ) @async_mock.patch("aries_cloudagent.storage.indy.IndySdkStorage.add_record") @async_mock.patch("indy.ledger.build_schema_request") + @async_mock.patch("aries_cloudagent.ledger.indy.IndySdkLedger.is_ledger_read_only") async def test_send_schema_ledger_transaction_error_already_exists( self, + mock_is_ledger_read_only, mock_build_schema_req, mock_add_record, mock_check_existing, @@ -690,6 +695,7 @@ async def test_send_schema_ledger_transaction_error_already_exists( mock_wallet = async_mock.MagicMock() self.session.context.injector.bind_provider(BaseWallet, mock_wallet) + mock_is_ledger_read_only.return_value = False issuer = async_mock.MagicMock(IndyIssuer) issuer.create_schema.return_value = ("1", "{}") @@ -764,8 +770,10 @@ async def test_send_schema_ledger_read_only( @async_mock.patch( "aries_cloudagent.ledger.indy.IndySdkLedger.check_existing_schema" ) + @async_mock.patch("aries_cloudagent.ledger.indy.IndySdkLedger.is_ledger_read_only") async def test_send_schema_issuer_error( self, + mock_is_ledger_read_only, mock_check_existing, mock_close_pool, mock_open_ledger, @@ -775,6 +783,7 @@ async def test_send_schema_issuer_error( mock_wallet = async_mock.MagicMock() self.session.context.injector.bind_provider(BaseWallet, mock_wallet) + mock_is_ledger_read_only.return_value = False issuer = async_mock.MagicMock(IndyIssuer) issuer.create_schema = async_mock.CoroutineMock( @@ -850,8 +859,10 @@ async def test_send_schema_ledger_transaction_error( ) @async_mock.patch("aries_cloudagent.storage.indy.IndySdkStorage.add_record") @async_mock.patch("indy.ledger.build_schema_request") + @async_mock.patch("aries_cloudagent.ledger.indy.IndySdkLedger.is_ledger_read_only") async def test_send_schema_no_seq_no( self, + mock_is_ledger_read_only, mock_build_schema_req, mock_add_record, mock_fetch_schema_by_seq_no, @@ -863,6 +874,7 @@ async def test_send_schema_no_seq_no( mock_wallet = async_mock.MagicMock() self.session.context.injector.bind_provider(BaseWallet, mock_wallet) issuer = async_mock.MagicMock(IndyIssuer) + mock_is_ledger_read_only.return_value = False ledger = IndySdkLedger(IndySdkLedgerPool("name", checked=True), self.profile) issuer.create_schema.return_value = ("schema_issuer_did:name:1.0", "{}") mock_fetch_schema_by_id.return_value = None @@ -1143,8 +1155,10 @@ async def test_get_schema_by_wrong_seq_no( @async_mock.patch("aries_cloudagent.storage.indy.IndySdkStorage.find_all_records") @async_mock.patch("aries_cloudagent.storage.indy.IndySdkStorage.add_record") @async_mock.patch("indy.ledger.build_cred_def_request") + @async_mock.patch("aries_cloudagent.ledger.indy.IndySdkLedger.is_ledger_read_only") async def test_send_credential_definition( self, + mock_is_ledger_read_only, mock_build_cred_def, mock_add_record, mock_find_all_records, @@ -1157,6 +1171,7 @@ async def test_send_credential_definition( mock_wallet = async_mock.MagicMock() self.session.context.injector.bind_provider(BaseWallet, mock_wallet) mock_find_all_records.return_value = [] + mock_is_ledger_read_only.return_value = False mock_get_schema.return_value = {"seqNo": 999} cred_def_id = f"{self.test_did}:3:CL:999:default" @@ -1231,8 +1246,10 @@ async def test_send_credential_definition( @async_mock.patch("aries_cloudagent.storage.indy.IndySdkStorage.add_record") @async_mock.patch("indy.ledger.build_cred_def_request") @async_mock.patch("indy.ledger.append_request_endorser") + @async_mock.patch("aries_cloudagent.ledger.indy.IndySdkLedger.is_ledger_read_only") async def test_send_credential_definition_endorse_only( self, + mock_is_ledger_read_only, mock_append_request_endorser, mock_build_cred_def, mock_add_record, @@ -1246,6 +1263,7 @@ async def test_send_credential_definition_endorse_only( mock_wallet = async_mock.MagicMock() self.session.context.injector.bind_provider(BaseWallet, mock_wallet) mock_find_all_records.return_value = [] + mock_is_ledger_read_only.return_value = False mock_get_schema.return_value = {"seqNo": 999} cred_def_id = f"{self.test_did}:3:CL:999:default" @@ -2229,8 +2247,10 @@ async def test_get_endpoint_for_did_no_endpoint( @async_mock.patch("indy.ledger.build_get_attrib_request") @async_mock.patch("indy.ledger.build_attrib_request") @async_mock.patch("aries_cloudagent.ledger.indy.IndySdkLedger._submit") + @async_mock.patch("aries_cloudagent.ledger.indy.IndySdkLedger.is_ledger_read_only") async def test_update_endpoint_for_did( self, + mock_is_ledger_read_only, mock_submit, mock_build_attrib_req, mock_build_get_attrib_req, @@ -2240,6 +2260,7 @@ async def test_update_endpoint_for_did( mock_wallet = async_mock.MagicMock() self.session.context.injector.bind_provider(BaseWallet, mock_wallet) endpoint = ["http://old.aries.ca", "http://new.aries.ca"] + mock_is_ledger_read_only.return_value = False mock_submit.side_effect = [ json.dumps( { @@ -2283,8 +2304,10 @@ async def test_update_endpoint_for_did( @async_mock.patch("indy.ledger.build_get_attrib_request") @async_mock.patch("indy.ledger.build_attrib_request") @async_mock.patch("aries_cloudagent.ledger.indy.IndySdkLedger._submit") + @async_mock.patch("aries_cloudagent.ledger.indy.IndySdkLedger.is_ledger_read_only") async def test_update_endpoint_for_did_no_prior_endpoints( self, + mock_is_ledger_read_only, mock_submit, mock_build_attrib_req, mock_build_get_attrib_req, @@ -2294,6 +2317,7 @@ async def test_update_endpoint_for_did_no_prior_endpoints( mock_wallet = async_mock.MagicMock() self.session.context.injector.bind_provider(BaseWallet, mock_wallet) endpoint = "http://new.aries.ca" + mock_is_ledger_read_only.return_value = False ledger = IndySdkLedger(IndySdkLedgerPool("name", checked=True), self.profile) with async_mock.patch.object( IndySdkWallet, "get_public_did" @@ -2329,8 +2353,10 @@ async def test_update_endpoint_for_did_no_prior_endpoints( @async_mock.patch("indy.ledger.build_get_attrib_request") @async_mock.patch("indy.ledger.build_attrib_request") @async_mock.patch("aries_cloudagent.ledger.indy.IndySdkLedger._submit") + @async_mock.patch("aries_cloudagent.ledger.indy.IndySdkLedger.is_ledger_read_only") async def test_update_endpoint_of_type_profile_for_did( self, + mock_is_ledger_read_only, mock_submit, mock_build_attrib_req, mock_build_get_attrib_req, @@ -2341,6 +2367,7 @@ async def test_update_endpoint_of_type_profile_for_did( self.session.context.injector.bind_provider(BaseWallet, mock_wallet) endpoint = ["http://company.com/oldProfile", "http://company.com/newProfile"] endpoint_type = EndpointType.PROFILE + mock_is_ledger_read_only.return_value = False mock_submit.side_effect = [ json.dumps( { @@ -2354,6 +2381,11 @@ async def test_update_endpoint_of_type_profile_for_did( for i in range(len(endpoint)) ] ledger = IndySdkLedger(IndySdkLedgerPool("name", checked=True), self.profile) + #ledger = async_mock.patch.object( + # ledger, + # "is_ledger_read_only", + # async_mock.CoroutineMock(return_value=False), + #) with async_mock.patch.object( IndySdkWallet, "get_public_did" ) as mock_wallet_get_public_did: @@ -2446,11 +2478,18 @@ async def test_update_endpoint_for_did_read_only( @async_mock.patch("aries_cloudagent.ledger.indy.IndySdkLedgerPool.context_close") @async_mock.patch("indy.ledger.build_nym_request") @async_mock.patch("aries_cloudagent.ledger.indy.IndySdkLedger._submit") + @async_mock.patch("aries_cloudagent.ledger.indy.IndySdkLedger.is_ledger_read_only") async def test_register_nym( - self, mock_submit, mock_build_nym_req, mock_close, mock_open + self, + mock_is_ledger_read_only, + mock_submit, + mock_build_nym_req, + mock_close, + mock_open, ): mock_wallet = async_mock.MagicMock() self.session.context.injector.bind_provider(BaseWallet, mock_wallet) + mock_is_ledger_read_only.return_value = False with async_mock.patch.object( IndySdkWallet, "get_public_did" ) as mock_wallet_get_public_did, async_mock.patch.object( @@ -2518,12 +2557,19 @@ async def test_register_nym_read_only(self, mock_close, mock_open): @async_mock.patch("aries_cloudagent.ledger.indy.IndySdkLedgerPool.context_open") @async_mock.patch("aries_cloudagent.ledger.indy.IndySdkLedgerPool.context_close") - async def test_register_nym_no_public_did(self, mock_close, mock_open): + @async_mock.patch("aries_cloudagent.ledger.indy.IndySdkLedger.is_ledger_read_only") + async def test_register_nym_no_public_did( + self, + mock_is_ledger_read_only, + mock_close, + mock_open, + ): mock_wallet = async_mock.MagicMock( type="indy", get_local_did=async_mock.CoroutineMock(), replace_local_did_metadata=async_mock.CoroutineMock(), ) + mock_is_ledger_read_only.return_value = False self.session.context.injector.bind_provider(BaseWallet, mock_wallet) ledger = IndySdkLedger(IndySdkLedgerPool("name", checked=True), self.profile) with async_mock.patch.object( @@ -2543,14 +2589,21 @@ async def test_register_nym_no_public_did(self, mock_close, mock_open): @async_mock.patch("aries_cloudagent.ledger.indy.IndySdkLedgerPool.context_close") @async_mock.patch("indy.ledger.build_nym_request") @async_mock.patch("aries_cloudagent.ledger.indy.IndySdkLedger._submit") + @async_mock.patch("aries_cloudagent.ledger.indy.IndySdkLedger.is_ledger_read_only") async def test_register_nym_ledger_x( - self, mock_submit, mock_build_nym_req, mock_close, mock_open + self, + mock_is_ledger_read_only, + mock_submit, + mock_build_nym_req, + mock_close, + mock_open, ): mock_wallet = async_mock.MagicMock() mock_build_nym_req.side_effect = IndyError( error_code=ErrorCode.CommonInvalidParam1, error_details={"message": "not today"}, ) + mock_is_ledger_read_only.return_value = False self.session.context.injector.bind_provider(BaseWallet, mock_wallet) ledger = IndySdkLedger(IndySdkLedgerPool("name", checked=True), self.profile) with async_mock.patch.object( @@ -2570,11 +2623,18 @@ async def test_register_nym_ledger_x( @async_mock.patch("aries_cloudagent.ledger.indy.IndySdkLedgerPool.context_close") @async_mock.patch("indy.ledger.build_nym_request") @async_mock.patch("aries_cloudagent.ledger.indy.IndySdkLedger._submit") + @async_mock.patch("aries_cloudagent.ledger.indy.IndySdkLedger.is_ledger_read_only") async def test_register_nym_steward_register_others_did( - self, mock_submit, mock_build_nym_req, mock_close, mock_open + self, + mock_is_ledger_read_only, + mock_submit, + mock_build_nym_req, + mock_close, + mock_open, ): mock_wallet = async_mock.MagicMock() self.session.context.injector.bind_provider(BaseWallet, mock_wallet) + mock_is_ledger_read_only.return_value = False ledger = IndySdkLedger(IndySdkLedgerPool("name", checked=True), self.profile) with async_mock.patch.object( IndySdkWallet, "get_public_did" diff --git a/aries_cloudagent/ledger/tests/test_indy_vdr.py b/aries_cloudagent/ledger/tests/test_indy_vdr.py index f6ef98962a..04df80fb35 100644 --- a/aries_cloudagent/ledger/tests/test_indy_vdr.py +++ b/aries_cloudagent/ledger/tests/test_indy_vdr.py @@ -37,7 +37,8 @@ async def close(): ledger.pool.handle = None with async_mock.patch.object(ledger.pool, "open", open), async_mock.patch.object( - ledger.pool, "close", close + ledger.pool, "close", close), async_mock.patch.object( + ledger, "is_ledger_read_only", async_mock.CoroutineMock(return_value=False) ): yield ledger @@ -302,6 +303,10 @@ async def test_send_schema_ledger_read_only( ledger, "check_existing_schema", async_mock.CoroutineMock(return_value=False), + ), async_mock.patch.object( + ledger, + "is_ledger_read_only", + async_mock.CoroutineMock(return_value=True), ): with pytest.raises(LedgerError): schema_id, schema_def = await ledger.create_and_send_schema( From 978e35bc705a02b43d621d27a93f35357243e834 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Tue, 16 Aug 2022 12:57:00 -0400 Subject: [PATCH 400/872] test: add gha for testing with in repo dockerfiles Signed-off-by: Daniel Bluhm --- .dockerignore | 4 +- .github/workflows/nightly.yml | 84 +++++++++++++ .github/workflows/tests-indy.yml | 198 +++++++++++++++++++++++++++++++ .github/workflows/tests.yml | 38 ++++++ docker/Dockerfile | 16 ++- docker/Dockerfile.test | 9 +- docker/Dockerfile.test-indy | 7 +- 7 files changed, 346 insertions(+), 10 deletions(-) create mode 100644 .github/workflows/nightly.yml create mode 100644 .github/workflows/tests-indy.yml create mode 100644 .github/workflows/tests.yml diff --git a/.dockerignore b/.dockerignore index 910edaa6ff..7ea06888de 100644 --- a/.dockerignore +++ b/.dockerignore @@ -6,4 +6,6 @@ build docs dist test-reports -.python-version \ No newline at end of file +.python-version +docker +env diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml new file mode 100644 index 0000000000..4a02ae3f1e --- /dev/null +++ b/.github/workflows/nightly.yml @@ -0,0 +1,84 @@ +name: Nightly Build +on: + schedule: + - cron: '0 0 * * *' + workflow_dispatch: + +env: + NAME: aries-cloudagent-python + PYTHON_VERSION: 3.7 + +jobs: + nightly: + name: Nightly + runs-on: ubuntu-latest + steps: + - name: Gather image info + id: info + run: | + echo "::set-output name=repo-owner::${GITHUB_REPOSITORY_OWNER,,}" + + - name: Check image exists + id: image-exists + uses: dbluhm/image-tag-exists@257851f02e3473a75719e26b5a566ea5457da4ef + with: + tag: ghcr.io/${{ steps.info.outputs.repo-owner }}/${{ env.NAME }}:py${{ env.PYTHON_VERSION }}-nightly-${{ github.sha }} + token: ${{ secrets.GITHUB_TOKEN }} + + - uses: actions/checkout@v3 + if: steps.image-exists.outputs.exists != 'true' + + - name: Cache Docker layers + if: steps.image-exists.outputs.exists != 'true' + uses: actions/cache@v3 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx- + + - name: Set up Docker Buildx + if: steps.image-exists.outputs.exists != 'true' + uses: docker/setup-buildx-action@v1 + + - name: Log in to the GitHub Container Registry + if: steps.image-exists.outputs.exists != 'true' + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Setup Base Image Metadata + if: steps.image-exists.outputs.exists != 'true' + id: base-meta + uses: docker/metadata-action@v3 + with: + images: | + ghcr.io/${{ steps.info.outputs.repo-owner }}/${{ env.NAME }} + tags: | + type=raw,value=py${{ env.PYTHON_VERSION }}-nightly + type=sha,format=long,prefix=py${{ env.PYTHON_VERSION }}-nightly- + + - name: Build and Push Base Image to ghcr.io + if: steps.image-exists.outputs.exists != 'true' + uses: docker/build-push-action@v3 + with: + push: true + context: . + file: docker/Dockerfile + tags: ${{ steps.base-meta.outputs.tags }} + labels: ${{ steps.base-meta.outputs.labels }} + build-args: | + python_version=${{ env.PYTHON_VERSION }} + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max + + # Temp fix + # https://github.com/docker/build-push-action/issues/252 + # https://github.com/moby/buildkit/issues/1896 + - name: Move cache + if: steps.image-exists.outputs.exists != 'true' + run: | + rm -rf /tmp/.buildx-cache + mv /tmp/.buildx-cache-new /tmp/.buildx-cache-base diff --git a/.github/workflows/tests-indy.yml b/.github/workflows/tests-indy.yml new file mode 100644 index 0000000000..86d5c38601 --- /dev/null +++ b/.github/workflows/tests-indy.yml @@ -0,0 +1,198 @@ +name: Tests (Indy) +on: + pull_request: + +env: + INDY_VERSION: 1.16.0 + +jobs: + info: + name: Gather image info + runs-on: ubuntu-latest + outputs: + repo-owner: ${{ steps.info.outputs.owner-lc }} + indy-version: ${{ steps.info.outputs.indy-version }} + indy-sdk-url: ${{ steps.info.outputs.indy-sdk-url }} + base-dep-hash: ${{ steps.info.outputs.base-hash }} + test-dep-hash: ${{ steps.info.outputs.test-hash }} + steps: + - uses: actions/checkout@v3 + - name: Gather image info + id: info + run: | + echo "::set-output name=owner-lc::${GITHUB_REPOSITORY_OWNER,,}" + echo "::set-output name=indy-version::${{env.INDY_VERSION}}" + echo "::set-output name=indy-sdk-url::https://codeload.github.com/hyperledger/indy-sdk/tar.gz/refs/tags/v${{ env.INDY_VERSION }}" + echo "::set-output name=base-hash::${{ hashFiles('docker/Dockerfile.indy-base') }}" + echo "::set-output name=test-hash::${{ hashFiles('requirements*.txt', 'docker/Dockerfile.test-indy') }}" + + base-image: + name: Publish base image + needs: info + runs-on: ubuntu-latest + strategy: + fail-fast: true + matrix: + python-version: ["3.7", "3.8", "3.9", "3.10"] + + steps: + - name: Check image exists + id: image-exists + uses: dbluhm/image-tag-exists@257851f02e3473a75719e26b5a566ea5457da4ef + with: + tag: ghcr.io/${{ needs.info.outputs.repo-owner }}/indy-python-test:py${{ matrix.python-version }}-${{ env.INDY_VERSION }}-${{ needs.info.outputs.base-dep-hash }} + token: ${{ secrets.GITHUB_TOKEN }} + + - uses: actions/checkout@v3 + if: steps.image-exists.outputs.exists != 'true' + + - name: Cache Docker layers + if: steps.image-exists.outputs.exists != 'true' + uses: actions/cache@v3 + with: + path: /tmp/.buildx-cache-base + key: ${{ runner.os }}-buildx-base-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx-base- + + - name: Set up Docker Buildx + if: steps.image-exists.outputs.exists != 'true' + uses: docker/setup-buildx-action@v1 + + - name: Log in to the GitHub Container Registry + if: steps.image-exists.outputs.exists != 'true' + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Setup Base Image Metadata + if: steps.image-exists.outputs.exists != 'true' + id: base-meta + uses: docker/metadata-action@v3 + with: + images: | + ghcr.io/${{ needs.info.outputs.repo-owner }}/indy-python-test + tags: | + type=raw,value=py${{ matrix.python-version }}-${{ env.INDY_VERSION }}-${{ needs.info.outputs.base-dep-hash }} + + - name: Build and Push Base Image to ghcr.io + if: steps.image-exists.outputs.exists != 'true' + uses: docker/build-push-action@v3 + with: + push: true + context: . + file: docker/Dockerfile.indy-base + tags: ${{ steps.base-meta.outputs.tags }} + labels: ${{ steps.base-meta.outputs.labels }} + build-args: | + python_version=${{ matrix.python-version }} + indy_version=${{ needs.info.outputs.indy_version }} + indy_sdk_url=${{ needs.info.outputs.indy-sdk-url }} + cache-from: type=local,src=/tmp/.buildx-cache-base + cache-to: type=local,dest=/tmp/.buildx-cache-base-new,mode=max + + # Temp fix + # https://github.com/docker/build-push-action/issues/252 + # https://github.com/moby/buildkit/issues/1896 + - name: Move cache + if: steps.image-exists.outputs.exists != 'true' + run: | + rm -rf /tmp/.buildx-cache-base + mv /tmp/.buildx-cache-base-new /tmp/.buildx-cache-base + + + test-image: + name: Publish test image + needs: ["info", "base-image"] + runs-on: ubuntu-latest + strategy: + fail-fast: true + matrix: + python-version: ["3.7", "3.8", "3.9", "3.10"] + + steps: + + - name: Check image exists + id: image-exists + uses: dbluhm/image-tag-exists@257851f02e3473a75719e26b5a566ea5457da4ef + with: + tag: ghcr.io/${{ needs.info.outputs.repo-owner }}/acapy-test:py${{ matrix.python-version }}-${{ needs.info.outputs.indy-version }}-${{ needs.info.outputs.test-dep-hash }} + token: ${{ secrets.GITHUB_TOKEN }} + + - uses: actions/checkout@v3 + if: steps.image-exists.outputs.exists != 'true' + + - name: Cache Docker layers + if: steps.image-exists.outputs.exists != 'true' + uses: actions/cache@v3 + with: + path: /tmp/.buildx-cache-test + key: ${{ runner.os }}-buildx-test-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx-test- + + - name: Set up Docker Buildx + if: steps.image-exists.outputs.exists != 'true' + uses: docker/setup-buildx-action@v1 + + - name: Log in to the GitHub Container Registry + if: steps.image-exists.outputs.exists != 'true' + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Setup Test Image Metadata + if: steps.image-exists.outputs.exists != 'true' + id: test-meta + uses: docker/metadata-action@v3 + with: + images: | + ghcr.io/${{ needs.info.outputs.repo-owner }}/acapy-test + tags: | + type=raw,value=py${{ matrix.python-version }}-${{ env.INDY_VERSION }}-${{ needs.info.outputs.test-dep-hash }} + + - name: Build and Push Test Image to ghcr.io + if: steps.image-exists.outputs.exists != 'true' + uses: docker/build-push-action@v3 + with: + push: true + context: . + file: docker/Dockerfile.test-indy + tags: ${{ steps.test-meta.outputs.tags }} + labels: ${{ steps.test-meta.outputs.labels }} + build-args: | + base_image=ghcr.io/${{ needs.info.outputs.repo-owner }}/indy-python-test:py${{ matrix.python-version }}-${{ needs.info.outputs.indy-version }}-${{ needs.info.outputs.base-dep-hash }} + cache-from: type=local,src=/tmp/.buildx-cache-test + cache-to: type=local,dest=/tmp/.buildx-cache-test-new,mode=max + + # Temp fix + # https://github.com/docker/build-push-action/issues/252 + # https://github.com/moby/buildkit/issues/1896 + - name: Move cache + if: steps.image-exists.outputs.exists != 'true' + run: | + rm -rf /tmp/.buildx-cache-test + mv /tmp/.buildx-cache-test-new /tmp/.buildx-cache-test + + tests: + name: Tests (Indy) + needs: + - info + - base-image + - test-image + runs-on: ubuntu-latest + container: ghcr.io/${{ needs.info.outputs.repo-owner }}/acapy-test:py${{ matrix.python-version }}-${{ needs.info.outputs.indy-version }}-${{ needs.info.outputs.test-dep-hash }} + strategy: + fail-fast: false + matrix: + python-version: ["3.7", "3.8", "3.9", "3.10"] + + steps: + - uses: actions/checkout@v3 + - name: Run pytest + run: | + pytest diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000000..6205a865fc --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,38 @@ +name: Tests + +on: + pull_request: + +jobs: + tests: + name: Tests + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.7", "3.8", "3.9", "3.10"] + + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + cache: 'pip' + cache-dependency-path: 'requirements*.txt' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip3 install --no-cache-dir \ + -r requirements.txt \ + -r requirements.askar.txt \ + -r requirements.bbs.txt \ + -r requirements.dev.txt + - name: Tests + run: | + pytest --junitxml=pytest.xml + - name: Test Report + uses: mikepenz/action-junit-report@v3 + if: always() + with: + report_paths: pytest.xml diff --git a/docker/Dockerfile b/docker/Dockerfile index 3fc1899b04..0a40fbbe40 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,11 +1,19 @@ ARG python_version=3.6.13 -FROM python:${python_version}-slim-buster +FROM python:${python_version}-slim-buster AS build + +WORKDIR /src + +ADD . . + +RUN pip install setuptools wheel +RUN python setup.py sdist bdist_wheel + +FROM python:${python_version}-slim-buster AS main ARG uid=1001 ARG user=aries ARG acapy_version ARG acapy_reqs -ARG git_egg_ref ENV HOME="/home/$user" \ APP_ROOT="$HOME" \ @@ -76,7 +84,9 @@ RUN mkdir -p \ # Also ensure the permissions on the python 'site-packages' folder are set correctly. RUN chmod -R ug+rw $HOME/log $HOME/ledger $HOME/.aries_cloudagent $HOME/.cache -RUN pip install --no-cache-dir ${git_egg_ref}aries-cloudagent${acapy_reqs}==${acapy_version} +COPY --from=build /src/dist/aries_cloudagent*.whl . + +RUN pip install --no-cache-dir --find-links=. aries_cloudagent${acapy_reqs} && rm aries_cloudagent*.whl USER $user diff --git a/docker/Dockerfile.test b/docker/Dockerfile.test index e949e9274f..6a1b4df76a 100644 --- a/docker/Dockerfile.test +++ b/docker/Dockerfile.test @@ -1,11 +1,10 @@ -FROM python:3.6.13 +ARG python_version=3.6.13 +FROM python:${python_version}-slim-buster RUN apt-get update -y && \ apt-get install -y --no-install-recommends \ - python3 \ - python3-pip \ - python3-setuptools \ libsodium23 && \ + apt-get clean && \ rm -rf /var/lib/apt/lists/* WORKDIR /usr/src/app @@ -20,4 +19,4 @@ RUN pip3 install --no-cache-dir \ ADD . . -ENTRYPOINT ["/bin/bash", "-c", "pytest \"$@\"", "--"] \ No newline at end of file +ENTRYPOINT ["/bin/bash", "-c", "pytest \"$@\"", "--"] diff --git a/docker/Dockerfile.test-indy b/docker/Dockerfile.test-indy index 1acafb7d56..6b38d885e6 100644 --- a/docker/Dockerfile.test-indy +++ b/docker/Dockerfile.test-indy @@ -1,4 +1,7 @@ -FROM ghcr.io/hyperledger/indy-python:py3.6-1.16.0 +ARG python_version=3.6.13 +ARG indy_version=1.16.0 +ARG base_image=ghcr.io/hyperledger/indy-python:py${python_version}-${indy_version} +FROM ${base_image} USER indy @@ -10,6 +13,7 @@ RUN mkdir -p test-reports && chown -R indy:indy test-reports && chmod -R ug+rw t ADD requirements*.txt ./ +USER root RUN pip3 install --no-cache-dir \ -r requirements.txt \ -r requirements.askar.txt \ @@ -17,5 +21,6 @@ RUN pip3 install --no-cache-dir \ -r requirements.dev.txt ADD --chown=indy:root . . +USER indy ENTRYPOINT ["/bin/bash", "-c", "pytest \"$@\"", "--"] From 6c641fedc57f0901fa4facdc8f74f1de5c4217df Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Mon, 15 Aug 2022 17:53:18 -0400 Subject: [PATCH 401/872] feat: add nightly workflow for indy aca-py Signed-off-by: Daniel Bluhm --- .github/workflows/nightly-indy.yml | 86 ++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 .github/workflows/nightly-indy.yml diff --git a/.github/workflows/nightly-indy.yml b/.github/workflows/nightly-indy.yml new file mode 100644 index 0000000000..82288898b0 --- /dev/null +++ b/.github/workflows/nightly-indy.yml @@ -0,0 +1,86 @@ +name: Nightly Build (Indy) +on: + schedule: + - cron: '0 0 * * *' + workflow_dispatch: + +env: + NAME: aries-cloudagent-python + PYTHON_VERSION: 3.7 + INDY_VERSION: 1.16.0 + +jobs: + nightly: + name: Nightly (Indy) + runs-on: ubuntu-latest + steps: + - name: Gather image info + id: info + run: | + echo "::set-output name=repo-owner::${GITHUB_REPOSITORY_OWNER,,}" + + - name: Check image exists + id: image-exists + uses: dbluhm/image-tag-exists@257851f02e3473a75719e26b5a566ea5457da4ef + with: + tag: ghcr.io/${{ steps.info.outputs.repo-owner }}/${{ env.NAME }}:py${{ env.PYTHON_VERSION }}-indy-${{ env.INDY_VERSION }}-nightly-${{ github.sha }} + token: ${{ secrets.GITHUB_TOKEN }} + + - uses: actions/checkout@v3 + if: steps.image-exists.outputs.exists != 'true' + + - name: Cache Docker layers + if: steps.image-exists.outputs.exists != 'true' + uses: actions/cache@v3 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx- + + - name: Set up Docker Buildx + if: steps.image-exists.outputs.exists != 'true' + uses: docker/setup-buildx-action@v1 + + - name: Log in to the GitHub Container Registry + if: steps.image-exists.outputs.exists != 'true' + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Setup Base Image Metadata + if: steps.image-exists.outputs.exists != 'true' + id: base-meta + uses: docker/metadata-action@v3 + with: + images: | + ghcr.io/${{ steps.info.outputs.repo-owner }}/${{ env.NAME }} + tags: | + type=raw,value=py${{ env.PYTHON_VERSION }}-indy-${{ env.INDY_VERSION }}-nightly + type=sha,format=long,prefix=py${{ env.PYTHON_VERSION }}-indy-${{ env.INDY_VERSION }}-nightly- + + - name: Build and Push Base Image to ghcr.io + if: steps.image-exists.outputs.exists != 'true' + uses: docker/build-push-action@v3 + with: + push: true + context: . + file: docker/Dockerfile.indy + tags: ${{ steps.base-meta.outputs.tags }} + labels: ${{ steps.base-meta.outputs.labels }} + build-args: | + python_version=${{ env.PYTHON_VERSION }} + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max + + # Temp fix + # https://github.com/docker/build-push-action/issues/252 + # https://github.com/moby/buildkit/issues/1896 + - name: Move cache + if: steps.image-exists.outputs.exists != 'true' + run: | + rm -rf /tmp/.buildx-cache + mv /tmp/.buildx-cache-new /tmp/.buildx-cache-base + From 2fe605add368a5e80aad238195d73d53daec9855 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Mon, 15 Aug 2022 17:53:43 -0400 Subject: [PATCH 402/872] feat: add publish indy-python workflow Signed-off-by: Daniel Bluhm --- .github/workflows/publish-indy-python.yml | 101 ++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 .github/workflows/publish-indy-python.yml diff --git a/.github/workflows/publish-indy-python.yml b/.github/workflows/publish-indy-python.yml new file mode 100644 index 0000000000..520eacbf9f --- /dev/null +++ b/.github/workflows/publish-indy-python.yml @@ -0,0 +1,101 @@ +name: Publish Indy Python +on: + workflow_dispatch: + inputs: + indy_sdk_url: + description: 'Indy SDK download URL' + required: false + type: string + indy_postgres_url: + description: 'Indy postgres download URL' + required: false + type: string + indy_version: + description: 'Indy SDK Version' + required: false + type: string + default: '1.16.0' + tag: + description: 'Image tag' + required: false + type: string + +env: + INDY_SDK_TAG_URL: "https://codeload.github.com/hyperledger/indy-sdk/tar.gz/refs/tags/" + +jobs: + publish-image: + strategy: + fail-fast: false + matrix: + python-version: ['3.7', '3.8', '3.9', '3.10'] + + name: Publish Indy Python + runs-on: ubuntu-latest + steps: + - name: Gather image info + id: info + run: | + echo "::set-output name=repo-owner::${GITHUB_REPOSITORY_OWNER,,}" + + [ -n "${{ inputs.indy_sdk_url }}"] && echo "::set-output name=indy-sdk-url::${{ inputs.indy_sdk_url }}" + [ -z "${{ inputs.indy_sdk_url }}"] && echo "::set-output name=indy-sdk-url::${{ env.INDY_SDK_TAG_URL }}v${{ env.INDY_VERSION }}" + + [ -n "${{ inputs.indy_postgres_url }}"] && echo "::set-output name=indy-postgres-url::${{ inputs.indy_postgres_url }}" + [ -z "${{ inputs.indy_postgres_url }}"] && echo "::set-output name=indy-postgres-url::${{ env.INDY_SDK_TAG_URL }}v${{ env.INDY_VERSION }}" + + [ -n "${{ inputs.tag }}" ] && echo "::set-output name=tag::${{ inputs.tag }}" + [ -z "${{ inputs.tag }}" ] && echo "::set-output name=tag::py${{ matrix.python-version }}-${{ inputs.indy_version }}" + + - uses: actions/checkout@v3 + + - name: Cache Docker layers + uses: actions/cache@v3 + with: + path: /tmp/.buildx-cache-base + key: ${{ runner.os }}-buildx-base-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx-base- + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + + - name: Log in to the GitHub Container Registry + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Setup Base Image Metadata + id: base-meta + uses: docker/metadata-action@v3 + with: + images: | + ghcr.io/${{ steps.info.outputs.repo-owner }}/indy-python + tags: | + type=raw,value=${{ steps.info.outputs.tag }} + + - name: Build and Push Base Image to ghcr.io + uses: docker/build-push-action@v3 + with: + push: true + context: . + file: docker/Dockerfile.indy-base + tags: ${{ steps.base-meta.outputs.tags }} + labels: ${{ steps.base-meta.outputs.labels }} + build-args: | + python_version=${{ matrix.python-version }} + indy_version=${{ inputs.indy_version }} + indy_sdk_url=${{ steps.info.outputs.indy-sdk-url }} + indy_postgres_url=${{ steps.info.outputs.indy-postgres-url }} + cache-from: type=local,src=/tmp/.buildx-cache-base + cache-to: type=local,dest=/tmp/.buildx-cache-base-new,mode=max + + # Temp fix + # https://github.com/docker/build-push-action/issues/252 + # https://github.com/moby/buildkit/issues/1896 + - name: Move cache + run: | + rm -rf /tmp/.buildx-cache-base + mv /tmp/.buildx-cache-base-new /tmp/.buildx-cache-base From 9603484d02c425b16f663a111211839740538708 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Mon, 15 Aug 2022 17:54:03 -0400 Subject: [PATCH 403/872] feat: add publish workflow Signed-off-by: Daniel Bluhm --- .github/workflows/publish.yml | 86 +++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 .github/workflows/publish.yml diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000000..8f1e2197ee --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,86 @@ +name: Publish Aries Cloud Agent - Python +on: + workflow_dispatch: + inputs: + tag: + description: 'Image tag' + required: false + type: string + +env: + ACAPY_REQS: '[askar,bbs]' + + +jobs: + publish-image: + strategy: + fail-fast: false + matrix: + python-version: ['3.7', '3.8', '3.9', '3.10'] + + name: Publish Aries Cloud Agent - Python + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Gather image info + id: info + run: | + echo "::set-output name=repo-owner::${GITHUB_REPOSITORY_OWNER,,}" + echo "::set-output name=acapy-version::$(sed -ne 's/__version__ = \"\([0-9.]\+\)\"/\1/p' aries_cloudagent/version.py)" + + - name: Tag image + id: tag + run: | + [ -n "${{ inputs.tag }}" ] && echo "::set-output name=tag::${{ inputs.tag }}" + [ -z "${{ inputs.tag }}" ] && echo "::set-output name=tag::py${{ matrix.python-version }}-${{ steps.info.outputs.acapy-version }}" + + - name: Cache Docker layers + uses: actions/cache@v3 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-base-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx-base- + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + + - name: Log in to the GitHub Container Registry + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Setup Base Image Metadata + id: base-meta + uses: docker/metadata-action@v3 + with: + images: | + ghcr.io/${{ steps.info.outputs.repo-owner }}/aries-cloudagent-python + tags: | + type=raw,value=${{ steps.tag.outputs.tag }} + + - name: Build and Push Base Image to ghcr.io + uses: docker/build-push-action@v3 + with: + push: true + context: . + file: docker/Dockerfile + tags: ${{ steps.base-meta.outputs.tags }} + labels: ${{ steps.base-meta.outputs.labels }} + build-args: | + python_version=${{ matrix.python-version }} + acapy_version=${{ steps.info.outputs.acapy-version }} + acapy_reqs=${{ env.ACAPY_REQS }} + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max + + # Temp fix + # https://github.com/docker/build-push-action/issues/252 + # https://github.com/moby/buildkit/issues/1896 + - name: Move cache + run: | + rm -rf /tmp/.buildx-cache + mv /tmp/.buildx-cache-new /tmp/.buildx-cache From 4c9795fe4c67c3250d453cc54170757a45baf664 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Tue, 16 Aug 2022 10:37:13 -0400 Subject: [PATCH 404/872] feat: add actions for publishing images Signed-off-by: Daniel Bluhm fix: url construction for indy sdk download Signed-off-by: Daniel Bluhm fix: bad labels on steps Signed-off-by: Daniel Bluhm --- .github/workflows/publish-indy-python.yml | 8 +- .github/workflows/publish-indy.yml | 93 +++++++++++++++++++++++ .github/workflows/publish.yml | 8 +- docker/Dockerfile.indy | 19 ++++- 4 files changed, 116 insertions(+), 12 deletions(-) create mode 100644 .github/workflows/publish-indy.yml diff --git a/.github/workflows/publish-indy-python.yml b/.github/workflows/publish-indy-python.yml index 520eacbf9f..f2b0bea15b 100644 --- a/.github/workflows/publish-indy-python.yml +++ b/.github/workflows/publish-indy-python.yml @@ -39,10 +39,10 @@ jobs: echo "::set-output name=repo-owner::${GITHUB_REPOSITORY_OWNER,,}" [ -n "${{ inputs.indy_sdk_url }}"] && echo "::set-output name=indy-sdk-url::${{ inputs.indy_sdk_url }}" - [ -z "${{ inputs.indy_sdk_url }}"] && echo "::set-output name=indy-sdk-url::${{ env.INDY_SDK_TAG_URL }}v${{ env.INDY_VERSION }}" + [ -z "${{ inputs.indy_sdk_url }}"] && echo "::set-output name=indy-sdk-url::${{ env.INDY_SDK_TAG_URL }}v${{ inputs.indy_version }}" [ -n "${{ inputs.indy_postgres_url }}"] && echo "::set-output name=indy-postgres-url::${{ inputs.indy_postgres_url }}" - [ -z "${{ inputs.indy_postgres_url }}"] && echo "::set-output name=indy-postgres-url::${{ env.INDY_SDK_TAG_URL }}v${{ env.INDY_VERSION }}" + [ -z "${{ inputs.indy_postgres_url }}"] && echo "::set-output name=indy-postgres-url::${{ env.INDY_SDK_TAG_URL }}v${{ inputs.indy_version }}" [ -n "${{ inputs.tag }}" ] && echo "::set-output name=tag::${{ inputs.tag }}" [ -z "${{ inputs.tag }}" ] && echo "::set-output name=tag::py${{ matrix.python-version }}-${{ inputs.indy_version }}" @@ -67,7 +67,7 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Setup Base Image Metadata + - name: Setup Image Metadata id: base-meta uses: docker/metadata-action@v3 with: @@ -76,7 +76,7 @@ jobs: tags: | type=raw,value=${{ steps.info.outputs.tag }} - - name: Build and Push Base Image to ghcr.io + - name: Build and Push Image to ghcr.io uses: docker/build-push-action@v3 with: push: true diff --git a/.github/workflows/publish-indy.yml b/.github/workflows/publish-indy.yml new file mode 100644 index 0000000000..058187d030 --- /dev/null +++ b/.github/workflows/publish-indy.yml @@ -0,0 +1,93 @@ +name: Publish ACA-Py (Indy) +on: + workflow_dispatch: + inputs: + indy_version: + description: 'Indy SDK Version' + required: false + type: string + default: '1.16.0' + tag: + description: 'Image tag' + required: false + type: string + +env: + ACAPY_REQS: '[askar,bbs]' + + +jobs: + publish-image: + strategy: + fail-fast: false + matrix: + python-version: ['3.7', '3.8', '3.9', '3.10'] + + name: Publish (Indy) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Gather image info + id: info + run: | + echo "::set-output name=repo-owner::${GITHUB_REPOSITORY_OWNER,,}" + echo "::set-output name=acapy-version::$(sed -ne 's/__version__ = \"\([0-9.]\+\)\"/\1/p' aries_cloudagent/version.py)" + + - name: Tag image + id: tag + run: | + [ -n "${{ inputs.tag }}" ] && echo "::set-output name=tag::${{ inputs.tag }}" + [ -z "${{ inputs.tag }}" ] && echo "::set-output name=tag::py${{ matrix.python-version }}-indy-${{ inputs.indy_version }}-${{ steps.info.outputs.acapy-version }}" + + - name: Cache Docker layers + uses: actions/cache@v3 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-base-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx-base- + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + + - name: Log in to the GitHub Container Registry + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Setup Image Metadata + id: base-meta + uses: docker/metadata-action@v3 + with: + images: | + ghcr.io/${{ steps.info.outputs.repo-owner }}/aries-cloudagent-python + tags: | + type=raw,value=${{ steps.tag.outputs.tag }} + + - name: Build and Push Image to ghcr.io + uses: docker/build-push-action@v3 + with: + push: true + context: . + file: docker/Dockerfile.indy + tags: ${{ steps.base-meta.outputs.tags }} + labels: ${{ steps.base-meta.outputs.labels }} + build-args: | + python_version=${{ matrix.python-version }} + indy_version=${{ inputs.indy_version }} + acapy_version=${{ steps.info.outputs.acapy-version }} + acapy_reqs=${{ env.ACAPY_REQS }} + org=${{ steps.info.outputs.repo-owner }} + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max + + # Temp fix + # https://github.com/docker/build-push-action/issues/252 + # https://github.com/moby/buildkit/issues/1896 + - name: Move cache + run: | + rm -rf /tmp/.buildx-cache + mv /tmp/.buildx-cache-new /tmp/.buildx-cache diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 8f1e2197ee..15449f4350 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,4 +1,4 @@ -name: Publish Aries Cloud Agent - Python +name: Publish ACA-Py on: workflow_dispatch: inputs: @@ -18,7 +18,7 @@ jobs: matrix: python-version: ['3.7', '3.8', '3.9', '3.10'] - name: Publish Aries Cloud Agent - Python + name: Publish runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -53,7 +53,7 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Setup Base Image Metadata + - name: Setup Image Metadata id: base-meta uses: docker/metadata-action@v3 with: @@ -62,7 +62,7 @@ jobs: tags: | type=raw,value=${{ steps.tag.outputs.tag }} - - name: Build and Push Base Image to ghcr.io + - name: Build and Push Image to ghcr.io uses: docker/build-push-action@v3 with: push: true diff --git a/docker/Dockerfile.indy b/docker/Dockerfile.indy index 681921f8ce..9fd35fd5b6 100644 --- a/docker/Dockerfile.indy +++ b/docker/Dockerfile.indy @@ -1,12 +1,21 @@ -ARG python_version +ARG python_version=3.6.13 ARG indy_version -FROM ghcr.io/hyperledger/indy-python:py${python_version}-${indy_version} +ARG org=hyperledger +FROM python:${python_version}-slim-buster AS build + +WORKDIR /src + +ADD . . + +RUN pip install setuptools wheel +RUN python setup.py sdist bdist_wheel + +FROM ghcr.io/${org}/indy-python:py${python_version}-${indy_version} AS main ARG uid=1001 ARG user=indy ARG acapy_version ARG acapy_reqs -ARG git_egg_ref ENV HOME="/home/$user" \ APP_ROOT="$HOME" \ @@ -38,6 +47,8 @@ RUN mkdir -p $HOME/.aries_cloudagent # Also ensure the permissions on the python 'site-packages' folder are set correctly. RUN chmod -R ug+rw $HOME/.aries_cloudagent -RUN pip install --no-cache-dir ${git_egg_ref}aries-cloudagent${acapy_reqs}==${acapy_version} +COPY --from=build /src/dist/aries_cloudagent*.whl . + +RUN pip install --no-cache-dir --find-links=. aries_cloudagent${acapy_reqs} && rm aries_cloudagent*.whl ENTRYPOINT ["aca-py"] From 331ff1619b322dd7b08a40a848452d4c658d084b Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Tue, 16 Aug 2022 09:59:38 -0700 Subject: [PATCH 405/872] Fixed formatting Signed-off-by: Ian Costanzo --- aries_cloudagent/ledger/tests/test_indy.py | 4 ++-- aries_cloudagent/ledger/tests/test_indy_vdr.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/aries_cloudagent/ledger/tests/test_indy.py b/aries_cloudagent/ledger/tests/test_indy.py index 83dedb9936..bac9a63037 100644 --- a/aries_cloudagent/ledger/tests/test_indy.py +++ b/aries_cloudagent/ledger/tests/test_indy.py @@ -2381,11 +2381,11 @@ async def test_update_endpoint_of_type_profile_for_did( for i in range(len(endpoint)) ] ledger = IndySdkLedger(IndySdkLedgerPool("name", checked=True), self.profile) - #ledger = async_mock.patch.object( + # ledger = async_mock.patch.object( # ledger, # "is_ledger_read_only", # async_mock.CoroutineMock(return_value=False), - #) + # ) with async_mock.patch.object( IndySdkWallet, "get_public_did" ) as mock_wallet_get_public_did: diff --git a/aries_cloudagent/ledger/tests/test_indy_vdr.py b/aries_cloudagent/ledger/tests/test_indy_vdr.py index 04df80fb35..be08412312 100644 --- a/aries_cloudagent/ledger/tests/test_indy_vdr.py +++ b/aries_cloudagent/ledger/tests/test_indy_vdr.py @@ -37,7 +37,8 @@ async def close(): ledger.pool.handle = None with async_mock.patch.object(ledger.pool, "open", open), async_mock.patch.object( - ledger.pool, "close", close), async_mock.patch.object( + ledger.pool, "close", close + ), async_mock.patch.object( ledger, "is_ledger_read_only", async_mock.CoroutineMock(return_value=False) ): yield ledger From 59640da72028262e58bdf92b88575c7b0fe93de3 Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Tue, 16 Aug 2022 11:03:35 -0600 Subject: [PATCH 406/872] ci: Fix coordinate mediation tests Signed-off-by: Colton Wolkins (Indicio work address) --- .../v1_0/tests/test_mediation_manager.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py index dc0c42766c..7266c7f32a 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py @@ -29,7 +29,7 @@ TEST_CONN_ID = "conn-id" TEST_THREAD_ID = "thread-id" TEST_ENDPOINT = "https://example.com" -# TEST_RECORD_VERKEY = "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx" +TEST_RECORD_VERKEY = "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx" TEST_VERKEY = "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL" TEST_ROUTE_RECORD_VERKEY = "9WCgWKUaAJj3VWxxtzvvMQN3AoFxoBtBDo9ntwJnVVCC" TEST_ROUTE_VERKEY = "did:key:z6MknxTj6Zj1VrDWc1ofaZtmCVv2zNXpD58Xup4ijDGoQhya" @@ -133,7 +133,7 @@ async def test_deny_request(self, manager): async def test_update_keylist_delete(self, session, manager, record): """test_update_keylist_delete.""" - await RouteRecord(connection_id=TEST_CONN_ID, recipient_key=TEST_VERKEY).save( + await RouteRecord(connection_id=TEST_CONN_ID, recipient_key=TEST_RECORD_VERKEY).save( session ) response = await manager.update_keylist( @@ -168,7 +168,7 @@ async def test_update_keylist_create(self, manager, record): async def test_update_keylist_create_existing(self, session, manager, record): """test_update_keylist_create_existing.""" - await RouteRecord(connection_id=TEST_CONN_ID, recipient_key=TEST_VERKEY).save( + await RouteRecord(connection_id=TEST_CONN_ID, recipient_key=TEST_RECORD_VERKEY).save( session ) response = await manager.update_keylist( From dc85e32cf6ee0b60d6774e6b7b7b4ebf016ab8e2 Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Tue, 16 Aug 2022 11:17:56 -0600 Subject: [PATCH 407/872] ci: Fix black formatting errors Signed-off-by: Colton Wolkins (Indicio work address) --- aries_cloudagent/messaging/valid.py | 6 ++- .../coordinate_mediation/v1_0/manager.py | 3 +- .../v1_0/messages/inner/keylist_key.py | 6 +-- .../messages/inner/keylist_update_rule.py | 6 +-- .../v1_0/messages/inner/keylist_updated.py | 6 +-- .../v1_0/messages/mediate_grant.py | 16 ++++++-- .../messages/tests/test_keylist_update.py | 4 +- .../v1_0/messages/tests/test_mediate_grant.py | 5 ++- .../v1_0/tests/test_mediation_manager.py | 22 +++++----- .../v1_0/tests/test_route_manager.py | 1 + .../v1_0/tests/test_routes.py | 40 +++++++++++++++---- 11 files changed, 78 insertions(+), 37 deletions(-) diff --git a/aries_cloudagent/messaging/valid.py b/aries_cloudagent/messaging/valid.py index a62d587fff..6253c6b304 100644 --- a/aries_cloudagent/messaging/valid.py +++ b/aries_cloudagent/messaging/valid.py @@ -365,8 +365,10 @@ def __init__(self): super().__init__( RoutingKey.PATTERN, - error=("Value {input} is not in W3C did:key" - " or Ed25519VerificationKey2018 key format"), + error=( + "Value {input} is not in W3C did:key" + " or Ed25519VerificationKey2018 key format" + ), ) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py index ec15019ec9..c959ea9437 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py @@ -458,7 +458,8 @@ async def request_granted(self, record: MediationRecord, grant: MediationGrant): for key in grant.routing_keys: routing_keys.append( DIDKey.from_did(key).public_key_b58 - if key.startswith("did:key:") else key + if key.startswith("did:key:") + else key ) record.routing_keys = routing_keys async with self._profile.session() as session: diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_key.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_key.py index c320ea62aa..7c47a1ea0e 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_key.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_key.py @@ -37,9 +37,9 @@ def __init__( if recipient_key.startswith("did:key:"): self.recipient_key = recipient_key else: - self.recipient_key = ( - DIDKey.from_public_key_b58(recipient_key, KeyType.ED25519).did - ) + self.recipient_key = DIDKey.from_public_key_b58( + recipient_key, KeyType.ED25519 + ).did class KeylistKeySchema(BaseModelSchema): diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_update_rule.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_update_rule.py index 1ad0dc5d71..a8d85746e8 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_update_rule.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_update_rule.py @@ -37,9 +37,9 @@ def __init__(self, recipient_key: str, action: str, **kwargs): if recipient_key.startswith("did:key:"): self.recipient_key = recipient_key else: - self.recipient_key = ( - DIDKey.from_public_key_b58(recipient_key, KeyType.ED25519).did - ) + self.recipient_key = DIDKey.from_public_key_b58( + recipient_key, KeyType.ED25519 + ).did self.action = action diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_updated.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_updated.py index a2e4a11cd2..b2daec9856 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_updated.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_updated.py @@ -45,9 +45,9 @@ def __init__( if recipient_key.startswith("did:key:"): self.recipient_key = recipient_key else: - self.recipient_key = ( - DIDKey.from_public_key_b58(recipient_key, KeyType.ED25519).did - ) + self.recipient_key = DIDKey.from_public_key_b58( + recipient_key, KeyType.ED25519 + ).did self.action = action self.result = result diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/mediate_grant.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/mediate_grant.py index f15ec524d6..c4da77dc8d 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/mediate_grant.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/mediate_grant.py @@ -43,10 +43,18 @@ def __init__( """ super(MediationGrant, self).__init__(**kwargs) self.endpoint = endpoint - self.routing_keys = list( - (key if key.startswith("did:key:") - else DIDKey.from_public_key_b58(key, KeyType.ED25519).did) - for key in routing_keys) if routing_keys else [] + self.routing_keys = ( + list( + ( + key + if key.startswith("did:key:") + else DIDKey.from_public_key_b58(key, KeyType.ED25519).did + ) + for key in routing_keys + ) + if routing_keys + else [] + ) class MediationGrantSchema(AgentMessageSchema): diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/tests/test_keylist_update.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/tests/test_keylist_update.py index c22a75ba9e..caa0b3c976 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/tests/test_keylist_update.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/tests/test_keylist_update.py @@ -15,6 +15,8 @@ class TestKeylistUpdate(MessageTest, TestCase): SCHEMA = KeylistUpdateSchema VALUES = { "updates": [ - KeylistUpdateRule("did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", "add") + KeylistUpdateRule( + "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", "add" + ) ] } diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/tests/test_mediate_grant.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/tests/test_mediate_grant.py index 5d24ca330b..9c992ff883 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/tests/test_mediate_grant.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/tests/test_mediate_grant.py @@ -12,4 +12,7 @@ class TestMediateGrant(MessageTest, TestCase): TYPE = MEDIATE_GRANT CLASS = MediationGrant SCHEMA = MediationGrantSchema - VALUES = {"endpoint": "http://localhost:3000", "routing_keys": ["did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL"]} + VALUES = { + "endpoint": "http://localhost:3000", + "routing_keys": ["did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL"], + } diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py index 7266c7f32a..fe541b5488 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py @@ -116,11 +116,11 @@ async def test_grant_request(self, session, manager): assert record.connection_id == TEST_CONN_ID record, grant = await manager.grant_request(record.mediation_id) assert grant.endpoint == session.settings.get("default_endpoint") - routing_key = (await manager._retrieve_routing_did(session)) - routing_key = DIDKey.from_public_key_b58(routing_key.verkey, routing_key.key_type).did - assert grant.routing_keys == [ - routing_key - ] + routing_key = await manager._retrieve_routing_did(session) + routing_key = DIDKey.from_public_key_b58( + routing_key.verkey, routing_key.key_type + ).did + assert grant.routing_keys == [routing_key] async def test_deny_request(self, manager): """test_deny_request.""" @@ -133,9 +133,9 @@ async def test_deny_request(self, manager): async def test_update_keylist_delete(self, session, manager, record): """test_update_keylist_delete.""" - await RouteRecord(connection_id=TEST_CONN_ID, recipient_key=TEST_RECORD_VERKEY).save( - session - ) + await RouteRecord( + connection_id=TEST_CONN_ID, recipient_key=TEST_RECORD_VERKEY + ).save(session) response = await manager.update_keylist( record=record, updates=[ @@ -168,9 +168,9 @@ async def test_update_keylist_create(self, manager, record): async def test_update_keylist_create_existing(self, session, manager, record): """test_update_keylist_create_existing.""" - await RouteRecord(connection_id=TEST_CONN_ID, recipient_key=TEST_RECORD_VERKEY).save( - session - ) + await RouteRecord( + connection_id=TEST_CONN_ID, recipient_key=TEST_RECORD_VERKEY + ).save(session) response = await manager.update_keylist( record=record, updates=[ diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_route_manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_route_manager.py index cd56195317..2196ec76db 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_route_manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_route_manager.py @@ -23,6 +23,7 @@ TEST_ROUTE_RECORD_VERKEY = "9WCgWKUaAJj3VWxxtzvvMQN3AoFxoBtBDo9ntwJnVVCC" TEST_ROUTE_VERKEY = "did:key:z6MknxTj6Zj1VrDWc1ofaZtmCVv2zNXpD58Xup4ijDGoQhya" + class MockRouteManager(RouteManager): """Concretion of RouteManager for testing.""" diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_routes.py index ea86aac6b5..4cbd017a45 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_routes.py @@ -449,14 +449,26 @@ async def test_get_keylist_storage_error(self): async def test_send_keylist_update(self): body = { "updates": [ - {"recipient_key": "EwUKjVLboiLSuoWSEtDvrgrd41EUxG5bLecQrkHB63Up", "action": "add"}, - {"recipient_key": "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx", "action": "remove"}, + { + "recipient_key": "EwUKjVLboiLSuoWSEtDvrgrd41EUxG5bLecQrkHB63Up", + "action": "add", + }, + { + "recipient_key": "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx", + "action": "remove", + }, ] } body_with_didkey = { "updates": [ - {"recipient_key": "did:key:z6MktPjNKjb39Fpv2JM8vTBmhnQcsaWLN9Kx2fXLh2FC1GGC", "action": "add"}, - {"recipient_key": "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", "action": "remove"}, + { + "recipient_key": "did:key:z6MktPjNKjb39Fpv2JM8vTBmhnQcsaWLN9Kx2fXLh2FC1GGC", + "action": "add", + }, + { + "recipient_key": "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", + "action": "remove", + }, ] } @@ -484,7 +496,10 @@ async def test_send_keylist_update(self): async def test_send_keylist_update_bad_action(self): self.request.json.return_value = { "updates": [ - {"recipient_key": "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx", "action": "wrong"}, + { + "recipient_key": "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx", + "action": "wrong", + }, ] } @@ -494,7 +509,10 @@ async def test_send_keylist_update_bad_action(self): async def test_send_keylist_update_bad_mediation_state(self): self.request.json.return_value = { "updates": [ - {"recipient_key": "EwUKjVLboiLSuoWSEtDvrgrd41EUxG5bLecQrkHB63Up", "action": "add"}, + { + "recipient_key": "EwUKjVLboiLSuoWSEtDvrgrd41EUxG5bLecQrkHB63Up", + "action": "add", + }, ] } @@ -517,7 +535,10 @@ async def test_send_keylist_update_bad_updates(self): async def test_send_keylist_update_x_no_mediation_rec(self): self.request.json.return_value = { "updates": [ - {"recipient_key": "EwUKjVLboiLSuoWSEtDvrgrd41EUxG5bLecQrkHB63Up", "action": "add"}, + { + "recipient_key": "EwUKjVLboiLSuoWSEtDvrgrd41EUxG5bLecQrkHB63Up", + "action": "add", + }, ] } with async_mock.patch.object( @@ -530,7 +551,10 @@ async def test_send_keylist_update_x_no_mediation_rec(self): async def test_send_keylist_update_x_storage_error(self): self.request.json.return_value = { "updates": [ - {"recipient_key": "EwUKjVLboiLSuoWSEtDvrgrd41EUxG5bLecQrkHB63Up", "action": "add"}, + { + "recipient_key": "EwUKjVLboiLSuoWSEtDvrgrd41EUxG5bLecQrkHB63Up", + "action": "add", + }, ] } From 910aa205db8f25a6ecfad395139dbf37ba253585 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Tue, 16 Aug 2022 11:53:17 -0700 Subject: [PATCH 408/872] type fix Signed-off-by: Ian Costanzo --- aries_cloudagent/revocation/models/issuer_rev_reg_record.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/revocation/models/issuer_rev_reg_record.py b/aries_cloudagent/revocation/models/issuer_rev_reg_record.py index b9c12d8065..2e9d7b7e42 100644 --- a/aries_cloudagent/revocation/models/issuer_rev_reg_record.py +++ b/aries_cloudagent/revocation/models/issuer_rev_reg_record.py @@ -7,7 +7,7 @@ from os.path import join from pathlib import Path from shutil import move -from typing import Any, Mapping, Sequence, Union +from typing import Any, Mapping, Sequence, Union, Tuple from urllib.parse import urlparse from marshmallow import fields, validate @@ -345,7 +345,7 @@ async def fix_ledger_entry( profile: Profile, apply_ledger_update: bool, genesis_transactions: str, - ) -> (dict, dict, dict): + ) -> Tuple[dict, dict, dict]: """Fix the ledger entry to match wallet-recorded credentials.""" # get rev reg delta (revocations published to ledger) ledger = profile.inject(BaseLedger) From 5f940f3aa541282bdd6a2778860d7a96a9c754e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Humbert?= Date: Thu, 18 Aug 2022 13:46:23 +0200 Subject: [PATCH 409/872] fix: update RouteManager methods use to pass profile as parameter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Clément Humbert --- .../protocols/coordinate_mediation/v1_0/routes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/routes.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/routes.py index 94abb9dcb4..74b1c69291 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/routes.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/routes.py @@ -532,13 +532,13 @@ async def update_keylist_for_connection(request: web.BaseRequest): async with context.session() as session: connection_record = await ConnRecord.retrieve_by_id(session, connection_id) mediation_record = await route_manager.mediation_record_for_connection( - connection_record, mediation_id, or_default=True + context.profile, connection_record, mediation_id, or_default=True ) # MediationRecord is permitted to be None; route manager will # ensure the correct mediator is notified. keylist_update = await route_manager.route_connection( - connection_record, mediation_record + context.profile, connection_record, mediation_record ) results = keylist_update.serialize() if keylist_update else {} From ef49d03affc449948e02f898b742d7ed3b7f8029 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Thu, 18 Aug 2022 12:08:46 -0700 Subject: [PATCH 410/872] Update for the v1.0.0-rc0 release. Changelog comment added for v0.7.4 Signed-off-by: Stephen Curran --- CHANGELOG.md | 59 +++++++++++++++++++ aries_cloudagent/version.py | 2 +- .../aries_cloudagent.connections.rst | 8 --- .../aries_cloudagent.multitenant.rst | 8 +++ ...nt.protocols.coordinate_mediation.v1_0.rst | 16 +++++ .../aries_cloudagent.resolver.default.rst | 8 +++ open-api/openapi.json | 2 +- 7 files changed, 93 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ab0f51c6f..ca3f7f729a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,66 @@ +# 1.0.0-rc0 + +## August 18, 2022 + +1.0.0 is a breaking update to ACA-Py whose version is intended to indicate the +maturity of the implementation. The final 1.0.0 release will be Aries Interop +Profile 2.0-complete, and based on Python 3.7 or higher. The initial (rc0) +release candidate is for early adopters to provide feedback. + +### Breaking Changes + +As of rc0, there are no breaking updates in the release from the previous v0.7.4. +However, we know that there are some pending updates that will be breaking, hence +the bumps to the major/minor version elements. + +### Categorized List of Pull Requests + +In rc0, there are not a lot of new features, as the focus is on cleanup and +optimization. The biggest is the inclusion with ACA-Py of a universal resolver +interface, allowing an instance to have both local resolvers for some DID +Methods and a call out to an external universal resolver for other DID Methods. +While some work has been done on moving the default Python version beyond 3.6, +more work is still to be done on that before the final v1.0.0 release. + +### Categorized List of Pull Requests + +- Verifiable credential and revocation handling updates + - Refactor ledger correction code and insert into revocation error handling [\#1892](https://github.com/hyperledger/aries-cloudagent-python/pull/1892) ([ianco](https://github.com/ianco)) + - Indy ledger fixes and cleanups [\#1870](https://github.com/hyperledger/aries-cloudagent-python/pull/1870) ([andrewwhitehead](https://github.com/andrewwhitehead)) + - Refactoring of revocation registry creation [\#1813](https://github.com/hyperledger/aries-cloudagent-python/pull/1813) ([andrewwhitehead](https://github.com/andrewwhitehead)) + +- DID Registration and Resolution related updates + - feat: add universal resolver [\#1866](https://github.com/hyperledger/aries-cloudagent-python/pull/1866) ([dbluhm](https://github.com/dbluhm)) + - fix: resolve dids following new endpoint rules [\#1863](https://github.com/hyperledger/aries-cloudagent-python/pull/1863) ([dbluhm](https://github.com/dbluhm)) + - fix: didx request cannot be accepted [\#1881](https://github.com/hyperledger/aries-cloudagent-python/pull/1881) ([rmnre](https://github.com/rmnre)) + +- Internal Aries framework data handling updates + - fix: update RouteManager methods use to pass profile as parameter [\#1902](https://github.com/hyperledger/aries-cloudagent-python/pull/1902) ([chumbert](https://github.com/chumbert)) + - Allow fully qualified class names for profile managers [\#1880](https://github.com/hyperledger/aries-cloudagent-python/pull/1880) ([chumbert](https://github.com/chumbert)) + - fix: unable to use askar with in memory db [\#1878](https://github.com/hyperledger/aries-cloudagent-python/pull/1878) ([dbluhm](https://github.com/dbluhm)) + - Enable manually triggering keylist updates during connection [\#1851](https://github.com/hyperledger/aries-cloudagent-python/pull/1851) ([dbluhm](https://github.com/dbluhm)) + - feat: make base wallet route access configurable [\#1836](https://github.com/hyperledger/aries-cloudagent-python/pull/1836) ([dbluhm](https://github.com/dbluhm)) + - feat: event and webhook on keylist update stored [\#1769](https://github.com/hyperledger/aries-cloudagent-python/pull/1769) ([dbluhm](https://github.com/dbluhm)) + +- Unit, Integration and Aries Agent Test Harness Test updates + - Fixes a few AATH failures [\#1897](https://github.com/hyperledger/aries-cloudagent-python/pull/1897) ([ianco](https://github.com/ianco)) + - fix: warnings in tests from IndySdkProfile [\#1865](https://github.com/hyperledger/aries-cloudagent-python/pull/1865) ([dbluhm](https://github.com/dbluhm)) + - Unit test fixes for python 3.9 [\#1858](https://github.com/hyperledger/aries-cloudagent-python/pull/1858) ([andrewwhitehead](https://github.com/andrewwhitehead)) + +- Release management pull requests + - Release 1.0.0-rc0 + # 0.7.4 ## June 30, 2022 +> :warning: **Existing multitenant JWTs invalidated when a new JWT is +generated**: If you have a pre-existing implementation with existing Admin API +authorization JWTs, invoking the endpoint to get a JWT now invalidates the +existing JWT. Previously an identical JWT would be created. Please see this +[comment on PR \#1725](https://github.com/hyperledger/aries-cloudagent-python/pull/1725#issuecomment-1096172144) +for more details. + 0.7.4 is a significant release focused on stability and production deployments. As the "patch" release number indicates, there were no breaking changes in the Admin API, but a huge volume of updates and improvements. Highlights of this diff --git a/aries_cloudagent/version.py b/aries_cloudagent/version.py index 302f20a3ad..dd12aefad4 100644 --- a/aries_cloudagent/version.py +++ b/aries_cloudagent/version.py @@ -1,4 +1,4 @@ """Library version information.""" -__version__ = "0.7.4" +__version__ = "1.0.0-rc0" RECORD_TYPE_ACAPY_VERSION = "acapy_version" diff --git a/docs/generated/aries_cloudagent.connections.rst b/docs/generated/aries_cloudagent.connections.rst index ba841ef4b2..90d1f68626 100644 --- a/docs/generated/aries_cloudagent.connections.rst +++ b/docs/generated/aries_cloudagent.connections.rst @@ -24,11 +24,3 @@ aries\_cloudagent.connections.base\_manager module :members: :undoc-members: :show-inheritance: - -aries\_cloudagent.connections.util module ------------------------------------------ - -.. automodule:: aries_cloudagent.connections.util - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/generated/aries_cloudagent.multitenant.rst b/docs/generated/aries_cloudagent.multitenant.rst index 2748e843d7..913a17a652 100644 --- a/docs/generated/aries_cloudagent.multitenant.rst +++ b/docs/generated/aries_cloudagent.multitenant.rst @@ -64,3 +64,11 @@ aries\_cloudagent.multitenant.manager\_provider module :members: :undoc-members: :show-inheritance: + +aries\_cloudagent.multitenant.route\_manager module +--------------------------------------------------- + +.. automodule:: aries_cloudagent.multitenant.route_manager + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.rst b/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.rst index 80c49734be..ca2bc3dae6 100644 --- a/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.rst @@ -43,6 +43,22 @@ aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.message\_types module :undoc-members: :show-inheritance: +aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.route\_manager module +----------------------------------------------------------------------------- + +.. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.route_manager + :members: + :undoc-members: + :show-inheritance: + +aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.route\_manager\_provider module +--------------------------------------------------------------------------------------- + +.. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.route_manager_provider + :members: + :undoc-members: + :show-inheritance: + aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.routes module --------------------------------------------------------------------- diff --git a/docs/generated/aries_cloudagent.resolver.default.rst b/docs/generated/aries_cloudagent.resolver.default.rst index 375c99ee0f..b7dd8eac96 100644 --- a/docs/generated/aries_cloudagent.resolver.default.rst +++ b/docs/generated/aries_cloudagent.resolver.default.rst @@ -25,6 +25,14 @@ aries\_cloudagent.resolver.default.key module :undoc-members: :show-inheritance: +aries\_cloudagent.resolver.default.universal module +--------------------------------------------------- + +.. automodule:: aries_cloudagent.resolver.default.universal + :members: + :undoc-members: + :show-inheritance: + aries\_cloudagent.resolver.default.web module --------------------------------------------- diff --git a/open-api/openapi.json b/open-api/openapi.json index 3494407fdf..e673680e53 100644 --- a/open-api/openapi.json +++ b/open-api/openapi.json @@ -1,7 +1,7 @@ { "swagger" : "2.0", "info" : { - "version" : "v0.7.4", + "version" : "v1.0.0-rc0", "title" : "Aries Cloud Agent" }, "tags" : [ { From 82c1d91d65b8d3f2c3385d0fc4286b2301d6c47a Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Thu, 18 Aug 2022 12:10:18 -0700 Subject: [PATCH 411/872] Added this PR to changelog Signed-off-by: Stephen Curran --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca3f7f729a..68422bf950 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,7 +48,7 @@ more work is still to be done on that before the final v1.0.0 release. - Unit test fixes for python 3.9 [\#1858](https://github.com/hyperledger/aries-cloudagent-python/pull/1858) ([andrewwhitehead](https://github.com/andrewwhitehead)) - Release management pull requests - - Release 1.0.0-rc0 + - Release 1.0.0-rc0 [\#1903](https://github.com/hyperledger/aries-cloudagent-python/pull/1903) ([swcurran](https://github.com/swcurran)) # 0.7.4 From 3456371ab9a73f7d5e2f4c030e43380ea5872231 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Thu, 18 Aug 2022 12:42:53 -0700 Subject: [PATCH 412/872] Update CHANGELOG to include this PR Signed-off-by: Stephen Curran --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 68422bf950..befc3bd50f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,7 +48,7 @@ more work is still to be done on that before the final v1.0.0 release. - Unit test fixes for python 3.9 [\#1858](https://github.com/hyperledger/aries-cloudagent-python/pull/1858) ([andrewwhitehead](https://github.com/andrewwhitehead)) - Release management pull requests - - Release 1.0.0-rc0 [\#1903](https://github.com/hyperledger/aries-cloudagent-python/pull/1903) ([swcurran](https://github.com/swcurran)) + - Release 1.0.0-rc0 [\#1904](https://github.com/hyperledger/aries-cloudagent-python/pull/1904) ([swcurran](https://github.com/swcurran)) # 0.7.4 From e5c277d68543d2f2e40e3f3bf74e374d4e35f767 Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Fri, 19 Aug 2022 13:20:45 -0600 Subject: [PATCH 413/872] fix: Seperate w3c credential delete webhook from indy Signed-off-by: Colton Wolkins (Indicio work address) --- aries_cloudagent/holder/routes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aries_cloudagent/holder/routes.py b/aries_cloudagent/holder/routes.py index dc97b479e6..7dd924f8c1 100644 --- a/aries_cloudagent/holder/routes.py +++ b/aries_cloudagent/holder/routes.py @@ -378,7 +378,7 @@ async def w3c_cred_remove(request: web.BaseRequest): try: vc_record = await holder.retrieve_credential_by_id(credential_id) await holder.delete_credential(vc_record) - topic = f"acapy::record::credential::delete" + topic = f"acapy::record::w3c_credential::delete" await session.profile.notify(topic, {"id": credential_id, "state": "deleted"}) except StorageNotFoundError as err: raise web.HTTPNotFound(reason=err.roll_up) from err From 87c64dc0a7e5fbcd24ce8b5d7e90e22e0b43a06e Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Mon, 22 Aug 2022 08:11:35 -0600 Subject: [PATCH 414/872] chore: Reduce code duplication Signed-off-by: Colton Wolkins (Indicio work address) --- .../coordinate_mediation/v1_0/manager.py | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py index c959ea9437..22fc6887b9 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py @@ -223,6 +223,12 @@ async def deny_request( ) return mediation_record, deny + def normalize_public_key(self, key: str): + if key.startswith("did:key:"): + return DIDKey.from_did(key).public_key_b58 + + return key + async def update_keylist( self, record: MediationRecord, updates: Sequence[KeylistUpdateRule] ) -> KeylistUpdateResponse: @@ -249,14 +255,8 @@ async def update_keylist( RouteUpdate.ACTION_CREATE: KeylistUpdateRule.RULE_ADD, } - def normalize_public_key(key: str): - if key.startswith("did:key:"): - return DIDKey.from_did(key).public_key_b58 - - return key - def rule_to_update(rule: KeylistUpdateRule): - recipient_key = normalize_public_key(rule.recipient_key) + recipient_key = self.normalize_public_key(rule.recipient_key) return RouteUpdate( recipient_key=recipient_key, action=action_map[rule.action] ) @@ -456,11 +456,7 @@ async def request_granted(self, record: MediationRecord, grant: MediationGrant): # record.routing_keys = grant.routing_keys routing_keys = [] for key in grant.routing_keys: - routing_keys.append( - DIDKey.from_did(key).public_key_b58 - if key.startswith("did:key:") - else key - ) + routing_keys.append(self.normalize_public_key(key)) record.routing_keys = routing_keys async with self._profile.session() as session: await record.save(session, reason="Mediation request granted.") From 36f12301b530401579fcb7a9db30af7ca51c6a5c Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Mon, 22 Aug 2022 08:42:45 -0600 Subject: [PATCH 415/872] chore: further reduce code duplication Signed-off-by: Colton Wolkins (Indicio work address) --- .../coordinate_mediation/v1_0/manager.py | 12 +++--------- .../v1_0/messages/inner/keylist_key.py | 10 ++-------- .../messages/inner/keylist_update_rule.py | 10 ++-------- .../v1_0/messages/inner/keylist_updated.py | 10 ++-------- .../v1_0/messages/mediate_grant.py | 12 ++---------- .../v1_0/normalization.py | 19 +++++++++++++++++++ 6 files changed, 30 insertions(+), 43 deletions(-) create mode 100644 aries_cloudagent/protocols/coordinate_mediation/v1_0/normalization.py diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py index 22fc6887b9..f525dd0088 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py @@ -6,7 +6,6 @@ from ....core.error import BaseError from ....core.profile import Profile, ProfileSession -from ....did.did_key import DIDKey from ....storage.base import BaseStorage from ....storage.error import StorageNotFoundError from ....storage.record import StorageRecord @@ -30,6 +29,7 @@ from .messages.mediate_grant import MediationGrant from .messages.mediate_request import MediationRequest from .models.mediation_record import MediationRecord +from .normalization import normalize_from_did_key LOGGER = logging.getLogger(__name__) @@ -223,12 +223,6 @@ async def deny_request( ) return mediation_record, deny - def normalize_public_key(self, key: str): - if key.startswith("did:key:"): - return DIDKey.from_did(key).public_key_b58 - - return key - async def update_keylist( self, record: MediationRecord, updates: Sequence[KeylistUpdateRule] ) -> KeylistUpdateResponse: @@ -256,7 +250,7 @@ async def update_keylist( } def rule_to_update(rule: KeylistUpdateRule): - recipient_key = self.normalize_public_key(rule.recipient_key) + recipient_key = normalize_from_did_key(rule.recipient_key) return RouteUpdate( recipient_key=recipient_key, action=action_map[rule.action] ) @@ -456,7 +450,7 @@ async def request_granted(self, record: MediationRecord, grant: MediationGrant): # record.routing_keys = grant.routing_keys routing_keys = [] for key in grant.routing_keys: - routing_keys.append(self.normalize_public_key(key)) + routing_keys.append(normalize_from_did_key(key)) record.routing_keys = routing_keys async with self._profile.session() as session: await record.save(session, reason="Mediation request granted.") diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_key.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_key.py index 7c47a1ea0e..90982731ba 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_key.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_key.py @@ -4,8 +4,7 @@ from ......messaging.models.base import BaseModel, BaseModelSchema from ......messaging.valid import DID_KEY -from ......did.did_key import DIDKey -from ......wallet.key_type import KeyType +from ...normalization import normalize_from_public_key class KeylistKey(BaseModel): @@ -34,12 +33,7 @@ def __init__( """ super().__init__(**kwargs) - if recipient_key.startswith("did:key:"): - self.recipient_key = recipient_key - else: - self.recipient_key = DIDKey.from_public_key_b58( - recipient_key, KeyType.ED25519 - ).did + self.recipient_key = normalize_from_public_key(recipient_key) class KeylistKeySchema(BaseModelSchema): diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_update_rule.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_update_rule.py index a8d85746e8..de3524d4f9 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_update_rule.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_update_rule.py @@ -9,8 +9,7 @@ from ......messaging.models.base import BaseModel, BaseModelSchema from ......messaging.valid import ROUTING_KEY -from ......did.did_key import DIDKey -from ......wallet.key_type import KeyType +from ...normalization import normalize_from_public_key class KeylistUpdateRule(BaseModel): @@ -34,12 +33,7 @@ def __init__(self, recipient_key: str, action: str, **kwargs): """ super().__init__(**kwargs) - if recipient_key.startswith("did:key:"): - self.recipient_key = recipient_key - else: - self.recipient_key = DIDKey.from_public_key_b58( - recipient_key, KeyType.ED25519 - ).did + self.recipient_key = normalize_from_public_key(recipient_key) self.action = action diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_updated.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_updated.py index b2daec9856..388e166451 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_updated.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/inner/keylist_updated.py @@ -7,8 +7,7 @@ from ......messaging.models.base import BaseModel, BaseModelSchema from ......messaging.valid import DID_KEY -from ......did.did_key import DIDKey -from ......wallet.key_type import KeyType +from ...normalization import normalize_from_public_key class KeylistUpdated(BaseModel): @@ -42,12 +41,7 @@ def __init__( """ super().__init__(**kwargs) - if recipient_key.startswith("did:key:"): - self.recipient_key = recipient_key - else: - self.recipient_key = DIDKey.from_public_key_b58( - recipient_key, KeyType.ED25519 - ).did + self.recipient_key = normalize_from_public_key(recipient_key) self.action = action self.result = result diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/mediate_grant.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/mediate_grant.py index c4da77dc8d..6f82d8be48 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/mediate_grant.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/messages/mediate_grant.py @@ -8,9 +8,8 @@ from marshmallow import fields from .....messaging.agent_message import AgentMessage, AgentMessageSchema -from .....did.did_key import DIDKey -from .....wallet.key_type import KeyType from ..message_types import MEDIATE_GRANT, PROTOCOL_PACKAGE +from ..normalization import normalize_from_public_key HANDLER_CLASS = ( f"{PROTOCOL_PACKAGE}.handlers.mediation_grant_handler.MediationGrantHandler" @@ -44,14 +43,7 @@ def __init__( super(MediationGrant, self).__init__(**kwargs) self.endpoint = endpoint self.routing_keys = ( - list( - ( - key - if key.startswith("did:key:") - else DIDKey.from_public_key_b58(key, KeyType.ED25519).did - ) - for key in routing_keys - ) + list(normalize_from_public_key(key) for key in routing_keys) if routing_keys else [] ) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/normalization.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/normalization.py new file mode 100644 index 0000000000..1d8610e60c --- /dev/null +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/normalization.py @@ -0,0 +1,19 @@ +"""Normalization methods used while transitioning to DID:Key method.""" +from ....did.did_key import DIDKey +from ....wallet.key_type import KeyType + + +def normalize_from_did_key(key: str): + """Normalize Recipient/Routing keys from DID:Key to public keys.""" + if key.startswith("did:key:"): + return DIDKey.from_did(key).public_key_b58 + + return key + + +def normalize_from_public_key(key: str): + """Normalize Recipient/Routing keys from public keys to DID:Key.""" + if key.startswith("did:key:"): + return key + + return DIDKey.from_public_key_b58(key, KeyType.ED25519).did From b761fc0a313ea76930359d09556e10fc7d0bb50f Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Mon, 22 Aug 2022 09:26:19 -0600 Subject: [PATCH 416/872] chore: Fix black formatting issues Signed-off-by: Colton Wolkins (Indicio work address) --- aries_cloudagent/holder/routes.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/holder/routes.py b/aries_cloudagent/holder/routes.py index 7dd924f8c1..c2976edf1d 100644 --- a/aries_cloudagent/holder/routes.py +++ b/aries_cloudagent/holder/routes.py @@ -275,7 +275,9 @@ async def credentials_remove(request: web.BaseRequest): holder = session.inject(IndyHolder) await holder.delete_credential(credential_id) topic = f"acapy::record::credential::delete" - await session.profile.notify(topic, {"id": credential_id, "state": "deleted"}) + await session.profile.notify( + topic, {"id": credential_id, "state": "deleted"} + ) except WalletNotFoundError as err: raise web.HTTPNotFound(reason=err.roll_up) from err @@ -379,7 +381,9 @@ async def w3c_cred_remove(request: web.BaseRequest): vc_record = await holder.retrieve_credential_by_id(credential_id) await holder.delete_credential(vc_record) topic = f"acapy::record::w3c_credential::delete" - await session.profile.notify(topic, {"id": credential_id, "state": "deleted"}) + await session.profile.notify( + topic, {"id": credential_id, "state": "deleted"} + ) except StorageNotFoundError as err: raise web.HTTPNotFound(reason=err.roll_up) from err except StorageError as err: From ad60ab5cd59783aa84b72a3c3a9b88e8ad776aac Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Mon, 22 Aug 2022 10:23:18 -0600 Subject: [PATCH 417/872] fix: Fix unittests for record delete change Signed-off-by: Colton Wolkins (Indicio work address) --- aries_cloudagent/holder/routes.py | 4 ++-- aries_cloudagent/messaging/models/base_record.py | 1 + .../protocols/out_of_band/v1_0/tests/test_manager.py | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/aries_cloudagent/holder/routes.py b/aries_cloudagent/holder/routes.py index c2976edf1d..614a3eedf9 100644 --- a/aries_cloudagent/holder/routes.py +++ b/aries_cloudagent/holder/routes.py @@ -274,7 +274,7 @@ async def credentials_remove(request: web.BaseRequest): async with context.profile.session() as session: holder = session.inject(IndyHolder) await holder.delete_credential(credential_id) - topic = f"acapy::record::credential::delete" + topic = "acapy::record::credential::delete" await session.profile.notify( topic, {"id": credential_id, "state": "deleted"} ) @@ -380,7 +380,7 @@ async def w3c_cred_remove(request: web.BaseRequest): try: vc_record = await holder.retrieve_credential_by_id(credential_id) await holder.delete_credential(vc_record) - topic = f"acapy::record::w3c_credential::delete" + topic = "acapy::record::w3c_credential::delete" await session.profile.notify( topic, {"id": credential_id, "state": "deleted"} ) diff --git a/aries_cloudagent/messaging/models/base_record.py b/aries_cloudagent/messaging/models/base_record.py index 1f0d5b4009..da1f7914f1 100644 --- a/aries_cloudagent/messaging/models/base_record.py +++ b/aries_cloudagent/messaging/models/base_record.py @@ -419,6 +419,7 @@ async def delete_record(self, session: ProfileSession): if self._id: storage = session.inject(BaseStorage) if self.state: + self._previous_state = self.state self.state = "deleted" await self.emit_event(session, self.serialize()) await storage.delete_record(self.storage_record) diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py index 903f50a410..738e295a1f 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py @@ -1321,7 +1321,8 @@ async def test_receive_invitation_connection_protocol(self): ] assert not invitation.routing_keys - assert oob_record.state == OobRecord.STATE_DONE + assert oob_record.state == "deleted" + assert oob_record._previous_state == OobRecord.STATE_DONE async def test_receive_invitation_services_with_neither_service_blocks_nor_dids( self, From 0cb514ff1fe1e7a6cdeccb1f2c5161a61a9dff05 Mon Sep 17 00:00:00 2001 From: Micah Peltier Date: Mon, 22 Aug 2022 12:07:28 -0600 Subject: [PATCH 418/872] feat: remove public/multi mutual exclusion Signed-off-by: Micah Peltier --- .../protocols/out_of_band/v1_0/manager.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py index 4bdfcc39a6..2670d4bf89 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py @@ -133,10 +133,10 @@ async def create_invitation( "Cannot store metadata without handshake protocols" ) if public: - if multi_use: - raise OutOfBandManagerError( - "Cannot create public invitation with multi_use" - ) + # if multi_use: + # raise OutOfBandManagerError( + # "Cannot create public invitation with multi_use" + # ) if metadata: raise OutOfBandManagerError( "Cannot store metadata on public invitations" @@ -235,9 +235,15 @@ async def create_invitation( # Only create connection record if hanshake_protocols is defined if handshake_protocols: + invitation_mode = ( + ConnRecord.INVITATION_MODE_MULTI + if multi_use + else ConnRecord.INVITATION_MODE_ONCE + ) conn_rec = ConnRecord( # create connection record invitation_key=public_did.verkey, invitation_msg_id=invi_msg._id, + invitation_mode=invitation_mode, their_role=ConnRecord.Role.REQUESTER.rfc23, state=ConnRecord.State.INVITATION.rfc23, accept=ConnRecord.ACCEPT_AUTO From e0032217535401b0d1d156e7b0f13535307b81e4 Mon Sep 17 00:00:00 2001 From: Ry Jones Date: Tue, 23 Aug 2022 09:16:59 -0700 Subject: [PATCH 419/872] Create sonarcloud.yml Signed-off-by: Ry Jones --- .github/workflows/sonarcloud.yml | 41 ++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 .github/workflows/sonarcloud.yml diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml new file mode 100644 index 0000000000..1a71527c05 --- /dev/null +++ b/.github/workflows/sonarcloud.yml @@ -0,0 +1,41 @@ +name: SonarCloud analysis + +on: + push: + branches: [ "main", release* ] + pull_request: + branches: [ "main" ] + workflow_dispatch: + +permissions: + pull-requests: read # allows SonarCloud to decorate PRs with analysis results + +jobs: + Analysis: + runs-on: ubuntu-latest + + steps: + - name: Analyze with SonarCloud + + # You can pin the exact commit or the version. + # uses: SonarSource/sonarcloud-github-action@de2e56b42aa84d0b1c5b622644ac17e505c9a049 + uses: SonarSource/sonarcloud-github-action@de2e56b42aa84d0b1c5b622644ac17e505c9a049 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} # Generate a token on Sonarcloud.io, add it to the secrets of this repo with the name SONAR_TOKEN (Settings > Secrets > Actions > add new repository secret) + with: + # Additional arguments for the sonarcloud scanner + args: + # Unique keys of your project and organization. You can find them in SonarCloud > Information (bottom-left menu) + # mandatory + -Dsonar.projectKey=hyperledger_aries-cloudagent-python + -Dsonar.organization=hyperledger + -Dsonar.cpd.exclusions=**/tests/** + # Comma-separated paths to directories containing main source files. + #-Dsonar.sources= # optional, default is project base directory + # When you need the analysis to take place in a directory other than the one from which it was launched + #-Dsonar.projectBaseDir= # optional, default is . + # Comma-separated paths to directories containing test source files. + #-Dsonar.tests= # optional. For more info about Code Coverage, please refer to https://docs.sonarcloud.io/enriching/test-coverage/overview/ + # Adds more detail to both client and server-side analysis logs, activating DEBUG mode for the scanner, and adding client-side environment variables and system properties to the server-side log of analysis report processing. + #-Dsonar.verbose= # optional, default is false From 67db5de43770933efd8cbc816ffb5d3850fa5785 Mon Sep 17 00:00:00 2001 From: Thomas Diesler Date: Fri, 12 Aug 2022 11:03:54 +0200 Subject: [PATCH 420/872] [#1895] Stopping the aca-py shell process keeps python process running Signed-off-by: Thomas Diesler --- bin/aca-py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/aca-py b/bin/aca-py index ae99bf1862..3ada78acc0 100755 --- a/bin/aca-py +++ b/bin/aca-py @@ -7,4 +7,4 @@ if [ -z "$PYTHON" ]; then fi fi -$PYTHON -m aries_cloudagent "$@" +exec $PYTHON -m aries_cloudagent "$@" From 4098bccaf885f66de8109c93f1e5fd996c663329 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Tue, 23 Aug 2022 15:10:05 -0400 Subject: [PATCH 421/872] fix: incorrect response schema for discover features Signed-off-by: Daniel Bluhm --- aries_cloudagent/protocols/discovery/v1_0/routes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aries_cloudagent/protocols/discovery/v1_0/routes.py b/aries_cloudagent/protocols/discovery/v1_0/routes.py index a39b8c2159..dad3dc761d 100644 --- a/aries_cloudagent/protocols/discovery/v1_0/routes.py +++ b/aries_cloudagent/protocols/discovery/v1_0/routes.py @@ -70,7 +70,7 @@ class QueryDiscoveryExchRecordsSchema(OpenAPISchema): summary="Query supported features", ) @querystring_schema(QueryFeaturesQueryStringSchema()) -@response_schema(V10DiscoveryExchangeResultSchema(), 200, description="") +@response_schema(V10DiscoveryRecordSchema(), 200, description="") async def query_features(request: web.BaseRequest): """ Request handler for creating and sending feature query. From 859d5f59147e5f6cf84ca657d0b7e8a15ebfacbc Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Tue, 23 Aug 2022 15:12:28 -0400 Subject: [PATCH 422/872] fix: remove unused schema Signed-off-by: Daniel Bluhm --- aries_cloudagent/protocols/discovery/v1_0/routes.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/aries_cloudagent/protocols/discovery/v1_0/routes.py b/aries_cloudagent/protocols/discovery/v1_0/routes.py index dad3dc761d..66bd9dedd0 100644 --- a/aries_cloudagent/protocols/discovery/v1_0/routes.py +++ b/aries_cloudagent/protocols/discovery/v1_0/routes.py @@ -18,15 +18,6 @@ ) -class V10DiscoveryExchangeResultSchema(OpenAPISchema): - """Result schema for Discover Features v1.0 exchange record.""" - - results = fields.Nested( - V10DiscoveryRecordSchema, - description="Discover Features v1.0 exchange record", - ) - - class V10DiscoveryExchangeListResultSchema(OpenAPISchema): """Result schema for Discover Features v1.0 exchange records.""" From f892d142c3c3bfcbd74a02615458578f378352ca Mon Sep 17 00:00:00 2001 From: Char Howland Date: Mon, 15 Aug 2022 16:56:14 -0600 Subject: [PATCH 423/872] feat: construct attr_json with routing_keys from mediation record Signed-off-by: Char Howland --- aries_cloudagent/ledger/base.py | 20 +++++++++++- aries_cloudagent/ledger/indy.py | 48 +++++++++++++++++++++++++---- aries_cloudagent/ledger/indy_vdr.py | 47 ++++++++++++++++++++++++---- aries_cloudagent/wallet/askar.py | 2 ++ aries_cloudagent/wallet/base.py | 1 + aries_cloudagent/wallet/indy.py | 2 ++ 6 files changed, 107 insertions(+), 13 deletions(-) diff --git a/aries_cloudagent/ledger/base.py b/aries_cloudagent/ledger/base.py index f12b91015e..4a78c8baed 100644 --- a/aries_cloudagent/ledger/base.py +++ b/aries_cloudagent/ledger/base.py @@ -7,7 +7,7 @@ from abc import ABC, abstractmethod, ABCMeta from enum import Enum from hashlib import sha256 -from typing import Sequence, Tuple, Union +from typing import List, Sequence, Tuple, Union from ..indy.issuer import DEFAULT_CRED_DEF_TAG, IndyIssuer, IndyIssuerError from ..utils import sentinel @@ -79,6 +79,23 @@ async def get_all_endpoints_for_did(self, did: str) -> dict: did: The DID to look up on the ledger or in the cache """ + @abstractmethod + async def construct_attr_json( + self, + endpoint: str, + endpoint_type: EndpointType = None, + all_exist_endpoints: dict = None, + routing_keys: List[str] = None, + ) -> str: + """Create attr_json string. + + Args: + all_exist_endpoings: Dictionary of all existing endpoints + endpoint: The endpoint address + endpoint_type: The type of the endpoint + routing_keys: List of routing_keys if mediator is present + """ + @abstractmethod async def update_endpoint_for_did( self, @@ -87,6 +104,7 @@ async def update_endpoint_for_did( endpoint_type: EndpointType = EndpointType.ENDPOINT, write_ledger: bool = True, endorser_did: str = None, + routing_keys: List[str] = None, ) -> bool: """Check and update the endpoint on the ledger. diff --git a/aries_cloudagent/ledger/indy.py b/aries_cloudagent/ledger/indy.py index ed7ff43a7b..89c466d676 100644 --- a/aries_cloudagent/ledger/indy.py +++ b/aries_cloudagent/ledger/indy.py @@ -8,7 +8,7 @@ from io import StringIO from os import path from time import time -from typing import TYPE_CHECKING, Tuple, Optional +from typing import TYPE_CHECKING, List, Tuple, Optional import indy.ledger import indy.pool @@ -738,6 +738,40 @@ async def get_endpoint_for_did( return address + async def construct_attr_json( + self, + endpoint: str, + endpoint_type: EndpointType = None, + all_exist_endpoints: dict = None, + routing_keys: List[str] = None, + ) -> str: + """Create attr_json string. + + Args: + all_exist_endpoings: Dictionary of all existing endpoints + endpoint: The endpoint address + endpoint_type: The type of the endpoint + routing_keys: List of routing_keys if mediator is present + """ + + if not routing_keys: + routing_keys = [] + + endpoint_dict = {"endpoint": endpoint} + + if all_exist_endpoints: + all_exist_endpoints[endpoint_type.indy] = endpoint_dict + endpoint_dict["routingKeys"] = routing_keys + attr_json = json.dumps({"endpoint": all_exist_endpoints}) + + else: + endpoint_val = {endpoint_type.indy: endpoint_dict} + endpoint_dict["routingKeys"] = routing_keys + attr_json = json.dumps({"endpoint": endpoint_val}) + + return attr_json + + async def update_endpoint_for_did( self, did: str, @@ -745,6 +779,7 @@ async def update_endpoint_for_did( endpoint_type: EndpointType = None, write_ledger: bool = True, endorser_did: str = None, + routing_keys: List[str] = None, ) -> bool: """Check and update the endpoint on the ledger. @@ -777,11 +812,12 @@ async def update_endpoint_for_did( nym = self.did_to_nym(did) - if all_exist_endpoints: - all_exist_endpoints[endpoint_type.indy] = endpoint - attr_json = json.dumps({"endpoint": all_exist_endpoints}) - else: - attr_json = json.dumps({"endpoint": {endpoint_type.indy: endpoint}}) + attr_json = await self.construct_attr_json( + endpoint, + endpoint_type, + all_exist_endpoints, + routing_keys, + ) with IndyErrorHandler("Exception building attribute request", LedgerError): request_json = await indy.ledger.build_attrib_request( diff --git a/aries_cloudagent/ledger/indy_vdr.py b/aries_cloudagent/ledger/indy_vdr.py index 061606a64e..ac72f2d2d7 100644 --- a/aries_cloudagent/ledger/indy_vdr.py +++ b/aries_cloudagent/ledger/indy_vdr.py @@ -12,7 +12,7 @@ from io import StringIO from pathlib import Path from time import time -from typing import Tuple, Union, Optional +from typing import List, Tuple, Union, Optional from indy_vdr import ledger, open_pool, Pool, Request, VdrError @@ -672,6 +672,39 @@ async def get_endpoint_for_did( return address + async def construct_attr_json( + self, + endpoint: str, + endpoint_type: EndpointType = None, + all_exist_endpoints: dict = None, + routing_keys: List[str] = None, + ) -> str: + """Create attr_json string. + + Args: + all_exist_endpoings: Dictionary of all existing endpoints + endpoint: The endpoint address + endpoint_type: The type of the endpoint + routing_keys: List of routing_keys if mediator is present + """ + + if not routing_keys: + routing_keys = [] + + endpoint_dict = {"endpoint": endpoint} + + if all_exist_endpoints: + all_exist_endpoints[endpoint_type.indy] = endpoint_dict + endpoint_dict["routingKeys"] = routing_keys + attr_json = json.dumps({"endpoint": all_exist_endpoints}) + + else: + endpoint_val = {endpoint_type.indy: endpoint_dict} + endpoint_dict["routingKeys"] = routing_keys + attr_json = json.dumps({"endpoint": endpoint_val}) + + return attr_json + async def update_endpoint_for_did( self, did: str, @@ -679,6 +712,7 @@ async def update_endpoint_for_did( endpoint_type: EndpointType = None, write_ledger: bool = True, endorser_did: str = None, + routing_keys: List[str] = None, ) -> bool: """Check and update the endpoint on the ledger. @@ -711,11 +745,12 @@ async def update_endpoint_for_did( nym = self.did_to_nym(did) - if all_exist_endpoints: - all_exist_endpoints[endpoint_type.indy] = endpoint - attr_json = json.dumps({"endpoint": all_exist_endpoints}) - else: - attr_json = json.dumps({"endpoint": {endpoint_type.indy: endpoint}}) + attr_json = await self.construct_attr_json( + endpoint, + endpoint_type, + all_exist_endpoints, + routing_keys, + ) try: attrib_req = ledger.build_attrib_request( diff --git a/aries_cloudagent/wallet/askar.py b/aries_cloudagent/wallet/askar.py index 7982df01ed..f2a5c790c6 100644 --- a/aries_cloudagent/wallet/askar.py +++ b/aries_cloudagent/wallet/askar.py @@ -448,6 +448,7 @@ async def set_did_endpoint( endpoint_type: EndpointType = None, write_ledger: bool = True, endorser_did: str = None, + routing_keys: List[str] = None, ): """ Update the endpoint for a DID in the wallet, send to ledger if public or posted. @@ -486,6 +487,7 @@ async def set_did_endpoint( endpoint_type, write_ledger=write_ledger, endorser_did=endorser_did, + routing_keys=routing_keys, ) if not write_ledger: return attrib_def diff --git a/aries_cloudagent/wallet/base.py b/aries_cloudagent/wallet/base.py index a9bf5091e3..bbb1288061 100644 --- a/aries_cloudagent/wallet/base.py +++ b/aries_cloudagent/wallet/base.py @@ -226,6 +226,7 @@ async def set_did_endpoint( endpoint_type: EndpointType = None, write_ledger: bool = True, endorser_did: str = None, + routing_keys: List[str] = None, ): """ Update the endpoint for a DID in the wallet, send to ledger if public or posted. diff --git a/aries_cloudagent/wallet/indy.py b/aries_cloudagent/wallet/indy.py index 888cce6b5f..bba9f6ed49 100644 --- a/aries_cloudagent/wallet/indy.py +++ b/aries_cloudagent/wallet/indy.py @@ -707,6 +707,7 @@ async def set_did_endpoint( endpoint_type: EndpointType = None, write_ledger: bool = True, endorser_did: str = None, + routing_keys: List[str] = None, ): """ Update the endpoint for a DID in the wallet, send to ledger if public or posted. @@ -746,6 +747,7 @@ async def set_did_endpoint( endpoint_type, write_ledger=write_ledger, endorser_did=endorser_did, + routing_keys=routing_keys, ) if not write_ledger: return attrib_def From 0f33f1edce89874cccf9e5b8c3e80a1f6b75c7c3 Mon Sep 17 00:00:00 2001 From: Char Howland Date: Mon, 15 Aug 2022 16:58:09 -0600 Subject: [PATCH 424/872] feat: retrieve routing_keys from optional mediation_id or default mediator Signed-off-by: Char Howland --- aries_cloudagent/wallet/routes.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/aries_cloudagent/wallet/routes.py b/aries_cloudagent/wallet/routes.py index 44e506ba36..68f0c22d07 100644 --- a/aries_cloudagent/wallet/routes.py +++ b/aries_cloudagent/wallet/routes.py @@ -2,6 +2,7 @@ import json import logging +from typing import List from aiohttp import web from aiohttp_apispec import docs, querystring_schema, request_schema, response_schema @@ -207,6 +208,12 @@ class AttribConnIdMatchInfoSchema(OpenAPISchema): conn_id = fields.Str(description="Connection identifier", required=False) +class MediationIDSchema(OpenAPISchema): + """Class for user to optionally input a mediation_id.""" + + mediation_id = fields.Str(description="Mediation identifier", required=False) + + def format_did_info(info: DIDInfo): """Serialize a DIDInfo object.""" if info: @@ -410,6 +417,7 @@ async def wallet_get_public_did(request: web.BaseRequest): @querystring_schema(DIDQueryStringSchema()) @querystring_schema(CreateAttribTxnForEndorserOptionSchema()) @querystring_schema(AttribConnIdMatchInfoSchema()) +@querystring_schema(MediationIDSchema()) @response_schema(DIDResultSchema, 200, description="") async def wallet_set_public_did(request: web.BaseRequest): """ @@ -442,6 +450,19 @@ async def wallet_set_public_did(request: web.BaseRequest): raise web.HTTPBadRequest(reason="Request query must include DID") info: DIDInfo = None + + mediation_id = request.query.get("mediation_id") + profile = context.profile + route_manager = profile.inject(RouteManager) + mediation_record = await route_manager.mediation_record_if_id( + profile=profile, + mediation_id=mediation_id, + or_default=True, + ) + routing_keys=None + if mediation_record: + routing_keys = mediation_record.routing_keys + try: info, attrib_def = await promote_wallet_public_did( context.profile, @@ -450,6 +471,7 @@ async def wallet_set_public_did(request: web.BaseRequest): did, write_ledger=write_ledger, connection_id=connection_id, + routing_keys=routing_keys, ) except LookupError as err: raise web.HTTPNotFound(reason=str(err)) from err @@ -496,6 +518,7 @@ async def promote_wallet_public_did( did: str, write_ledger: bool = False, connection_id: str = None, + routing_keys: List[str] = None, ) -> DIDInfo: """Promote supplied DID to the wallet public DID.""" info: DIDInfo = None @@ -570,6 +593,7 @@ async def promote_wallet_public_did( ledger, write_ledger=write_ledger, endorser_did=endorser_did, + routing_keys=routing_keys, ) # Commented the below lines as the function set_did_endpoint From 796b56ac6f10d9e5d2ed8b2c89398aafd00ab831 Mon Sep 17 00:00:00 2001 From: Char Howland Date: Mon, 15 Aug 2022 17:01:55 -0600 Subject: [PATCH 425/872] test: update_endpoint_for_did and construct_attr_json with routing keys Signed-off-by: Char Howland --- aries_cloudagent/ledger/tests/test_indy.py | 88 +++++++++++++ .../ledger/tests/test_indy_vdr.py | 118 ++++++++++++++++++ 2 files changed, 206 insertions(+) diff --git a/aries_cloudagent/ledger/tests/test_indy.py b/aries_cloudagent/ledger/tests/test_indy.py index bac9a63037..9d47e5da11 100644 --- a/aries_cloudagent/ledger/tests/test_indy.py +++ b/aries_cloudagent/ledger/tests/test_indy.py @@ -2299,6 +2299,94 @@ async def test_update_endpoint_for_did( ) assert response + @async_mock.patch("aries_cloudagent.ledger.indy.IndySdkLedgerPool.context_open") + @async_mock.patch("aries_cloudagent.ledger.indy.IndySdkLedgerPool.context_close") + @pytest.mark.asyncio + async def test_construct_attr_json_with_routing_keys( + self, + mock_close, + mock_open, + ): + ledger = IndySdkLedger(IndySdkLedgerPool("name", checked=True), self.profile) + async with ledger: + attr_json = await ledger.construct_attr_json( + "https://url", + EndpointType.ENDPOINT, + all_exist_endpoints={"Endpoint": "https://endpoint"}, + routing_keys=['3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn'], + ) + assert attr_json == json.dumps( + { + "endpoint": { + "Endpoint": "https://endpoint", + "endpoint": { + "endpoint": "https://url", + "routingKeys": ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"] + } + } + } + ) + + @async_mock.patch("aries_cloudagent.ledger.indy.IndySdkLedgerPool.context_open") + @async_mock.patch("aries_cloudagent.ledger.indy.IndySdkLedgerPool.context_close") + @async_mock.patch("indy.ledger.build_get_attrib_request") + @async_mock.patch("indy.ledger.build_attrib_request") + @async_mock.patch("aries_cloudagent.ledger.indy.IndySdkLedger._submit") + @pytest.mark.asyncio + async def test_update_endpoint_for_did_calls_attr_json( + self, + mock_submit, + mock_build_attrib_req, + mock_build_get_attrib_req, + mock_close, + mock_open, + ): + routing_keys = ['3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn'] + mock_wallet = async_mock.MagicMock() + self.session.context.injector.bind_provider(BaseWallet, mock_wallet) + ledger = IndySdkLedger(IndySdkLedgerPool("name", checked=True), self.profile) + async with ledger: + with async_mock.patch.object( + IndySdkWallet, "get_public_did" + ) as mock_wallet_get_public_did, async_mock.patch.object( + ledger, + "construct_attr_json", + async_mock.CoroutineMock( + return_value=json.dumps( + { + "endpoint": { + "endpoint": { + "endpoint": "https://url", + "routingKeys": [] + } + } + } + ) + ), + ) as mock_construct_attr_json, async_mock.patch.object( + ledger, + "get_all_endpoints_for_did", + async_mock.CoroutineMock( + return_value={} + ) + ), async_mock.patch.object( + ledger, + "did_to_nym", + ): + mock_wallet_get_public_did.return_value = self.test_did_info + await ledger.update_endpoint_for_did( + mock_wallet_get_public_did, + "https://url", + EndpointType.ENDPOINT, + routing_keys=routing_keys, + ) + mock_construct_attr_json.assert_called_once_with( + "https://url", + EndpointType.ENDPOINT, + {}, + routing_keys, + ) + @async_mock.patch("aries_cloudagent.ledger.indy.IndySdkLedgerPool.context_open") @async_mock.patch("aries_cloudagent.ledger.indy.IndySdkLedgerPool.context_close") @async_mock.patch("indy.ledger.build_get_attrib_request") diff --git a/aries_cloudagent/ledger/tests/test_indy_vdr.py b/aries_cloudagent/ledger/tests/test_indy_vdr.py index be08412312..1ac9db00f9 100644 --- a/aries_cloudagent/ledger/tests/test_indy_vdr.py +++ b/aries_cloudagent/ledger/tests/test_indy_vdr.py @@ -1,4 +1,5 @@ import json +from aries_cloudagent.messaging.valid import ENDPOINT_TYPE import pytest from asynctest import mock as async_mock @@ -607,6 +608,123 @@ async def test_update_endpoint_for_did( "55GkHamhTU1ZbTbV2ab9DE", "https://url", EndpointType.ENDPOINT ) + @pytest.mark.parametrize( + "all_exist_endpoints, routing_keys, result", + [ + ( + {"Endpoint": "https://endpoint"}, + ['3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn'], + { + "endpoint": { + "Endpoint": "https://endpoint", + "endpoint": { + "endpoint": "https://url", + "routingKeys": ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"] + } + } + }, + ), + ( + {"Endpoint": "https://endpoint"}, + None, + { + "endpoint": { + "Endpoint": "https://endpoint", + "endpoint": { + "endpoint": "https://url", + "routingKeys": [] + } + } + }, + ), + ( + None, + ['3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn'], + { + "endpoint": { + "endpoint": { + "endpoint": "https://url", + "routingKeys": ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"] + } + } + }, + ), + ( + None, + None, + { + "endpoint": { + "endpoint": { + "endpoint": "https://url", + "routingKeys": [] + } + } + }, + ) + ] + ) + @pytest.mark.asyncio + async def test_construct_attr_json( + self, + ledger: IndyVdrLedger, + all_exist_endpoints, + routing_keys, + result, + ): + async with ledger: + attr_json = await ledger.construct_attr_json( + "https://url", + EndpointType.ENDPOINT, + all_exist_endpoints, + routing_keys, + ) + assert attr_json == json.dumps(result) + + @pytest.mark.asyncio + async def test_update_endpoint_for_did_calls_attr_json( + self, + ledger: IndyVdrLedger, + ): + routing_keys = ['3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn'] + wallet = (await ledger.profile.session()).wallet + test_did = await wallet.create_public_did(DIDMethod.SOV, KeyType.ED25519) + + async with ledger: + with async_mock.patch.object( + ledger, + "construct_attr_json", + async_mock.CoroutineMock( + return_value=json.dumps( + { + "endpoint": { + "endpoint": { + "endpoint": "https://url", + "routingKeys": [] + } + } + } + ) + ), + ) as mock_construct_attr_json, async_mock.patch.object( + ledger, + "get_all_endpoints_for_did", + async_mock.CoroutineMock( + return_value={} + ) + ): + await ledger.update_endpoint_for_did( + test_did.did, + "https://url", + EndpointType.ENDPOINT, + routing_keys=routing_keys, + ) + mock_construct_attr_json.assert_called_once_with( + "https://url", + EndpointType.ENDPOINT, + {}, + routing_keys, + ) + @pytest.mark.asyncio async def test_update_endpoint_for_did_no_public( self, From e91a6dd6bb9ec93d4da3c87316d7895aefd5c8d4 Mon Sep 17 00:00:00 2001 From: Char Howland Date: Mon, 15 Aug 2022 17:02:24 -0600 Subject: [PATCH 426/872] test: set_did_endpoint_ledger with routing_keys Signed-off-by: Char Howland --- .../wallet/tests/test_indy_wallet.py | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/aries_cloudagent/wallet/tests/test_indy_wallet.py b/aries_cloudagent/wallet/tests/test_indy_wallet.py index e53551161e..4638d556e2 100644 --- a/aries_cloudagent/wallet/tests/test_indy_wallet.py +++ b/aries_cloudagent/wallet/tests/test_indy_wallet.py @@ -130,6 +130,7 @@ async def test_set_did_endpoint_ledger(self, wallet: IndySdkWallet): EndpointType.ENDPOINT, endorser_did=None, write_ledger=True, + routing_keys=None, ) info_pub2 = await wallet.get_public_did() assert info_pub2.metadata["endpoint"] == "http://1.2.3.4:8021" @@ -138,6 +139,27 @@ async def test_set_did_endpoint_ledger(self, wallet: IndySdkWallet): await wallet.set_did_endpoint(info_pub.did, "http://1.2.3.4:8021", None) assert "No ledger available" in str(excinfo.value) + @pytest.mark.asyncio + async def test_set_did_endpoint_ledger_with_routing_keys(self, wallet: IndySdkWallet): + routing_keys = ['3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn'] + mock_ledger = async_mock.MagicMock( + read_only=False, update_endpoint_for_did=async_mock.CoroutineMock() + ) + info_pub = await wallet.create_public_did( + DIDMethod.SOV, + KeyType.ED25519, + ) + await wallet.set_did_endpoint(info_pub.did, "http://1.2.3.4:8021", mock_ledger, routing_keys=routing_keys) + + mock_ledger.update_endpoint_for_did.assert_called_once_with( + info_pub.did, + "http://1.2.3.4:8021", + EndpointType.ENDPOINT, + endorser_did=None, + write_ledger=True, + routing_keys=routing_keys, + ) + @pytest.mark.asyncio async def test_set_did_endpoint_readonly_ledger(self, wallet: IndySdkWallet): mock_ledger = async_mock.MagicMock( From b4c651a43d32311da8362e6d87396e3051251278 Mon Sep 17 00:00:00 2001 From: Char Howland Date: Mon, 15 Aug 2022 17:49:08 -0600 Subject: [PATCH 427/872] fix: black reformatting Signed-off-by: Char Howland --- aries_cloudagent/ledger/indy.py | 6 +- aries_cloudagent/ledger/tests/test_indy.py | 36 ++++------- .../ledger/tests/test_indy_vdr.py | 60 +++++++------------ aries_cloudagent/wallet/routes.py | 6 +- .../wallet/tests/test_indy_wallet.py | 13 ++-- 5 files changed, 46 insertions(+), 75 deletions(-) diff --git a/aries_cloudagent/ledger/indy.py b/aries_cloudagent/ledger/indy.py index 89c466d676..dbbe173f89 100644 --- a/aries_cloudagent/ledger/indy.py +++ b/aries_cloudagent/ledger/indy.py @@ -771,7 +771,6 @@ async def construct_attr_json( return attr_json - async def update_endpoint_for_did( self, did: str, @@ -813,10 +812,7 @@ async def update_endpoint_for_did( nym = self.did_to_nym(did) attr_json = await self.construct_attr_json( - endpoint, - endpoint_type, - all_exist_endpoints, - routing_keys, + endpoint, endpoint_type, all_exist_endpoints, routing_keys ) with IndyErrorHandler("Exception building attribute request", LedgerError): diff --git a/aries_cloudagent/ledger/tests/test_indy.py b/aries_cloudagent/ledger/tests/test_indy.py index 9d47e5da11..01c87e0393 100644 --- a/aries_cloudagent/ledger/tests/test_indy.py +++ b/aries_cloudagent/ledger/tests/test_indy.py @@ -2302,28 +2302,24 @@ async def test_update_endpoint_for_did( @async_mock.patch("aries_cloudagent.ledger.indy.IndySdkLedgerPool.context_open") @async_mock.patch("aries_cloudagent.ledger.indy.IndySdkLedgerPool.context_close") @pytest.mark.asyncio - async def test_construct_attr_json_with_routing_keys( - self, - mock_close, - mock_open, - ): + async def test_construct_attr_json_with_routing_keys(self, mock_close, mock_open): ledger = IndySdkLedger(IndySdkLedgerPool("name", checked=True), self.profile) async with ledger: attr_json = await ledger.construct_attr_json( "https://url", EndpointType.ENDPOINT, all_exist_endpoints={"Endpoint": "https://endpoint"}, - routing_keys=['3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn'], + routing_keys=["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"], ) assert attr_json == json.dumps( { "endpoint": { - "Endpoint": "https://endpoint", - "endpoint": { - "endpoint": "https://url", - "routingKeys": ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"] - } - } + "Endpoint": "https://endpoint", + "endpoint": { + "endpoint": "https://url", + "routingKeys": ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"], + }, + } } ) @@ -2341,7 +2337,7 @@ async def test_update_endpoint_for_did_calls_attr_json( mock_close, mock_open, ): - routing_keys = ['3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn'] + routing_keys = ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"] mock_wallet = async_mock.MagicMock() self.session.context.injector.bind_provider(BaseWallet, mock_wallet) ledger = IndySdkLedger(IndySdkLedgerPool("name", checked=True), self.profile) @@ -2357,7 +2353,7 @@ async def test_update_endpoint_for_did_calls_attr_json( "endpoint": { "endpoint": { "endpoint": "https://url", - "routingKeys": [] + "routingKeys": [], } } } @@ -2366,12 +2362,9 @@ async def test_update_endpoint_for_did_calls_attr_json( ) as mock_construct_attr_json, async_mock.patch.object( ledger, "get_all_endpoints_for_did", - async_mock.CoroutineMock( - return_value={} - ) + async_mock.CoroutineMock(return_value={}), ), async_mock.patch.object( - ledger, - "did_to_nym", + ledger, "did_to_nym" ): mock_wallet_get_public_did.return_value = self.test_did_info await ledger.update_endpoint_for_did( @@ -2381,10 +2374,7 @@ async def test_update_endpoint_for_did_calls_attr_json( routing_keys=routing_keys, ) mock_construct_attr_json.assert_called_once_with( - "https://url", - EndpointType.ENDPOINT, - {}, - routing_keys, + "https://url", EndpointType.ENDPOINT, {}, routing_keys ) @async_mock.patch("aries_cloudagent.ledger.indy.IndySdkLedgerPool.context_open") diff --git a/aries_cloudagent/ledger/tests/test_indy_vdr.py b/aries_cloudagent/ledger/tests/test_indy_vdr.py index 1ac9db00f9..7c10ac443d 100644 --- a/aries_cloudagent/ledger/tests/test_indy_vdr.py +++ b/aries_cloudagent/ledger/tests/test_indy_vdr.py @@ -613,15 +613,17 @@ async def test_update_endpoint_for_did( [ ( {"Endpoint": "https://endpoint"}, - ['3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn'], + ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"], { "endpoint": { - "Endpoint": "https://endpoint", - "endpoint": { - "endpoint": "https://url", - "routingKeys": ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"] - } - } + "Endpoint": "https://endpoint", + "endpoint": { + "endpoint": "https://url", + "routingKeys": [ + "3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn" + ], + }, + } }, ), ( @@ -630,21 +632,20 @@ async def test_update_endpoint_for_did( { "endpoint": { "Endpoint": "https://endpoint", - "endpoint": { - "endpoint": "https://url", - "routingKeys": [] - } + "endpoint": {"endpoint": "https://url", "routingKeys": []}, } }, ), ( None, - ['3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn'], + ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"], { "endpoint": { "endpoint": { "endpoint": "https://url", - "routingKeys": ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"] + "routingKeys": [ + "3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn" + ], } } }, @@ -654,38 +655,25 @@ async def test_update_endpoint_for_did( None, { "endpoint": { - "endpoint": { - "endpoint": "https://url", - "routingKeys": [] - } + "endpoint": {"endpoint": "https://url", "routingKeys": []} } }, - ) - ] + ), + ], ) @pytest.mark.asyncio async def test_construct_attr_json( - self, - ledger: IndyVdrLedger, - all_exist_endpoints, - routing_keys, - result, + self, ledger: IndyVdrLedger, all_exist_endpoints, routing_keys, result ): async with ledger: attr_json = await ledger.construct_attr_json( - "https://url", - EndpointType.ENDPOINT, - all_exist_endpoints, - routing_keys, + "https://url", EndpointType.ENDPOINT, all_exist_endpoints, routing_keys ) assert attr_json == json.dumps(result) @pytest.mark.asyncio - async def test_update_endpoint_for_did_calls_attr_json( - self, - ledger: IndyVdrLedger, - ): - routing_keys = ['3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn'] + async def test_update_endpoint_for_did_calls_attr_json(self, ledger: IndyVdrLedger): + routing_keys = ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"] wallet = (await ledger.profile.session()).wallet test_did = await wallet.create_public_did(DIDMethod.SOV, KeyType.ED25519) @@ -699,7 +687,7 @@ async def test_update_endpoint_for_did_calls_attr_json( "endpoint": { "endpoint": { "endpoint": "https://url", - "routingKeys": [] + "routingKeys": [], } } } @@ -708,9 +696,7 @@ async def test_update_endpoint_for_did_calls_attr_json( ) as mock_construct_attr_json, async_mock.patch.object( ledger, "get_all_endpoints_for_did", - async_mock.CoroutineMock( - return_value={} - ) + async_mock.CoroutineMock(return_value={}), ): await ledger.update_endpoint_for_did( test_did.did, diff --git a/aries_cloudagent/wallet/routes.py b/aries_cloudagent/wallet/routes.py index 68f0c22d07..6e73f455ba 100644 --- a/aries_cloudagent/wallet/routes.py +++ b/aries_cloudagent/wallet/routes.py @@ -455,11 +455,9 @@ async def wallet_set_public_did(request: web.BaseRequest): profile = context.profile route_manager = profile.inject(RouteManager) mediation_record = await route_manager.mediation_record_if_id( - profile=profile, - mediation_id=mediation_id, - or_default=True, + profile=profile, mediation_id=mediation_id, or_default=True ) - routing_keys=None + routing_keys = None if mediation_record: routing_keys = mediation_record.routing_keys diff --git a/aries_cloudagent/wallet/tests/test_indy_wallet.py b/aries_cloudagent/wallet/tests/test_indy_wallet.py index 4638d556e2..c128fd972a 100644 --- a/aries_cloudagent/wallet/tests/test_indy_wallet.py +++ b/aries_cloudagent/wallet/tests/test_indy_wallet.py @@ -140,16 +140,17 @@ async def test_set_did_endpoint_ledger(self, wallet: IndySdkWallet): assert "No ledger available" in str(excinfo.value) @pytest.mark.asyncio - async def test_set_did_endpoint_ledger_with_routing_keys(self, wallet: IndySdkWallet): - routing_keys = ['3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn'] + async def test_set_did_endpoint_ledger_with_routing_keys( + self, wallet: IndySdkWallet + ): + routing_keys = ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"] mock_ledger = async_mock.MagicMock( read_only=False, update_endpoint_for_did=async_mock.CoroutineMock() ) - info_pub = await wallet.create_public_did( - DIDMethod.SOV, - KeyType.ED25519, + info_pub = await wallet.create_public_did(DIDMethod.SOV, KeyType.ED25519) + await wallet.set_did_endpoint( + info_pub.did, "http://1.2.3.4:8021", mock_ledger, routing_keys=routing_keys ) - await wallet.set_did_endpoint(info_pub.did, "http://1.2.3.4:8021", mock_ledger, routing_keys=routing_keys) mock_ledger.update_endpoint_for_did.assert_called_once_with( info_pub.did, From 5e0d54894c0752db9e0dd20b6457f3952d615ebc Mon Sep 17 00:00:00 2001 From: Char Howland Date: Tue, 16 Aug 2022 10:22:23 -0600 Subject: [PATCH 428/872] fix: mock route manager and bind to profile Signed-off-by: Char Howland --- aries_cloudagent/wallet/tests/test_routes.py | 54 ++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/aries_cloudagent/wallet/tests/test_routes.py b/aries_cloudagent/wallet/tests/test_routes.py index efc9aba79d..c6adb4b5dc 100644 --- a/aries_cloudagent/wallet/tests/test_routes.py +++ b/aries_cloudagent/wallet/tests/test_routes.py @@ -380,8 +380,13 @@ async def test_set_public_did(self): ledger.update_endpoint_for_did = async_mock.AsyncMock() ledger.__aenter__ = async_mock.AsyncMock(return_value=ledger) self.profile.context.injector.bind_instance(BaseLedger, ledger) + mock_route_manager = async_mock.MagicMock() mock_route_manager.route_public_did = async_mock.AsyncMock() + mock_route_manager.mediation_record_if_id = async_mock.AsyncMock() + mock_route_manager.__aenter__ = async_mock.AsyncMock( + return_value=mock_route_manager + ) self.profile.context.injector.bind_instance(RouteManager, mock_route_manager) with async_mock.patch.object( @@ -414,6 +419,13 @@ async def test_set_public_did_no_query_did(self): await test_module.wallet_set_public_did(self.request) async def test_set_public_did_no_ledger(self): + + mock_route_manager = async_mock.MagicMock() + mock_route_manager.mediation_record_if_id = async_mock.AsyncMock() + mock_route_manager.__aenter__ = async_mock.AsyncMock( + return_value=mock_route_manager + ) + self.profile.context.injector.bind_instance(RouteManager, mock_route_manager) self.request.query = {"did": self.test_did} with self.assertRaises(test_module.web.HTTPForbidden): @@ -428,6 +440,13 @@ async def test_set_public_did_not_public(self): ledger.__aenter__ = async_mock.AsyncMock(return_value=ledger) self.profile.context.injector.bind_instance(BaseLedger, ledger) + mock_route_manager = async_mock.MagicMock() + mock_route_manager.mediation_record_if_id = async_mock.AsyncMock() + mock_route_manager.__aenter__ = async_mock.AsyncMock( + return_value=mock_route_manager + ) + self.profile.context.injector.bind_instance(RouteManager, mock_route_manager) + with self.assertRaises(test_module.web.HTTPNotFound): await test_module.wallet_set_public_did(self.request) @@ -440,6 +459,13 @@ async def test_set_public_did_not_found(self): ledger.__aenter__ = async_mock.AsyncMock(return_value=ledger) self.profile.context.injector.bind_instance(BaseLedger, ledger) + mock_route_manager = async_mock.MagicMock() + mock_route_manager.mediation_record_if_id = async_mock.AsyncMock() + mock_route_manager.__aenter__ = async_mock.AsyncMock( + return_value=mock_route_manager + ) + self.profile.context.injector.bind_instance(RouteManager, mock_route_manager) + self.wallet.get_local_did.side_effect = test_module.WalletNotFoundError() with self.assertRaises(test_module.web.HTTPNotFound): await test_module.wallet_set_public_did(self.request) @@ -453,6 +479,14 @@ async def test_set_public_did_x(self): ledger.get_key_for_did = async_mock.AsyncMock() ledger.__aenter__ = async_mock.AsyncMock(return_value=ledger) self.profile.context.injector.bind_instance(BaseLedger, ledger) + + mock_route_manager = async_mock.MagicMock() + mock_route_manager.mediation_record_if_id = async_mock.AsyncMock() + mock_route_manager.__aenter__ = async_mock.AsyncMock( + return_value=mock_route_manager + ) + self.profile.context.injector.bind_instance(RouteManager, mock_route_manager) + with async_mock.patch.object( test_module.web, "json_response", async_mock.Mock() ) as json_response: @@ -477,6 +511,13 @@ async def test_set_public_did_no_wallet_did(self): ledger.__aenter__ = async_mock.AsyncMock(return_value=ledger) self.profile.context.injector.bind_instance(BaseLedger, ledger) + mock_route_manager = async_mock.MagicMock() + mock_route_manager.mediation_record_if_id = async_mock.AsyncMock() + mock_route_manager.__aenter__ = async_mock.AsyncMock( + return_value=mock_route_manager + ) + self.profile.context.injector.bind_instance(RouteManager, mock_route_manager) + with async_mock.patch.object( test_module.web, "json_response", async_mock.Mock() ) as json_response: @@ -500,8 +541,13 @@ async def test_set_public_did_update_endpoint(self): ledger.get_key_for_did = async_mock.AsyncMock() ledger.__aenter__ = async_mock.AsyncMock(return_value=ledger) self.profile.context.injector.bind_instance(BaseLedger, ledger) + mock_route_manager = async_mock.MagicMock() mock_route_manager.route_public_did = async_mock.AsyncMock() + mock_route_manager.mediation_record_if_id = async_mock.AsyncMock() + mock_route_manager.__aenter__ = async_mock.AsyncMock( + return_value=mock_route_manager + ) self.profile.context.injector.bind_instance(RouteManager, mock_route_manager) with async_mock.patch.object( @@ -541,8 +587,15 @@ async def test_set_public_did_update_endpoint_use_default_update_in_wallet(self) ledger.get_key_for_did = async_mock.AsyncMock() ledger.__aenter__ = async_mock.AsyncMock(return_value=ledger) self.profile.context.injector.bind_instance(BaseLedger, ledger) + mock_route_manager = async_mock.MagicMock() mock_route_manager.route_public_did = async_mock.AsyncMock() + mock_route_manager.mediation_record_if_id = async_mock.AsyncMock( + return_value=None + ) + mock_route_manager.__aenter__ = async_mock.AsyncMock( + return_value=mock_route_manager + ) self.profile.context.injector.bind_instance(RouteManager, mock_route_manager) with async_mock.patch.object( @@ -565,6 +618,7 @@ async def test_set_public_did_update_endpoint_use_default_update_in_wallet(self) ledger, write_ledger=True, endorser_did=None, + routing_keys=None, ) json_response.assert_called_once_with( { From 06b9496316b857c7b2ae1523b8135880229cb8af Mon Sep 17 00:00:00 2001 From: Char Howland Date: Tue, 16 Aug 2022 11:47:00 -0600 Subject: [PATCH 429/872] feat: move implementation of _construct_attr_json to BaseLedger Signed-off-by: Char Howland --- aries_cloudagent/ledger/base.py | 20 +++++++++- aries_cloudagent/ledger/indy.py | 35 +--------------- aries_cloudagent/ledger/indy_vdr.py | 40 +------------------ aries_cloudagent/ledger/tests/test_indy.py | 4 +- .../ledger/tests/test_indy_vdr.py | 4 +- 5 files changed, 25 insertions(+), 78 deletions(-) diff --git a/aries_cloudagent/ledger/base.py b/aries_cloudagent/ledger/base.py index 4a78c8baed..8d1ea4e5d0 100644 --- a/aries_cloudagent/ledger/base.py +++ b/aries_cloudagent/ledger/base.py @@ -79,8 +79,7 @@ async def get_all_endpoints_for_did(self, did: str) -> dict: did: The DID to look up on the ledger or in the cache """ - @abstractmethod - async def construct_attr_json( + async def _construct_attr_json( self, endpoint: str, endpoint_type: EndpointType = None, @@ -96,6 +95,23 @@ async def construct_attr_json( routing_keys: List of routing_keys if mediator is present """ + if not routing_keys: + routing_keys = [] + + endpoint_dict = {"endpoint": endpoint} + + if all_exist_endpoints: + all_exist_endpoints[endpoint_type.indy] = endpoint_dict + endpoint_dict["routingKeys"] = routing_keys + attr_json = json.dumps({"endpoint": all_exist_endpoints}) + + else: + endpoint_val = {endpoint_type.indy: endpoint_dict} + endpoint_dict["routingKeys"] = routing_keys + attr_json = json.dumps({"endpoint": endpoint_val}) + + return attr_json + @abstractmethod async def update_endpoint_for_did( self, diff --git a/aries_cloudagent/ledger/indy.py b/aries_cloudagent/ledger/indy.py index dbbe173f89..6f1c06e42f 100644 --- a/aries_cloudagent/ledger/indy.py +++ b/aries_cloudagent/ledger/indy.py @@ -738,39 +738,6 @@ async def get_endpoint_for_did( return address - async def construct_attr_json( - self, - endpoint: str, - endpoint_type: EndpointType = None, - all_exist_endpoints: dict = None, - routing_keys: List[str] = None, - ) -> str: - """Create attr_json string. - - Args: - all_exist_endpoings: Dictionary of all existing endpoints - endpoint: The endpoint address - endpoint_type: The type of the endpoint - routing_keys: List of routing_keys if mediator is present - """ - - if not routing_keys: - routing_keys = [] - - endpoint_dict = {"endpoint": endpoint} - - if all_exist_endpoints: - all_exist_endpoints[endpoint_type.indy] = endpoint_dict - endpoint_dict["routingKeys"] = routing_keys - attr_json = json.dumps({"endpoint": all_exist_endpoints}) - - else: - endpoint_val = {endpoint_type.indy: endpoint_dict} - endpoint_dict["routingKeys"] = routing_keys - attr_json = json.dumps({"endpoint": endpoint_val}) - - return attr_json - async def update_endpoint_for_did( self, did: str, @@ -811,7 +778,7 @@ async def update_endpoint_for_did( nym = self.did_to_nym(did) - attr_json = await self.construct_attr_json( + attr_json = await self._construct_attr_json( endpoint, endpoint_type, all_exist_endpoints, routing_keys ) diff --git a/aries_cloudagent/ledger/indy_vdr.py b/aries_cloudagent/ledger/indy_vdr.py index ac72f2d2d7..abd2e399b1 100644 --- a/aries_cloudagent/ledger/indy_vdr.py +++ b/aries_cloudagent/ledger/indy_vdr.py @@ -672,39 +672,6 @@ async def get_endpoint_for_did( return address - async def construct_attr_json( - self, - endpoint: str, - endpoint_type: EndpointType = None, - all_exist_endpoints: dict = None, - routing_keys: List[str] = None, - ) -> str: - """Create attr_json string. - - Args: - all_exist_endpoings: Dictionary of all existing endpoints - endpoint: The endpoint address - endpoint_type: The type of the endpoint - routing_keys: List of routing_keys if mediator is present - """ - - if not routing_keys: - routing_keys = [] - - endpoint_dict = {"endpoint": endpoint} - - if all_exist_endpoints: - all_exist_endpoints[endpoint_type.indy] = endpoint_dict - endpoint_dict["routingKeys"] = routing_keys - attr_json = json.dumps({"endpoint": all_exist_endpoints}) - - else: - endpoint_val = {endpoint_type.indy: endpoint_dict} - endpoint_dict["routingKeys"] = routing_keys - attr_json = json.dumps({"endpoint": endpoint_val}) - - return attr_json - async def update_endpoint_for_did( self, did: str, @@ -745,11 +712,8 @@ async def update_endpoint_for_did( nym = self.did_to_nym(did) - attr_json = await self.construct_attr_json( - endpoint, - endpoint_type, - all_exist_endpoints, - routing_keys, + attr_json = await self._construct_attr_json( + endpoint, endpoint_type, all_exist_endpoints, routing_keys ) try: diff --git a/aries_cloudagent/ledger/tests/test_indy.py b/aries_cloudagent/ledger/tests/test_indy.py index 01c87e0393..3585e25c17 100644 --- a/aries_cloudagent/ledger/tests/test_indy.py +++ b/aries_cloudagent/ledger/tests/test_indy.py @@ -2305,7 +2305,7 @@ async def test_update_endpoint_for_did( async def test_construct_attr_json_with_routing_keys(self, mock_close, mock_open): ledger = IndySdkLedger(IndySdkLedgerPool("name", checked=True), self.profile) async with ledger: - attr_json = await ledger.construct_attr_json( + attr_json = await ledger._construct_attr_json( "https://url", EndpointType.ENDPOINT, all_exist_endpoints={"Endpoint": "https://endpoint"}, @@ -2346,7 +2346,7 @@ async def test_update_endpoint_for_did_calls_attr_json( IndySdkWallet, "get_public_did" ) as mock_wallet_get_public_did, async_mock.patch.object( ledger, - "construct_attr_json", + "_construct_attr_json", async_mock.CoroutineMock( return_value=json.dumps( { diff --git a/aries_cloudagent/ledger/tests/test_indy_vdr.py b/aries_cloudagent/ledger/tests/test_indy_vdr.py index 7c10ac443d..13db2c5563 100644 --- a/aries_cloudagent/ledger/tests/test_indy_vdr.py +++ b/aries_cloudagent/ledger/tests/test_indy_vdr.py @@ -666,7 +666,7 @@ async def test_construct_attr_json( self, ledger: IndyVdrLedger, all_exist_endpoints, routing_keys, result ): async with ledger: - attr_json = await ledger.construct_attr_json( + attr_json = await ledger._construct_attr_json( "https://url", EndpointType.ENDPOINT, all_exist_endpoints, routing_keys ) assert attr_json == json.dumps(result) @@ -680,7 +680,7 @@ async def test_update_endpoint_for_did_calls_attr_json(self, ledger: IndyVdrLedg async with ledger: with async_mock.patch.object( ledger, - "construct_attr_json", + "_construct_attr_json", async_mock.CoroutineMock( return_value=json.dumps( { From 8a76a91340726c12b973d3dcb0843cc0e02f7e5b Mon Sep 17 00:00:00 2001 From: Char Howland Date: Tue, 16 Aug 2022 12:32:13 -0600 Subject: [PATCH 430/872] fix: update example endpoint Signed-off-by: Char Howland --- .../wallet/tests/test_indy_wallet.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/aries_cloudagent/wallet/tests/test_indy_wallet.py b/aries_cloudagent/wallet/tests/test_indy_wallet.py index c128fd972a..6044a6a658 100644 --- a/aries_cloudagent/wallet/tests/test_indy_wallet.py +++ b/aries_cloudagent/wallet/tests/test_indy_wallet.py @@ -123,20 +123,20 @@ async def test_set_did_endpoint_ledger(self, wallet: IndySdkWallet): DIDMethod.SOV, KeyType.ED25519, ) - await wallet.set_did_endpoint(info_pub.did, "http://1.2.3.4:8021", mock_ledger) + await wallet.set_did_endpoint(info_pub.did, "https://example.com", mock_ledger) mock_ledger.update_endpoint_for_did.assert_called_once_with( info_pub.did, - "http://1.2.3.4:8021", + "https://example.com", EndpointType.ENDPOINT, endorser_did=None, write_ledger=True, routing_keys=None, ) info_pub2 = await wallet.get_public_did() - assert info_pub2.metadata["endpoint"] == "http://1.2.3.4:8021" + assert info_pub2.metadata["endpoint"] == "https://example.com" with pytest.raises(test_module.LedgerConfigError) as excinfo: - await wallet.set_did_endpoint(info_pub.did, "http://1.2.3.4:8021", None) + await wallet.set_did_endpoint(info_pub.did, "https://example.com", None) assert "No ledger available" in str(excinfo.value) @pytest.mark.asyncio @@ -149,12 +149,12 @@ async def test_set_did_endpoint_ledger_with_routing_keys( ) info_pub = await wallet.create_public_did(DIDMethod.SOV, KeyType.ED25519) await wallet.set_did_endpoint( - info_pub.did, "http://1.2.3.4:8021", mock_ledger, routing_keys=routing_keys + info_pub.did, "https://example.com", mock_ledger, routing_keys=routing_keys ) mock_ledger.update_endpoint_for_did.assert_called_once_with( info_pub.did, - "http://1.2.3.4:8021", + "https://example.com", EndpointType.ENDPOINT, endorser_did=None, write_ledger=True, @@ -170,13 +170,13 @@ async def test_set_did_endpoint_readonly_ledger(self, wallet: IndySdkWallet): DIDMethod.SOV, KeyType.ED25519, ) - await wallet.set_did_endpoint(info_pub.did, "http://1.2.3.4:8021", mock_ledger) + await wallet.set_did_endpoint(info_pub.did, "https://example.com", mock_ledger) mock_ledger.update_endpoint_for_did.assert_not_called() info_pub2 = await wallet.get_public_did() - assert info_pub2.metadata["endpoint"] == "http://1.2.3.4:8021" + assert info_pub2.metadata["endpoint"] == "https://example.com" with pytest.raises(test_module.LedgerConfigError) as excinfo: - await wallet.set_did_endpoint(info_pub.did, "http://1.2.3.4:8021", None) + await wallet.set_did_endpoint(info_pub.did, "https://example.com", None) assert "No ledger available" in str(excinfo.value) @pytest.mark.asyncio From 34090a3cdc54451e4986bdcbdb2a7c59763da765 Mon Sep 17 00:00:00 2001 From: Char Howland Date: Mon, 22 Aug 2022 10:20:51 -0600 Subject: [PATCH 431/872] fix: mock is_ledger_read_only Signed-off-by: Char Howland --- aries_cloudagent/ledger/tests/test_indy.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/aries_cloudagent/ledger/tests/test_indy.py b/aries_cloudagent/ledger/tests/test_indy.py index 3585e25c17..767eaa50a3 100644 --- a/aries_cloudagent/ledger/tests/test_indy.py +++ b/aries_cloudagent/ledger/tests/test_indy.py @@ -2328,9 +2328,11 @@ async def test_construct_attr_json_with_routing_keys(self, mock_close, mock_open @async_mock.patch("indy.ledger.build_get_attrib_request") @async_mock.patch("indy.ledger.build_attrib_request") @async_mock.patch("aries_cloudagent.ledger.indy.IndySdkLedger._submit") + @async_mock.patch("aries_cloudagent.ledger.indy.IndySdkLedger.is_ledger_read_only") @pytest.mark.asyncio async def test_update_endpoint_for_did_calls_attr_json( self, + mock_is_ledger_read_only, mock_submit, mock_build_attrib_req, mock_build_get_attrib_req, @@ -2341,6 +2343,7 @@ async def test_update_endpoint_for_did_calls_attr_json( mock_wallet = async_mock.MagicMock() self.session.context.injector.bind_provider(BaseWallet, mock_wallet) ledger = IndySdkLedger(IndySdkLedgerPool("name", checked=True), self.profile) + mock_is_ledger_read_only.return_value = False async with ledger: with async_mock.patch.object( IndySdkWallet, "get_public_did" From 3edd2f81f12fed4bd51d29316c6d05f696c60e99 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Wed, 24 Aug 2022 06:30:06 -0700 Subject: [PATCH 432/872] Remove aca-py check for unrevealed revealed attrs on proof validation Signed-off-by: Ian Costanzo --- aries_cloudagent/indy/verifier.py | 8 ++++++++ demo/runners/agent_container.py | 10 ++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/indy/verifier.py b/aries_cloudagent/indy/verifier.py index f14ad14b24..db0a71bd93 100644 --- a/aries_cloudagent/indy/verifier.py +++ b/aries_cloudagent/indy/verifier.py @@ -159,6 +159,7 @@ async def check_timestamps( # timestamp superfluous, missing, or outside non-revocation interval revealed_attrs = pres["requested_proof"].get("revealed_attrs", {}) + unrevealed_attrs = pres["requested_proof"].get("unrevealed_attrs", {}) revealed_groups = pres["requested_proof"].get("revealed_attr_groups", {}) self_attested = pres["requested_proof"].get("self_attested_attrs", {}) preds = pres["requested_proof"].get("predicates", {}) @@ -190,6 +191,9 @@ async def check_timestamps( f"{uuid} falls outside non-revocation interval " f"{non_revoc_intervals[uuid]}" ) + elif uuid in unrevealed_attrs: + # nothing to do, attribute value is not revealed + pass elif uuid not in self_attested: raise ValueError( f"Presentation attributes mismatch requested attribute {uuid}" @@ -297,12 +301,16 @@ async def pre_verify(self, pres_req: dict, pres: dict): raise ValueError(f"Missing requested predicate '{uuid}'") revealed_attrs = pres["requested_proof"].get("revealed_attrs", {}) + unrevealed_attrs = pres["requested_proof"].get("unrevealed_attrs", {}) revealed_groups = pres["requested_proof"].get("revealed_attr_groups", {}) self_attested = pres["requested_proof"].get("self_attested_attrs", {}) for (uuid, req_attr) in pres_req["requested_attributes"].items(): if "name" in req_attr: if uuid in revealed_attrs: pres_req_attr_spec = {req_attr["name"]: revealed_attrs[uuid]} + elif uuid in unrevealed_attrs: + # unrevealed attribute, nothing to do + pres_req_attr_spec = {} elif uuid in self_attested: if not req_attr.get("restrictions"): continue diff --git a/demo/runners/agent_container.py b/demo/runners/agent_container.py index 065a3cb4ea..7018ee046f 100644 --- a/demo/runners/agent_container.py +++ b/demo/runners/agent_container.py @@ -331,14 +331,17 @@ async def handle_present_proof(self, message): if referent not in credentials_by_reft: credentials_by_reft[referent] = row + # submit the proof wit one unrevealed revealed attribute + revealed_flag = False for referent in presentation_request["requested_attributes"]: if referent in credentials_by_reft: revealed[referent] = { "cred_id": credentials_by_reft[referent]["cred_info"][ "referent" ], - "revealed": True, + "revealed": revealed_flag, } + revealed_flag = True else: self_attested[referent] = "my self-attested value" @@ -419,14 +422,17 @@ async def handle_present_proof_v2_0(self, message): if referent not in creds_by_reft: creds_by_reft[referent] = row + # submit the proof wit one unrevealed revealed attribute + revealed_flag = False for referent in pres_request_indy["requested_attributes"]: if referent in creds_by_reft: revealed[referent] = { "cred_id": creds_by_reft[referent]["cred_info"][ "referent" ], - "revealed": True, + "revealed": revealed_flag, } + revealed_flag = True else: self_attested[referent] = "my self-attested value" From 2e8b9b8e5d84c41f1cfc58cad90545512d8998a8 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Wed, 24 Aug 2022 10:53:46 -0400 Subject: [PATCH 433/872] feat: include connection ids in keylist update webhook Signed-off-by: Daniel Bluhm --- .../coordinate_mediation/v1_0/manager.py | 18 ++++++++ .../v1_0/tests/test_mediation_manager.py | 45 ++++++++++++++++++- 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py index 3a3ad0f151..c8dcb07622 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py @@ -598,12 +598,30 @@ async def notify_keylist_updated( self, connection_id: str, response: KeylistUpdateResponse ): """Notify of keylist update response received.""" + # Retrieve connection IDs associated with recipient keys + # recipient key -> connection id + async with self._profile.session() as session: + try: + routes = [ + await RouteRecord.retrieve_by_recipient_key( + session, updated.recipient_key + ) + for updated in response.updated + ] + except StorageNotFoundError as err: + raise MediationManagerError( + "Unknown recipient key received in keylist update response" + ) from err + await self._profile.notify( self.KEYLIST_UPDATED_EVENT, { "connection_id": connection_id, "thread_id": response._thread_id, "updated": [update.serialize() for update in response.updated], + "mediated_connections": { + route.recipient_key: route.connection_id for route in routes + }, }, ) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py index f3a03c0a30..75489850ef 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py @@ -474,9 +474,23 @@ async def test_store_update_results_errors(self, caplog, manager): print(caplog.text) async def test_notify_keylist_updated( - self, manager: MediationManager, mock_event_bus: MockEventBus + self, + manager: MediationManager, + mock_event_bus: MockEventBus, + session: ProfileSession, ): """test notify_keylist_updated.""" + await RouteRecord( + role=RouteRecord.ROLE_CLIENT, + connection_id="conn_id_1", + recipient_key=TEST_ROUTE_VERKEY, + ).save(session) + await RouteRecord( + role=RouteRecord.ROLE_CLIENT, + connection_id="conn_id_2", + recipient_key=TEST_VERKEY, + ).save(session) + response = KeylistUpdateResponse( updated=[ KeylistUpdated( @@ -491,6 +505,7 @@ async def test_notify_keylist_updated( ), ], ) + response.assign_thread_id(TEST_THREAD_ID) await manager.notify_keylist_updated(TEST_CONN_ID, response) assert mock_event_bus.events @@ -499,8 +514,36 @@ async def test_notify_keylist_updated( "connection_id": TEST_CONN_ID, "thread_id": TEST_THREAD_ID, "updated": [result.serialize() for result in response.updated], + "mediated_connections": { + TEST_ROUTE_VERKEY: "conn_id_1", + TEST_VERKEY: "conn_id_2", + }, } + async def test_notify_keylist_updated_x_unknown_recip_key( + self, + manager: MediationManager, + ): + """test notify_keylist_updated.""" + response = KeylistUpdateResponse( + updated=[ + KeylistUpdated( + recipient_key=TEST_ROUTE_VERKEY, + action=KeylistUpdateRule.RULE_ADD, + result=KeylistUpdated.RESULT_SUCCESS, + ), + KeylistUpdated( + recipient_key=TEST_VERKEY, + action=KeylistUpdateRule.RULE_REMOVE, + result=KeylistUpdated.RESULT_SUCCESS, + ), + ], + ) + + response.assign_thread_id(TEST_THREAD_ID) + with pytest.raises(MediationManagerError): + await manager.notify_keylist_updated(TEST_CONN_ID, response) + async def test_get_my_keylist(self, session, manager): """test_get_my_keylist.""" await RouteRecord( From 064a32d538ead727992d836bb88a717f1e60e774 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Wed, 24 Aug 2022 12:45:31 -0700 Subject: [PATCH 434/872] Proof validation status messages wip Signed-off-by: Ian Costanzo --- aries_cloudagent/indy/credx/verifier.py | 21 +++++--- aries_cloudagent/indy/sdk/verifier.py | 21 +++++--- aries_cloudagent/indy/verifier.py | 50 +++++++++++++++++-- .../protocols/present_proof/v1_0/manager.py | 22 ++++---- .../v1_0/models/presentation_exchange.py | 10 ++++ .../v2_0/formats/indy/handler.py | 18 +++---- .../v2_0/models/pres_exchange.py | 10 ++++ 7 files changed, 113 insertions(+), 39 deletions(-) diff --git a/aries_cloudagent/indy/credx/verifier.py b/aries_cloudagent/indy/credx/verifier.py index c6677cfa7b..309d0bfdaf 100644 --- a/aries_cloudagent/indy/credx/verifier.py +++ b/aries_cloudagent/indy/credx/verifier.py @@ -7,7 +7,7 @@ from ...core.profile import Profile -from ..verifier import IndyVerifier +from ..verifier import IndyVerifier, PresVerifyMsg LOGGER = logging.getLogger(__name__) @@ -33,7 +33,7 @@ async def verify_presentation( credential_definitions, rev_reg_defs, rev_reg_entries, - ) -> bool: + ) -> (bool, list): """ Verify a presentation. @@ -46,16 +46,20 @@ async def verify_presentation( rev_reg_entries: revocation registry entries """ + msgs = [] try: - self.non_revoc_intervals(pres_req, pres, credential_definitions) - await self.check_timestamps(self.profile, pres_req, pres, rev_reg_defs) - await self.pre_verify(pres_req, pres) + msgs += self.non_revoc_intervals(pres_req, pres, credential_definitions) + msgs += await self.check_timestamps(self.profile, pres_req, pres, rev_reg_defs) + msgs += await self.pre_verify(pres_req, pres) except ValueError as err: + msgs.append( + f"{PresVerifyMsg.PRES_VALUE_ERROR.value}::{err}" + ) LOGGER.error( f"Presentation on nonce={pres_req['nonce']} " f"cannot be validated: {str(err)}" ) - return False + return (False, msgs) try: presentation = Presentation.load(pres) @@ -69,10 +73,13 @@ async def verify_presentation( rev_reg_entries, ) except CredxError: + msgs.append( + f"{PresVerifyMsg.PRES_VERIFY_ERROR.value}::{err}" + ) LOGGER.exception( f"Validation of presentation on nonce={pres_req['nonce']} " "failed with error" ) verified = False - return verified + return (verified, msgs) diff --git a/aries_cloudagent/indy/sdk/verifier.py b/aries_cloudagent/indy/sdk/verifier.py index b9e087aa82..409582a757 100644 --- a/aries_cloudagent/indy/sdk/verifier.py +++ b/aries_cloudagent/indy/sdk/verifier.py @@ -8,7 +8,7 @@ from ...core.profile import Profile -from ..verifier import IndyVerifier +from ..verifier import IndyVerifier, PresVerifyMsg LOGGER = logging.getLogger(__name__) @@ -34,7 +34,7 @@ async def verify_presentation( credential_definitions, rev_reg_defs, rev_reg_entries, - ) -> bool: + ) -> (bool, list): """ Verify a presentation. @@ -49,16 +49,20 @@ async def verify_presentation( LOGGER.debug(f">>> received presentation: {pres}") LOGGER.debug(f">>> for pres_req: {pres_req}") + msgs = [] try: - self.non_revoc_intervals(pres_req, pres, credential_definitions) - await self.check_timestamps(self.profile, pres_req, pres, rev_reg_defs) - await self.pre_verify(pres_req, pres) + msgs += self.non_revoc_intervals(pres_req, pres, credential_definitions) + msgs += await self.check_timestamps(self.profile, pres_req, pres, rev_reg_defs) + msgs += await self.pre_verify(pres_req, pres) except ValueError as err: + msgs.append( + f"{PresVerifyMsg.PRES_VALUE_ERROR.value}::{err}" + ) LOGGER.error( f"Presentation on nonce={pres_req['nonce']} " f"cannot be validated: {str(err)}" ) - return False + return (False, msgs) LOGGER.debug(f">>> verifying presentation: {pres}") LOGGER.debug(f">>> for pres_req: {pres_req}") @@ -72,10 +76,13 @@ async def verify_presentation( json.dumps(rev_reg_entries), ) except IndyError: + msgs.append( + f"{PresVerifyMsg.PRES_VERIFY_ERROR.value}::{err}" + ) LOGGER.exception( f"Validation of presentation on nonce={pres_req['nonce']} " "failed with error" ) verified = False - return verified + return (verified, msgs) diff --git a/aries_cloudagent/indy/verifier.py b/aries_cloudagent/indy/verifier.py index db0a71bd93..0e2d260b84 100644 --- a/aries_cloudagent/indy/verifier.py +++ b/aries_cloudagent/indy/verifier.py @@ -3,6 +3,7 @@ import logging from abc import ABC, ABCMeta, abstractmethod +from enum import Enum from time import time from typing import Mapping @@ -16,8 +17,17 @@ from .models.xform import indy_proof_req2non_revoc_intervals + LOGGER = logging.getLogger(__name__) +class PresVerifyMsg(str, Enum): + RMV_REFERENT_NON_REVOC_INTERVAL = "RMV_RFNT_NRI" + RMV_GLOBAL_NON_REVOC_INTERVAL = "RMV_GLB_NRI" + TSTMP_OUT_NON_REVOC_INTRVAL = "TS_OUT_NRI" + CT_UNREVEALED_ATTRIBUTES = "UNRVL_ATTR" + PRES_VALUE_ERROR = "VALUE_ERROR" + PRES_VERIFY_ERROR = "VERIFY_ERROR" + class IndyVerifier(ABC, metaclass=ABCMeta): """Base class for Indy Verifier.""" @@ -32,7 +42,7 @@ def __repr__(self) -> str: """ return "<{}>".format(self.__class__.__name__) - def non_revoc_intervals(self, pres_req: dict, pres: dict, cred_defs: dict): + def non_revoc_intervals(self, pres_req: dict, pres: dict, cred_defs: dict) -> list: """ Remove superfluous non-revocation intervals in presentation request. @@ -45,6 +55,7 @@ def non_revoc_intervals(self, pres_req: dict, pres: dict, cred_defs: dict): pres: corresponding presentation """ + msgs = [] for (req_proof_key, pres_key) in { "revealed_attrs": "requested_attributes", "revealed_attr_groups": "requested_attributes", @@ -60,6 +71,10 @@ def non_revoc_intervals(self, pres_req: dict, pres: dict, cred_defs: dict): if uuid in pres_req[pres_key] and pres_req[pres_key][uuid].pop( "non_revoked", None ): + msgs.append( + f"{PresVerifyMsg.RMV_REFERENT_NON_REVOC_INTERVAL.value}::" + f"{uuid}" + ) LOGGER.info( ( "Amended presentation request (nonce=%s): removed " @@ -79,6 +94,7 @@ def non_revoc_intervals(self, pres_req: dict, pres: dict, cred_defs: dict): for spec in pres["identifiers"] ): pres_req.pop("non_revoked", None) + msgs.append(PresVerifyMsg.RMV_GLOBAL_NON_REVOC_INTERVAL.value) LOGGER.warning( ( "Amended presentation request (nonce=%s); removed global " @@ -86,6 +102,7 @@ def non_revoc_intervals(self, pres_req: dict, pres: dict, cred_defs: dict): ), pres_req["nonce"], ) + return msgs async def check_timestamps( self, @@ -93,7 +110,7 @@ async def check_timestamps( pres_req: Mapping, pres: Mapping, rev_reg_defs: Mapping, - ): + ) -> list: """ Check for suspicious, missing, and superfluous timestamps. @@ -106,6 +123,7 @@ async def check_timestamps( pres: indy proof request rev_reg_defs: rev reg defs by rev reg id, augmented with transaction times """ + msgs = [] now = int(time()) non_revoc_intervals = indy_proof_req2non_revoc_intervals(pres_req) LOGGER.debug(f">>> got non-revoc intervals: {non_revoc_intervals}") @@ -186,6 +204,10 @@ async def check_timestamps( < timestamp < non_revoc_intervals[uuid].get("to", now) ): + msgs.append( + f"{PresVerifyMsg.TSTMP_OUT_NON_REVOC_INTRVAL.value}::" + f"{uuid}" + ) LOGGER.info( f"Timestamp {timestamp} from ledger for item" f"{uuid} falls outside non-revocation interval " @@ -193,7 +215,10 @@ async def check_timestamps( ) elif uuid in unrevealed_attrs: # nothing to do, attribute value is not revealed - pass + msgs.append( + f"{PresVerifyMsg.CT_UNREVEALED_ATTRIBUTES.value}::" + f"{uuid}" + ) elif uuid not in self_attested: raise ValueError( f"Presentation attributes mismatch requested attribute {uuid}" @@ -221,6 +246,10 @@ async def check_timestamps( < timestamp < non_revoc_intervals[uuid].get("to", now) ): + msgs.append( + f"{PresVerifyMsg.TSTMP_OUT_NON_REVOC_INTRVAL.value}::" + f"{uuid}" + ) LOGGER.warning( f"Timestamp {timestamp} from ledger for item" f"{uuid} falls outside non-revocation interval " @@ -247,13 +276,18 @@ async def check_timestamps( < timestamp < non_revoc_intervals[uuid].get("to", now) ): + msgs.append( + f"{PresVerifyMsg.TSTMP_OUT_NON_REVOC_INTRVAL.value}::" + f"{uuid}" + ) LOGGER.warning( f"Best-effort timestamp {timestamp} " "from ledger falls outside non-revocation interval " f"{non_revoc_intervals[uuid]}" ) + return msgs - async def pre_verify(self, pres_req: dict, pres: dict): + async def pre_verify(self, pres_req: dict, pres: dict) -> list: """ Check for essential components and tampering in presentation. @@ -265,6 +299,7 @@ async def pre_verify(self, pres_req: dict, pres: dict): pres: corresponding presentation """ + msgs = [] if not ( pres_req and "requested_predicates" in pres_req @@ -311,6 +346,10 @@ async def pre_verify(self, pres_req: dict, pres: dict): elif uuid in unrevealed_attrs: # unrevealed attribute, nothing to do pres_req_attr_spec = {} + msgs.append( + f"{PresVerifyMsg.CT_UNREVEALED_ATTRIBUTES.value}::" + f"{uuid}" + ) elif uuid in self_attested: if not req_attr.get("restrictions"): continue @@ -347,6 +386,7 @@ async def pre_verify(self, pres_req: dict, pres: dict): raise ValueError(f"Encoded representation mismatch for '{attr}'") if primary_enco != encode(spec["raw"]): raise ValueError(f"Encoded representation mismatch for '{attr}'") + return msgs @abstractmethod def verify_presentation( @@ -357,7 +397,7 @@ def verify_presentation( credential_definitions, rev_reg_defs, rev_reg_entries, - ): + ) -> (bool, list): """ Verify a presentation. diff --git a/aries_cloudagent/protocols/present_proof/v1_0/manager.py b/aries_cloudagent/protocols/present_proof/v1_0/manager.py index c951aa8c02..b8ae25b122 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/manager.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/manager.py @@ -417,18 +417,18 @@ async def verify_presentation( ) = await indy_handler.process_pres_identifiers(indy_proof["identifiers"]) verifier = self._profile.inject(IndyVerifier) - presentation_exchange_record.verified = json.dumps( # tag: needs string value - await verifier.verify_presentation( - dict( - indy_proof_request - ), # copy to avoid changing the proof req in the stored pres exch - indy_proof, - schemas, - cred_defs, - rev_reg_defs, - rev_reg_entries, - ) + (verified_bool, verified_msgs) = await verifier.verify_presentation( + dict( + indy_proof_request + ), # copy to avoid changing the proof req in the stored pres exch + indy_proof, + schemas, + cred_defs, + rev_reg_defs, + rev_reg_entries, ) + presentation_exchange_record.verified = json.dumps(verified_bool) + presentation_exchange_record.verified_msgs = verified_msgs presentation_exchange_record.state = V10PresentationExchange.STATE_VERIFIED async with self._profile.session() as session: diff --git a/aries_cloudagent/protocols/present_proof/v1_0/models/presentation_exchange.py b/aries_cloudagent/protocols/present_proof/v1_0/models/presentation_exchange.py index 296740b5f9..80db45f86c 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/models/presentation_exchange.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/models/presentation_exchange.py @@ -74,6 +74,7 @@ def __init__( ] = None, # aries message presentation: Union[IndyProof, Mapping] = None, # indy proof verified: str = None, + verified_msgs: list = None, auto_present: bool = False, auto_verify: bool = False, error_msg: str = None, @@ -96,6 +97,7 @@ def __init__( ) self._presentation = IndyProof.serde(presentation) self.verified = verified + self.verified_msgs = verified_msgs self.auto_present = auto_present self.auto_verify = auto_verify self.error_msg = error_msg @@ -208,6 +210,7 @@ def record_value(self) -> Mapping: "auto_verify", "error_msg", "verified", + "verified_msgs", "trace", ) }, @@ -295,6 +298,13 @@ class Meta: example="true", validate=validate.OneOf(["true", "false"]), ) + verified_msgs = fields.List( + fields.Str( + required=False, + description="Proof verification warning or error information", + ), + required=False, + ) auto_present = fields.Bool( required=False, description="Prover choice to auto-present proof as verifier requests", diff --git a/aries_cloudagent/protocols/present_proof/v2_0/formats/indy/handler.py b/aries_cloudagent/protocols/present_proof/v2_0/formats/indy/handler.py index 54ef915af0..3bb2eaf1ee 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/formats/indy/handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/formats/indy/handler.py @@ -329,14 +329,14 @@ async def verify_pres(self, pres_ex_record: V20PresExRecord) -> V20PresExRecord: ) = await indy_handler.process_pres_identifiers(indy_proof["identifiers"]) verifier = self._profile.inject(IndyVerifier) - pres_ex_record.verified = json.dumps( # tag: needs string value - await verifier.verify_presentation( - indy_proof_request, - indy_proof, - schemas, - cred_defs, - rev_reg_defs, - rev_reg_entries, - ) + (verified, verified_msgs) = await verifier.verify_presentation( + indy_proof_request, + indy_proof, + schemas, + cred_defs, + rev_reg_defs, + rev_reg_entries, ) + pres_ex_record.verified = json.dumps(verified) + pres_ex_record.verified_msgs = verified_msgs return pres_ex_record diff --git a/aries_cloudagent/protocols/present_proof/v2_0/models/pres_exchange.py b/aries_cloudagent/protocols/present_proof/v2_0/models/pres_exchange.py index f77a53166b..cc314aa9db 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/models/pres_exchange.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/models/pres_exchange.py @@ -62,6 +62,7 @@ def __init__( pres_request: Union[V20PresRequest, Mapping] = None, # aries message pres: Union[V20Pres, Mapping] = None, # aries message verified: str = None, + verified_msgs: list = None, auto_present: bool = False, auto_verify: bool = False, error_msg: str = None, @@ -80,6 +81,7 @@ def __init__( self._pres_request = V20PresRequest.serde(pres_request) self._pres = V20Pres.serde(pres) self.verified = verified + self.verified_msgs = verified_msgs self.auto_present = auto_present self.auto_verify = auto_verify self.error_msg = error_msg @@ -191,6 +193,7 @@ def record_value(self) -> Mapping: "role", "state", "verified", + "verified_msgs", "auto_present", "auto_verify", "error_msg", @@ -307,6 +310,13 @@ class Meta: example="true", validate=validate.OneOf(["true", "false"]), ) + verified_msgs = fields.List( + fields.Str( + required=False, + description="Proof verification warning or error information", + ), + required=False, + ) auto_present = fields.Bool( required=False, description="Prover choice to auto-present proof as verifier requests", From eb4826a7e9ea0c5abe597e97af3701f14112aad2 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Thu, 25 Aug 2022 09:27:35 -0400 Subject: [PATCH 435/872] chore: update pydid Recent changes to the resolution of sov/indy DIDs caused PyDID to parse service endpoints that should have been recognized as DIDComm services as plain services. This broke some logic in accepting invitations from public DIDs. Pulling in these updates from PyDID resolves the issue. Signed-off-by: Daniel Bluhm --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 76ed1bf719..70bc72e54e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -20,7 +20,7 @@ pyld~=2.0.3 pyyaml~=5.4.0 ConfigArgParse~=1.5.3 pyjwt~=2.4.0 -pydid~=0.3.3 +pydid~=0.3.6 jsonpath_ng==1.5.2 pytz~=2021.1 python-dateutil~=2.8.1 From ccf3290e0a4880ed3da124404d04e8135a444b4a Mon Sep 17 00:00:00 2001 From: Ry Jones Date: Thu, 25 Aug 2022 07:28:26 -0700 Subject: [PATCH 436/872] Revert "[#1895] Stopping the aca-py shell process keeps python process running" This reverts commit 67db5de43770933efd8cbc816ffb5d3850fa5785. Signed-off-by: Ry Jones --- bin/aca-py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/aca-py b/bin/aca-py index 3ada78acc0..ae99bf1862 100755 --- a/bin/aca-py +++ b/bin/aca-py @@ -7,4 +7,4 @@ if [ -z "$PYTHON" ]; then fi fi -exec $PYTHON -m aries_cloudagent "$@" +$PYTHON -m aries_cloudagent "$@" From e5bc71ec211553adc5e2cca2d33e4f4569efbd87 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Thu, 25 Aug 2022 13:07:43 -0700 Subject: [PATCH 437/872] Fix up unit tests Signed-off-by: Ian Costanzo --- aries_cloudagent/indy/credx/verifier.py | 16 +++---- .../indy/sdk/tests/test_verifier.py | 44 +++++++++++++++---- aries_cloudagent/indy/sdk/verifier.py | 16 +++---- aries_cloudagent/indy/verifier.py | 12 ++--- .../protocols/present_proof/v1_0/manager.py | 2 +- .../v1_0/models/tests/test_record.py | 1 + .../present_proof/v1_0/tests/test_manager.py | 2 +- .../v2_0/formats/indy/handler.py | 2 +- .../v2_0/models/tests/test_record.py | 1 + .../present_proof/v2_0/tests/test_manager.py | 2 +- 10 files changed, 63 insertions(+), 35 deletions(-) diff --git a/aries_cloudagent/indy/credx/verifier.py b/aries_cloudagent/indy/credx/verifier.py index 309d0bfdaf..e625076ecd 100644 --- a/aries_cloudagent/indy/credx/verifier.py +++ b/aries_cloudagent/indy/credx/verifier.py @@ -49,12 +49,13 @@ async def verify_presentation( msgs = [] try: msgs += self.non_revoc_intervals(pres_req, pres, credential_definitions) - msgs += await self.check_timestamps(self.profile, pres_req, pres, rev_reg_defs) + msgs += await self.check_timestamps( + self.profile, pres_req, pres, rev_reg_defs + ) msgs += await self.pre_verify(pres_req, pres) except ValueError as err: - msgs.append( - f"{PresVerifyMsg.PRES_VALUE_ERROR.value}::{err}" - ) + s = str(err) + msgs.append(f"{PresVerifyMsg.PRES_VALUE_ERROR.value}::{s}") LOGGER.error( f"Presentation on nonce={pres_req['nonce']} " f"cannot be validated: {str(err)}" @@ -72,10 +73,9 @@ async def verify_presentation( rev_reg_defs.values(), rev_reg_entries, ) - except CredxError: - msgs.append( - f"{PresVerifyMsg.PRES_VERIFY_ERROR.value}::{err}" - ) + except CredxError as err: + s = str(err) + msgs.append(f"{PresVerifyMsg.PRES_VERIFY_ERROR.value}::{s}") LOGGER.exception( f"Validation of presentation on nonce={pres_req['nonce']} " "failed with error" diff --git a/aries_cloudagent/indy/sdk/tests/test_verifier.py b/aries_cloudagent/indy/sdk/tests/test_verifier.py index 44784b1ad5..0f1cbef3ab 100644 --- a/aries_cloudagent/indy/sdk/tests/test_verifier.py +++ b/aries_cloudagent/indy/sdk/tests/test_verifier.py @@ -336,7 +336,7 @@ async def test_verify_presentation(self, mock_verify): ) as mock_get_ledger: mock_get_ledger.return_value = (None, self.ledger) INDY_PROOF_REQ_X = deepcopy(INDY_PROOF_REQ_PRED_NAMES) - verified = await self.verifier.verify_presentation( + (verified, msgs) = await self.verifier.verify_presentation( INDY_PROOF_REQ_X, INDY_PROOF_PRED_NAMES, "schemas", @@ -370,7 +370,7 @@ async def test_verify_presentation_x_indy(self, mock_verify): IndyLedgerRequestsExecutor, "get_ledger_for_identifier" ) as mock_get_ledger: mock_get_ledger.return_value = ("test", self.ledger) - verified = await self.verifier.verify_presentation( + (verified, msgs) = await self.verifier.verify_presentation( INDY_PROOF_REQ_NAME, INDY_PROOF_NAME, "schemas", @@ -397,7 +397,7 @@ async def test_check_encoding_attr(self, mock_verify): ) as mock_get_ledger: mock_get_ledger.return_value = (None, self.ledger) mock_verify.return_value = True - verified = await self.verifier.verify_presentation( + (verified, msgs) = await self.verifier.verify_presentation( INDY_PROOF_REQ_NAME, INDY_PROOF_NAME, "schemas", @@ -415,6 +415,8 @@ async def test_check_encoding_attr(self, mock_verify): json.dumps("rev_reg_entries"), ) assert verified is True + assert len(msgs) == 1 + assert "TS_OUT_NRI::19_uuid" in msgs @async_mock.patch("indy.anoncreds.verifier_verify_proof") async def test_check_encoding_attr_tamper_raw(self, mock_verify): @@ -426,7 +428,7 @@ async def test_check_encoding_attr_tamper_raw(self, mock_verify): IndyLedgerRequestsExecutor, "get_ledger_for_identifier" ) as mock_get_ledger: mock_get_ledger.return_value = ("test", self.ledger) - verified = await self.verifier.verify_presentation( + (verified, msgs) = await self.verifier.verify_presentation( INDY_PROOF_REQ_NAME, INDY_PROOF_X, "schemas", @@ -438,6 +440,9 @@ async def test_check_encoding_attr_tamper_raw(self, mock_verify): mock_verify.assert_not_called() assert verified is False + assert len(msgs) == 2 + assert "TS_OUT_NRI::19_uuid" in msgs + assert "VALUE_ERROR::Encoded representation mismatch for 'Preferred Name'" in msgs @async_mock.patch("indy.anoncreds.verifier_verify_proof") async def test_check_encoding_attr_tamper_encoded(self, mock_verify): @@ -449,7 +454,7 @@ async def test_check_encoding_attr_tamper_encoded(self, mock_verify): IndyLedgerRequestsExecutor, "get_ledger_for_identifier" ) as mock_get_ledger: mock_get_ledger.return_value = (None, self.ledger) - verified = await self.verifier.verify_presentation( + (verified, msgs) = await self.verifier.verify_presentation( INDY_PROOF_REQ_NAME, INDY_PROOF_X, "schemas", @@ -461,6 +466,9 @@ async def test_check_encoding_attr_tamper_encoded(self, mock_verify): mock_verify.assert_not_called() assert verified is False + assert len(msgs) == 2 + assert "TS_OUT_NRI::19_uuid" in msgs + assert "VALUE_ERROR::Encoded representation mismatch for 'Preferred Name'" in msgs @async_mock.patch("indy.anoncreds.verifier_verify_proof") async def test_check_pred_names(self, mock_verify): @@ -470,7 +478,7 @@ async def test_check_pred_names(self, mock_verify): mock_get_ledger.return_value = ("test", self.ledger) mock_verify.return_value = True INDY_PROOF_REQ_X = deepcopy(INDY_PROOF_REQ_PRED_NAMES) - verified = await self.verifier.verify_presentation( + (verified, msgs) = await self.verifier.verify_presentation( INDY_PROOF_REQ_X, INDY_PROOF_PRED_NAMES, "schemas", @@ -491,6 +499,10 @@ async def test_check_pred_names(self, mock_verify): ) assert verified is True + assert len(msgs) == 3 + assert "TS_OUT_NRI::18_uuid" in msgs + assert "TS_OUT_NRI::18_id_GE_uuid" in msgs + assert "TS_OUT_NRI::18_busid_GE_uuid" in msgs @async_mock.patch("indy.anoncreds.verifier_verify_proof") async def test_check_pred_names_tamper_pred_value(self, mock_verify): @@ -502,7 +514,7 @@ async def test_check_pred_names_tamper_pred_value(self, mock_verify): IndyLedgerRequestsExecutor, "get_ledger_for_identifier" ) as mock_get_ledger: mock_get_ledger.return_value = (None, self.ledger) - verified = await self.verifier.verify_presentation( + (verified, msgs) = await self.verifier.verify_presentation( deepcopy(INDY_PROOF_REQ_PRED_NAMES), INDY_PROOF_X, "schemas", @@ -514,6 +526,11 @@ async def test_check_pred_names_tamper_pred_value(self, mock_verify): mock_verify.assert_not_called() assert verified is False + assert len(msgs) == 4 + assert "RMV_RFNT_NRI::18_uuid" in msgs + assert "RMV_RFNT_NRI::18_busid_GE_uuid" in msgs + assert "RMV_RFNT_NRI::18_id_GE_uuid" in msgs + assert "VALUE_ERROR::Timestamp on sub-proof #0 is superfluous vs. requested attribute group 18_uuid" in msgs @async_mock.patch("indy.anoncreds.verifier_verify_proof") async def test_check_pred_names_tamper_pred_req_attr(self, mock_verify): @@ -523,7 +540,7 @@ async def test_check_pred_names_tamper_pred_req_attr(self, mock_verify): IndyLedgerRequestsExecutor, "get_ledger_for_identifier" ) as mock_get_ledger: mock_get_ledger.return_value = (None, self.ledger) - verified = await self.verifier.verify_presentation( + (verified, msgs) = await self.verifier.verify_presentation( INDY_PROOF_REQ_X, INDY_PROOF_PRED_NAMES, "schemas", @@ -535,6 +552,11 @@ async def test_check_pred_names_tamper_pred_req_attr(self, mock_verify): mock_verify.assert_not_called() assert verified is False + assert len(msgs) == 4 + assert "RMV_RFNT_NRI::18_uuid" in msgs + assert "RMV_RFNT_NRI::18_busid_GE_uuid" in msgs + assert "RMV_RFNT_NRI::18_id_GE_uuid" in msgs + assert "VALUE_ERROR::Timestamp on sub-proof #0 is superfluous vs. requested attribute group 18_uuid" in msgs @async_mock.patch("indy.anoncreds.verifier_verify_proof") async def test_check_pred_names_tamper_attr_groups(self, mock_verify): @@ -546,7 +568,7 @@ async def test_check_pred_names_tamper_attr_groups(self, mock_verify): IndyLedgerRequestsExecutor, "get_ledger_for_identifier" ) as mock_get_ledger: mock_get_ledger.return_value = ("test", self.ledger) - verified = await self.verifier.verify_presentation( + (verified, msgs) = await self.verifier.verify_presentation( deepcopy(INDY_PROOF_REQ_PRED_NAMES), INDY_PROOF_X, "schemas", @@ -558,3 +580,7 @@ async def test_check_pred_names_tamper_attr_groups(self, mock_verify): mock_verify.assert_not_called() assert verified is False + assert len(msgs) == 3 + assert "RMV_RFNT_NRI::18_busid_GE_uuid" in msgs + assert "RMV_RFNT_NRI::18_id_GE_uuid" in msgs + assert "VALUE_ERROR::Missing requested attribute group 18_uuid" in msgs diff --git a/aries_cloudagent/indy/sdk/verifier.py b/aries_cloudagent/indy/sdk/verifier.py index 409582a757..5c67463eed 100644 --- a/aries_cloudagent/indy/sdk/verifier.py +++ b/aries_cloudagent/indy/sdk/verifier.py @@ -52,12 +52,13 @@ async def verify_presentation( msgs = [] try: msgs += self.non_revoc_intervals(pres_req, pres, credential_definitions) - msgs += await self.check_timestamps(self.profile, pres_req, pres, rev_reg_defs) + msgs += await self.check_timestamps( + self.profile, pres_req, pres, rev_reg_defs + ) msgs += await self.pre_verify(pres_req, pres) except ValueError as err: - msgs.append( - f"{PresVerifyMsg.PRES_VALUE_ERROR.value}::{err}" - ) + s = str(err) + msgs.append(f"{PresVerifyMsg.PRES_VALUE_ERROR.value}::{s}") LOGGER.error( f"Presentation on nonce={pres_req['nonce']} " f"cannot be validated: {str(err)}" @@ -75,10 +76,9 @@ async def verify_presentation( json.dumps(rev_reg_defs), json.dumps(rev_reg_entries), ) - except IndyError: - msgs.append( - f"{PresVerifyMsg.PRES_VERIFY_ERROR.value}::{err}" - ) + except IndyError as err: + s = str(err) + msgs.append(f"{PresVerifyMsg.PRES_VERIFY_ERROR.value}::{s}") LOGGER.exception( f"Validation of presentation on nonce={pres_req['nonce']} " "failed with error" diff --git a/aries_cloudagent/indy/verifier.py b/aries_cloudagent/indy/verifier.py index 0e2d260b84..f61ca829f2 100644 --- a/aries_cloudagent/indy/verifier.py +++ b/aries_cloudagent/indy/verifier.py @@ -20,7 +20,10 @@ LOGGER = logging.getLogger(__name__) + class PresVerifyMsg(str, Enum): + """Credential verification codes.""" + RMV_REFERENT_NON_REVOC_INTERVAL = "RMV_RFNT_NRI" RMV_GLOBAL_NON_REVOC_INTERVAL = "RMV_GLB_NRI" TSTMP_OUT_NON_REVOC_INTRVAL = "TS_OUT_NRI" @@ -216,8 +219,7 @@ async def check_timestamps( elif uuid in unrevealed_attrs: # nothing to do, attribute value is not revealed msgs.append( - f"{PresVerifyMsg.CT_UNREVEALED_ATTRIBUTES.value}::" - f"{uuid}" + f"{PresVerifyMsg.CT_UNREVEALED_ATTRIBUTES.value}::" f"{uuid}" ) elif uuid not in self_attested: raise ValueError( @@ -277,8 +279,7 @@ async def check_timestamps( < non_revoc_intervals[uuid].get("to", now) ): msgs.append( - f"{PresVerifyMsg.TSTMP_OUT_NON_REVOC_INTRVAL.value}::" - f"{uuid}" + f"{PresVerifyMsg.TSTMP_OUT_NON_REVOC_INTRVAL.value}::" f"{uuid}" ) LOGGER.warning( f"Best-effort timestamp {timestamp} " @@ -347,8 +348,7 @@ async def pre_verify(self, pres_req: dict, pres: dict) -> list: # unrevealed attribute, nothing to do pres_req_attr_spec = {} msgs.append( - f"{PresVerifyMsg.CT_UNREVEALED_ATTRIBUTES.value}::" - f"{uuid}" + f"{PresVerifyMsg.CT_UNREVEALED_ATTRIBUTES.value}::" f"{uuid}" ) elif uuid in self_attested: if not req_attr.get("restrictions"): diff --git a/aries_cloudagent/protocols/present_proof/v1_0/manager.py b/aries_cloudagent/protocols/present_proof/v1_0/manager.py index b8ae25b122..2f3af46da5 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/manager.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/manager.py @@ -428,7 +428,7 @@ async def verify_presentation( rev_reg_entries, ) presentation_exchange_record.verified = json.dumps(verified_bool) - presentation_exchange_record.verified_msgs = verified_msgs + presentation_exchange_record.verified_msgs = list(set(verified_msgs)) presentation_exchange_record.state = V10PresentationExchange.STATE_VERIFIED async with self._profile.session() as session: diff --git a/aries_cloudagent/protocols/present_proof/v1_0/models/tests/test_record.py b/aries_cloudagent/protocols/present_proof/v1_0/models/tests/test_record.py index a757dcae2b..13d9e5aac3 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/models/tests/test_record.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/models/tests/test_record.py @@ -113,6 +113,7 @@ async def test_record(self): "auto_verify": False, "error_msg": None, "verified": None, + "verified_msgs": None, "trace": False, } diff --git a/aries_cloudagent/protocols/present_proof/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/present_proof/v1_0/tests/test_manager.py index 310cc4421f..044c21d317 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/tests/test_manager.py @@ -317,7 +317,7 @@ async def setUp(self): Verifier = async_mock.MagicMock(IndyVerifier, autospec=True) self.verifier = Verifier() self.verifier.verify_presentation = async_mock.CoroutineMock( - return_value="true" + return_value=("true", []) ) injector.bind_instance(IndyVerifier, self.verifier) diff --git a/aries_cloudagent/protocols/present_proof/v2_0/formats/indy/handler.py b/aries_cloudagent/protocols/present_proof/v2_0/formats/indy/handler.py index 3bb2eaf1ee..8f0a3e5057 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/formats/indy/handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/formats/indy/handler.py @@ -338,5 +338,5 @@ async def verify_pres(self, pres_ex_record: V20PresExRecord) -> V20PresExRecord: rev_reg_entries, ) pres_ex_record.verified = json.dumps(verified) - pres_ex_record.verified_msgs = verified_msgs + pres_ex_record.verified_msgs = list(set(verified_msgs)) return pres_ex_record diff --git a/aries_cloudagent/protocols/present_proof/v2_0/models/tests/test_record.py b/aries_cloudagent/protocols/present_proof/v2_0/models/tests/test_record.py index 529b72bb6a..c22a6ff23b 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/models/tests/test_record.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/models/tests/test_record.py @@ -110,6 +110,7 @@ async def test_record(self): "state": "state", "pres_proposal": pres_proposal.serialize(), "verified": "false", + "verified_msgs": None, "auto_present": True, "auto_verify": False, "error_msg": "error", diff --git a/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py b/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py index 0e352c51e2..9081c61946 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py @@ -476,7 +476,7 @@ async def setUp(self): Verifier = async_mock.MagicMock(IndyVerifier, autospec=True) self.verifier = Verifier() self.verifier.verify_presentation = async_mock.CoroutineMock( - return_value="true" + return_value=("true", []) ) injector.bind_instance(IndyVerifier, self.verifier) From c5f384d503591c5376e3506e656515fa87c774b7 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Thu, 25 Aug 2022 13:47:59 -0700 Subject: [PATCH 438/872] Add some proof verification docs Signed-off-by: Ian Costanzo --- AnoncredsProofValidation.md | 84 +++++++++++++++++++ .../indy/sdk/tests/test_verifier.py | 18 +++- 2 files changed, 98 insertions(+), 4 deletions(-) create mode 100644 AnoncredsProofValidation.md diff --git a/AnoncredsProofValidation.md b/AnoncredsProofValidation.md new file mode 100644 index 0000000000..3f5c0e138e --- /dev/null +++ b/AnoncredsProofValidation.md @@ -0,0 +1,84 @@ +# Anoncreds Proof Validation in Aca-Py + +Aca-Py does some pre-validation when verifying Anoncreds presentations (proofs), some scenarios are rejected (things that are indicative of tampering, for example) and some attributes are removed before running the anoncreds validation (for example removing superfluous non-revocation timestamps). Any Aca-Py validations or presentation modifications are indicated by the "verify_msgs" attribute in the final presentation exchange object + +The list of possible verification messages is [here](https://github.com/hyperledger/aries-cloudagent-python/blob/main/aries_cloudagent/indy/verifier.py#L24), and consists of: + +``` +class PresVerifyMsg(str, Enum): + """Credential verification codes.""" + + RMV_REFERENT_NON_REVOC_INTERVAL = "RMV_RFNT_NRI" + RMV_GLOBAL_NON_REVOC_INTERVAL = "RMV_GLB_NRI" + TSTMP_OUT_NON_REVOC_INTRVAL = "TS_OUT_NRI" + CT_UNREVEALED_ATTRIBUTES = "UNRVL_ATTR" + PRES_VALUE_ERROR = "VALUE_ERROR" + PRES_VERIFY_ERROR = "VERIFY_ERROR" +``` + +If there is additional information, it will be included like this: `TS_OUT_NRI::19_uuid` (which means the attribute identified by `19_uuid` contained a timestamp outside of the non-revocation interval (which is just a warning)). + +A presentation verification may include multiple messages, for example: + +``` + ... + "verified": "true", + "verified_msgs": [ + "TS_OUT_NRI::18_uuid", + "TS_OUT_NRI::18_id_GE_uuid", + "TS_OUT_NRI::18_busid_GE_uuid" + ], + ... +``` + +... or it may include a single message, for example: + +``` + ... + "verified": "false", + "verified_msgs": [ + "VALUE_ERROR::Encoded representation mismatch for 'Preferred Name'" + ], + ... +``` + +... or the `verified_msgs` may be null or an empty array. + +## Presentation Modifications and Warnings + +The following modifications/warnings may be done by Aca-Py 9which shouldn't affect the verification of the received proof): + +- "RMV_RFNT_NRI": Referent contains a non-revocation interval for a non-revocable credential (timestamp is removed) +- "RMV_GLB_NRI": Presentation contains a global interval for a non-revocable credential (timestamp is removed) +- "TS_OUT_NRI": Presentation contains a non-revocation timestamp outside of the requested non-revocation interval (warning) +- "UNRVL_ATTR": Presentation contains attributes with unrevealed values (warning) + +## Presentation Pre-validation Errors + +The following pre-verification checks are done, which will fail the proof (before calling anoncreds) and will result in the following message: + +``` +VALUE_ERROR:: +``` + +These validations are all done within the [Indy verifier class](https://github.com/hyperledger/aries-cloudagent-python/blob/main/aries_cloudagent/indy/verifier.py) - to see the detailed validation just look for anywhere a `raise ValueError(...)` appears in the code. + +A summary of the possible errors is: + +- information missing in presentation exchange record +- timestamp provided for irrevocable credential +- referenced revocation registry not found on ledger +- timestamp outside of reasonable range (future date or pre-dates revocation registry) +- mis-match between provided and requested timestamps for non-revocation +- mis-match between requested and provided attributes or predicates +- self-attested attribute is provided for a requested attribute with restrictions +- encoded value doesn't match raw value + +## Anoncreds Verification Exceptions + +Typically when you call the anoncreds `verifier_verify_proof()` method, it will return a `True` or `False` based on whether the presentation cryptographically verifies. However in the case where anoncreds throws an exception, the exception text will be included in a verification message as follows: + +``` +VERIFY_ERROR:: +``` + diff --git a/aries_cloudagent/indy/sdk/tests/test_verifier.py b/aries_cloudagent/indy/sdk/tests/test_verifier.py index 0f1cbef3ab..d4abc1bdd1 100644 --- a/aries_cloudagent/indy/sdk/tests/test_verifier.py +++ b/aries_cloudagent/indy/sdk/tests/test_verifier.py @@ -442,7 +442,9 @@ async def test_check_encoding_attr_tamper_raw(self, mock_verify): assert verified is False assert len(msgs) == 2 assert "TS_OUT_NRI::19_uuid" in msgs - assert "VALUE_ERROR::Encoded representation mismatch for 'Preferred Name'" in msgs + assert ( + "VALUE_ERROR::Encoded representation mismatch for 'Preferred Name'" in msgs + ) @async_mock.patch("indy.anoncreds.verifier_verify_proof") async def test_check_encoding_attr_tamper_encoded(self, mock_verify): @@ -468,7 +470,9 @@ async def test_check_encoding_attr_tamper_encoded(self, mock_verify): assert verified is False assert len(msgs) == 2 assert "TS_OUT_NRI::19_uuid" in msgs - assert "VALUE_ERROR::Encoded representation mismatch for 'Preferred Name'" in msgs + assert ( + "VALUE_ERROR::Encoded representation mismatch for 'Preferred Name'" in msgs + ) @async_mock.patch("indy.anoncreds.verifier_verify_proof") async def test_check_pred_names(self, mock_verify): @@ -530,7 +534,10 @@ async def test_check_pred_names_tamper_pred_value(self, mock_verify): assert "RMV_RFNT_NRI::18_uuid" in msgs assert "RMV_RFNT_NRI::18_busid_GE_uuid" in msgs assert "RMV_RFNT_NRI::18_id_GE_uuid" in msgs - assert "VALUE_ERROR::Timestamp on sub-proof #0 is superfluous vs. requested attribute group 18_uuid" in msgs + assert ( + "VALUE_ERROR::Timestamp on sub-proof #0 is superfluous vs. requested attribute group 18_uuid" + in msgs + ) @async_mock.patch("indy.anoncreds.verifier_verify_proof") async def test_check_pred_names_tamper_pred_req_attr(self, mock_verify): @@ -556,7 +563,10 @@ async def test_check_pred_names_tamper_pred_req_attr(self, mock_verify): assert "RMV_RFNT_NRI::18_uuid" in msgs assert "RMV_RFNT_NRI::18_busid_GE_uuid" in msgs assert "RMV_RFNT_NRI::18_id_GE_uuid" in msgs - assert "VALUE_ERROR::Timestamp on sub-proof #0 is superfluous vs. requested attribute group 18_uuid" in msgs + assert ( + "VALUE_ERROR::Timestamp on sub-proof #0 is superfluous vs. requested attribute group 18_uuid" + in msgs + ) @async_mock.patch("indy.anoncreds.verifier_verify_proof") async def test_check_pred_names_tamper_attr_groups(self, mock_verify): From e9d4f9a73ba3f056e3747c0391927544e3e86d85 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Thu, 25 Aug 2022 14:40:11 -0700 Subject: [PATCH 439/872] Fix typo Signed-off-by: Ian Costanzo --- AnoncredsProofValidation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AnoncredsProofValidation.md b/AnoncredsProofValidation.md index 3f5c0e138e..9114c3a727 100644 --- a/AnoncredsProofValidation.md +++ b/AnoncredsProofValidation.md @@ -46,7 +46,7 @@ A presentation verification may include multiple messages, for example: ## Presentation Modifications and Warnings -The following modifications/warnings may be done by Aca-Py 9which shouldn't affect the verification of the received proof): +The following modifications/warnings may be done by Aca-Py which shouldn't affect the verification of the received proof): - "RMV_RFNT_NRI": Referent contains a non-revocation interval for a non-revocable credential (timestamp is removed) - "RMV_GLB_NRI": Presentation contains a global interval for a non-revocable credential (timestamp is removed) From 56adc18ca26eadbcc701df501411fd1baed08aa8 Mon Sep 17 00:00:00 2001 From: Micah Peltier Date: Mon, 29 Aug 2022 11:24:45 -0600 Subject: [PATCH 440/872] feat: remove commented code Signed-off-by: Micah Peltier --- aries_cloudagent/protocols/out_of_band/v1_0/manager.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py index 2670d4bf89..1fa0756740 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py @@ -133,10 +133,6 @@ async def create_invitation( "Cannot store metadata without handshake protocols" ) if public: - # if multi_use: - # raise OutOfBandManagerError( - # "Cannot create public invitation with multi_use" - # ) if metadata: raise OutOfBandManagerError( "Cannot store metadata on public invitations" From 4788507cba7a3f296184f73de9596624a6339120 Mon Sep 17 00:00:00 2001 From: Adam Burdett Date: Tue, 9 Aug 2022 13:02:15 -0600 Subject: [PATCH 441/872] Signed-off-by: Adam Burdett Co-authored-by: Daniel Bluhm --- DIDResolution.md | 8 +++--- aries_cloudagent/config/default_context.py | 6 +--- aries_cloudagent/core/tests/test_conductor.py | 4 +-- .../connections/v1_0/tests/test_manager.py | 9 ++---- .../dif/tests/test_pres_exch_handler.py | 5 +--- aries_cloudagent/resolver/__init__.py | 14 +++++----- aries_cloudagent/resolver/did_resolver.py | 13 +++++---- .../resolver/did_resolver_registry.py | 28 ------------------- .../resolver/tests/test_did_resolver.py | 19 ++++--------- .../tests/test_did_resolver_registry.py | 12 -------- aries_cloudagent/wallet/did_method.py | 9 ++++-- 11 files changed, 36 insertions(+), 91 deletions(-) delete mode 100644 aries_cloudagent/resolver/did_resolver_registry.py delete mode 100644 aries_cloudagent/resolver/tests/test_did_resolver_registry.py diff --git a/DIDResolution.md b/DIDResolution.md index c1666adab2..dbf11965ce 100644 --- a/DIDResolution.md +++ b/DIDResolution.md @@ -29,7 +29,7 @@ In practice, DIDs and DID Documents are used for a variety of purposes but espec ## `DIDResolver` -In ACA-Py, the `DIDResolver` provides the interface to resolve DIDs using registered method resolvers. Method resolver registration happens on startup through the `DIDResolverRegistry`. This registry enables additional resolvers to be loaded via plugin. +In ACA-Py, the `DIDResolver` provides the interface to resolve DIDs using registered method resolvers. Method resolver registration happens on startup in a `did_resolers` list. This registry enables additional resolvers to be loaded via plugin. #### Example usage: ```python= @@ -73,17 +73,17 @@ The following is an example method resolver implementation. In this example, we ```python= from aries_cloudagent.config.injection_context import InjectionContext -from aries_cloudagent.resolver.did_resolver_registry import DIDResolverRegistry +from ..resolver.did_resolver import DIDResolver from .example_resolver import ExampleResolver async def setup(context: InjectionContext): """Setup the plugin.""" - registry = context.inject(DIDResolverRegistry) + registry = context.inject(DIDResolver) resolver = ExampleResolver() await resolver.setup(context) - registry.register(resolver) + registry.append(resolver) ``` #### `example_resolver.py` diff --git a/aries_cloudagent/config/default_context.py b/aries_cloudagent/config/default_context.py index fb0867cddc..e3d9b24770 100644 --- a/aries_cloudagent/config/default_context.py +++ b/aries_cloudagent/config/default_context.py @@ -12,7 +12,6 @@ from ..core.protocol_registry import ProtocolRegistry from ..core.goal_code_registry import GoalCodeRegistry from ..resolver.did_resolver import DIDResolver -from ..resolver.did_resolver_registry import DIDResolverRegistry from ..tails.base import BaseTailsServer from ..protocols.actionmenu.v1_0.base_service import BaseMenuService @@ -50,12 +49,9 @@ async def build_context(self) -> InjectionContext: # Global event bus context.injector.bind_instance(EventBus, EventBus()) - # Global did resolver registry - did_resolver_registry = DIDResolverRegistry() - context.injector.bind_instance(DIDResolverRegistry, did_resolver_registry) # Global did resolver - context.injector.bind_instance(DIDResolver, DIDResolver(did_resolver_registry)) + context.injector.bind_instance(DIDResolver, DIDResolver([])) await self.bind_providers(context) await self.load_plugins(context) diff --git a/aries_cloudagent/core/tests/test_conductor.py b/aries_cloudagent/core/tests/test_conductor.py index 02fbe4ff49..203ca1b3b4 100644 --- a/aries_cloudagent/core/tests/test_conductor.py +++ b/aries_cloudagent/core/tests/test_conductor.py @@ -24,7 +24,7 @@ from ...protocols.coordinate_mediation.v1_0.models.mediation_record import ( MediationRecord, ) -from ...resolver.did_resolver import DIDResolver, DIDResolverRegistry +from ...resolver.did_resolver import DIDResolver from ...multitenant.base import BaseMultitenantManager from ...multitenant.manager import MultitenantManager from ...storage.base import BaseStorage @@ -92,7 +92,7 @@ async def build_context(self) -> InjectionContext: context.injector.bind_instance(ProfileManager, InMemoryProfileManager()) context.injector.bind_instance(ProtocolRegistry, ProtocolRegistry()) context.injector.bind_instance(BaseWireFormat, self.wire_format) - context.injector.bind_instance(DIDResolver, DIDResolver(DIDResolverRegistry())) + context.injector.bind_instance(DIDResolver, DIDResolver([])) context.injector.bind_instance(EventBus, MockEventBus()) return context diff --git a/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py index d7c3836236..80efb456bf 100644 --- a/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py @@ -20,7 +20,6 @@ from .....multitenant.manager import MultitenantManager from .....protocols.routing.v1_0.manager import RoutingManager from .....resolver.did_resolver import DIDResolver -from .....resolver.did_resolver_registry import DIDResolverRegistry from .....storage.error import StorageNotFoundError from .....transport.inbound.receipt import MessageReceipt from .....wallet.base import DIDInfo @@ -2024,9 +2023,7 @@ async def test_fetch_connection_targets_no_my_did(self): async def test_fetch_connection_targets_conn_invitation_did_no_resolver(self): async with self.profile.session() as session: - self.context.injector.bind_instance( - DIDResolver, DIDResolver(DIDResolverRegistry()) - ) + self.context.injector.bind_instance(DIDResolver, DIDResolver([])) await session.wallet.create_local_did( method=DIDMethod.SOV, key_type=KeyType.ED25519, @@ -2320,9 +2317,7 @@ async def test_fetch_connection_targets_conn_invitation_unsupported_key_type(sel async def test_fetch_connection_targets_oob_invitation_svc_did_no_resolver(self): async with self.profile.session() as session: - self.context.injector.bind_instance( - DIDResolver, DIDResolver(DIDResolverRegistry()) - ) + self.context.injector.bind_instance(DIDResolver, DIDResolver([])) await session.wallet.create_local_did( method=DIDMethod.SOV, key_type=KeyType.ED25519, diff --git a/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py b/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py index 509e87ba6f..87587eb674 100644 --- a/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py +++ b/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py @@ -8,7 +8,6 @@ from .....core.in_memory import InMemoryProfile from .....did.did_key import DIDKey -from .....resolver.did_resolver_registry import DIDResolverRegistry from .....resolver.did_resolver import DIDResolver from .....storage.vc_holder.vc_record import VCRecord from .....wallet.base import BaseWallet, DIDInfo @@ -69,9 +68,7 @@ def event_loop(request): def profile(): profile = InMemoryProfile.test_profile() context = profile.context - did_resolver_registry = DIDResolverRegistry() - context.injector.bind_instance(DIDResolverRegistry, did_resolver_registry) - context.injector.bind_instance(DIDResolver, DIDResolver(did_resolver_registry)) + context.injector.bind_instance(DIDResolver, DIDResolver([])) context.injector.bind_instance(DocumentLoader, custom_document_loader) context.settings["debug.auto_respond_presentation_request"] = True return profile diff --git a/aries_cloudagent/resolver/__init__.py b/aries_cloudagent/resolver/__init__.py index 51bef53657..e4b82549d7 100644 --- a/aries_cloudagent/resolver/__init__.py +++ b/aries_cloudagent/resolver/__init__.py @@ -5,30 +5,30 @@ from ..config.injection_context import InjectionContext from ..config.provider import ClassProvider -from .did_resolver_registry import DIDResolverRegistry +from ..resolver.did_resolver import DIDResolver LOGGER = logging.getLogger(__name__) async def setup(context: InjectionContext): """Set up default resolvers.""" - registry = context.inject_or(DIDResolverRegistry) + registry = context.inject_or(DIDResolver) if not registry: - LOGGER.warning("No DID Resolver Registry instance found in context") + LOGGER.warning("No DID Resolver instance found in context") return key_resolver = ClassProvider( "aries_cloudagent.resolver.default.key.KeyDIDResolver" ).provide(context.settings, context.injector) await key_resolver.setup(context) - registry.register(key_resolver) + registry.register_resolver(key_resolver) if not context.settings.get("ledger.disabled"): indy_resolver = ClassProvider( "aries_cloudagent.resolver.default.indy.IndyDIDResolver" ).provide(context.settings, context.injector) await indy_resolver.setup(context) - registry.register(indy_resolver) + registry.register_resolver(indy_resolver) else: LOGGER.warning("Ledger is not configured, not loading IndyDIDResolver") @@ -36,11 +36,11 @@ async def setup(context: InjectionContext): "aries_cloudagent.resolver.default.web.WebDIDResolver" ).provide(context.settings, context.injector) await web_resolver.setup(context) - registry.register(web_resolver) + registry.register_resolver(web_resolver) if context.settings.get("resolver.universal"): universal_resolver = ClassProvider( "aries_cloudagent.resolver.default.universal.UniversalResolver" ).provide(context.settings, context.injector) await universal_resolver.setup(context) - registry.register(universal_resolver) + registry.register_resolver(universal_resolver) diff --git a/aries_cloudagent/resolver/did_resolver.py b/aries_cloudagent/resolver/did_resolver.py index f57ec47fd9..bf52959043 100644 --- a/aries_cloudagent/resolver/did_resolver.py +++ b/aries_cloudagent/resolver/did_resolver.py @@ -8,7 +8,7 @@ from datetime import datetime from itertools import chain import logging -from typing import Sequence, Tuple, Type, TypeVar, Union +from typing import List, Sequence, Tuple, Type, TypeVar, Union from pydid import DID, DIDError, DIDUrl, Resource, NonconformantDocument from pydid.doc.doc import IDNotFoundError @@ -22,7 +22,6 @@ ResolutionResult, ResolverError, ) -from .did_resolver_registry import DIDResolverRegistry LOGGER = logging.getLogger(__name__) @@ -33,9 +32,13 @@ class DIDResolver: """did resolver singleton.""" - def __init__(self, registry: DIDResolverRegistry): + def __init__(self, resolvers: List[BaseDIDResolver] = None): """Create DID Resolver.""" - self.did_resolver_registry = registry + self.resolvers = resolvers or [] + + def register_resolver(self, resolver: BaseDIDResolver): + """Register a new resolver.""" + self.resolvers.append(resolver) async def _resolve( self, profile: Profile, did: Union[str, DID] @@ -90,7 +93,7 @@ async def _match_did_to_resolver( """ valid_resolvers = [ resolver - for resolver in self.did_resolver_registry.resolvers + for resolver in self.resolvers if await resolver.supports(profile, did) ] native_resolvers = filter(lambda resolver: resolver.native, valid_resolvers) diff --git a/aries_cloudagent/resolver/did_resolver_registry.py b/aries_cloudagent/resolver/did_resolver_registry.py deleted file mode 100644 index 4154454236..0000000000 --- a/aries_cloudagent/resolver/did_resolver_registry.py +++ /dev/null @@ -1,28 +0,0 @@ -"""In memmory storage for registering did resolvers.""" - -import logging -from typing import Sequence - -from .base import BaseDIDResolver - -LOGGER = logging.getLogger(__name__) - - -class DIDResolverRegistry: - """Registry for did resolvers.""" - - def __init__(self): - """Initialize list for did resolvers.""" - self._resolvers = [] - - @property - def resolvers( - self, - ) -> Sequence[BaseDIDResolver]: - """Accessor for a list of all did resolvers.""" - return self._resolvers - - def register(self, resolver) -> None: - """Register a resolver.""" - LOGGER.debug("Registering resolver %s", resolver) - self._resolvers.append(resolver) diff --git a/aries_cloudagent/resolver/tests/test_did_resolver.py b/aries_cloudagent/resolver/tests/test_did_resolver.py index b08480e805..38d7a1cf71 100644 --- a/aries_cloudagent/resolver/tests/test_did_resolver.py +++ b/aries_cloudagent/resolver/tests/test_did_resolver.py @@ -18,7 +18,6 @@ ResolverType, ) from ..did_resolver import DIDResolver -from ..did_resolver_registry import DIDResolverRegistry from . import DOC @@ -88,10 +87,10 @@ async def _resolve(self, profile, did): @pytest.fixture def resolver(): - did_resolver_registry = DIDResolverRegistry() + did_resolver_registry = [] for method in TEST_DID_METHODS: resolver = MockResolver([method], DIDDocument.deserialize(DOC)) - did_resolver_registry.register(resolver) + did_resolver_registry.append(resolver) return DIDResolver(did_resolver_registry) @@ -121,11 +120,9 @@ async def test_match_did_to_resolver_x_not_supported(resolver): @pytest.mark.asyncio async def test_match_did_to_resolver_native_priority(profile): - registry = DIDResolverRegistry() native = MockResolver(["sov"], native=True) non_native = MockResolver(["sov"], native=False) - registry.register(non_native) - registry.register(native) + registry = [non_native, native] resolver = DIDResolver(registry) assert [native, non_native] == await resolver._match_did_to_resolver( profile, TEST_DID0 @@ -134,15 +131,11 @@ async def test_match_did_to_resolver_native_priority(profile): @pytest.mark.asyncio async def test_match_did_to_resolver_registration_order(profile): - registry = DIDResolverRegistry() native1 = MockResolver(["sov"], native=True) - registry.register(native1) native2 = MockResolver(["sov"], native=True) - registry.register(native2) non_native3 = MockResolver(["sov"], native=False) - registry.register(non_native3) native4 = MockResolver(["sov"], native=True) - registry.register(native4) + registry = [native1, native2, non_native3, native4] resolver = DIDResolver(registry) assert [ native1, @@ -200,8 +193,6 @@ async def test_resolve_did_x_not_supported(resolver, profile): async def test_resolve_did_x_not_found(profile): py_did = DID("did:cowsay:EiDahaOGH-liLLdDtTxEAdc8i-cfCz-WUcQdRJheMVNn3A") cowsay_resolver_not_found = MockResolver(["cowsay"], resolved=DIDNotFound()) - registry = DIDResolverRegistry() - registry.register(cowsay_resolver_not_found) - resolver = DIDResolver(registry) + resolver = DIDResolver([cowsay_resolver_not_found]) with pytest.raises(DIDNotFound): await resolver.resolve(profile, py_did) diff --git a/aries_cloudagent/resolver/tests/test_did_resolver_registry.py b/aries_cloudagent/resolver/tests/test_did_resolver_registry.py deleted file mode 100644 index dba7afffbd..0000000000 --- a/aries_cloudagent/resolver/tests/test_did_resolver_registry.py +++ /dev/null @@ -1,12 +0,0 @@ -"""Test did resolver registery.""" - -import pytest -import unittest -from ..did_resolver_registry import DIDResolverRegistry - - -def test_create_registry(): - did_resolver_registry = DIDResolverRegistry() - test_resolver = unittest.mock.MagicMock() - did_resolver_registry.register(test_resolver) - assert did_resolver_registry.resolvers == [test_resolver] diff --git a/aries_cloudagent/wallet/did_method.py b/aries_cloudagent/wallet/did_method.py index 82382ea39b..797c26fd20 100644 --- a/aries_cloudagent/wallet/did_method.py +++ b/aries_cloudagent/wallet/did_method.py @@ -47,7 +47,8 @@ def supports_key_type(self, key_type: KeyType) -> bool: """Check whether the current method supports the key type.""" return key_type in self.supported_key_types - def from_metadata(metadata: Mapping) -> "DIDMethod": + @classmethod + def from_metadata(cls, metadata: Mapping) -> "DIDMethod": """Get DID method instance from metadata object. Returns SOV if no metadata was found for backwards compatability. @@ -63,7 +64,8 @@ def from_metadata(metadata: Mapping) -> "DIDMethod": # return default SOV for backward compat return DIDMethod.SOV - def from_method(method: str) -> Optional["DIDMethod"]: + @classmethod + def from_method(cls, method: str) -> Optional["DIDMethod"]: """Get DID method instance from the method name.""" for did_method in DIDMethod: if method == did_method.method_name: @@ -71,7 +73,8 @@ def from_method(method: str) -> Optional["DIDMethod"]: return None - def from_did(did: str) -> "DIDMethod": + @classmethod + def from_did(cls, did: str) -> "DIDMethod": """Get DID method instance from the method name.""" if not did.startswith("did:"): # sov has no prefix From 2d15ca4e190dbcce53649c59734da2cd7d8a43f3 Mon Sep 17 00:00:00 2001 From: Adam Burdett Date: Tue, 9 Aug 2022 13:36:40 -0600 Subject: [PATCH 442/872] formatting Signed-off-by: Adam Burdett --- aries_cloudagent/config/default_context.py | 1 - 1 file changed, 1 deletion(-) diff --git a/aries_cloudagent/config/default_context.py b/aries_cloudagent/config/default_context.py index e3d9b24770..0c5f90cac2 100644 --- a/aries_cloudagent/config/default_context.py +++ b/aries_cloudagent/config/default_context.py @@ -49,7 +49,6 @@ async def build_context(self) -> InjectionContext: # Global event bus context.injector.bind_instance(EventBus, EventBus()) - # Global did resolver context.injector.bind_instance(DIDResolver, DIDResolver([])) From 06a7a1855bb6310c609e7ef4cd949a08a539b393 Mon Sep 17 00:00:00 2001 From: Adam Burdett Date: Wed, 10 Aug 2022 10:36:42 -0600 Subject: [PATCH 443/872] test update Signed-off-by: Adam Burdett --- aries_cloudagent/resolver/tests/test_did_resolver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aries_cloudagent/resolver/tests/test_did_resolver.py b/aries_cloudagent/resolver/tests/test_did_resolver.py index 38d7a1cf71..f2c4ba9ab5 100644 --- a/aries_cloudagent/resolver/tests/test_did_resolver.py +++ b/aries_cloudagent/resolver/tests/test_did_resolver.py @@ -100,7 +100,7 @@ def profile(): def test_create_resolver(resolver): - assert len(resolver.did_resolver_registry.resolvers) == len(TEST_DID_METHODS) + assert len(resolver.resolvers) == len(TEST_DID_METHODS) @pytest.mark.asyncio From 7a006850b81a6c535d3af619d9f49ef836ecafe5 Mon Sep 17 00:00:00 2001 From: Adam Burdett Date: Mon, 29 Aug 2022 13:37:49 -0400 Subject: [PATCH 444/872] fix: typo in DIDResolution.md Co-authored-by: Andrew Whitehead Signed-off-by: Adam Burdett --- DIDResolution.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DIDResolution.md b/DIDResolution.md index dbf11965ce..cbe00edc1d 100644 --- a/DIDResolution.md +++ b/DIDResolution.md @@ -29,7 +29,7 @@ In practice, DIDs and DID Documents are used for a variety of purposes but espec ## `DIDResolver` -In ACA-Py, the `DIDResolver` provides the interface to resolve DIDs using registered method resolvers. Method resolver registration happens on startup in a `did_resolers` list. This registry enables additional resolvers to be loaded via plugin. +In ACA-Py, the `DIDResolver` provides the interface to resolve DIDs using registered method resolvers. Method resolver registration happens on startup in a `did_resolvers` list. This registry enables additional resolvers to be loaded via plugin. #### Example usage: ```python= From 4939c596b2d2a7d25acd1bbfd745bf142820135b Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Tue, 30 Aug 2022 11:49:29 -0400 Subject: [PATCH 445/872] fix: actually return mediated conn ids Signed-off-by: Daniel Bluhm --- .../coordinate_mediation/v1_0/manager.py | 33 ++++++---- .../v1_0/tests/test_mediation_manager.py | 60 ++++++++++--------- 2 files changed, 55 insertions(+), 38 deletions(-) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py index 5cdf2ccc5b..b6f1d108af 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py @@ -6,12 +6,14 @@ from ....core.error import BaseError from ....core.profile import Profile, ProfileSession +from ....connections.models.conn_record import ConnRecord from ....storage.base import BaseStorage from ....storage.error import StorageNotFoundError from ....storage.record import StorageRecord from ....wallet.base import BaseWallet from ....wallet.did_info import DIDInfo from ....wallet.did_method import DIDMethod +from ....wallet.error import WalletNotFoundError from ....wallet.key_type import KeyType from ...routing.v1_0.manager import RoutingManager from ...routing.v1_0.models.route_record import RouteRecord @@ -600,21 +602,34 @@ async def store_update_results( for record_for_removal in to_remove: await record_for_removal.delete_record(session) + async def _conn_id_from_recipient_key( + self, session: ProfileSession, wallet: BaseWallet, recipient_key: str + ) -> str: + try: + conn = await ConnRecord.retrieve_by_invitation_key( + session, invitation_key=normalize_from_did_key(recipient_key) + ) + except StorageNotFoundError: + did_info = await wallet.get_local_did_for_verkey( + normalize_from_did_key(recipient_key) + ) + conn = await ConnRecord.retrieve_by_did(session, my_did=did_info.did) + return conn.connection_id + async def notify_keylist_updated( self, connection_id: str, response: KeylistUpdateResponse ): """Notify of keylist update response received.""" - # Retrieve connection IDs associated with recipient keys - # recipient key -> connection id async with self._profile.session() as session: + wallet = session.inject(BaseWallet) try: - routes = [ - await RouteRecord.retrieve_by_recipient_key( - session, updated.recipient_key + routes = { + updated.recipient_key: await self._conn_id_from_recipient_key( + session, wallet, updated.recipient_key ) for updated in response.updated - ] - except StorageNotFoundError as err: + } + except (StorageNotFoundError, WalletNotFoundError) as err: raise MediationManagerError( "Unknown recipient key received in keylist update response" ) from err @@ -625,9 +640,7 @@ async def notify_keylist_updated( "connection_id": connection_id, "thread_id": response._thread_id, "updated": [update.serialize() for update in response.updated], - "mediated_connections": { - route.recipient_key: route.connection_id for route in routes - }, + "mediated_connections": routes, }, ) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py index 8439caeffa..8d767a2d4e 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py @@ -1,7 +1,8 @@ """Test MediationManager.""" import logging -from typing import AsyncIterable, Iterable +from typing import AsyncGenerator, AsyncIterable, Iterable +from functools import partial from asynctest import mock as async_mock import pytest @@ -482,37 +483,40 @@ async def test_notify_keylist_updated( self, manager: MediationManager, mock_event_bus: MockEventBus, - session: ProfileSession, ): """test notify_keylist_updated.""" - await RouteRecord( - role=RouteRecord.ROLE_CLIENT, - connection_id="conn_id_1", - recipient_key=TEST_ROUTE_VERKEY, - ).save(session) - await RouteRecord( - role=RouteRecord.ROLE_CLIENT, - connection_id="conn_id_2", - recipient_key=TEST_VERKEY, - ).save(session) - response = KeylistUpdateResponse( - updated=[ - KeylistUpdated( - recipient_key=TEST_ROUTE_VERKEY, - action=KeylistUpdateRule.RULE_ADD, - result=KeylistUpdated.RESULT_SUCCESS, - ), - KeylistUpdated( - recipient_key=TEST_VERKEY, - action=KeylistUpdateRule.RULE_REMOVE, - result=KeylistUpdated.RESULT_SUCCESS, - ), - ], - ) + async def _result_generator(): + yield "conn_id_1" + yield "conn_id_2" - response.assign_thread_id(TEST_THREAD_ID) - await manager.notify_keylist_updated(TEST_CONN_ID, response) + async def _retrieve_by_invitation_key( + generator: AsyncGenerator, *args, **kwargs + ): + return await generator.__anext__() + + with async_mock.patch.object( + manager, + "_conn_id_from_recipient_key", + partial(_retrieve_by_invitation_key, _result_generator()), + ): + response = KeylistUpdateResponse( + updated=[ + KeylistUpdated( + recipient_key=TEST_ROUTE_VERKEY, + action=KeylistUpdateRule.RULE_ADD, + result=KeylistUpdated.RESULT_SUCCESS, + ), + KeylistUpdated( + recipient_key=TEST_VERKEY, + action=KeylistUpdateRule.RULE_REMOVE, + result=KeylistUpdated.RESULT_SUCCESS, + ), + ], + ) + + response.assign_thread_id(TEST_THREAD_ID) + await manager.notify_keylist_updated(TEST_CONN_ID, response) assert mock_event_bus.events assert mock_event_bus.events[0][1].topic == manager.KEYLIST_UPDATED_EVENT assert mock_event_bus.events[0][1].payload == { From 048feba4c32a4442bbb5919d3df5198cea2ac800 Mon Sep 17 00:00:00 2001 From: Char Howland Date: Tue, 30 Aug 2022 11:52:58 -0600 Subject: [PATCH 446/872] fix: propagate endpoint from mediation record Signed-off-by: Char Howland --- aries_cloudagent/wallet/routes.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/wallet/routes.py b/aries_cloudagent/wallet/routes.py index 6e73f455ba..ea039df1aa 100644 --- a/aries_cloudagent/wallet/routes.py +++ b/aries_cloudagent/wallet/routes.py @@ -458,8 +458,10 @@ async def wallet_set_public_did(request: web.BaseRequest): profile=profile, mediation_id=mediation_id, or_default=True ) routing_keys = None + mediator_endpoint = None if mediation_record: routing_keys = mediation_record.routing_keys + mediator_endpoint = mediation_record.endpoint try: info, attrib_def = await promote_wallet_public_did( @@ -470,6 +472,7 @@ async def wallet_set_public_did(request: web.BaseRequest): write_ledger=write_ledger, connection_id=connection_id, routing_keys=routing_keys, + mediator_endpoint=mediator_endpoint, ) except LookupError as err: raise web.HTTPNotFound(reason=str(err)) from err @@ -517,6 +520,7 @@ async def promote_wallet_public_did( write_ledger: bool = False, connection_id: str = None, routing_keys: List[str] = None, + mediator_endpoint: str = None, ) -> DIDInfo: """Promote supplied DID to the wallet public DID.""" info: DIDInfo = None @@ -584,7 +588,7 @@ async def promote_wallet_public_did( if not endpoint: async with session_fn() as session: wallet = session.inject_or(BaseWallet) - endpoint = context.settings.get("default_endpoint") + endpoint = mediator_endpoint or context.settings.get("default_endpoint") attrib_def = await wallet.set_did_endpoint( info.did, endpoint, From 7bb7809c3630cfd9427d34195ecd55e8019b5106 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Thu, 1 Sep 2022 11:46:05 -0400 Subject: [PATCH 447/872] refactor: connection retrieval through route manager Signed-off-by: Daniel Bluhm --- aries_cloudagent/multitenant/base.py | 13 +++ aries_cloudagent/multitenant/route_manager.py | 22 +++++ .../keylist_update_response_handler.py | 40 +++++++- .../test_keylist_update_response_handler.py | 96 ++++++++++++++++++- .../coordinate_mediation/v1_0/manager.py | 45 --------- .../v1_0/route_manager.py | 22 +++++ .../v1_0/tests/test_mediation_manager.py | 74 -------------- 7 files changed, 186 insertions(+), 126 deletions(-) diff --git a/aries_cloudagent/multitenant/base.py b/aries_cloudagent/multitenant/base.py index cecd2314dd..ae5f5c60bd 100644 --- a/aries_cloudagent/multitenant/base.py +++ b/aries_cloudagent/multitenant/base.py @@ -381,6 +381,19 @@ async def _get_wallet_by_key(self, recipient_key: str) -> Optional[WalletRecord] except (RouteNotFoundError): pass + async def get_profile_for_key( + self, context: InjectionContext, recipient_key: str + ) -> Optional[Profile]: + """Retrieve a wallet profile by recipient key.""" + wallet = await self._get_wallet_by_key(recipient_key) + if not wallet: + return None + + if wallet.requires_external_key: + raise WalletKeyMissingError() + + return await self.get_wallet_profile(context, wallet) + async def get_wallets_by_message( self, message_body, wire_format: BaseWireFormat = None ) -> List[WalletRecord]: diff --git a/aries_cloudagent/multitenant/route_manager.py b/aries_cloudagent/multitenant/route_manager.py index a7d6cf6878..f9d1751cf9 100644 --- a/aries_cloudagent/multitenant/route_manager.py +++ b/aries_cloudagent/multitenant/route_manager.py @@ -4,6 +4,7 @@ import logging from typing import List, Optional, Tuple +from ..connections.models.conn_record import ConnRecord from ..core.profile import Profile from ..messaging.responder import BaseResponder from ..protocols.coordinate_mediation.v1_0.manager import MediationManager @@ -14,6 +15,7 @@ from ..protocols.routing.v1_0.manager import RoutingManager from ..protocols.routing.v1_0.models.route_record import RouteRecord from ..storage.error import StorageNotFoundError +from .manager import MultitenantManager LOGGER = logging.getLogger(__name__) @@ -103,3 +105,23 @@ async def routing_info( my_endpoint = mediation_record.endpoint return routing_keys, my_endpoint + + async def connection_from_recipient_key( + self, profile: Profile, recipient_key: str + ) -> ConnRecord: + """Retrieve a connection by recipient key. + + The recipient key is expected to be a local key owned by this agent. + + Since the multi-tenant base wallet can receive and send keylist updates + for sub wallets, we check the sub wallet's connections before the base + wallet. + """ + manager = MultitenantManager(self.root_profile) + profile_to_search = ( + await manager.get_profile_for_key(profile.context, recipient_key) or profile + ) + + return await super().connection_from_recipient_key( + profile_to_search, recipient_key + ) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/keylist_update_response_handler.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/keylist_update_response_handler.py index 79e2b56aec..c2d2a21c1b 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/keylist_update_response_handler.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/keylist_update_response_handler.py @@ -1,11 +1,14 @@ """Handler for keylist-update-response message.""" +from .....core.profile import Profile from .....messaging.base_handler import BaseHandler, HandlerException from .....messaging.request_context import RequestContext from .....messaging.responder import BaseResponder - -from ..messages.keylist_update_response import KeylistUpdateResponse +from .....storage.error import StorageNotFoundError +from .....wallet.error import WalletNotFoundError from ..manager import MediationManager +from ..messages.keylist_update_response import KeylistUpdateResponse +from ..route_manager import RouteManager class KeylistUpdateResponseHandler(BaseHandler): @@ -25,6 +28,35 @@ async def handle(self, context: RequestContext, responder: BaseResponder): await mgr.store_update_results( context.connection_record.connection_id, context.message.updated ) - await mgr.notify_keylist_updated( - context.connection_record.connection_id, context.message + await self.notify_keylist_updated( + context.profile, context.connection_record.connection_id, context.message + ) + + async def notify_keylist_updated( + self, profile: Profile, connection_id: str, response: KeylistUpdateResponse + ): + """Notify of keylist update response received.""" + route_manager = profile.inject(RouteManager) + try: + key_to_connection = { + updated.recipient_key: await route_manager.connection_from_recipient_key( + profile, updated.recipient_key + ) + for updated in response.updated + } + except (StorageNotFoundError, WalletNotFoundError) as err: + raise HandlerException( + "Unknown recipient key received in keylist update response" + ) from err + + await profile.notify( + MediationManager.KEYLIST_UPDATED_EVENT, + { + "connection_id": connection_id, + "thread_id": response._thread_id, + "updated": [update.serialize() for update in response.updated], + "mediated_connections": { + key: conn.connection_id for key, conn in key_to_connection.items() + }, + }, ) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/tests/test_keylist_update_response_handler.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/tests/test_keylist_update_response_handler.py index 75218935d0..346e1fe9c0 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/tests/test_keylist_update_response_handler.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/tests/test_keylist_update_response_handler.py @@ -1,10 +1,14 @@ """Test handler for keylist-update-response message.""" +from functools import partial +from typing import AsyncGenerator import pytest from asynctest import TestCase as AsyncTestCase from asynctest import mock as async_mock + from ......connections.models.conn_record import ConnRecord +from ......core.event_bus import EventBus, MockEventBus from ......messaging.base_handler import HandlerException from ......messaging.request_context import RequestContext from ......messaging.responder import MockResponder @@ -12,10 +16,14 @@ from ...messages.inner.keylist_updated import KeylistUpdated from ...messages.keylist_update_response import KeylistUpdateResponse from ...manager import MediationManager +from ...route_manager import RouteManager +from ...tests.test_route_manager import MockRouteManager from ..keylist_update_response_handler import KeylistUpdateResponseHandler TEST_CONN_ID = "conn-id" -TEST_VERKEY = "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx" +TEST_THREAD_ID = "thread-id" +TEST_VERKEY = "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL" +TEST_ROUTE_VERKEY = "did:key:z6MknxTj6Zj1VrDWc1ofaZtmCVv2zNXpD58Xup4ijDGoQhya" class TestKeylistUpdateResponseHandler(AsyncTestCase): @@ -34,6 +42,10 @@ async def setUp(self): self.context.message = KeylistUpdateResponse(updated=self.updated) self.context.connection_ready = True self.context.connection_record = ConnRecord(connection_id=TEST_CONN_ID) + self.mock_event_bus = MockEventBus() + self.context.injector.bind_instance(EventBus, self.mock_event_bus) + self.route_manager = MockRouteManager() + self.context.injector.bind_instance(RouteManager, self.route_manager) async def test_handler_no_active_connection(self): handler, responder = KeylistUpdateResponseHandler(), MockResponder() @@ -47,8 +59,86 @@ async def test_handler(self): with async_mock.patch.object( MediationManager, "store_update_results" ) as mock_store, async_mock.patch.object( - MediationManager, "notify_keylist_updated" + handler, "notify_keylist_updated" ) as mock_notify: await handler.handle(self.context, responder) mock_store.assert_called_once_with(TEST_CONN_ID, self.updated) - mock_notify.assert_called_once_with(TEST_CONN_ID, self.context.message) + mock_notify.assert_called_once_with( + self.context.profile, TEST_CONN_ID, self.context.message + ) + + async def test_notify_keylist_updated(self): + """test notify_keylist_updated.""" + handler = KeylistUpdateResponseHandler() + + async def _result_generator(): + yield ConnRecord(connection_id="conn_id_1") + yield ConnRecord(connection_id="conn_id_2") + + async def _retrieve_by_invitation_key( + generator: AsyncGenerator, *args, **kwargs + ): + return await generator.__anext__() + + with async_mock.patch.object( + self.route_manager, + "connection_from_recipient_key", + partial(_retrieve_by_invitation_key, _result_generator()), + ): + response = KeylistUpdateResponse( + updated=[ + KeylistUpdated( + recipient_key=TEST_ROUTE_VERKEY, + action=KeylistUpdateRule.RULE_ADD, + result=KeylistUpdated.RESULT_SUCCESS, + ), + KeylistUpdated( + recipient_key=TEST_VERKEY, + action=KeylistUpdateRule.RULE_REMOVE, + result=KeylistUpdated.RESULT_SUCCESS, + ), + ], + ) + + response.assign_thread_id(TEST_THREAD_ID) + await handler.notify_keylist_updated( + self.context.profile, TEST_CONN_ID, response + ) + assert self.mock_event_bus.events + assert ( + self.mock_event_bus.events[0][1].topic + == MediationManager.KEYLIST_UPDATED_EVENT + ) + assert self.mock_event_bus.events[0][1].payload == { + "connection_id": TEST_CONN_ID, + "thread_id": TEST_THREAD_ID, + "updated": [result.serialize() for result in response.updated], + "mediated_connections": { + TEST_ROUTE_VERKEY: "conn_id_1", + TEST_VERKEY: "conn_id_2", + }, + } + + async def test_notify_keylist_updated_x_unknown_recip_key(self): + """test notify_keylist_updated.""" + handler = KeylistUpdateResponseHandler() + response = KeylistUpdateResponse( + updated=[ + KeylistUpdated( + recipient_key=TEST_ROUTE_VERKEY, + action=KeylistUpdateRule.RULE_ADD, + result=KeylistUpdated.RESULT_SUCCESS, + ), + KeylistUpdated( + recipient_key=TEST_VERKEY, + action=KeylistUpdateRule.RULE_REMOVE, + result=KeylistUpdated.RESULT_SUCCESS, + ), + ], + ) + + response.assign_thread_id(TEST_THREAD_ID) + with pytest.raises(HandlerException): + await handler.notify_keylist_updated( + self.context.profile, TEST_CONN_ID, response + ) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py index b6f1d108af..61bd6160f2 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py @@ -3,17 +3,14 @@ import logging from typing import Optional, Sequence, Tuple - from ....core.error import BaseError from ....core.profile import Profile, ProfileSession -from ....connections.models.conn_record import ConnRecord from ....storage.base import BaseStorage from ....storage.error import StorageNotFoundError from ....storage.record import StorageRecord from ....wallet.base import BaseWallet from ....wallet.did_info import DIDInfo from ....wallet.did_method import DIDMethod -from ....wallet.error import WalletNotFoundError from ....wallet.key_type import KeyType from ...routing.v1_0.manager import RoutingManager from ...routing.v1_0.models.route_record import RouteRecord @@ -602,48 +599,6 @@ async def store_update_results( for record_for_removal in to_remove: await record_for_removal.delete_record(session) - async def _conn_id_from_recipient_key( - self, session: ProfileSession, wallet: BaseWallet, recipient_key: str - ) -> str: - try: - conn = await ConnRecord.retrieve_by_invitation_key( - session, invitation_key=normalize_from_did_key(recipient_key) - ) - except StorageNotFoundError: - did_info = await wallet.get_local_did_for_verkey( - normalize_from_did_key(recipient_key) - ) - conn = await ConnRecord.retrieve_by_did(session, my_did=did_info.did) - return conn.connection_id - - async def notify_keylist_updated( - self, connection_id: str, response: KeylistUpdateResponse - ): - """Notify of keylist update response received.""" - async with self._profile.session() as session: - wallet = session.inject(BaseWallet) - try: - routes = { - updated.recipient_key: await self._conn_id_from_recipient_key( - session, wallet, updated.recipient_key - ) - for updated in response.updated - } - except (StorageNotFoundError, WalletNotFoundError) as err: - raise MediationManagerError( - "Unknown recipient key received in keylist update response" - ) from err - - await self._profile.notify( - self.KEYLIST_UPDATED_EVENT, - { - "connection_id": connection_id, - "thread_id": response._thread_id, - "updated": [update.serialize() for update in response.updated], - "mediated_connections": routes, - }, - ) - async def get_my_keylist( self, connection_id: Optional[str] = None ) -> Sequence[RouteRecord]: diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py index 07da03fd51..7f3b839e53 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py @@ -20,6 +20,7 @@ from .manager import MediationManager from .messages.keylist_update import KeylistUpdate from .models.mediation_record import MediationRecord +from .normalization import normalize_from_did_key LOGGER = logging.getLogger(__name__) @@ -242,6 +243,27 @@ async def routing_info( ) -> Tuple[List[str], str]: """Retrieve routing keys.""" + async def connection_from_recipient_key( + self, profile: Profile, recipient_key: str + ) -> ConnRecord: + """Retrieve connection for a recipient_key. + + The recipient key is expected to be a local key owned by this agent. + """ + async with profile.session() as session: + wallet = session.inject(BaseWallet) + try: + conn = await ConnRecord.retrieve_by_invitation_key( + session, invitation_key=normalize_from_did_key(recipient_key) + ) + except StorageNotFoundError: + did_info = await wallet.get_local_did_for_verkey( + normalize_from_did_key(recipient_key) + ) + conn = await ConnRecord.retrieve_by_did(session, my_did=did_info.did) + + return conn + class CoordinateMediationV1RouteManager(RouteManager): """Manage routes using Coordinate Mediation protocol.""" diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py index 8d767a2d4e..97bc2f80f7 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py @@ -479,80 +479,6 @@ async def test_store_update_results_errors(self, caplog, manager): assert "server_error" in caplog.text print(caplog.text) - async def test_notify_keylist_updated( - self, - manager: MediationManager, - mock_event_bus: MockEventBus, - ): - """test notify_keylist_updated.""" - - async def _result_generator(): - yield "conn_id_1" - yield "conn_id_2" - - async def _retrieve_by_invitation_key( - generator: AsyncGenerator, *args, **kwargs - ): - return await generator.__anext__() - - with async_mock.patch.object( - manager, - "_conn_id_from_recipient_key", - partial(_retrieve_by_invitation_key, _result_generator()), - ): - response = KeylistUpdateResponse( - updated=[ - KeylistUpdated( - recipient_key=TEST_ROUTE_VERKEY, - action=KeylistUpdateRule.RULE_ADD, - result=KeylistUpdated.RESULT_SUCCESS, - ), - KeylistUpdated( - recipient_key=TEST_VERKEY, - action=KeylistUpdateRule.RULE_REMOVE, - result=KeylistUpdated.RESULT_SUCCESS, - ), - ], - ) - - response.assign_thread_id(TEST_THREAD_ID) - await manager.notify_keylist_updated(TEST_CONN_ID, response) - assert mock_event_bus.events - assert mock_event_bus.events[0][1].topic == manager.KEYLIST_UPDATED_EVENT - assert mock_event_bus.events[0][1].payload == { - "connection_id": TEST_CONN_ID, - "thread_id": TEST_THREAD_ID, - "updated": [result.serialize() for result in response.updated], - "mediated_connections": { - TEST_ROUTE_VERKEY: "conn_id_1", - TEST_VERKEY: "conn_id_2", - }, - } - - async def test_notify_keylist_updated_x_unknown_recip_key( - self, - manager: MediationManager, - ): - """test notify_keylist_updated.""" - response = KeylistUpdateResponse( - updated=[ - KeylistUpdated( - recipient_key=TEST_ROUTE_VERKEY, - action=KeylistUpdateRule.RULE_ADD, - result=KeylistUpdated.RESULT_SUCCESS, - ), - KeylistUpdated( - recipient_key=TEST_VERKEY, - action=KeylistUpdateRule.RULE_REMOVE, - result=KeylistUpdated.RESULT_SUCCESS, - ), - ], - ) - - response.assign_thread_id(TEST_THREAD_ID) - with pytest.raises(MediationManagerError): - await manager.notify_keylist_updated(TEST_CONN_ID, response) - async def test_get_my_keylist(self, session, manager): """test_get_my_keylist.""" await RouteRecord( From 4e57f019e0a58541a64464b6cb5644b61dabbeb0 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Thu, 1 Sep 2022 21:24:06 -0400 Subject: [PATCH 448/872] fix: tests for keylist update response handler Signed-off-by: Daniel Bluhm --- .../tests/test_keylist_update_response_handler.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/tests/test_keylist_update_response_handler.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/tests/test_keylist_update_response_handler.py index 346e1fe9c0..b5774c5723 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/tests/test_keylist_update_response_handler.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/tests/test_keylist_update_response_handler.py @@ -43,9 +43,13 @@ async def setUp(self): self.context.connection_ready = True self.context.connection_record = ConnRecord(connection_id=TEST_CONN_ID) self.mock_event_bus = MockEventBus() - self.context.injector.bind_instance(EventBus, self.mock_event_bus) + self.context.profile.context.injector.bind_instance( + EventBus, self.mock_event_bus + ) self.route_manager = MockRouteManager() - self.context.injector.bind_instance(RouteManager, self.route_manager) + self.context.profile.context.injector.bind_instance( + RouteManager, self.route_manager + ) async def test_handler_no_active_connection(self): handler, responder = KeylistUpdateResponseHandler(), MockResponder() From 6f48afbe28f50fde7ef3cc9a891fb5fb6c275936 Mon Sep 17 00:00:00 2001 From: Ethan Sung Date: Fri, 2 Sep 2022 13:47:21 +0900 Subject: [PATCH 449/872] Change type from posix path to string Signed-off-by: Ethan Sung --- aries_cloudagent/revocation/models/revocation_registry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aries_cloudagent/revocation/models/revocation_registry.py b/aries_cloudagent/revocation/models/revocation_registry.py index e6c98a41bd..cfb97eb467 100644 --- a/aries_cloudagent/revocation/models/revocation_registry.py +++ b/aries_cloudagent/revocation/models/revocation_registry.py @@ -198,7 +198,7 @@ async def retrieve_tails(self): "The hash of the downloaded tails file does not match." ) - self.tails_local_path = tails_file_path + self.tails_local_path = str(tails_file_path) return self.tails_local_path async def get_or_fetch_local_tails_path(self): From 469e93666bfd9ed53f4f736e579608d8e4fc26ef Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Fri, 2 Sep 2022 11:50:03 -0400 Subject: [PATCH 450/872] test: connection from recip key in route manager Signed-off-by: Daniel Bluhm --- .../v1_0/tests/test_route_manager.py | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_route_manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_route_manager.py index c1c7ab1509..bf5931c42e 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_route_manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_route_manager.py @@ -3,6 +3,7 @@ from .....connections.models.conn_record import ConnRecord from .....core.in_memory import InMemoryProfile +from .....wallet.base import BaseWallet from .....core.profile import Profile from .....messaging.responder import BaseResponder, MockResponder from .....storage.error import StorageNotFoundError @@ -480,6 +481,38 @@ async def test_save_mediator_for_connection_no_mediator( conn_record.metadata_set.assert_not_called() +@pytest.mark.asyncio +async def test_connection_from_recipient_key_invite( + profile: Profile, route_manager: RouteManager, conn_record: ConnRecord +): + with mock.patch.object( + ConnRecord, + "retrieve_by_invitation_key", + mock.CoroutineMock(return_value=conn_record), + ): + result = await route_manager.connection_from_recipient_key(profile, TEST_VERKEY) + assert conn_record == result + + +@pytest.mark.asyncio +async def test_connection_from_recipient_key_local_did( + profile: Profile, route_manager: RouteManager, conn_record: ConnRecord +): + mock_provider = mock.MagicMock() + mock_wallet = mock.MagicMock() + mock_wallet.get_local_did_for_verkey = mock.CoroutineMock() + mock_provider.provide = mock.MagicMock(return_value=mock_wallet) + session = await profile.session() + session.context.injector.bind_provider(BaseWallet, mock_provider) + with mock.patch.object( + profile, "session", mock.MagicMock(return_value=session) + ), mock.patch.object( + ConnRecord, "retrieve_by_did", mock.CoroutineMock(return_value=conn_record) + ): + result = await route_manager.connection_from_recipient_key(profile, TEST_VERKEY) + assert conn_record == result + + @pytest.mark.asyncio async def test_mediation_route_for_key( profile: Profile, From 7cd74a953b98528fd1e844175bc1fd80f7662e6e Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Fri, 2 Sep 2022 14:28:34 -0400 Subject: [PATCH 451/872] test: multitenant route manager conn from recip Signed-off-by: Daniel Bluhm --- aries_cloudagent/multitenant/route_manager.py | 4 ++-- .../multitenant/tests/test_route_manager.py | 20 +++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/multitenant/route_manager.py b/aries_cloudagent/multitenant/route_manager.py index f9d1751cf9..0e0bdee1ff 100644 --- a/aries_cloudagent/multitenant/route_manager.py +++ b/aries_cloudagent/multitenant/route_manager.py @@ -15,7 +15,7 @@ from ..protocols.routing.v1_0.manager import RoutingManager from ..protocols.routing.v1_0.models.route_record import RouteRecord from ..storage.error import StorageNotFoundError -from .manager import MultitenantManager +from .base import BaseMultitenantManager LOGGER = logging.getLogger(__name__) @@ -117,7 +117,7 @@ async def connection_from_recipient_key( for sub wallets, we check the sub wallet's connections before the base wallet. """ - manager = MultitenantManager(self.root_profile) + manager = self.root_profile.inject(BaseMultitenantManager) profile_to_search = ( await manager.get_profile_for_key(profile.context, recipient_key) or profile ) diff --git a/aries_cloudagent/multitenant/tests/test_route_manager.py b/aries_cloudagent/multitenant/tests/test_route_manager.py index 75f4547a0a..94d750aa6f 100644 --- a/aries_cloudagent/multitenant/tests/test_route_manager.py +++ b/aries_cloudagent/multitenant/tests/test_route_manager.py @@ -8,9 +8,11 @@ from ...protocols.coordinate_mediation.v1_0.models.mediation_record import ( MediationRecord, ) +from ...protocols.coordinate_mediation.v1_0.route_manager import RouteManager from ...protocols.routing.v1_0.manager import RoutingManager from ...protocols.routing.v1_0.models.route_record import RouteRecord from ...storage.error import StorageNotFoundError +from ..base import BaseMultitenantManager from ..route_manager import MultitenantRouteManager TEST_RECORD_VERKEY = "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx" @@ -360,3 +362,21 @@ async def test_routing_info_with_base_mediator_and_sub_mediator( ) assert keys == [*base_mediation_record.routing_keys, *mediation_record.routing_keys] assert endpoint == mediation_record.endpoint + + +@pytest.mark.asyncio +async def test_connection_from_recipient_key( + sub_profile: Profile, route_manager: MultitenantRouteManager +): + manager = mock.MagicMock() + manager.get_profile_for_key = mock.CoroutineMock(return_value=sub_profile) + route_manager.root_profile.context.injector.bind_instance( + BaseMultitenantManager, manager + ) + with mock.patch.object( + RouteManager, "connection_from_recipient_key", mock.CoroutineMock() + ) as mock_conn_for_recip: + result = await route_manager.connection_from_recipient_key( + route_manager.root_profile, TEST_VERKEY + ) + assert result == mock_conn_for_recip.return_value From f0ba7da53c502bdccbfc14d9f99bed84a2c9773d Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Fri, 2 Sep 2022 14:36:10 -0400 Subject: [PATCH 452/872] test: multitenant manager get profile for key Signed-off-by: Daniel Bluhm --- aries_cloudagent/multitenant/tests/test_base.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/aries_cloudagent/multitenant/tests/test_base.py b/aries_cloudagent/multitenant/tests/test_base.py index 6f7c7b7e78..9f76c70676 100644 --- a/aries_cloudagent/multitenant/tests/test_base.py +++ b/aries_cloudagent/multitenant/tests/test_base.py @@ -620,3 +620,18 @@ async def test_get_wallets_by_message(self): assert wallets[0] == return_wallets[0] assert wallets[1] == return_wallets[3] assert get_wallet_by_key.call_count == 4 + + async def test_get_profile_for_key(self): + mock_wallet = async_mock.MagicMock() + mock_wallet.requires_external_key = False + with async_mock.patch.object( + self.manager, + "_get_wallet_by_key", + async_mock.CoroutineMock(return_value=mock_wallet), + ), async_mock.patch.object( + self.manager, "get_wallet_profile", async_mock.CoroutineMock() + ) as mock_get_wallet_profile: + profile = await self.manager.get_profile_for_key( + self.context, "test-verkey" + ) + assert profile == mock_get_wallet_profile.return_value From 25b6ec64c44a50b1c8c14b33f0c448b7ef9a878b Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Fri, 2 Sep 2022 22:29:56 -0400 Subject: [PATCH 453/872] feat: add base wallet route manager Necessary for actions specific to just the base wallet for route management tasks Signed-off-by: Daniel Bluhm --- aries_cloudagent/multitenant/route_manager.py | 18 +++++++++++++++--- .../multitenant/tests/test_route_manager.py | 9 +++++++-- .../keylist_update_response_handler.py | 4 ++++ .../coordinate_mediation/v1_0/manager.py | 2 ++ .../coordinate_mediation/v1_0/route_manager.py | 4 ++-- .../v1_0/route_manager_provider.py | 14 ++++++++++---- 6 files changed, 40 insertions(+), 11 deletions(-) diff --git a/aries_cloudagent/multitenant/route_manager.py b/aries_cloudagent/multitenant/route_manager.py index 0e0bdee1ff..ff8f1d37e7 100644 --- a/aries_cloudagent/multitenant/route_manager.py +++ b/aries_cloudagent/multitenant/route_manager.py @@ -11,7 +11,11 @@ from ..protocols.coordinate_mediation.v1_0.models.mediation_record import ( MediationRecord, ) -from ..protocols.coordinate_mediation.v1_0.route_manager import RouteManager +from ..protocols.coordinate_mediation.v1_0.normalization import normalize_from_did_key +from ..protocols.coordinate_mediation.v1_0.route_manager import ( + CoordinateMediationV1RouteManager, + RouteManager, +) from ..protocols.routing.v1_0.manager import RoutingManager from ..protocols.routing.v1_0.models.route_record import RouteRecord from ..storage.error import StorageNotFoundError @@ -106,6 +110,10 @@ async def routing_info( return routing_keys, my_endpoint + +class BaseWalletRouteManager(CoordinateMediationV1RouteManager): + """Route manager for operations specific to the base wallet.""" + async def connection_from_recipient_key( self, profile: Profile, recipient_key: str ) -> ConnRecord: @@ -117,9 +125,13 @@ async def connection_from_recipient_key( for sub wallets, we check the sub wallet's connections before the base wallet. """ - manager = self.root_profile.inject(BaseMultitenantManager) + LOGGER.debug("Retrieving connection for recipient key for multitenant wallet") + manager = profile.inject(BaseMultitenantManager) profile_to_search = ( - await manager.get_profile_for_key(profile.context, recipient_key) or profile + await manager.get_profile_for_key( + profile.context, normalize_from_did_key(recipient_key) + ) + or profile ) return await super().connection_from_recipient_key( diff --git a/aries_cloudagent/multitenant/tests/test_route_manager.py b/aries_cloudagent/multitenant/tests/test_route_manager.py index 94d750aa6f..ddcc528a2b 100644 --- a/aries_cloudagent/multitenant/tests/test_route_manager.py +++ b/aries_cloudagent/multitenant/tests/test_route_manager.py @@ -13,7 +13,7 @@ from ...protocols.routing.v1_0.models.route_record import RouteRecord from ...storage.error import StorageNotFoundError from ..base import BaseMultitenantManager -from ..route_manager import MultitenantRouteManager +from ..route_manager import BaseWalletRouteManager, MultitenantRouteManager TEST_RECORD_VERKEY = "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx" TEST_VERKEY = "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL" @@ -57,6 +57,11 @@ def route_manager(root_profile: Profile, sub_profile: Profile, wallet_id: str): yield MultitenantRouteManager(root_profile) +@pytest.fixture +def base_route_manager(root_profile: Profile, sub_profile: Profile, wallet_id: str): + yield BaseWalletRouteManager(root_profile) + + @pytest.mark.asyncio async def test_route_for_key_sub_mediator_no_base_mediator( route_manager: MultitenantRouteManager, @@ -366,7 +371,7 @@ async def test_routing_info_with_base_mediator_and_sub_mediator( @pytest.mark.asyncio async def test_connection_from_recipient_key( - sub_profile: Profile, route_manager: MultitenantRouteManager + sub_profile: Profile, base_route_manager: MultitenantRouteManager ): manager = mock.MagicMock() manager.get_profile_for_key = mock.CoroutineMock(return_value=sub_profile) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/keylist_update_response_handler.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/keylist_update_response_handler.py index c2d2a21c1b..d2c33704d8 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/keylist_update_response_handler.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/keylist_update_response_handler.py @@ -37,6 +37,10 @@ async def notify_keylist_updated( ): """Notify of keylist update response received.""" route_manager = profile.inject(RouteManager) + self._logger.debug( + "Retrieving connection ID from route manager of type %s", + type(route_manager).__name__, + ) try: key_to_connection = { updated.recipient_key: await route_manager.connection_from_recipient_key( diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py index 61bd6160f2..90f1061317 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py @@ -538,6 +538,8 @@ async def store_update_results( session: An active profile session """ + # TODO The stored recipient keys are did:key! + to_save: Sequence[RouteRecord] = [] to_remove: Sequence[RouteRecord] = [] diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py index 7f3b839e53..46e23204f6 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py @@ -253,8 +253,8 @@ async def connection_from_recipient_key( async with profile.session() as session: wallet = session.inject(BaseWallet) try: - conn = await ConnRecord.retrieve_by_invitation_key( - session, invitation_key=normalize_from_did_key(recipient_key) + conn = await ConnRecord.retrieve_by_tag_filter( + session, {"invitation_key": normalize_from_did_key(recipient_key)} ) except StorageNotFoundError: did_info = await wallet.get_local_did_for_verkey( diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager_provider.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager_provider.py index 693766c922..a48dddaec8 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager_provider.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager_provider.py @@ -2,7 +2,10 @@ from ....config.base import BaseInjector, BaseProvider, BaseSettings from ....core.profile import Profile from ....multitenant.base import BaseMultitenantManager -from ....multitenant.route_manager import MultitenantRouteManager +from ....multitenant.route_manager import ( + MultitenantRouteManager, + BaseWalletRouteManager, +) from .route_manager import CoordinateMediationV1RouteManager @@ -19,8 +22,11 @@ def __init__(self, root_profile: Profile): def provide(self, settings: BaseSettings, injector: BaseInjector): """Create the appropriate route manager instance.""" wallet_id = settings.get("wallet.id") - multitenant_mgr = injector.inject_or(BaseMultitenantManager) - if multitenant_mgr and wallet_id: - return MultitenantRouteManager(self.root_profile) + multitenant_mgr = self.root_profile.inject_or(BaseMultitenantManager) + if multitenant_mgr: + if wallet_id: + return MultitenantRouteManager(self.root_profile) + else: + return BaseWalletRouteManager() return CoordinateMediationV1RouteManager() From 8694bb5032483d297ddbf15845503895b9111dfd Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Tue, 6 Sep 2022 07:14:42 -0700 Subject: [PATCH 454/872] Add endorser docs and fix some bugs in the ATTRIB endorse process Signed-off-by: Ian Costanzo --- AnoncredsProofValidation.md | 84 ++++++++++++++++++ DIDResolution.md | 8 +- Endorser.md | 43 ++++++++- aries_cloudagent/config/default_context.py | 7 +- aries_cloudagent/core/tests/test_conductor.py | 4 +- aries_cloudagent/indy/credx/verifier.py | 23 +++-- .../indy/sdk/tests/test_verifier.py | 54 +++++++++-- aries_cloudagent/indy/sdk/verifier.py | 23 +++-- aries_cloudagent/indy/verifier.py | 56 +++++++++++- aries_cloudagent/ledger/indy.py | 3 +- .../connections/v1_0/tests/test_manager.py | 9 +- .../endorse_transaction/v1_0/manager.py | 10 ++- .../protocols/present_proof/dif/pres_exch.py | 4 +- .../present_proof/dif/tests/test_pres_exch.py | 12 +++ .../dif/tests/test_pres_exch_handler.py | 5 +- .../protocols/present_proof/v1_0/manager.py | 22 ++--- .../v1_0/models/presentation_exchange.py | 10 +++ .../v1_0/models/tests/test_record.py | 1 + .../present_proof/v1_0/tests/test_manager.py | 2 +- .../v2_0/formats/indy/handler.py | 18 ++-- .../v2_0/models/pres_exchange.py | 10 +++ .../v2_0/models/tests/test_record.py | 1 + .../present_proof/v2_0/tests/test_manager.py | 2 +- aries_cloudagent/resolver/__init__.py | 14 +-- aries_cloudagent/resolver/did_resolver.py | 13 +-- .../resolver/did_resolver_registry.py | 28 ------ .../resolver/tests/test_did_resolver.py | 21 ++--- .../tests/test_did_resolver_registry.py | 12 --- aries_cloudagent/wallet/did_method.py | 9 +- aries_cloudagent/wallet/indy.py | 3 + aries_cloudagent/wallet/routes.py | 68 ++++++++++++-- aries_cloudagent/wallet/util.py | 10 +++ demo/features/0453-issue-credential.feature | 1 + demo/features/0586-sign-transaction.feature | 1 + demo/features/steps/0586-sign-transaction.py | 9 +- demo/runners/agent_container.py | 10 ++- demo/runners/support/agent.py | 2 + docs/assets/endorse-cred-def.png | Bin 0 -> 134605 bytes docs/assets/endorse-cred-def.puml | 75 ++++++++++++++++ docs/assets/endorse-public-did.png | Bin 0 -> 93680 bytes docs/assets/endorse-public-did.puml | 53 +++++++++++ docs/assets/endorser-design.png | Bin 0 -> 25582 bytes docs/assets/endorser-design.puml | 31 +++++++ 43 files changed, 611 insertions(+), 160 deletions(-) create mode 100644 AnoncredsProofValidation.md delete mode 100644 aries_cloudagent/resolver/did_resolver_registry.py delete mode 100644 aries_cloudagent/resolver/tests/test_did_resolver_registry.py create mode 100644 docs/assets/endorse-cred-def.png create mode 100644 docs/assets/endorse-cred-def.puml create mode 100644 docs/assets/endorse-public-did.png create mode 100644 docs/assets/endorse-public-did.puml create mode 100644 docs/assets/endorser-design.png create mode 100644 docs/assets/endorser-design.puml diff --git a/AnoncredsProofValidation.md b/AnoncredsProofValidation.md new file mode 100644 index 0000000000..9114c3a727 --- /dev/null +++ b/AnoncredsProofValidation.md @@ -0,0 +1,84 @@ +# Anoncreds Proof Validation in Aca-Py + +Aca-Py does some pre-validation when verifying Anoncreds presentations (proofs), some scenarios are rejected (things that are indicative of tampering, for example) and some attributes are removed before running the anoncreds validation (for example removing superfluous non-revocation timestamps). Any Aca-Py validations or presentation modifications are indicated by the "verify_msgs" attribute in the final presentation exchange object + +The list of possible verification messages is [here](https://github.com/hyperledger/aries-cloudagent-python/blob/main/aries_cloudagent/indy/verifier.py#L24), and consists of: + +``` +class PresVerifyMsg(str, Enum): + """Credential verification codes.""" + + RMV_REFERENT_NON_REVOC_INTERVAL = "RMV_RFNT_NRI" + RMV_GLOBAL_NON_REVOC_INTERVAL = "RMV_GLB_NRI" + TSTMP_OUT_NON_REVOC_INTRVAL = "TS_OUT_NRI" + CT_UNREVEALED_ATTRIBUTES = "UNRVL_ATTR" + PRES_VALUE_ERROR = "VALUE_ERROR" + PRES_VERIFY_ERROR = "VERIFY_ERROR" +``` + +If there is additional information, it will be included like this: `TS_OUT_NRI::19_uuid` (which means the attribute identified by `19_uuid` contained a timestamp outside of the non-revocation interval (which is just a warning)). + +A presentation verification may include multiple messages, for example: + +``` + ... + "verified": "true", + "verified_msgs": [ + "TS_OUT_NRI::18_uuid", + "TS_OUT_NRI::18_id_GE_uuid", + "TS_OUT_NRI::18_busid_GE_uuid" + ], + ... +``` + +... or it may include a single message, for example: + +``` + ... + "verified": "false", + "verified_msgs": [ + "VALUE_ERROR::Encoded representation mismatch for 'Preferred Name'" + ], + ... +``` + +... or the `verified_msgs` may be null or an empty array. + +## Presentation Modifications and Warnings + +The following modifications/warnings may be done by Aca-Py which shouldn't affect the verification of the received proof): + +- "RMV_RFNT_NRI": Referent contains a non-revocation interval for a non-revocable credential (timestamp is removed) +- "RMV_GLB_NRI": Presentation contains a global interval for a non-revocable credential (timestamp is removed) +- "TS_OUT_NRI": Presentation contains a non-revocation timestamp outside of the requested non-revocation interval (warning) +- "UNRVL_ATTR": Presentation contains attributes with unrevealed values (warning) + +## Presentation Pre-validation Errors + +The following pre-verification checks are done, which will fail the proof (before calling anoncreds) and will result in the following message: + +``` +VALUE_ERROR:: +``` + +These validations are all done within the [Indy verifier class](https://github.com/hyperledger/aries-cloudagent-python/blob/main/aries_cloudagent/indy/verifier.py) - to see the detailed validation just look for anywhere a `raise ValueError(...)` appears in the code. + +A summary of the possible errors is: + +- information missing in presentation exchange record +- timestamp provided for irrevocable credential +- referenced revocation registry not found on ledger +- timestamp outside of reasonable range (future date or pre-dates revocation registry) +- mis-match between provided and requested timestamps for non-revocation +- mis-match between requested and provided attributes or predicates +- self-attested attribute is provided for a requested attribute with restrictions +- encoded value doesn't match raw value + +## Anoncreds Verification Exceptions + +Typically when you call the anoncreds `verifier_verify_proof()` method, it will return a `True` or `False` based on whether the presentation cryptographically verifies. However in the case where anoncreds throws an exception, the exception text will be included in a verification message as follows: + +``` +VERIFY_ERROR:: +``` + diff --git a/DIDResolution.md b/DIDResolution.md index c1666adab2..cbe00edc1d 100644 --- a/DIDResolution.md +++ b/DIDResolution.md @@ -29,7 +29,7 @@ In practice, DIDs and DID Documents are used for a variety of purposes but espec ## `DIDResolver` -In ACA-Py, the `DIDResolver` provides the interface to resolve DIDs using registered method resolvers. Method resolver registration happens on startup through the `DIDResolverRegistry`. This registry enables additional resolvers to be loaded via plugin. +In ACA-Py, the `DIDResolver` provides the interface to resolve DIDs using registered method resolvers. Method resolver registration happens on startup in a `did_resolvers` list. This registry enables additional resolvers to be loaded via plugin. #### Example usage: ```python= @@ -73,17 +73,17 @@ The following is an example method resolver implementation. In this example, we ```python= from aries_cloudagent.config.injection_context import InjectionContext -from aries_cloudagent.resolver.did_resolver_registry import DIDResolverRegistry +from ..resolver.did_resolver import DIDResolver from .example_resolver import ExampleResolver async def setup(context: InjectionContext): """Setup the plugin.""" - registry = context.inject(DIDResolverRegistry) + registry = context.inject(DIDResolver) resolver = ExampleResolver() await resolver.setup(context) - registry.register(resolver) + registry.append(resolver) ``` #### `example_resolver.py` diff --git a/Endorser.md b/Endorser.md index 54f545fce3..bf3ec5da8c 100644 --- a/Endorser.md +++ b/Endorser.md @@ -1,7 +1,5 @@ # Transaction Endorser Support -Note that the ACA-Py transaction support is in the process of code refactor and cleanup. The following documents the current state, but is subject to change. - ACA-Py supports an [Endorser Protocol](https://github.com/hyperledger/aries-rfcs/pull/586), that allows an un-privileged agent (an "Author") to request another agent (the "Endorser") to sign their transactions so they can write these transactions to the ledger. This is required on Indy ledgers, where new agents will typically be granted only "Author" privileges. Transaction Endorsement is built into the protocols for Schema, Credential Definition and Revocation, and endorsements can be explicitely requested, or ACA-Py can be configured to automate the endorsement workflow. @@ -61,3 +59,44 @@ Endorsement: For Authors, specify whether to automatically promote a DID to the wallet public DID after writing to the ledger. ``` +## How Aca-py Handles Endorsements + +Internally, the Endorsement functionality is implemented as a protocol, and is implemented consistently with other protocols: + +- a [routes.py](https://github.com/hyperledger/aries-cloudagent-python/blob/main/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py) file exposes the admin endpoints +- [handler files](https://github.com/hyperledger/aries-cloudagent-python/tree/main/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers) implement responses to any received Endorse protocol messages +- a [manager.py](https://github.com/hyperledger/aries-cloudagent-python/blob/main/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py) file implements common functionality that is called from both the routes.py and handler classes (as well as from other classes that need to interact with Endorser functionality) + +The Endorser makes use of the [Event Bus](https://github.com/hyperledger/aries-cloudagent-python/blob/main/CHANGELOG.md#july-14-2021) (links to the PR which links to a hackmd doc) to notify other protocols of any Endorser events of interest. For example, after a Credential Definition endorsement is received, the TransactionManager writes the endorsed transaction to the ledger and uses the Event Bus to notify the Credential Defintition manager that it can do any required post-processing (such as writing the cred def record to the wallet, initiating the revocation registry, etc.). + +The overall architecture can be illustrated as: + +![Class Diagram](./docs/assets/endorser-design.png) + +### Create Credential Definition and Revocation Registry + +An example of an Endorser flow is as follows, showing how a credential definition endorsement is received and processed, and optionally kicks off the revocation registry process: + +![Sequence Diagram](./docs/assets/endorse-cred-def.png) + +You can see that there is a standard endorser flow happening each time there is a ledger write (illustrated in the "Endorser" process). + +At the end of each endorse sequence, the TransactionManager sends a notification via the EventBus so that any dependant processing can continue. Each Router is responsible for listening and responding to these notifications if necessary. + +For example: + +- Once the credential definition is created, a revocation registry must be created (for revocable cred defs) +- Once the revocation registry is created, a revocation entry must be created +- Potentially, the cred def status could be updated once the revocation entry is completed + +Using the EventBus decouples the event sequence. Any functions triggered by an event notification are typically also available directly via Admin endpoints. + +### Create DID and Promote to Public + +... and an example of creating a DID and promoting it to public (and creating an ATTRIB for the endpoint: + +![Sequence Diagram](./docs/assets/endorse-public-did.png) + +You can see the same endorsement processes in this sequence. + +Once the DID is written, the DID can (optionally) be promoted to the public DID, which will also invoke an ATTRIB transaction to write the endpoint. diff --git a/aries_cloudagent/config/default_context.py b/aries_cloudagent/config/default_context.py index fb0867cddc..0c5f90cac2 100644 --- a/aries_cloudagent/config/default_context.py +++ b/aries_cloudagent/config/default_context.py @@ -12,7 +12,6 @@ from ..core.protocol_registry import ProtocolRegistry from ..core.goal_code_registry import GoalCodeRegistry from ..resolver.did_resolver import DIDResolver -from ..resolver.did_resolver_registry import DIDResolverRegistry from ..tails.base import BaseTailsServer from ..protocols.actionmenu.v1_0.base_service import BaseMenuService @@ -50,12 +49,8 @@ async def build_context(self) -> InjectionContext: # Global event bus context.injector.bind_instance(EventBus, EventBus()) - # Global did resolver registry - did_resolver_registry = DIDResolverRegistry() - context.injector.bind_instance(DIDResolverRegistry, did_resolver_registry) - # Global did resolver - context.injector.bind_instance(DIDResolver, DIDResolver(did_resolver_registry)) + context.injector.bind_instance(DIDResolver, DIDResolver([])) await self.bind_providers(context) await self.load_plugins(context) diff --git a/aries_cloudagent/core/tests/test_conductor.py b/aries_cloudagent/core/tests/test_conductor.py index 02fbe4ff49..203ca1b3b4 100644 --- a/aries_cloudagent/core/tests/test_conductor.py +++ b/aries_cloudagent/core/tests/test_conductor.py @@ -24,7 +24,7 @@ from ...protocols.coordinate_mediation.v1_0.models.mediation_record import ( MediationRecord, ) -from ...resolver.did_resolver import DIDResolver, DIDResolverRegistry +from ...resolver.did_resolver import DIDResolver from ...multitenant.base import BaseMultitenantManager from ...multitenant.manager import MultitenantManager from ...storage.base import BaseStorage @@ -92,7 +92,7 @@ async def build_context(self) -> InjectionContext: context.injector.bind_instance(ProfileManager, InMemoryProfileManager()) context.injector.bind_instance(ProtocolRegistry, ProtocolRegistry()) context.injector.bind_instance(BaseWireFormat, self.wire_format) - context.injector.bind_instance(DIDResolver, DIDResolver(DIDResolverRegistry())) + context.injector.bind_instance(DIDResolver, DIDResolver([])) context.injector.bind_instance(EventBus, MockEventBus()) return context diff --git a/aries_cloudagent/indy/credx/verifier.py b/aries_cloudagent/indy/credx/verifier.py index c6677cfa7b..e625076ecd 100644 --- a/aries_cloudagent/indy/credx/verifier.py +++ b/aries_cloudagent/indy/credx/verifier.py @@ -7,7 +7,7 @@ from ...core.profile import Profile -from ..verifier import IndyVerifier +from ..verifier import IndyVerifier, PresVerifyMsg LOGGER = logging.getLogger(__name__) @@ -33,7 +33,7 @@ async def verify_presentation( credential_definitions, rev_reg_defs, rev_reg_entries, - ) -> bool: + ) -> (bool, list): """ Verify a presentation. @@ -46,16 +46,21 @@ async def verify_presentation( rev_reg_entries: revocation registry entries """ + msgs = [] try: - self.non_revoc_intervals(pres_req, pres, credential_definitions) - await self.check_timestamps(self.profile, pres_req, pres, rev_reg_defs) - await self.pre_verify(pres_req, pres) + msgs += self.non_revoc_intervals(pres_req, pres, credential_definitions) + msgs += await self.check_timestamps( + self.profile, pres_req, pres, rev_reg_defs + ) + msgs += await self.pre_verify(pres_req, pres) except ValueError as err: + s = str(err) + msgs.append(f"{PresVerifyMsg.PRES_VALUE_ERROR.value}::{s}") LOGGER.error( f"Presentation on nonce={pres_req['nonce']} " f"cannot be validated: {str(err)}" ) - return False + return (False, msgs) try: presentation = Presentation.load(pres) @@ -68,11 +73,13 @@ async def verify_presentation( rev_reg_defs.values(), rev_reg_entries, ) - except CredxError: + except CredxError as err: + s = str(err) + msgs.append(f"{PresVerifyMsg.PRES_VERIFY_ERROR.value}::{s}") LOGGER.exception( f"Validation of presentation on nonce={pres_req['nonce']} " "failed with error" ) verified = False - return verified + return (verified, msgs) diff --git a/aries_cloudagent/indy/sdk/tests/test_verifier.py b/aries_cloudagent/indy/sdk/tests/test_verifier.py index 44784b1ad5..d4abc1bdd1 100644 --- a/aries_cloudagent/indy/sdk/tests/test_verifier.py +++ b/aries_cloudagent/indy/sdk/tests/test_verifier.py @@ -336,7 +336,7 @@ async def test_verify_presentation(self, mock_verify): ) as mock_get_ledger: mock_get_ledger.return_value = (None, self.ledger) INDY_PROOF_REQ_X = deepcopy(INDY_PROOF_REQ_PRED_NAMES) - verified = await self.verifier.verify_presentation( + (verified, msgs) = await self.verifier.verify_presentation( INDY_PROOF_REQ_X, INDY_PROOF_PRED_NAMES, "schemas", @@ -370,7 +370,7 @@ async def test_verify_presentation_x_indy(self, mock_verify): IndyLedgerRequestsExecutor, "get_ledger_for_identifier" ) as mock_get_ledger: mock_get_ledger.return_value = ("test", self.ledger) - verified = await self.verifier.verify_presentation( + (verified, msgs) = await self.verifier.verify_presentation( INDY_PROOF_REQ_NAME, INDY_PROOF_NAME, "schemas", @@ -397,7 +397,7 @@ async def test_check_encoding_attr(self, mock_verify): ) as mock_get_ledger: mock_get_ledger.return_value = (None, self.ledger) mock_verify.return_value = True - verified = await self.verifier.verify_presentation( + (verified, msgs) = await self.verifier.verify_presentation( INDY_PROOF_REQ_NAME, INDY_PROOF_NAME, "schemas", @@ -415,6 +415,8 @@ async def test_check_encoding_attr(self, mock_verify): json.dumps("rev_reg_entries"), ) assert verified is True + assert len(msgs) == 1 + assert "TS_OUT_NRI::19_uuid" in msgs @async_mock.patch("indy.anoncreds.verifier_verify_proof") async def test_check_encoding_attr_tamper_raw(self, mock_verify): @@ -426,7 +428,7 @@ async def test_check_encoding_attr_tamper_raw(self, mock_verify): IndyLedgerRequestsExecutor, "get_ledger_for_identifier" ) as mock_get_ledger: mock_get_ledger.return_value = ("test", self.ledger) - verified = await self.verifier.verify_presentation( + (verified, msgs) = await self.verifier.verify_presentation( INDY_PROOF_REQ_NAME, INDY_PROOF_X, "schemas", @@ -438,6 +440,11 @@ async def test_check_encoding_attr_tamper_raw(self, mock_verify): mock_verify.assert_not_called() assert verified is False + assert len(msgs) == 2 + assert "TS_OUT_NRI::19_uuid" in msgs + assert ( + "VALUE_ERROR::Encoded representation mismatch for 'Preferred Name'" in msgs + ) @async_mock.patch("indy.anoncreds.verifier_verify_proof") async def test_check_encoding_attr_tamper_encoded(self, mock_verify): @@ -449,7 +456,7 @@ async def test_check_encoding_attr_tamper_encoded(self, mock_verify): IndyLedgerRequestsExecutor, "get_ledger_for_identifier" ) as mock_get_ledger: mock_get_ledger.return_value = (None, self.ledger) - verified = await self.verifier.verify_presentation( + (verified, msgs) = await self.verifier.verify_presentation( INDY_PROOF_REQ_NAME, INDY_PROOF_X, "schemas", @@ -461,6 +468,11 @@ async def test_check_encoding_attr_tamper_encoded(self, mock_verify): mock_verify.assert_not_called() assert verified is False + assert len(msgs) == 2 + assert "TS_OUT_NRI::19_uuid" in msgs + assert ( + "VALUE_ERROR::Encoded representation mismatch for 'Preferred Name'" in msgs + ) @async_mock.patch("indy.anoncreds.verifier_verify_proof") async def test_check_pred_names(self, mock_verify): @@ -470,7 +482,7 @@ async def test_check_pred_names(self, mock_verify): mock_get_ledger.return_value = ("test", self.ledger) mock_verify.return_value = True INDY_PROOF_REQ_X = deepcopy(INDY_PROOF_REQ_PRED_NAMES) - verified = await self.verifier.verify_presentation( + (verified, msgs) = await self.verifier.verify_presentation( INDY_PROOF_REQ_X, INDY_PROOF_PRED_NAMES, "schemas", @@ -491,6 +503,10 @@ async def test_check_pred_names(self, mock_verify): ) assert verified is True + assert len(msgs) == 3 + assert "TS_OUT_NRI::18_uuid" in msgs + assert "TS_OUT_NRI::18_id_GE_uuid" in msgs + assert "TS_OUT_NRI::18_busid_GE_uuid" in msgs @async_mock.patch("indy.anoncreds.verifier_verify_proof") async def test_check_pred_names_tamper_pred_value(self, mock_verify): @@ -502,7 +518,7 @@ async def test_check_pred_names_tamper_pred_value(self, mock_verify): IndyLedgerRequestsExecutor, "get_ledger_for_identifier" ) as mock_get_ledger: mock_get_ledger.return_value = (None, self.ledger) - verified = await self.verifier.verify_presentation( + (verified, msgs) = await self.verifier.verify_presentation( deepcopy(INDY_PROOF_REQ_PRED_NAMES), INDY_PROOF_X, "schemas", @@ -514,6 +530,14 @@ async def test_check_pred_names_tamper_pred_value(self, mock_verify): mock_verify.assert_not_called() assert verified is False + assert len(msgs) == 4 + assert "RMV_RFNT_NRI::18_uuid" in msgs + assert "RMV_RFNT_NRI::18_busid_GE_uuid" in msgs + assert "RMV_RFNT_NRI::18_id_GE_uuid" in msgs + assert ( + "VALUE_ERROR::Timestamp on sub-proof #0 is superfluous vs. requested attribute group 18_uuid" + in msgs + ) @async_mock.patch("indy.anoncreds.verifier_verify_proof") async def test_check_pred_names_tamper_pred_req_attr(self, mock_verify): @@ -523,7 +547,7 @@ async def test_check_pred_names_tamper_pred_req_attr(self, mock_verify): IndyLedgerRequestsExecutor, "get_ledger_for_identifier" ) as mock_get_ledger: mock_get_ledger.return_value = (None, self.ledger) - verified = await self.verifier.verify_presentation( + (verified, msgs) = await self.verifier.verify_presentation( INDY_PROOF_REQ_X, INDY_PROOF_PRED_NAMES, "schemas", @@ -535,6 +559,14 @@ async def test_check_pred_names_tamper_pred_req_attr(self, mock_verify): mock_verify.assert_not_called() assert verified is False + assert len(msgs) == 4 + assert "RMV_RFNT_NRI::18_uuid" in msgs + assert "RMV_RFNT_NRI::18_busid_GE_uuid" in msgs + assert "RMV_RFNT_NRI::18_id_GE_uuid" in msgs + assert ( + "VALUE_ERROR::Timestamp on sub-proof #0 is superfluous vs. requested attribute group 18_uuid" + in msgs + ) @async_mock.patch("indy.anoncreds.verifier_verify_proof") async def test_check_pred_names_tamper_attr_groups(self, mock_verify): @@ -546,7 +578,7 @@ async def test_check_pred_names_tamper_attr_groups(self, mock_verify): IndyLedgerRequestsExecutor, "get_ledger_for_identifier" ) as mock_get_ledger: mock_get_ledger.return_value = ("test", self.ledger) - verified = await self.verifier.verify_presentation( + (verified, msgs) = await self.verifier.verify_presentation( deepcopy(INDY_PROOF_REQ_PRED_NAMES), INDY_PROOF_X, "schemas", @@ -558,3 +590,7 @@ async def test_check_pred_names_tamper_attr_groups(self, mock_verify): mock_verify.assert_not_called() assert verified is False + assert len(msgs) == 3 + assert "RMV_RFNT_NRI::18_busid_GE_uuid" in msgs + assert "RMV_RFNT_NRI::18_id_GE_uuid" in msgs + assert "VALUE_ERROR::Missing requested attribute group 18_uuid" in msgs diff --git a/aries_cloudagent/indy/sdk/verifier.py b/aries_cloudagent/indy/sdk/verifier.py index b9e087aa82..5c67463eed 100644 --- a/aries_cloudagent/indy/sdk/verifier.py +++ b/aries_cloudagent/indy/sdk/verifier.py @@ -8,7 +8,7 @@ from ...core.profile import Profile -from ..verifier import IndyVerifier +from ..verifier import IndyVerifier, PresVerifyMsg LOGGER = logging.getLogger(__name__) @@ -34,7 +34,7 @@ async def verify_presentation( credential_definitions, rev_reg_defs, rev_reg_entries, - ) -> bool: + ) -> (bool, list): """ Verify a presentation. @@ -49,16 +49,21 @@ async def verify_presentation( LOGGER.debug(f">>> received presentation: {pres}") LOGGER.debug(f">>> for pres_req: {pres_req}") + msgs = [] try: - self.non_revoc_intervals(pres_req, pres, credential_definitions) - await self.check_timestamps(self.profile, pres_req, pres, rev_reg_defs) - await self.pre_verify(pres_req, pres) + msgs += self.non_revoc_intervals(pres_req, pres, credential_definitions) + msgs += await self.check_timestamps( + self.profile, pres_req, pres, rev_reg_defs + ) + msgs += await self.pre_verify(pres_req, pres) except ValueError as err: + s = str(err) + msgs.append(f"{PresVerifyMsg.PRES_VALUE_ERROR.value}::{s}") LOGGER.error( f"Presentation on nonce={pres_req['nonce']} " f"cannot be validated: {str(err)}" ) - return False + return (False, msgs) LOGGER.debug(f">>> verifying presentation: {pres}") LOGGER.debug(f">>> for pres_req: {pres_req}") @@ -71,11 +76,13 @@ async def verify_presentation( json.dumps(rev_reg_defs), json.dumps(rev_reg_entries), ) - except IndyError: + except IndyError as err: + s = str(err) + msgs.append(f"{PresVerifyMsg.PRES_VERIFY_ERROR.value}::{s}") LOGGER.exception( f"Validation of presentation on nonce={pres_req['nonce']} " "failed with error" ) verified = False - return verified + return (verified, msgs) diff --git a/aries_cloudagent/indy/verifier.py b/aries_cloudagent/indy/verifier.py index f14ad14b24..f61ca829f2 100644 --- a/aries_cloudagent/indy/verifier.py +++ b/aries_cloudagent/indy/verifier.py @@ -3,6 +3,7 @@ import logging from abc import ABC, ABCMeta, abstractmethod +from enum import Enum from time import time from typing import Mapping @@ -16,9 +17,21 @@ from .models.xform import indy_proof_req2non_revoc_intervals + LOGGER = logging.getLogger(__name__) +class PresVerifyMsg(str, Enum): + """Credential verification codes.""" + + RMV_REFERENT_NON_REVOC_INTERVAL = "RMV_RFNT_NRI" + RMV_GLOBAL_NON_REVOC_INTERVAL = "RMV_GLB_NRI" + TSTMP_OUT_NON_REVOC_INTRVAL = "TS_OUT_NRI" + CT_UNREVEALED_ATTRIBUTES = "UNRVL_ATTR" + PRES_VALUE_ERROR = "VALUE_ERROR" + PRES_VERIFY_ERROR = "VERIFY_ERROR" + + class IndyVerifier(ABC, metaclass=ABCMeta): """Base class for Indy Verifier.""" @@ -32,7 +45,7 @@ def __repr__(self) -> str: """ return "<{}>".format(self.__class__.__name__) - def non_revoc_intervals(self, pres_req: dict, pres: dict, cred_defs: dict): + def non_revoc_intervals(self, pres_req: dict, pres: dict, cred_defs: dict) -> list: """ Remove superfluous non-revocation intervals in presentation request. @@ -45,6 +58,7 @@ def non_revoc_intervals(self, pres_req: dict, pres: dict, cred_defs: dict): pres: corresponding presentation """ + msgs = [] for (req_proof_key, pres_key) in { "revealed_attrs": "requested_attributes", "revealed_attr_groups": "requested_attributes", @@ -60,6 +74,10 @@ def non_revoc_intervals(self, pres_req: dict, pres: dict, cred_defs: dict): if uuid in pres_req[pres_key] and pres_req[pres_key][uuid].pop( "non_revoked", None ): + msgs.append( + f"{PresVerifyMsg.RMV_REFERENT_NON_REVOC_INTERVAL.value}::" + f"{uuid}" + ) LOGGER.info( ( "Amended presentation request (nonce=%s): removed " @@ -79,6 +97,7 @@ def non_revoc_intervals(self, pres_req: dict, pres: dict, cred_defs: dict): for spec in pres["identifiers"] ): pres_req.pop("non_revoked", None) + msgs.append(PresVerifyMsg.RMV_GLOBAL_NON_REVOC_INTERVAL.value) LOGGER.warning( ( "Amended presentation request (nonce=%s); removed global " @@ -86,6 +105,7 @@ def non_revoc_intervals(self, pres_req: dict, pres: dict, cred_defs: dict): ), pres_req["nonce"], ) + return msgs async def check_timestamps( self, @@ -93,7 +113,7 @@ async def check_timestamps( pres_req: Mapping, pres: Mapping, rev_reg_defs: Mapping, - ): + ) -> list: """ Check for suspicious, missing, and superfluous timestamps. @@ -106,6 +126,7 @@ async def check_timestamps( pres: indy proof request rev_reg_defs: rev reg defs by rev reg id, augmented with transaction times """ + msgs = [] now = int(time()) non_revoc_intervals = indy_proof_req2non_revoc_intervals(pres_req) LOGGER.debug(f">>> got non-revoc intervals: {non_revoc_intervals}") @@ -159,6 +180,7 @@ async def check_timestamps( # timestamp superfluous, missing, or outside non-revocation interval revealed_attrs = pres["requested_proof"].get("revealed_attrs", {}) + unrevealed_attrs = pres["requested_proof"].get("unrevealed_attrs", {}) revealed_groups = pres["requested_proof"].get("revealed_attr_groups", {}) self_attested = pres["requested_proof"].get("self_attested_attrs", {}) preds = pres["requested_proof"].get("predicates", {}) @@ -185,11 +207,20 @@ async def check_timestamps( < timestamp < non_revoc_intervals[uuid].get("to", now) ): + msgs.append( + f"{PresVerifyMsg.TSTMP_OUT_NON_REVOC_INTRVAL.value}::" + f"{uuid}" + ) LOGGER.info( f"Timestamp {timestamp} from ledger for item" f"{uuid} falls outside non-revocation interval " f"{non_revoc_intervals[uuid]}" ) + elif uuid in unrevealed_attrs: + # nothing to do, attribute value is not revealed + msgs.append( + f"{PresVerifyMsg.CT_UNREVEALED_ATTRIBUTES.value}::" f"{uuid}" + ) elif uuid not in self_attested: raise ValueError( f"Presentation attributes mismatch requested attribute {uuid}" @@ -217,6 +248,10 @@ async def check_timestamps( < timestamp < non_revoc_intervals[uuid].get("to", now) ): + msgs.append( + f"{PresVerifyMsg.TSTMP_OUT_NON_REVOC_INTRVAL.value}::" + f"{uuid}" + ) LOGGER.warning( f"Timestamp {timestamp} from ledger for item" f"{uuid} falls outside non-revocation interval " @@ -243,13 +278,17 @@ async def check_timestamps( < timestamp < non_revoc_intervals[uuid].get("to", now) ): + msgs.append( + f"{PresVerifyMsg.TSTMP_OUT_NON_REVOC_INTRVAL.value}::" f"{uuid}" + ) LOGGER.warning( f"Best-effort timestamp {timestamp} " "from ledger falls outside non-revocation interval " f"{non_revoc_intervals[uuid]}" ) + return msgs - async def pre_verify(self, pres_req: dict, pres: dict): + async def pre_verify(self, pres_req: dict, pres: dict) -> list: """ Check for essential components and tampering in presentation. @@ -261,6 +300,7 @@ async def pre_verify(self, pres_req: dict, pres: dict): pres: corresponding presentation """ + msgs = [] if not ( pres_req and "requested_predicates" in pres_req @@ -297,12 +337,19 @@ async def pre_verify(self, pres_req: dict, pres: dict): raise ValueError(f"Missing requested predicate '{uuid}'") revealed_attrs = pres["requested_proof"].get("revealed_attrs", {}) + unrevealed_attrs = pres["requested_proof"].get("unrevealed_attrs", {}) revealed_groups = pres["requested_proof"].get("revealed_attr_groups", {}) self_attested = pres["requested_proof"].get("self_attested_attrs", {}) for (uuid, req_attr) in pres_req["requested_attributes"].items(): if "name" in req_attr: if uuid in revealed_attrs: pres_req_attr_spec = {req_attr["name"]: revealed_attrs[uuid]} + elif uuid in unrevealed_attrs: + # unrevealed attribute, nothing to do + pres_req_attr_spec = {} + msgs.append( + f"{PresVerifyMsg.CT_UNREVEALED_ATTRIBUTES.value}::" f"{uuid}" + ) elif uuid in self_attested: if not req_attr.get("restrictions"): continue @@ -339,6 +386,7 @@ async def pre_verify(self, pres_req: dict, pres: dict): raise ValueError(f"Encoded representation mismatch for '{attr}'") if primary_enco != encode(spec["raw"]): raise ValueError(f"Encoded representation mismatch for '{attr}'") + return msgs @abstractmethod def verify_presentation( @@ -349,7 +397,7 @@ def verify_presentation( credential_definitions, rev_reg_defs, rev_reg_entries, - ): + ) -> (bool, list): """ Verify a presentation. diff --git a/aries_cloudagent/ledger/indy.py b/aries_cloudagent/ledger/indy.py index 6f1c06e42f..de3e8d40fe 100644 --- a/aries_cloudagent/ledger/indy.py +++ b/aries_cloudagent/ledger/indy.py @@ -793,7 +793,7 @@ async def update_endpoint_for_did( ) resp = await self._submit( request_json, - True, + sign=True, sign_did=public_info, write_ledger=write_ledger, ) @@ -802,6 +802,7 @@ async def update_endpoint_for_did( await self._submit(request_json, True, True) return True + return False async def register_nym( diff --git a/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py index d7c3836236..80efb456bf 100644 --- a/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py @@ -20,7 +20,6 @@ from .....multitenant.manager import MultitenantManager from .....protocols.routing.v1_0.manager import RoutingManager from .....resolver.did_resolver import DIDResolver -from .....resolver.did_resolver_registry import DIDResolverRegistry from .....storage.error import StorageNotFoundError from .....transport.inbound.receipt import MessageReceipt from .....wallet.base import DIDInfo @@ -2024,9 +2023,7 @@ async def test_fetch_connection_targets_no_my_did(self): async def test_fetch_connection_targets_conn_invitation_did_no_resolver(self): async with self.profile.session() as session: - self.context.injector.bind_instance( - DIDResolver, DIDResolver(DIDResolverRegistry()) - ) + self.context.injector.bind_instance(DIDResolver, DIDResolver([])) await session.wallet.create_local_did( method=DIDMethod.SOV, key_type=KeyType.ED25519, @@ -2320,9 +2317,7 @@ async def test_fetch_connection_targets_conn_invitation_unsupported_key_type(sel async def test_fetch_connection_targets_oob_invitation_svc_did_no_resolver(self): async with self.profile.session() as session: - self.context.injector.bind_instance( - DIDResolver, DIDResolver(DIDResolverRegistry()) - ) + self.context.injector.bind_instance(DIDResolver, DIDResolver([])) await session.wallet.create_local_did( method=DIDMethod.SOV, key_type=KeyType.ED25519, diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py index 8ce93c45a8..3669a16eeb 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py @@ -21,7 +21,10 @@ from ....storage.error import StorageError, StorageNotFoundError from ....transport.inbound.receipt import MessageReceipt from ....wallet.base import BaseWallet -from ....wallet.util import notify_endorse_did_event +from ....wallet.util import ( + notify_endorse_did_event, + notify_endorse_did_attrib_event, +) from .messages.cancel_transaction import CancelTransaction from .messages.endorsed_transaction_response import EndorsedTransactionResponse @@ -794,6 +797,11 @@ async def endorsed_txn_post_processing( did = ledger_response["result"]["txn"]["data"]["dest"] await notify_endorse_did_event(self._profile, did, meta_data) + elif ledger_response["result"]["txn"]["type"] == "100": + # write DID ATTRIB to ledger + did = ledger_response["result"]["txn"]["data"]["dest"] + await notify_endorse_did_attrib_event(self._profile, did, meta_data) + else: # TODO unknown ledger transaction type, just ignore for now ... pass diff --git a/aries_cloudagent/protocols/present_proof/dif/pres_exch.py b/aries_cloudagent/protocols/present_proof/dif/pres_exch.py index dafe0b8857..3155d28860 100644 --- a/aries_cloudagent/protocols/present_proof/dif/pres_exch.py +++ b/aries_cloudagent/protocols/present_proof/dif/pres_exch.py @@ -237,7 +237,9 @@ def extract_info(self, data, **kwargs): """deserialize.""" new_data = {} if isinstance(data, dict): - if "oneof_filter" in data: + if "uri_groups" in data: + return data + elif "oneof_filter" in data and isinstance(data["oneof_filter"], list): new_data["oneof_filter"] = True uri_group_list_of_list = [] uri_group_list = data.get("oneof_filter") diff --git a/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch.py b/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch.py index 34638ef764..2709024263 100644 --- a/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch.py +++ b/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch.py @@ -395,6 +395,10 @@ def test_schemas_input_desc_filter(self): deser_schema_filter = SchemasInputDescriptorFilter.deserialize( test_schemas_filter ) + ser_schema_filter = deser_schema_filter.serialize() + deser_schema_filter = SchemasInputDescriptorFilter.deserialize( + ser_schema_filter + ) assert deser_schema_filter.oneof_filter assert deser_schema_filter.uri_groups[0][0].uri == test_schema_list[0][0].get( "uri" @@ -418,6 +422,10 @@ def test_schemas_input_desc_filter(self): deser_schema_filter = SchemasInputDescriptorFilter.deserialize( test_schemas_filter ) + ser_schema_filter = deser_schema_filter.serialize() + deser_schema_filter = SchemasInputDescriptorFilter.deserialize( + ser_schema_filter + ) assert deser_schema_filter.oneof_filter assert deser_schema_filter.uri_groups[0][0].uri == test_schema_list[0].get( "uri" @@ -428,6 +436,10 @@ def test_schemas_input_desc_filter(self): assert isinstance(deser_schema_filter, SchemasInputDescriptorFilter) deser_schema_filter = SchemasInputDescriptorFilter.deserialize(test_schema_list) + ser_schema_filter = deser_schema_filter.serialize() + deser_schema_filter = SchemasInputDescriptorFilter.deserialize( + ser_schema_filter + ) assert not deser_schema_filter.oneof_filter assert deser_schema_filter.uri_groups[0][0].uri == test_schema_list[0].get( "uri" diff --git a/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py b/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py index 509e87ba6f..87587eb674 100644 --- a/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py +++ b/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py @@ -8,7 +8,6 @@ from .....core.in_memory import InMemoryProfile from .....did.did_key import DIDKey -from .....resolver.did_resolver_registry import DIDResolverRegistry from .....resolver.did_resolver import DIDResolver from .....storage.vc_holder.vc_record import VCRecord from .....wallet.base import BaseWallet, DIDInfo @@ -69,9 +68,7 @@ def event_loop(request): def profile(): profile = InMemoryProfile.test_profile() context = profile.context - did_resolver_registry = DIDResolverRegistry() - context.injector.bind_instance(DIDResolverRegistry, did_resolver_registry) - context.injector.bind_instance(DIDResolver, DIDResolver(did_resolver_registry)) + context.injector.bind_instance(DIDResolver, DIDResolver([])) context.injector.bind_instance(DocumentLoader, custom_document_loader) context.settings["debug.auto_respond_presentation_request"] = True return profile diff --git a/aries_cloudagent/protocols/present_proof/v1_0/manager.py b/aries_cloudagent/protocols/present_proof/v1_0/manager.py index c951aa8c02..2f3af46da5 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/manager.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/manager.py @@ -417,18 +417,18 @@ async def verify_presentation( ) = await indy_handler.process_pres_identifiers(indy_proof["identifiers"]) verifier = self._profile.inject(IndyVerifier) - presentation_exchange_record.verified = json.dumps( # tag: needs string value - await verifier.verify_presentation( - dict( - indy_proof_request - ), # copy to avoid changing the proof req in the stored pres exch - indy_proof, - schemas, - cred_defs, - rev_reg_defs, - rev_reg_entries, - ) + (verified_bool, verified_msgs) = await verifier.verify_presentation( + dict( + indy_proof_request + ), # copy to avoid changing the proof req in the stored pres exch + indy_proof, + schemas, + cred_defs, + rev_reg_defs, + rev_reg_entries, ) + presentation_exchange_record.verified = json.dumps(verified_bool) + presentation_exchange_record.verified_msgs = list(set(verified_msgs)) presentation_exchange_record.state = V10PresentationExchange.STATE_VERIFIED async with self._profile.session() as session: diff --git a/aries_cloudagent/protocols/present_proof/v1_0/models/presentation_exchange.py b/aries_cloudagent/protocols/present_proof/v1_0/models/presentation_exchange.py index 296740b5f9..80db45f86c 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/models/presentation_exchange.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/models/presentation_exchange.py @@ -74,6 +74,7 @@ def __init__( ] = None, # aries message presentation: Union[IndyProof, Mapping] = None, # indy proof verified: str = None, + verified_msgs: list = None, auto_present: bool = False, auto_verify: bool = False, error_msg: str = None, @@ -96,6 +97,7 @@ def __init__( ) self._presentation = IndyProof.serde(presentation) self.verified = verified + self.verified_msgs = verified_msgs self.auto_present = auto_present self.auto_verify = auto_verify self.error_msg = error_msg @@ -208,6 +210,7 @@ def record_value(self) -> Mapping: "auto_verify", "error_msg", "verified", + "verified_msgs", "trace", ) }, @@ -295,6 +298,13 @@ class Meta: example="true", validate=validate.OneOf(["true", "false"]), ) + verified_msgs = fields.List( + fields.Str( + required=False, + description="Proof verification warning or error information", + ), + required=False, + ) auto_present = fields.Bool( required=False, description="Prover choice to auto-present proof as verifier requests", diff --git a/aries_cloudagent/protocols/present_proof/v1_0/models/tests/test_record.py b/aries_cloudagent/protocols/present_proof/v1_0/models/tests/test_record.py index a757dcae2b..13d9e5aac3 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/models/tests/test_record.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/models/tests/test_record.py @@ -113,6 +113,7 @@ async def test_record(self): "auto_verify": False, "error_msg": None, "verified": None, + "verified_msgs": None, "trace": False, } diff --git a/aries_cloudagent/protocols/present_proof/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/present_proof/v1_0/tests/test_manager.py index 310cc4421f..044c21d317 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/tests/test_manager.py @@ -317,7 +317,7 @@ async def setUp(self): Verifier = async_mock.MagicMock(IndyVerifier, autospec=True) self.verifier = Verifier() self.verifier.verify_presentation = async_mock.CoroutineMock( - return_value="true" + return_value=("true", []) ) injector.bind_instance(IndyVerifier, self.verifier) diff --git a/aries_cloudagent/protocols/present_proof/v2_0/formats/indy/handler.py b/aries_cloudagent/protocols/present_proof/v2_0/formats/indy/handler.py index 54ef915af0..8f0a3e5057 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/formats/indy/handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/formats/indy/handler.py @@ -329,14 +329,14 @@ async def verify_pres(self, pres_ex_record: V20PresExRecord) -> V20PresExRecord: ) = await indy_handler.process_pres_identifiers(indy_proof["identifiers"]) verifier = self._profile.inject(IndyVerifier) - pres_ex_record.verified = json.dumps( # tag: needs string value - await verifier.verify_presentation( - indy_proof_request, - indy_proof, - schemas, - cred_defs, - rev_reg_defs, - rev_reg_entries, - ) + (verified, verified_msgs) = await verifier.verify_presentation( + indy_proof_request, + indy_proof, + schemas, + cred_defs, + rev_reg_defs, + rev_reg_entries, ) + pres_ex_record.verified = json.dumps(verified) + pres_ex_record.verified_msgs = list(set(verified_msgs)) return pres_ex_record diff --git a/aries_cloudagent/protocols/present_proof/v2_0/models/pres_exchange.py b/aries_cloudagent/protocols/present_proof/v2_0/models/pres_exchange.py index f77a53166b..cc314aa9db 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/models/pres_exchange.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/models/pres_exchange.py @@ -62,6 +62,7 @@ def __init__( pres_request: Union[V20PresRequest, Mapping] = None, # aries message pres: Union[V20Pres, Mapping] = None, # aries message verified: str = None, + verified_msgs: list = None, auto_present: bool = False, auto_verify: bool = False, error_msg: str = None, @@ -80,6 +81,7 @@ def __init__( self._pres_request = V20PresRequest.serde(pres_request) self._pres = V20Pres.serde(pres) self.verified = verified + self.verified_msgs = verified_msgs self.auto_present = auto_present self.auto_verify = auto_verify self.error_msg = error_msg @@ -191,6 +193,7 @@ def record_value(self) -> Mapping: "role", "state", "verified", + "verified_msgs", "auto_present", "auto_verify", "error_msg", @@ -307,6 +310,13 @@ class Meta: example="true", validate=validate.OneOf(["true", "false"]), ) + verified_msgs = fields.List( + fields.Str( + required=False, + description="Proof verification warning or error information", + ), + required=False, + ) auto_present = fields.Bool( required=False, description="Prover choice to auto-present proof as verifier requests", diff --git a/aries_cloudagent/protocols/present_proof/v2_0/models/tests/test_record.py b/aries_cloudagent/protocols/present_proof/v2_0/models/tests/test_record.py index 529b72bb6a..c22a6ff23b 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/models/tests/test_record.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/models/tests/test_record.py @@ -110,6 +110,7 @@ async def test_record(self): "state": "state", "pres_proposal": pres_proposal.serialize(), "verified": "false", + "verified_msgs": None, "auto_present": True, "auto_verify": False, "error_msg": "error", diff --git a/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py b/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py index 0e352c51e2..9081c61946 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py @@ -476,7 +476,7 @@ async def setUp(self): Verifier = async_mock.MagicMock(IndyVerifier, autospec=True) self.verifier = Verifier() self.verifier.verify_presentation = async_mock.CoroutineMock( - return_value="true" + return_value=("true", []) ) injector.bind_instance(IndyVerifier, self.verifier) diff --git a/aries_cloudagent/resolver/__init__.py b/aries_cloudagent/resolver/__init__.py index 51bef53657..e4b82549d7 100644 --- a/aries_cloudagent/resolver/__init__.py +++ b/aries_cloudagent/resolver/__init__.py @@ -5,30 +5,30 @@ from ..config.injection_context import InjectionContext from ..config.provider import ClassProvider -from .did_resolver_registry import DIDResolverRegistry +from ..resolver.did_resolver import DIDResolver LOGGER = logging.getLogger(__name__) async def setup(context: InjectionContext): """Set up default resolvers.""" - registry = context.inject_or(DIDResolverRegistry) + registry = context.inject_or(DIDResolver) if not registry: - LOGGER.warning("No DID Resolver Registry instance found in context") + LOGGER.warning("No DID Resolver instance found in context") return key_resolver = ClassProvider( "aries_cloudagent.resolver.default.key.KeyDIDResolver" ).provide(context.settings, context.injector) await key_resolver.setup(context) - registry.register(key_resolver) + registry.register_resolver(key_resolver) if not context.settings.get("ledger.disabled"): indy_resolver = ClassProvider( "aries_cloudagent.resolver.default.indy.IndyDIDResolver" ).provide(context.settings, context.injector) await indy_resolver.setup(context) - registry.register(indy_resolver) + registry.register_resolver(indy_resolver) else: LOGGER.warning("Ledger is not configured, not loading IndyDIDResolver") @@ -36,11 +36,11 @@ async def setup(context: InjectionContext): "aries_cloudagent.resolver.default.web.WebDIDResolver" ).provide(context.settings, context.injector) await web_resolver.setup(context) - registry.register(web_resolver) + registry.register_resolver(web_resolver) if context.settings.get("resolver.universal"): universal_resolver = ClassProvider( "aries_cloudagent.resolver.default.universal.UniversalResolver" ).provide(context.settings, context.injector) await universal_resolver.setup(context) - registry.register(universal_resolver) + registry.register_resolver(universal_resolver) diff --git a/aries_cloudagent/resolver/did_resolver.py b/aries_cloudagent/resolver/did_resolver.py index f57ec47fd9..bf52959043 100644 --- a/aries_cloudagent/resolver/did_resolver.py +++ b/aries_cloudagent/resolver/did_resolver.py @@ -8,7 +8,7 @@ from datetime import datetime from itertools import chain import logging -from typing import Sequence, Tuple, Type, TypeVar, Union +from typing import List, Sequence, Tuple, Type, TypeVar, Union from pydid import DID, DIDError, DIDUrl, Resource, NonconformantDocument from pydid.doc.doc import IDNotFoundError @@ -22,7 +22,6 @@ ResolutionResult, ResolverError, ) -from .did_resolver_registry import DIDResolverRegistry LOGGER = logging.getLogger(__name__) @@ -33,9 +32,13 @@ class DIDResolver: """did resolver singleton.""" - def __init__(self, registry: DIDResolverRegistry): + def __init__(self, resolvers: List[BaseDIDResolver] = None): """Create DID Resolver.""" - self.did_resolver_registry = registry + self.resolvers = resolvers or [] + + def register_resolver(self, resolver: BaseDIDResolver): + """Register a new resolver.""" + self.resolvers.append(resolver) async def _resolve( self, profile: Profile, did: Union[str, DID] @@ -90,7 +93,7 @@ async def _match_did_to_resolver( """ valid_resolvers = [ resolver - for resolver in self.did_resolver_registry.resolvers + for resolver in self.resolvers if await resolver.supports(profile, did) ] native_resolvers = filter(lambda resolver: resolver.native, valid_resolvers) diff --git a/aries_cloudagent/resolver/did_resolver_registry.py b/aries_cloudagent/resolver/did_resolver_registry.py deleted file mode 100644 index 4154454236..0000000000 --- a/aries_cloudagent/resolver/did_resolver_registry.py +++ /dev/null @@ -1,28 +0,0 @@ -"""In memmory storage for registering did resolvers.""" - -import logging -from typing import Sequence - -from .base import BaseDIDResolver - -LOGGER = logging.getLogger(__name__) - - -class DIDResolverRegistry: - """Registry for did resolvers.""" - - def __init__(self): - """Initialize list for did resolvers.""" - self._resolvers = [] - - @property - def resolvers( - self, - ) -> Sequence[BaseDIDResolver]: - """Accessor for a list of all did resolvers.""" - return self._resolvers - - def register(self, resolver) -> None: - """Register a resolver.""" - LOGGER.debug("Registering resolver %s", resolver) - self._resolvers.append(resolver) diff --git a/aries_cloudagent/resolver/tests/test_did_resolver.py b/aries_cloudagent/resolver/tests/test_did_resolver.py index b08480e805..f2c4ba9ab5 100644 --- a/aries_cloudagent/resolver/tests/test_did_resolver.py +++ b/aries_cloudagent/resolver/tests/test_did_resolver.py @@ -18,7 +18,6 @@ ResolverType, ) from ..did_resolver import DIDResolver -from ..did_resolver_registry import DIDResolverRegistry from . import DOC @@ -88,10 +87,10 @@ async def _resolve(self, profile, did): @pytest.fixture def resolver(): - did_resolver_registry = DIDResolverRegistry() + did_resolver_registry = [] for method in TEST_DID_METHODS: resolver = MockResolver([method], DIDDocument.deserialize(DOC)) - did_resolver_registry.register(resolver) + did_resolver_registry.append(resolver) return DIDResolver(did_resolver_registry) @@ -101,7 +100,7 @@ def profile(): def test_create_resolver(resolver): - assert len(resolver.did_resolver_registry.resolvers) == len(TEST_DID_METHODS) + assert len(resolver.resolvers) == len(TEST_DID_METHODS) @pytest.mark.asyncio @@ -121,11 +120,9 @@ async def test_match_did_to_resolver_x_not_supported(resolver): @pytest.mark.asyncio async def test_match_did_to_resolver_native_priority(profile): - registry = DIDResolverRegistry() native = MockResolver(["sov"], native=True) non_native = MockResolver(["sov"], native=False) - registry.register(non_native) - registry.register(native) + registry = [non_native, native] resolver = DIDResolver(registry) assert [native, non_native] == await resolver._match_did_to_resolver( profile, TEST_DID0 @@ -134,15 +131,11 @@ async def test_match_did_to_resolver_native_priority(profile): @pytest.mark.asyncio async def test_match_did_to_resolver_registration_order(profile): - registry = DIDResolverRegistry() native1 = MockResolver(["sov"], native=True) - registry.register(native1) native2 = MockResolver(["sov"], native=True) - registry.register(native2) non_native3 = MockResolver(["sov"], native=False) - registry.register(non_native3) native4 = MockResolver(["sov"], native=True) - registry.register(native4) + registry = [native1, native2, non_native3, native4] resolver = DIDResolver(registry) assert [ native1, @@ -200,8 +193,6 @@ async def test_resolve_did_x_not_supported(resolver, profile): async def test_resolve_did_x_not_found(profile): py_did = DID("did:cowsay:EiDahaOGH-liLLdDtTxEAdc8i-cfCz-WUcQdRJheMVNn3A") cowsay_resolver_not_found = MockResolver(["cowsay"], resolved=DIDNotFound()) - registry = DIDResolverRegistry() - registry.register(cowsay_resolver_not_found) - resolver = DIDResolver(registry) + resolver = DIDResolver([cowsay_resolver_not_found]) with pytest.raises(DIDNotFound): await resolver.resolve(profile, py_did) diff --git a/aries_cloudagent/resolver/tests/test_did_resolver_registry.py b/aries_cloudagent/resolver/tests/test_did_resolver_registry.py deleted file mode 100644 index dba7afffbd..0000000000 --- a/aries_cloudagent/resolver/tests/test_did_resolver_registry.py +++ /dev/null @@ -1,12 +0,0 @@ -"""Test did resolver registery.""" - -import pytest -import unittest -from ..did_resolver_registry import DIDResolverRegistry - - -def test_create_registry(): - did_resolver_registry = DIDResolverRegistry() - test_resolver = unittest.mock.MagicMock() - did_resolver_registry.register(test_resolver) - assert did_resolver_registry.resolvers == [test_resolver] diff --git a/aries_cloudagent/wallet/did_method.py b/aries_cloudagent/wallet/did_method.py index 82382ea39b..797c26fd20 100644 --- a/aries_cloudagent/wallet/did_method.py +++ b/aries_cloudagent/wallet/did_method.py @@ -47,7 +47,8 @@ def supports_key_type(self, key_type: KeyType) -> bool: """Check whether the current method supports the key type.""" return key_type in self.supported_key_types - def from_metadata(metadata: Mapping) -> "DIDMethod": + @classmethod + def from_metadata(cls, metadata: Mapping) -> "DIDMethod": """Get DID method instance from metadata object. Returns SOV if no metadata was found for backwards compatability. @@ -63,7 +64,8 @@ def from_metadata(metadata: Mapping) -> "DIDMethod": # return default SOV for backward compat return DIDMethod.SOV - def from_method(method: str) -> Optional["DIDMethod"]: + @classmethod + def from_method(cls, method: str) -> Optional["DIDMethod"]: """Get DID method instance from the method name.""" for did_method in DIDMethod: if method == did_method.method_name: @@ -71,7 +73,8 @@ def from_method(method: str) -> Optional["DIDMethod"]: return None - def from_did(did: str) -> "DIDMethod": + @classmethod + def from_did(cls, did: str) -> "DIDMethod": """Get DID method instance from the method name.""" if not did.startswith("did:"): # sov has no prefix diff --git a/aries_cloudagent/wallet/indy.py b/aries_cloudagent/wallet/indy.py index bba9f6ed49..4e2406d04f 100644 --- a/aries_cloudagent/wallet/indy.py +++ b/aries_cloudagent/wallet/indy.py @@ -1,6 +1,7 @@ """Indy implementation of BaseWallet interface.""" import json +import logging from typing import List, Sequence, Tuple, Union @@ -36,6 +37,8 @@ from .util import b58_to_bytes, bytes_to_b58, bytes_to_b64 +LOGGER = logging.getLogger(__name__) + RECORD_TYPE_CONFIG = "config" RECORD_NAME_PUBLIC_DID = "default_public_did" diff --git a/aries_cloudagent/wallet/routes.py b/aries_cloudagent/wallet/routes.py index 6e73f455ba..cccab4f637 100644 --- a/aries_cloudagent/wallet/routes.py +++ b/aries_cloudagent/wallet/routes.py @@ -17,6 +17,7 @@ from ..ledger.error import LedgerConfigError, LedgerError from ..messaging.models.base import BaseModelError from ..messaging.models.openapi import OpenAPISchema +from ..messaging.responder import BaseResponder from ..messaging.valid import ( DID_POSTURE, ENDPOINT, @@ -442,6 +443,17 @@ async def wallet_set_public_did(request: web.BaseRequest): connection_id = request.query.get("conn_id") attrib_def = None + # check if we need to endorse + if is_author_role(context.profile): + # authors cannot write to the ledger + write_ledger = False + create_transaction_for_endorser = True + if not connection_id: + # author has not provided a connection id, so determine which to use + connection_id = await get_endorser_connection_id(context.profile) + if not connection_id: + raise web.HTTPBadRequest(reason="No endorser connection found") + wallet = session.inject_or(BaseWallet) if not wallet: raise web.HTTPForbidden(reason="No wallet available") @@ -542,7 +554,6 @@ async def promote_wallet_public_did( connection_id = await get_endorser_connection_id(context.profile) if not connection_id: raise web.HTTPBadRequest(reason="No endorser connection found") - if not write_ledger: try: async with profile.session() as session: @@ -594,11 +605,6 @@ async def promote_wallet_public_did( routing_keys=routing_keys, ) - # Commented the below lines as the function set_did_endpoint - # was calling update_endpoint_for_did of ledger - # async with ledger: - # await ledger.update_endpoint_for_did(info.did, endpoint) - # Route the public DID route_manager = profile.inject(RouteManager) await route_manager.route_public_did(profile, info.verkey) @@ -814,14 +820,60 @@ async def on_register_nym_event(profile: Profile, event: Event): did = event.payload["did"] connection_id = event.payload.get("connection_id") try: - await promote_wallet_public_did( + info, attrib_def = await promote_wallet_public_did( profile, profile.context, profile.session, did, connection_id ) - except Exception: + except Exception as err: + # log the error, but continue + LOGGER.exception( + "Error promoting to public DID: %s", + err, + ) + return + + transaction_mgr = TransactionManager(profile) + try: + transaction = await transaction_mgr.create_record( + messages_attach=attrib_def["signed_txn"], connection_id=connection_id + ) + except StorageError as err: # log the error, but continue LOGGER.exception( "Error accepting endorser invitation/configuring endorser connection: %s", + err, ) + return + + # if auto-request, send the request to the endorser + if profile.settings.get_value("endorser.auto_request"): + try: + transaction, transaction_request = await transaction_mgr.create_request( + transaction=transaction, + # TODO see if we need to parameterize these params + # expires_time=expires_time, + # endorser_write_txn=endorser_write_txn, + ) + except (StorageError, TransactionManagerError) as err: + # log the error, but continue + LOGGER.exception( + "Error creating endorser transaction request: %s", + err, + ) + + # TODO not sure how to get outbound_handler in an event ... + # await outbound_handler(transaction_request, connection_id=connection_id) + responder = profile.inject_or(BaseResponder) + if responder: + await responder.send( + transaction_request, + connection_id=connection_id, + ) + else: + LOGGER.warning( + "Configuration has no BaseResponder: cannot update " + "ATTRIB record on DID: %s", + did, + ) async def register(app: web.Application): diff --git a/aries_cloudagent/wallet/util.py b/aries_cloudagent/wallet/util.py index f87e6a53da..942744bca8 100644 --- a/aries_cloudagent/wallet/util.py +++ b/aries_cloudagent/wallet/util.py @@ -102,7 +102,9 @@ def abbr_verkey(full_verkey: str, did: str = None) -> str: DID_EVENT_PREFIX = "acapy::ENDORSE_DID::" +DID_ATTRIB_EVENT_PREFIX = "acapy::ENDORSE_DID_ATTRIB::" EVENT_LISTENER_PATTERN = re.compile(f"^{DID_EVENT_PREFIX}(.*)?$") +ATTRIB_EVENT_LISTENER_PATTERN = re.compile(f"^{DID_ATTRIB_EVENT_PREFIX}(.*)?$") async def notify_endorse_did_event(profile: Profile, did: str, meta_data: dict): @@ -111,3 +113,11 @@ async def notify_endorse_did_event(profile: Profile, did: str, meta_data: dict): DID_EVENT_PREFIX + did, meta_data, ) + + +async def notify_endorse_did_attrib_event(profile: Profile, did: str, meta_data: dict): + """Send notification for a DID ATTRIB post-process event.""" + await profile.notify( + DID_ATTRIB_EVENT_PREFIX + did, + meta_data, + ) diff --git a/demo/features/0453-issue-credential.feature b/demo/features/0453-issue-credential.feature index 0c74f53645..d8b0188604 100644 --- a/demo/features/0453-issue-credential.feature +++ b/demo/features/0453-issue-credential.feature @@ -1,3 +1,4 @@ +@RFC0453 Feature: RFC 0453 Aries agent issue credential @T003-RFC0453 @GHA diff --git a/demo/features/0586-sign-transaction.feature b/demo/features/0586-sign-transaction.feature index e8c85d1e0b..1a66ef69ab 100644 --- a/demo/features/0586-sign-transaction.feature +++ b/demo/features/0586-sign-transaction.feature @@ -1,3 +1,4 @@ +@RFC0586 Feature: RFC 0586 Aries sign (endorse) transactions functions @T001-RFC0586 diff --git a/demo/features/steps/0586-sign-transaction.py b/demo/features/steps/0586-sign-transaction.py index 04b6249598..61702e84a8 100644 --- a/demo/features/steps/0586-sign-transaction.py +++ b/demo/features/steps/0586-sign-transaction.py @@ -34,11 +34,18 @@ def step_impl(context, agent_name, did_role): ) # make the new did the wallet's public did - created_did = agent_container_POST( + published_did = agent_container_POST( agent["agent"], "/wallet/did/public", params={"did": created_did["result"]["did"]}, ) + if "result" in published_did: + # published right away! + pass + elif "txn" in published_did: + # we are an author and need to go through the endorser process + # assume everything works! + async_sleep(3.0) if not "public_dids" in context: context.public_dids = {} diff --git a/demo/runners/agent_container.py b/demo/runners/agent_container.py index 065a3cb4ea..7018ee046f 100644 --- a/demo/runners/agent_container.py +++ b/demo/runners/agent_container.py @@ -331,14 +331,17 @@ async def handle_present_proof(self, message): if referent not in credentials_by_reft: credentials_by_reft[referent] = row + # submit the proof wit one unrevealed revealed attribute + revealed_flag = False for referent in presentation_request["requested_attributes"]: if referent in credentials_by_reft: revealed[referent] = { "cred_id": credentials_by_reft[referent]["cred_info"][ "referent" ], - "revealed": True, + "revealed": revealed_flag, } + revealed_flag = True else: self_attested[referent] = "my self-attested value" @@ -419,14 +422,17 @@ async def handle_present_proof_v2_0(self, message): if referent not in creds_by_reft: creds_by_reft[referent] = row + # submit the proof wit one unrevealed revealed attribute + revealed_flag = False for referent in pres_request_indy["requested_attributes"]: if referent in creds_by_reft: revealed[referent] = { "cred_id": creds_by_reft[referent]["cred_info"][ "referent" ], - "revealed": True, + "revealed": revealed_flag, } + revealed_flag = True else: self_attested[referent] = "my self-attested value" diff --git a/demo/runners/support/agent.py b/demo/runners/support/agent.py index 07da797d0a..bd82314937 100644 --- a/demo/runners/support/agent.py +++ b/demo/runners/support/agent.py @@ -600,8 +600,10 @@ async def register_or_switch_wallet( if self.endorser_role and self.endorser_role == "author": if endorser_agent: await self.admin_POST("/wallet/did/public?did=" + self.did) + await asyncio.sleep(3.0) else: await self.admin_POST("/wallet/did/public?did=" + self.did) + await asyncio.sleep(3.0) elif cred_type == CRED_FORMAT_JSON_LD: # create did of appropriate type data = {"method": DID_METHOD_KEY, "options": {"key_type": KEY_TYPE_BLS}} diff --git a/docs/assets/endorse-cred-def.png b/docs/assets/endorse-cred-def.png new file mode 100644 index 0000000000000000000000000000000000000000..ceb3d2fbb1ae920ddc0a0f8654514612222eb6f4 GIT binary patch literal 134605 zcmd?RbyU>b+cu8J07XSwL_#TP=>|pV?gmANhEcjv6e$VmM!G?UZbc9Q0qJIt25A^# zm|@;M;Q6NKJkPt{-+w>WI_t3HGc!Bxecji6UH6_qRb`ngmu_6b!NIvAC;Lzx2j>De z_z#Zv2l$r{GfSVr|Ck-6bRC~SpSjsU%p7rKAa)S@$BvMvw5D#f7LJb3ggH5%**vy$ zbh5SKcmlO0yeo7Q2j`DTOHEzJKmQ%)40s#Yq&1y!yYai1y#&I2rk@0VGD@(KdVKi- z)S!yveh}kYLE4xN0~4Zqb3*iCveH8RZZ^ zG)&{!neUvhS6K=d73ypzNv)pdP?Cq_WwLh&K5}Nbm-f^*!O+T6yg3_*7Z649mbpxq z&TG}TrH&TY--yJvlttJtP;;63g9%y1@)thM(2t9(RXNX#W3LWxj?uD4SH1765{B0N zknZX$^9tN#zU&b=&fvb!uW&AtWSjI0%+-8`KwoNc;vGQ2!l^$~Gh&{8NbT=O{*o{NwrSSobzXm)mJE*ee+#Y2i z|0KSDCt3W{WxI0W$3CoY*f@@jJP>C}|_K_O@Q3>gX zHB=+RGimto#{FlBy|0ko!Y@f#){y%L7Q8pFguP+Hbzq&YSG|kEYs;|kyb&VvOw8y- z8O;UU;VRX$++^5?0uDO3EF^a*eJ;Js_03_H7>V`5E&yI^g5H|$ZfpV!r;v}T)yA z4=^~l<#O3x;thO_xOS1`zNhE4^WGzc=Fw^Ypk8O#$CBbhK@e>mhBAv-SM^46WU1AOk-@fgO z@LV*pvrF&B9xCt?6Wr%~`Wwy<89nSt20#4o+0LK-M)|scZT@KeTWJW*nh!+lmgwyvWw~2FRcxv z?&TZ`;?m4MeNdF+DI~9-sVRCR61hnpmLi6Jm(9qKxI}K<8du2wDs)vsS6^RWODiiG z@AL)K*I=*#bK-^eeYs?z3L}$n^#om3I1ggIgo&JlgoKn-EeVTPI5?Z{zei&4SOy!ZN&lHQ7cPfV?m$;<8WIdcW}_=Dkr0nNiVc}Yo0 z>FKxE>Q4t5pqj?4aDFTHfZN;t$(NtYd3kx3mX@8doNyRK;ql`%XXDf79_E5|dfmhL ztutVyrg2})T-kOwHa&`QsL=y$!Nt9^C&SIdL(XA%FlJrCZdB{|`OTH{=g*JHorc`C zG)e6kVXy7+!HW-g(%ro`B~JFRk(MC=>|xJvrv7e^qEg1M2%Oft#j0@eG%mh?xY#fh z(8r8>rZ27;FT$CkdvjeD-U`LX$5T*HponNr#`i+N560$HT+3aNj<8*{R#6kvak)|X ze0n5AAZ*li+{;@1O=tJcMr3}m+{NYPMG1qTFj3r-S8l$)7Mt~&?uek#1H%(9A%v;S z!`Gjs5u5dV=6|JOSBbycoJXO^XklF0{ zo^E4%#3b0l)?o$}4BsrmF^e`jC56LUeY;5ApwfmgG2qI-zgA02OH(r{Hn!^V=Qjf0 z;^K7qi!+1-S=t7=N*tXD1KabRt#`R}ZEo`qbf0eQ3p`RJY@lXEK#`P;Y+-)B=y3k^ z&PL3Q!zD0GWB27OA5CI>&L)(-ySux#c1)pM;HP@6(-Zztqgg#BKR=&^gM)*O%}~P5 zN?Nn0&;ujD%F4>ehlsW^{ZTP&+}Yhd1%m|t*9(PpjN)0?+0oPcW!&lR#WVtZOibpZ zFbKt9OLIpwD_EFImo5bb1-0C@0+ayzV@zBOi`s4P-vJxGaJ&&LB_##cU{qC!&2#e; zE;v&pnWqnmvVMgPlq!jAr|PLb8F$^O23tN;pgXnu-O;I^fXuk+RgZlxayGpykCqO#z=WwP? zZ{1Ni^fdYAMaO!qt@l#jxjkg#%AwObde%kRTicVgGhL>b=t+w#m$ArMU&&vvU#&~k zsczoE3UAs8gRPYR$mWt74er*QM|#o}BrXx6O^a?aehDrrCr;b@*TKkhW>V!lWY=#e zV~(>%Jf(IL35r~`G(!3WxTN2b%$9jf9Ch{1g6JNjP^0|EeDi_{+ORMDvD4tQ7->3Q zLFFH{?xkSzdV3r@EE~k0Ede=|xS_tj{BstIe|?!hM<$hf8s|deEjSaYtCD=4V*9?L zEwUf~PjvPr{sJ3_56WMSs_SklWuNCLRs1!>*GaW(6&T5}G!{5I$8$bwQDRm!96s4E zP#Qh57qZ}msc^HtfB#H$-ZVDtiFi1VQ^9>6#aWmh?w~w>Z8~ka?~zuKQ9rqvrq7p} z#yOHlk*O~lw{?i^?d@|F&odc9^6Ht41u@dq)o1+6_=8IpdC;AKXN)O6d|ZYeJJ|GJ zO@OTJwnHm~zTaZzu%aJO>%w2&Y!>Q6Ro+u4;M`oeLj4u7(e<@D_ua#>(vKXkYVnKN z5YAy3LIY9=XWww*A2z1rI~%Q*5{`(KLCtJm;ux6bCwNRy3!Cl0;=uyd45YV?CJ55I zvv<%mr#ar}CE^B)brrs|0m;z&KG*OlG!wkFstg}z=4bpx*`4t}$h-MLpxj)ux>HkvTUdu!u|neHC#IhYjk z6p2daS7bw#=0iJY#DnF6SZ`c((%g|0u74KF+r+*(@EO0}`4z4=+Jl`NEYdsYLjKtM zljqf9SoF)ZY+#U0FQlnPQ9|7PZ;^WkJOKQmO=xFhV`DWn!uimTe${MR+r8_M5U8*Z zpXgCX4ULt0TNsUm4Afsq{SD{xfIieFVu#>v)5%VC*v4;lXf8O0<}~k`?9{EhHpVVCSJ) z=#|m>f?%kgT^IscX%>1!m~tG$cgW^#J@m(O{JHu0KqgH!wRdpV)c^G$@0Y7!c;$TN zDy&Q|aH$}T!gBs33c-==92`T`w=cggWOG8!RY9Gp*dt2wFb~XCd|Zl=Me1N=!^K9} zy{ly3D5_sTwGEWW-cZ|YSJsQ4xuQ`yy$sch?3u~x`DWPQjsUF3-oatMZQ4>pQr)mm zHa8=IyzhsfL*}9F%N%Os=?Y5G&7W}VchA-60(nhrL1>quC)E|!F&b7@DvOb@^tz%B4 z-;m2Zv~r*rNUmjzPE4$`9j{bMA2i?g_0j|K??%H3zDia9X@-c?~Xy(vm9FN*WgPm7&00RWj;NWD;|q%m=77D zY}HPrj!!F0=Y}wxjb*8I)P%=Din z5aty_^1$I2H6ge8USNUnSmY>_Yp;^evsTv{oO_{SVsxvLXndjDJ)|QF;fgdE`oo7T zl0=7>XeD>S&(ZbAO-pZs={$dO*R=}-DCEBWQ^Nt-+g&<|o$uFuSru%(Q6;7YR|GN z5tHKs_*G+BrEP0a{Kbn!lHAdJ?3?DL>~a@v9n_3{?f4maxtl4+%06&WC6JBns0%D6 zWgyr+)tscT+vWSU?o*cmfB?RCrTfpH&QC>q4SMB-%9~EAFjbokd;c7u=>BA6VO%7VI#M>@Ixs3%;Xpp8Wu(xxC&?`uyy}tJ^8AKMX(eG0RcZ zBLp;BFGG3P5;5H#(ys`wE~sFFf(UXH-QFw2wk$0LkC$5txjst`p)?sHD6rL#n9hug zlU^IEpb$qb?Cw&S_pE=1rb7b*V{OM{^5b*VGJ+!`h>j21OuUZPj*v=LgDc0IgXJGn zTN&jpw#C26v~1RDTk@Ob{pl);B+GxjB4TZ`VBkmRe#$TXY!zo{G-d# z?DUJdhScuJPyGB{TLW90Eej3qMtK`edt=2;Nuvp5t}8#zS2TP=d|nyhb;#dm@HQlo zND)IWEtND)Wy0@PlN}(SL=?S^hYJl%Db-cB<3m>{jp-XhX(Sfr+KZ5nk=YU^=BZ!WYQwTWw@Za*(+S?9mqVl!yQ?uNG0OFJ9$4W$W5F6@@QmYj!imMh$@d8Qb*g{6^)o|3#E!l zZ7R%PF~J<~C$j1^KKpz{Y?sb@e3w8_$j5BKq^0TRg{$?-tNKmR*@Gn}Cd`lW77tg; zvP|E=kN2*k87r)ud=9Mir+Z8zGhfdtKIoIAEPj?`W)b$CeC72}ems;kbjdF>p2nHV zK(_A(e(U`quL~Ju`3{=;2UjcB*~{A)%@pJ;n=JePa9aEl!WrECef^H&Ns3goduCm1 z-eo+%$;u)=QDNN*^>%1`ab|c$;BxVKZ*=0r>_WC2kA2pbo^p@DvRt6OrWlLQYcRl79Cv*9I;Ih9=-m%T+^*;Rsk zN8VyGbClAvf1IomWUIq;^8XpGKdKBWtkTxwB`|rJndhG4 z%=}c~6f4^ONpLS-wG2P?)4Kp|)erLLSi~C-RlJEjCY>gS3f_T&&ID1 zY%ld9*!6j}Fw+b%voi_QB9))aZ&C;i`k-|d!66lKvTAbO;vKCqR~h>H4&Az-lz2S1 zH-1Dd(b&7lX}B`WJLY%2%xchvkc(?DEA~qW1y6PL!4bx=Xp2{AL6bnkokLJuhLN{J z@P@dLOZY(zCPyMfhx{34C)JkSfd2^jQ>h~fww#e8@m#=|K7lO4@vGR1h}W@ic442n zj29Stg96R0FXvY}hX~QN(DTld3VfJcZ2z~(zUQ5&0;HSPgSVNF<1K^d4YJ7C^`jCJ5>ir9l9Q8TV|5G-3n#B!J_Uwt5S}f= z4icKNynM`Cef5kC?Y8@i^G!T+r9GAC0`2j14#kvX9plk0^gj8@5rnuWxzP)AH2ulu z(EjI;POFQ>mX@eiYuV9|L5atDjsIM}06dA+TNqw$`SQSg2OL@edt)1Cl`TzkOo#cD z3#PPr!VX-j4=U5@4I5FY9MvKdjJoBr_Lde~uz4x`%Ai<+7Nd+kVvxBwA z-QZa0*|bxF5Ck8kXA?oP!^0>1HSGaZRsKRcyZ-LPhsOekiga|1vz3q!H-n94`vN!;_#?LA<`&-%czzFka(CeB~Qn!pE&lqpBV96-BQx1a7Y2llU=^9h&(~gw- z`nRcQHaSNY9!jdT_HyaOkvMnPm+e=IoAMw$?#+oN;nF&dy=%(_-c?Ztv76P;U060^ z`e-8hp6phCFaKEL6ZzF~ZEUMyzZ_FfBBJFz%`2pgJ`76KN00dYEUK}OK0Mp;)H_2p z(KEPLC3l_IU3l=h$pZP?UgB@$>GVi32t@1JA|wUG*ma`<#xq=_C4IbNrYr@jQ4(SaZkbRzDl2KnB8^r=3gvIDklpx`pmtD@%t zvQw*abnHx`>c*7LGE?uag?9YSc7|A9GETmoC4-ZU6PKFTd!NGyIc#9JoJGC&g%Y#K z6Y|Yz*ooMHxd6q#)g!yLromy|oK?>|3ef3^Of5|Q*rnLap()2wJg2W@`MIm4Qu<>@ z+(4Qe>gr>xO&#w2N*Kgeo5FwtG9;xpux|Z!Z#kd~IU=&7Z+#bdxlKfZn2lxwbC zg`)xxh*E=q^KQ+j4Ad5d#E&jc*$h^b?mXVLvkLj4^QUqqX|_dLYB*Q@q57=*eH2*-0BnRbWtyxg-#o z;}Zg5-U>6Ghjhkt-QdmTOi(Q`7Aw_e>(qMwTuDK`@(x{V5)wD5HadF#&IX!4Ub|s! zzh|1J+-gu#^l@}WSE7JgYJe`87*dY+1XH%>BA>L5!O?be&3-*(qzxJIs+K1S&i+TB zRnNc@Ei0=8Nx__eCV-;OiTH=osQ1~JGs@-7%}yZZIK>-lL~)vyRhxK6r8E&o%Y>{ub_x#RqB76 zvbjK>*&AIl8jQ?}B2h~2<{;0 zSEwWZZ$M#JEFG-jeZ}Z_w_^$AC;$kHE*G-;8-N-ySc+AdIn;D@4@$ZWbdIegrTx<= zV1Hy;QoS(?J#IkNd8{NvZU6MBsi`rI&>wt&^&!$|&Z((IG={6nN zuEfVc=bf(MfQ>Hys|`-bUFYYQrGKa!C#*`QdZBH+Rd_aqjG|N{E#fBbFE8^TcZDz# z9+8Y_?MB3dhBWw6d{T8C2ayuKobcNPfr|-p&b$RJFj&V^$tGOd(+%~qbpJy!E4yuD z?FnymuNKKzft21gi6x1HX``8K?Wy#vLT+KnNXroZQZ7&gM^aAAv1}i`E*U&;>8Pq;=1aVbI`!Fr~jr&)p;nekoT8s}C#3Uy5@5f;^pMRupK_Nwb>?G@#WoJj8i z{j~yZ^0&2q^nr|Z-%Bpd)&J2G+cT}tcQK&dJ34z~OUvi#m=rl*oG{|ZBzQ+eG4)21 z^`r+VxU0i~(oEZ!f?vr7&aalCA=_0>AZ7z)ZQ0@Y-Cj#nc_}8*yNyo4`$+Q(jrr4V zBiaHr*ol%ap(3A@tV;%T{MH3iB6*4mA{Z>{cC~$kdVKz@C|-DbzX@vE^!VN4N^FCN z2_JOo9Qv-wBPg!cm6?Gf@_k1Vt?FCvW3$`tMDy9*Nkr~(C3GyY zTv{7UXG;>R%P{XLPG81^7xWS)C|Z{XhMaOpMxA$XCeI=c*{rsuJQCoYSJ;noHD3sNh<9j&cViCP0$uyA+;mOe-fvaR)}ncFe9Lt)`RqcE zk30vbiMYn9=BmCB&iqSIc-rny@W`T2yK7Y&(2-5`SdPFBO;E-+94;8nixo4<+&K8U?!ZKOQ`&r9e@>%U~q^rVuHKldb zEK11GENB4M15krmM*eJS<45+d5)GxqeN+zgjl$)_0HxDF6Qyqd=m7g4LQS4k3Os4O6;?#oBGD{L{bf*if&ynqWK(`bo z8A;2pMP=o4WEvS!CgiOMQYyxYqP+%vBwe@X;8~gF(1E3yp(2CJ)DtqIX>%-S2N}`k z7LB~;vvCVui4*n;C5WFU>e}qOck{dusU7PhTlQV4H=@%sILQbaAAhzZvW~{I@TY2f z;UHhR0rsk6lAKF2%Y41iIs#I7HOBW*-B~l5w)U+v-a;FUYPiWtU zLjRK}`N%6Fb~G7zo)`WzPQ;IZ^KnA;)P|5=&A8t8S;Uaz8n+fy6zf;pnc9MUE_QzU zw|Fr@l=O=qABX{2prwCyyVF2ytEGWS#Tz4Tn!5aarN?D!#&WWjp{<3N2whUot=B-F zfg9D1>hSFw5R-&HOK60J}PzFj(dl{FtQ+jcpr#se6GSYhR4(-Cx z;D*!$_e{DvhUF(9OhM4lbokQ;Xcng&56)NLg&l9Ltt4n_>0|@77}%b`(u@GrWps1} zQ2ew0JAkI88C87rEwIEK2*L~DuZxY*=?8K;@QsRvaL2fEfZ%Zvea3JaSBGO@2=TK5 z2D~|Th57)oQE_4jC6oXg#3E0EO7?s4)q7fq4SUcy5SY=`+1c6N4jkCd3#41>jzLHr?XAR4P+GLd=aucgA3evp=w(9 zcb4ys4W5Z?&6}B-Nl#A)j?f>cV<5>~>GmNTH0w$j%vH-6Ei<>)G5osZdslsY3y&SB z&lNz8q;Dnd+It=3Z$A5l%X$cP(B5HeSDEt}s-TqIS7F^YGO`pEwZ~(AY`n7{XFZ|S-ktG))0eH~-B`TT<7zT)a@TTbNv98bt=;e5ASRK|i}+9%rPe@#2QqqDQlWkcL~ zRhMgFAWviFr`P`EaE44+P|zhI+K({JYkw4a45;A|2u2KCivrw!s=*_*K)H`8)id1~ zu~JaN$jHcVGjbOgK4NH8sz@~2^zl@^8`zGJz`&aA4ps+ydyu7Cu5WF>p|lfxHvNKI zQBN26D^9~k!{wI!3Y-m&-{QDTe9#q!ew#ml1p^cTMRmj8|Iu(cYuORS{NT;yUdN!6 zJdK=`0uHL&cIrse=6tP!;@Xvb>5v-?jEq4PjYsPuqnwrcA0FM8l#~RY{4`8%ayBd0 z*b3g*(767*+tIs8#_7k8Vb!3ms2iqROBrE7--G>x92P{64`$^t2G9lu`!ctk1*|LA zpREK84XtJtGP~*@Zzk(pg0*~5vowi(*3Ppb0xX>VSE=$ZYmn7CF5fOz=6au-y95X; zg_wK!|Lx@RfLmfvWjE;9xIg6qwl~XgNYg_z}Z4WA=DYQZDSSI(Y5{uW5q@y^ElrY1%z4h)x6`+&+jhxOB{{%C?^Xu#3;zi zbAWaVP{mEU&W04|lxRzvK_GdZiXa+dI!#%-Izg~Ae~$7ClQ`Dpzz4#yb#J=gHqqne zemIQm;D6~WsyjOF1`;#zCZ{toGD>s&>{OR-eSPNa1sh;j>NzcaPYb0I>420D=A?S( z7jox$OaMLFdZ8EhIvt zv|uRu*jC@3MGfs`lNIvr6tRNKswI*Ko4Zwo=a1PKnTsv{)t3JFI~00J@=&iMH|{$Y za|Tz2OH4pn+RGUe%>~0_h#wG35P;fg?%z#?FXiSs9YDyUKvRp9kwr@Yp?$QRJMT+= z_dFBTC68061$SJ1k8m-}~nOw*-B#SW~` z3mq$8_Br06$;ts8Si2eFHqLi)csg!G^|f=*4p zG{j83J#h#j=bR~IArQ!9tyA4b<5A5_aG5JXJ=+wyqk`ZKLH?u*0BAxaUGroLnWs$KIhIxT{k`1_1k=cUixYp(*8X1X z%b7%IqZ?|gjhKjtqjo916Z9+vGNs&MXV)6yFlnp@M(dRuyx~%3y_WAyVo{~h>xkcO zq8j{GHDMn-QsJ!_;uMC(6kv!&I)wq988H*gsr}|WcT={E11db=eE0m^T%mD;u$Y+F zt!Bk@daw1*)qnV)n)n9)9f=NYTIkG)0r^Ac-~Gqe^rnw(InK5b;ig7Jq_0UOtHm_e z06Yht>r)6Mkg3%gkCM=s@F}4c;U>^;Y;SNpR8I>dcQWQkOzGO8Un6G#Xc%h zU!-Jj8yCz&rtduY@^-Yw!Q9R5(4WS8q{yHOyMtqdxjY@}f;y_I;faZ?DTDxBjDtQ9 z{vf?imPU3amL|>IH&c-2q7%tU+Xh~fRWy)GbL=+tjW8)eZ-c`hvK3+98=stG6+oGd zjg7o~N1gMbJ6mDh!$kz;f6;d*wIFXi+zD@3qd!f*7Ll`@Cy7 zm=_EaxE%@tc^=~!2kcHcVX?EXYAM_CpeHWU$nM_yM>l~$@1&W8xD|W+FIt4%BF<)Q5 zGU~lb3}q~pCXAWCF}TA00lxyjPBq5&=$Ts0Rb~hBZBkxIbB6RQx`86y@(;)HzrxPG zBl-W}f!7Uet!-~^%~j_b`bX8^A6~kVRz}o&p|FJteA?Rir=u@rgRs*+?e4-ygt^gVWPSS5tH3^PTh688`ev;LtNM4GVrNt$(H_bzOadV~*A0 zHCS%S4@53W(E*j@;Sk*0%+AlPA2+K>YQgiKr`*7m<-7W`Kc^>%ly%NNxk1P zzH`mp9g+hC`W04)U=!5L&ypr>?X`&-hwtz2OXM+VkS}sH&=hchJ?X zcHNo*eHPlq5#I`!IO{i?tDn!7H#j|Mlp(H|y!&yB?`pyG7bc%aW0U=RQ{J6T;_ zotc>lkdWDf_Xg*M%&aV@}{z?EJA};Gu$(JB;h9iKVe0@jPio$l4p!ddugM<0b z@>)5GCZO~8izQ?sFU(jGZ@`Sh#P;Iwo)l;owfJs}vt?WaX&s&+L-4MxJ6QyqLtw-w z>**ST;g-RR_q2&qAHD|I$Pj1`u9*NF((7oCx!~Lu%<1Qly&Kp?mr18#FfSGsmVqZE zE0%o9sp6AhEuf8u(rgm@6Al7!&$75fFf;(US0wVeC7=1(|`0RaV z=2O6RLGp@4M>45U-+lD)2iQg`;ejfs2_#H0T=ye@#~(zQe*&TbJND`u_f_j*bqR zwqy|#kED?bMR`ROGCPX^6}0>R|gd3v^t{Zh6$Cl z@z|WcKMw)Oog(Hjx80i|1AH~6*vNgmDEbNUE9{d&!*No`HqbtNw+ z#T#i^bCZgyh}k>2U}|V_hnW)udekctKnppK2FHWWV>c@LOhT+Qdb|p{0ip`bdtMYI z_3xKeoo|^8&AUcHoO0ma8D|n2f@2fFlOoQm*SSm|29vQzL`H&U>%k{~JpJ3PnD=eI zFBq4ptx>7^mRd|7GpY`bUrDXZR+j+W(7v5vg*wC14=k(QC`^ijiHYgRt11c!$x%)Q zmHVKWy1IH_u~FTey|;HGEV|b<8f}ZkIZ5ejBnb+2||1RQRy`4D0hYy+8|(GkVUn5vJrA zNa!_gxgxc95OAVFJ75x@b<8&4i=Cht?EoL+u?AY;_BP-?D+760AX;~6qU5y*cX!CJ z1cgTeucbY5*YePQtlaYC*vRaRs!P~CTc21REyKNh{R1d|wzs!IPbOg6?Z+VCuHF>f ze8$IhZ?warUhO5LxVSMhQ{E9Mkbph`&N{Cc~4+{)0)f>5N zTQxaUJ@)bCS&73rV#wgpMw8F5k&BV@D5STymrB^tbt%nXIY}^WY~a}#aJce(CO1IA zMa-xuD=TZ!pWTHz+=1knjhC7CbjR~BF)#pR+e@%6)~m3JUMRgp`Uoh zMV&(3Ua$)w!BC62+e}oei80<|BqvP@{L6HYiWR!ux8c2Cht)Maeq1LtA>Hs1`Z*A& zzINJSe=Sf{Ge+I#98uCT=*eZBxlk{#yWeWyF3$_Vt=z6y+fhPA@;|UMT4Doumled2nK(YrTp`m<3dncNC zbSj|XK6-(fgJt;EUqxT+huEyAdiNQ1Jt6V)II#k7;8H>0mQ-|>>81Na{?tP28W!b#hC^`ZKzsg$1l`1yf3FquwMyYQ^stScoZQ|0i4 zz0doNA09Ru1pNB3#vYYtdg<(TUH1--T8C5GX*nfPV+-AkJH}$0MWl~@uvlBNSy!j ztv0E7;lErbBCsz$PYM72A<%KZ{`v3T<^TNw>^NvB&0@)oU4LZb@l~Uv0sp7`3)qkP z;J(#LUcnbkgI$-pfEJn4`w1f}KrR>>V)MSI$70$J(@YijI>vL2@Z3B3^J-tA9;jKU z89LQxXcie>Fa`cJ@z6PmUN(9K;;5|puU}wMo{=e)n@wjzW<4mSlIU$}B3AjM_MZ4vpi1lpG=S7z3Q+}~)j8RZt*ZV4ndB?mXB z0z*PbU$ruV+ZyVT#4H2JD-C!+x*|>n5Z$!fr{J>{Y=X_|E1$eo`j@!OSkCX(AX_lb!P=;80BT zijex8DM)7?d#mr{4v*lNV&j6)z;@-3?q+&s=~C_$0)N0>`RpciJ#=XA{>5r^aBi_1 zR&Ro&Z2)9TkYd1nr>HYgS+}*nLzu51@_QsOhHNw<&TRR}uKV^{^728h@t~?V2|AB> z1H!;mNJ7GdZDXQFgDsf4M*iw}gGZGoGq3f~Dv&M6hL_&a$wVpt0IPV7P6D|^S8AxM zdnX3e0GZV9e=UGMD6Dl8!^3G!I!?EKfVT{IFUs|(&Cdj!HzBU!_BDS_H!=#a_k!X4 zC#EosjIz6OL289>kq_*Ej1&tli2;AA!sYxQpB+SgQ^q|u_0bCoyHchArezD|>taE4 z#>HJPEhM?o;_Ghf=vb}`MDRFWarfO^U9#DEmae4cZ}B`&o;;Bmh2a39;`0d-b5ANe5oDz)exiE<0%?lMv?6Vx;%2370d(;oaBE+H?7h=_RFgqq6` zS+AZA=4`~4X)~?CC3WkKquDp%B!dfHfiQ1eN@w#6NaGZh z;y=B8IDKy~HtS&IE>MEDxmBXGa^?JP_N@Izc6l9$#>y_~Mt2n4;$1q_2Qr&q{ysyz zhDEuk#YnbfFp@I<1w?@Cr?5aB@F;{*0Jz6j|L&<{Y?3X0Ch<1}hlugl93DQ?`#Yj_ zGKCQa-_FwuiXRWbRLFnbQG7%PLR9M4O}LSOlPLt@+;JCv4CjLD%7EB+5PO1>8UzHeE6GDIo$+eCM*Ck{UDVyuCbm`@y>bQc_H2#0z8tp) zk`Z-RPd>t3regAg_N?{I2@q_cf7}+TWR=8!58O`%w~ICQFW=y`Xpdl6NeTbHh=1nx zZ#crq((~56|JJu2V~1S&{V^imVU#VHMLCi zigsLazhhirHRrov0}ef6Ov-wwe)KO5XO-mn^}p3{K-L+re*Q#Csuk#ZG~Ne4KuKsa zYRDlgop=P{UJM|l1NC@kM@P0|d|#albW{S^y1@IoDeidyP!q6(!BzbXAY*mLuon~- zPJqO@9Yc}v8NZ$S)zuQ&_})-dXly@H>`@mX3#XDKde$PDHdKC8Td#OaH6+ySzZ4%( z@ojJ0*RTbiN!I|I%~*B)+keZkCx=R${~HIPVGMUouCViJZhghWTQkpoqt@gUsz=4d zFw)b1g(xZA3hzSqeRG@pN?2oPY+S5K1_1w5$x&{p&$I``ttXym5!fA?E;p!*W%sT- zx=MAtb)IuU3C*76XW7b)&s3oc$cVsSnZH3WxD-cBOg!kJGqwMe_c6%5*=!8PU%q_F z_mFI!BYP$E`{Q)J4j@Ly>jM`L2qY_8qvaD;ooSWW&d@v&XR4s^sS=YW5SyJ?K6)pS zbv%VY|1QCSw@SWVHj4m{HLVxZ$?U#+Q3I&eE#YO8khx0p!LqL(J zb+SI}TiTR_wF$o&-UxmBg_7z6Bh?D=Mf#KBWEd?Sg@lgIO{&}9-?0Zrdq%C;I65K0 zeSgxWzyphCAB6xu#tL+*V@Y*RJ-7wl2An#Nk1x*DIj^yBa`tp}QSsZvtwKTQ zf=eyHw;1sR!byiVKr{LI=BEx#Zx?v;MHIofr$4^*F9>V^i*}ht95ovzQCwJPSm$gD zQsmQ|-XDHX!<;Ub^!_r?(#)q5N7ls9h(6M5~i^ehWf2cVp3&i1R&@J=s2jf zH2`vu)u}|HVSK}OJ+?PV+Q}aP!SmTK;1)jC{GT1Z+^<_|Dh2QwsCZZo_k31*)Y8%Q z*60(XX6tggj4f&0LOFlfwMV&pEB2bwb>H_XsT?4XdAcyszWfATRqwPOi)Nrj#jqQ! zPuA%)PL!FeczSw*GavSVkorHBlI&GZg{{)Z&Vkb~67uMu;V@NDDj-2oFb~s><9hPN z@8^S8p37A5`X^EoF9_C?HR}?z5BEQ_$Dh)|_Ro&$1cZpn!H4h2MNQmiU~q zhDZ2%k-`}5Jog4}O5>)|jaO||o`qEBKYWr8#Qv7{U{?=((ZV9_U+7;%jHiQ7di58Io_yTt6rNrED{b>1wGbdDe}>m@oWPU4 z@HM(!{p)KVlEO}i)~PqhpAN7Oj6{y!NG}EO=I~n&=C*-jVD>F8t?=X#+KJP^fvR_? z1D^-WnAi7!AK%v2rlYBe5f2MywEApc|j4!r4LZJI7r=M zIXy^9m!1uK6L4f9~(&w1( z!S*DATkzWN?BmnugT`b5Zc**9sH~I|@cGj#+~`aEEgA-8VjJAvHxP*Kzu-DAJr9Y+d~0e*U@}bx92gkqFhRA z+yUuIN_ec65AKKp6(j2LV!lrxCwPO&ClrxUpI-h}Rc>xMhLN0nHZhPzkX2O7(Ki6m zRR-VWu|f^zE;m8X1PFn{f})&UAS@b%$K(en@_Fp6ZMW_6XmGV?_WohpIAb$>CFZ#y zLM8=VC7N70$(T6J{--IE&vuOWdB@TI#;|8)l>4{ex+Z|bn}dsCT3Uf!GNCu~Y)ENn zN(Q_X`S)2r8XbwP=A^jGAKK)3qmg3h-F}z}&mz$2aIfBUtxmXJQb;O*0u*(S&i^vS zHjwmA83qm>%`d9F+@Hgw7#AKM{ykeM5h!z=%1Ts=U=+aW1Qs?B`WhAzthI~u8IFek z&AGU&TA2W%6R3kxvHv(A|C((zRCvt6fdSSu=)fc*CT^|l82f$pHTdHNotCFoJ!Za8 zgGs)E6^rWp0FF8nAz!|Mj;R-WkR8Ie!}c8qU`mGN*L&q_R_ATF1JQXPO+cY zefzI31O+Vvrhs@T_!qhPHRv?(4HDdxhE>4%NyHV;KDJ^0_pX%_cs`--mvF9ah1yOu z?xz3bUO{M1QD;$MS#(>*Ng~0);c)WY356&jt){54dd@I*q*xQf4&2|`I*u(nNyGQf zVLA7o-=P!o|F_)h`_8gI`w9j3LkK(_}? zA>bQG@J}5ORh<*wpqU$R*Q(A?$^rGCdc!HktaShLROorCN$KiY{g;wpPnL7p>EZ9J z;Zt#kE*MLJquQkqKpy=+)ctulmF*Wk4r}x@Q4%s%sALM6lA)9_6csX+F|(~Qvz15Y zc}{2`$t+`roiY2YxdJl40mf1D07&UOtFC6_gLa8v}}mgKm7X!H}7W z$-m)D$Jymr1sQ+^;>0}w zNbO8ic_>vB6uuwt4_Cf?dPH@HT1q|}1(w5|oO|@c4*Vj?Lk^(RAU<-WIDk#7yuSsr z_-F?%kchDQY&LUdbuIAKfEv4XT8wrSBZ1XwY8d#~naP3Z z{_F2_J0mJD=+`I9r|QYW0SLK;AE$u!Nhc>KI%Z~BtgoM+ljQ$&wIEpm(o_b*+<&F; z@~1pMAK5l|o{p{uDAs_|wkcf(@uQ#b|4Q9`nQ1@MKr#DilCRlOGP3BwJ=}i|cSx22 z+1n8c3MF~@s2)Pp@U&G4Q0jrF{XnW&x-ig?h6QFAJT{*GzXA+d6 z@f8>@SKshweX0ezVrn5*umZdSZaF3Dj4$ zmXO%tQpJcwpi4tDyn{+V(+9i>}hjnqncL(#^)97To z?Y9E-Ly8~K+3i7)Ht}sLx7que*`bCHF{Wd&9!!QcAne&HlxC{#8(4vyXOU{SsM zzsGJ+2_)5$`6bNvFnBt8%!IDOMt6gsY zL1Jtnu3ZL3pi^00qu*pBzrnTNYxl5fAS6T*=KQ}XB7S8GE=~}NFP)0<>66dm(FQdX z^8A)UQu1_(EL!3iy_}Yk?!A5q{>C5AXN2Zw9tKg~?Nq&}3lBhtM?poU0^+saJhaZV5{t@e>KFBm(`Vi_P~ zesjfzD9Um*WnTL5%QU~fl7{j1SQShTq;HT*fklbU{KR*S?yZUflP*)gi8tq0qBa0~ zIF%u6YblpRy?txZpICT1&7kS+O?o8nvcYslo<0(RhL8rK5O`WdRFxN}fsTFcx9=f8 zapEo49ym(>d2Xho*Yutp{y^i=`SZ8SGAywM++~KI`G?%eI3@cPqol8@svjZ&HBv-n^-g zO>2JJe;TSJSy_r9tSrGlZzz9!^dDu7&HxoYD20MV&#M#4T@M-*F`O?u2{$G{6{%y>@=sZ9!EY=jKbgKz&fsb@8eK_WmJV%uFvm zC^%W3euD+yMsFBNU^%x~mr2%ox}2v4dkN1;(?s7+S)jx@z$@m;!BdLTJ4q0_LX#ELXei+rt{fkeVp65vtA>*X8;|wZ4>T=3MfdFgA*XjStiK$o=NJBynMZL0; z5>WM?fAG`u*?#*=+9!K@dW@UBD+7Ie&Q~qd4L{6dcG9vXhP<&_ zE3v^*gT=rr-`y3heXsdC4FNakY%(<3n4dp>yj|@OpKmLHpsKGV?b`!y@{U9jRM%`% znmqorjX;&oy6334DCnQ{3=GOYfA({cd408~xA(oi+i{abuqM8%s~b%ocK+cRl&_kp zf=`yXh-wM?QtIuoiua|*VmJ}34zNbm{1UN0JUP93CdV)d1dcn^ ziQNU5KwWKZ;9(9~J_8m{P|z;X3sMe`qihA_r}W`0A#ieRidD?`IyclhsPj&An!$uD zk)ePwiR&XI1O`w!YI~OjvT;O8Jyv<|91qVdfFt9?>}=jsry}FyrI?mDPic}w-<)yR zh*n$#q|p$t5OD&^H~Mjjo5-%d{&qVpoV*g_N`pO(s5`B_5Bv7vcz$(^7{9`>DZT^^A0O=jGiQVNs3ri;;PJ^iufq~S5sTw)n3?fdG^T|dN175o#4*B~o{_RoY}?&= z62TbyrflF>JG#3)$T$b$KE|yq~fg%XpbYLP_t!W86nH)4BGX(FR<7{j{KLUWt``ywGZlaQ)toFkzrwC_1Y!7XTs7)tc3S ze8!v8Dm0O-T_}g>tY(Z+V^ND?F=blT`}>9#qU}R!QE8NON|#d>b29>3cx#IAuaQ14 z<-07Sc!?n*zREo!MrN4vHW9;k>DtZ_j}zzTAMGNhJaV&X)?T>9+N`+YFy`UCdX>=W zAC%LUGjCGyekS2p5(XR;?{SgayKu4evAqqqP|uB*^|oS)C{Y?V_{?XsgX4?oi;pre zw^MVve+qXUS$i3*Tyf^j7s`7JrJvn+B)IX_#l}4$uMFKovpMGvjSXBUx}kvkpw8i* zvv`(AM5`a6JMbAewjZYZi=1A^L^`wp(?RD7hfy9Pa3S#{Z;x%K#v8XYBB7xHFYQQIB-ckP=ucKr&6+cv-SHuw_fmlvqbnjxQAeq3zJ z%RF;kpW=%K)k`|2P1yMOdK?)0!)4IL zrA~R}p6|>7J2Qwv+)VWJ(Xp}F^AlB(>80t1myfGqpq81fD9NJ8I5i3+-Zd%T2WTUz`)Q5rGz#)v8*#S=2Jf$@+I@7 z{E%uyd_41+GbKeu2OIQsb$z1qc@q{E7LL&0a-=vyNC>c|x)!2I&(EKpYNB(tXYdM; ztN+{68Y%r)2W;{OS}^=XE-5J~N7IP)K~@2OyDP#l5a*$<^;WzxBXYf<7FSYI%FfP4 z5FxK+>N6FLjWu|O=erQ|0}l+H6|N-WegOfd4<0zWRaI3rq8H~u=K@9m#@FZaj?21O z-T`qwBRzfQIdv%27Pwz}aqS$bpPHzi#MP%-(#nsSKG84Vv^no9TOTgS#X-0Zs<&3(9!IOnSxGwk-SX6WZom3OtgqbfW< zKX0tJ8sN#r+ii%m0U@E8{W6r;V*hLB>9k~-zstzr=H!%Rl7x1i=;*#w_9wMh zM4%!N8yjnE1$!$zPujclL;oP+Ur)dvQj&N>MFpc?OaO0BmRNetGZvGPeVeip#?dkwz)ZU>1jSi z%j2ywm8$!y+Q5en3xU(sQo_Tl0=fKni^8(ZN=558Iku3A3cix9?L9n z1mM-7B}e+{`BE+X{Y|81~I2p)VfpI-~g@6Y|gn%7X6xL=5m`c zaSzm(FY7O?wZ%#s$)xW3<&@@!w1*yDUPkf+PMy4v;$(I+9w1}5}vx* zy7_ciAFKaz7uJyU9}bzdw^{z=#pfEimoVh79>CfO(8h83tpSOx@yAMNHjbiei!|=L zakjdu3aPxJfHBn99~g*l80GVCOT7E1J+gdtOzgDms!@Q+`}_5%#dUOb!BeHDuP@U& zc4{3Efu+J!IYy9@l0pJh#s)=tpetD_YL_v=(pplAxyixdgjlC{1bruPXoz9B2Yfrbjy=x)>|Zr8j-~au&h9T#C?;_Vw$DZrt6wLqQ8)R#pba zDnKwEO}qidhaiph4G!i3BAw-+OM!{WU}I2N7{5-{>BOmp*`bO+7HY!15>R*t4UaKJ zrkbO;-nD)#bU_l4O_#{#7(*rtR|RLetLuK$Gktx1HTfF&lhgGEBIlf^rvw^xT{vF9KQaHr?hc;H7l4|=>6%SuZdFlaOb zk*=;Tn}Ki=&y)1L-N1wLoIjuU2yW=QusTwFD$F-Jzo`=FTo4N~9SGt_=b(mPTsuwb zR+31Qdh#S(FWfUl3q2Ac-@k3#{-@H4ijB?9Oog&>ib3bJy~*KAK)IUMKKHrBUPb;fqOeFbeia79O5@}C9= zgV|C?coR#WhUp#ueAv9W+7}eWtoEvPhRt=HLb^uPqO!6Y&8}If{`J7=f6B_mB_}Hj zZouSt4nX;sgQk<)kQf_?4r+pqYq#lj*)adlcY2X95(%_-fN}xr0%hgzV^Vl9py`o3 zQTw0>c{S)^IYH$jLW3v(JU~HQF8rzzMrA;vU6emLISKQYa&q`G0t2y$zD;VMT2ud? zk1&A6Rl5j{DuV&=oRH)K$^xufAns&NC7z4qkxPW5Z8#WP-!wJF!oGw%gdHtqR#3^Y zT>9e6q9WRed^M$>LH)WUT7G_XV>+sWZ&vRdX-bY2_SaMWf1WTsn2Pdq(|)I}n}89)AB!r0{XH1K@KJxuBzGkPDr$IbmT4+`Xv4S;Oggo}=DD%QZ(cEH{@5DF&Cpi^yf>6fJmxSeWR)4*S1BN9Zs{2kU z&*duMm+O!PRjU|FLSMkYg9;V6^l8XL>_+ip=eDCtz0>$7BAtSFcj%j;-BOD~K) zn~u|zj&l3!Lx0b?XY!43-pi@`Z_>piPUbgBGqqWa?fqkZO9M{*(N(&X-#%w@I3= zY*tR}3HloI-|kmX@Ynm}uh0uMi}PIpl!wui_LS5??}-68u4TPA=_TprZ&6h-)-MW0 zUq4$HoUw0!nOGbh7~;_2m0<0}`2~AKoBn3Dg@Y*e$}wr6>JE(LV6qFFg-tqgP8 zzg-pJKfq=q!OYqJ1(p7{gi;9NWCALTLahb#-+@XDin+2_lDnoi$rA33k5As2W+zEt!OiCBy%P&kB`n zpvzCIEb!1?``3~o?~&RC_tq%tLhXN{C|dW3ylB-Bu-}pEAE#HDE`h|VudmO=*_oA< zHIJjAzP>{Tg&%{#prJOaD!c-TObDGWgM&kd+=;zOaWKdzx08U)16Kyvdo)p75_h=0zjX3O;#2Q;41JGc!V@GG;~hEJU65W zqc$>j$J8{9T=>;>YAhWC!v*V^Wb(A+WMDp&mOzhCf9g@}2Y^Vf4N-ZNZWJ2o>T;@d zi6nRqpV@l34fYaH|B*8@YV3pp{*7C=z)THz6DUB5NIqG^KgGdiQP&uVeAHMHVl?lj z9V%6A+;NzvcW5X;ZOr|IR}qxam+pw(#fy*3g?I}kF8(FtV{1D>h|0mq+1=Gul20e| z`oY78O{oP3GyjQ7NJvOYx#=zya^BBZPArK$u&D{L^ft^Ic|tBeF);yEK$eO(rXCWH zDE^B}gf9f#P}l`?Rw5U6_^i&(;$mQ$p)n*iHC5+cLiQB4d>C?oCUy_h+lusQX0Oas=r{q->x_-Hfa6ha=QFV|ocA=o7dzCc>E@TmA0 zPT}I>_VxC{Si(;~hJk9whvviXffF=i%tbH<%u-r4Djk=|B_9lMG7{Aag-&>VS3z4t zHek$0>ZYls6~=vEh$%HK4Z3^BCnk!ogp;g=Jb{A;6tkN%+67uVDf{+40{eF=;S-0{ zPzaGn%;c-a@+LNxUHOvpuY-1{ctxd$`$9h2hW_37JL*$0@w>KS7tRT+h1h@oh4Z(8 zbFqJ5Pm2yYd*5f~@>^k|e}XRi4M+=;)smpH>yKy>e9_^U+?c(3Kih3zJd2gJ}GyI;0Y{&5!r}`NSvfY?6+Luk6%Pk6wgN6DQ43DA854 z_zTB;)-+=!gyHcj^on}Z;iGyo@*r+D6XO1v7Pw;;;1(R2H6vj~(o9HWzfvGt0xjV* z=3_TxH0g>7rFX1_!()!S|Iah)YXtu~$^_-aJYPe% zUk3gtD>D^Bz7G&Q4Fi=T?4I$v`cLiAk&6K+{2Sw>O)a1LPO}u-6N#iB!l)`dWW!kJdT?a`+q~@IkNeu* z6;1ENrAqq;#c5zl<0TUQ>Xo27?MO)Cn^5!JpSW|{T5;CuUvT2t6u(6HtMzo3ce@VM zC|9Y2#w{Z%OsLXr)4eAwEDX63#IeKi44oXMu;k!?2m_$LAgQM6tQ&8fY9vvO$oF zj(}(^?TJCE^W_u9!X`t`rEq&)R_g(-(kt}XmH6YGlXWctz0S_5sF03LWL!I-)#Kh!j z7;?ep!EKpw@86$K1nCntF1`O&s<`?xgL3CWXmOwln2(N_y#{~;JrXseLfYW30cso9 zd{rQEV4Ah@S0HiTf3hfO@-mA@4TBPaGxYdF2V6sjf~NE)FGoX1V#eDvh0^Bc86(dh z)S|5NbGI$?HycWfjZVMPrqJZeWKi~d`0ycg6aFwVgH}Uad`jQ3FyJuh zl*@Va!D(<|-utx2Tz^wa^zZosu(E-HH3%XEa#eu%A|G@dIdQPusdEZ0|Nug z2ea`ZsCp8wM7#Q4?wLx=Tt3>M#A0R%tK@Pru=ub-$zbpa{N- z@ULx&aUG{>)F@PydYGDKiJ(?f0qnz8XhvSYeew+?v1H#go%juThkX~>=#Cjp%}Q(# zr)*U?ygpDmMA<-WbLsW-`zDZos?(lE=09GOADi0K6(v9WNy_u;wqgRTvz_=_2>@bY zG5Y+z%}__WO~({t*9;lhS zj^f}PBwXPPJ}6M1WT%ev{E(gX{Xp^g(BD)BI5-zq>4PVk(2M-=;RLi@-M@byB#>uL z&CbrMSoF}cAYBDe3^7)=qHO{$0RXi_^B#!nGr_#SxmkJo?eSb4)B$unNq=Jo8}O#k(xEy6Srtrm zOoPdiRA9f!zc|J})YHRqOZVQrKsYS8$cd<4L_}OE$3SY1xIIzbUsXexkR4V2lwL@U zyGMr%D{{h1L|E7XD&sugKYqM4wjb7R@78H$M|ypH_TiXllv^M;&f?&?CUC zm>y5i9>gf5NtV0Bhl8oFY9|!0KokNv3PO27rP7> z3glt|wnj&bTm1`8TZp4|`9>AR$vS-+g98p!9^^D{^c$)7*k7u=cf*}F@i7n(0=yfr z!ymLZ#)<$h?lWFftCvJ4Jb`)5)NDgJtv4A*bLWSYVkTr#PiU0dOW;q zf0FyaErE&CiE<3N;Utb%xKmwjGq|XFP7SO;o~EQrZPjf_S+WcuC;28fHxZA9*4ReK zxJx9Ux5v6br{H{7{_pq(S-F8!tF#oi%O|?aL)b7hIrkY)pFX>J;kE!>z+63P>G5fG z59NCIe5WuYHb| zCxU{19S9o1r;2!hgCOmRN#XsplvvPMw4^>&c^eJAy+Uk7=+%Tnc3O=Q}b}ays8W-V9N{etUxuftE&rIM7Xs<(#f986Pg(tJN4ut zkS1LLaGtqECVZlIkLThNSe39slI&dg+M6@cO{~$s7?H@1P(U4J06_i;_f)!9*AV$wniM+P$XdB}E zv+%%KVSvB!0=o`ED|xH$74c7(C0D$(y3-J`^m>I8fD@Ie9%lTC*#^)sioUiD7qQAe zp0OU&Y=QnFc%mlPLzPyhrs5+G>MvH)FE~PXA%Zago$zPQV~wiE`5^dzrJkW+N-JNe+tvf z#`#9GpF0Q53XJ4<0lvP_`9;#VAN!kH<0}E#Q8*%@+3oS;#{kE`zVq&#J0AjDmN5-O zDs_$zm&N!!pq60Z11Jl)Y*SM?#!MK(E;(L4K|w)b%-}S{52gFhY_KCl^p|&m?enuQ z$$ucHTAS0U$-yT2f;NmQgJ#G1&C>AQB02=T)svgitf@nUkT)Cp96{$$5qVdE~7%%+VX+k%l&undOz;S@fyKPn17R4xT zrV!dM$gxR7Fby`x^b7O_1?QWZnvBWagbsJ}fdB8C=;+xFAE`2*J*rWlUoRa&I0>?+ zd2^km0iAOFm4i6_s!w!oa%d(9W z^>OfwHuXpRdgKcu$k=s!%|~8&@Rd|l80zTEj0RxzXn0Jbu5DjO9dtDWfZpyiN_we* zfzP_$3aA4b<#W6Jm$`%KvuoVJeP(i5GTR-Bs6RYiWvCwL2X(#kCM^vctuo&WU6OfI zA*RjK;5OO}ir-ScBhI>;DFEZi{yLDg9&F%;t^KF>QZ$q2q#~;HWJ|QC|Ez9@{)(q5MBAS|AK;!}DcQfY} z$-1|{EUc~Oi%&RA_N#ctO-&3HE3mLr&+{jwzC|WL@L%VJ__g<;uc+pA>So|f=I1f~ zICHxf0_C@l_WL^J_Ak{x=R;}UaM*^oZ_!&ph_f>v4reW!^zJXon8x6RqTp8;b5OEF zs{pd`SF{7eo>{y)i#zVO%=fYx@}BMf%zVM3X8@fV_n-oy0T62)v*l=k^Bf7B4Tp0k z28Rs1&uCu_?v!3~X`CWo<|zack&J$^wGjd_T0zva!x5{!ojDktC&BL}w*S zohxZ5&f5ErDOo>nZ=X+NZp__42OGEhIOn zp{yW4dC41p(dcnxJwT_MpV3Ds5*iuNb6NFZ zBC-Ay-T)y9dgYEKvkl$lWb&M=HKD zR!Jv@qZm$QV@2>MQ&6y+?Tf0&rpNoVR?XNPggcZ|ZVFvFgWXkFJvcD%xxAbs=aIoW zxg%kB^AU$1|A9?tv2O>svW$+6)_;Z5PFGLtKZagX&fyr*_o?Efyu zzoM?B$q6E&I9CDZMfwWJ`0{P53>|n4d4L1`@YVwZO$m2Dfpbjf9GC`(i;FWWXHE4) zhJizV;Xl;C|K`s{3g*qdf7s_bzyLq`8X<1X!pv+4{zi#%F|o0q)9nfdN%|*`1WZ?h zQ86-V37lu+Nn94469c{a^5(ChocubQcd?jFRLgc|HJx&stt6m&r=CVF4&QIKUN_#! z@DAu8AZ}^Qn3U#(0~bFS&h784kPN1=Fo#2llof)B+WZm}=#(QcB=lKgd{)_3i!)42 z28M8VA03fUn}srUl>_nHK4XTfzgi$P4eF@unsOkk zI+J?X>2l6&(~vWKfkeqh{38K!b+xkyV31PBiniM%=M^&44HQYA&z$EQ|04Lwa5jlv zyV1^-N83H3jYxuAN?=Dpp5)xY0 zCj=9_7&F{THy?XVzVEj-$z-10nb!I2bJc+DoQkRDT5$_pH9~$EvK)>tn7?cic5YBu zJZoREfv|R3K)C^?%iK;$w4GADQMA2?kt$b?aF%`H_=^mlRYj+h;p=^%M`fd9&OPD~ z{%T!DuvG(-*tLOWv4gzUz$ZtL64%t+;^uqU>&s$j%Iu-R^A*z6;NV~&)jD4`vD6OG zGfm^1{3J|e?Hd->QdKn$7Up##qWyd~6x(j94Yj>;GfVSTpT6h+3TjQl{-5487+$|# zISdV!VrS&>SO+^CLEg9|pJ?OJ=JrP#h8W~kLq*N^#BtG3tTD11PJU4A?|GZOMbX2M zSO}_@%yJ1I%no`lt3r!cDTS?7p>X|i;9^YO*fTIN-z$r;00&MMo9+ z4Ezm^o0(kCE-s{i=?B?ZqFg_WCM4&-yx$dlOS4Y$H$*f51wR!O(xU|SOvWKYR^2>s z9-dXUfR1t?#%y$Jb7`9I)PG?va=Cn>4d+&zqV@u=E{b2f(L zsD67B5(8o;#XvyKV3n$8Ew8p70{^PiAM*=GRIUbcY3!HF6=btHYpnNsjn&-Tp$cZP z(25NQvn5!`h|K4)c_0Yu+!;-LlVaZ3vwb!JCP9@9yfZVA*^5WAu{rYaGt<<^1oB52IQ2Ky1XC z+2m{6uM+jYp@fR)f$42HXHXxb*V?#|#Cb7bBQk_eNVt4nejEW>NwW<%=a`>j<|JuWqi_br{)MYVc{k?vui#qSk`j7ID} z+aG*tG5nPhA-n~V>2F z@9oaez#Svvz=OW0#oDGwv2CP(<(QIgWCEQB-Ly8)iCnEXMQrG9;|p99(}K;0W>Md) z?-ae8Itn7vt2qop>me-xP7T;BbtlXGT-vqmwvIBq+I*pUsbU5wWVQ3>OXGUp3GUsW zOJrM@E&l(jvsU`rfLftD$a0*RU)K=d$cR?*NKfjfDtx(#h@x0{+3IZfX!UtpR~Q>) zc^nLG+A&d4J(I+K@1y_G&hz74~|(gVDDRtvIC@WS|@JL60<2 zQWDv=ilRlWrq?P|Oi%^W*7h2MN;`A*xHQn~QzPy90?p@rHC3~kSdocO!G;~vY3SKB zn0x}oA5NH830_@*ECJM_jcQR+n1X`X< zP2&OZOFBz1u7GD0jB77U{R<7HzQXso&+g0g8{h~v(@oQB zPKP$CF$bu?D8ZDfrjoRN7sEQs(}d1Kuey#V=0F|ngX~7M0O#En_4|mOpzT@!kiFE3 zbXWM|#oX0aq;yWLJUOwc0?wR7FsxG$lfGAcl%XkAYz^++AQKl ziF8q4C8w#2)k_=6Q^?d9D*!<%q$grmcE?NPvW;gteM=H^j>Zs_xco_-^NqOl zLLbG`q_=rKSUWs6$11o5N@c!YUWb-Qv%HrYyGmc0FLd)SPJ_#f&1^#plhXU-xVZ8_ zs`4yiyV@#1n{N5PdWbhHV1J7oq*t}Ir*}hNoq1P~7D4HiB&>Ib;iHZjtMVbvXj!x4>`E|fJ}=9^k1LN9QS47~&Gw6kTJuQ>{)Vnao> z>!>LCG7oqDVaX`d+AZyGnF3u9@669vs^AW5JlY`&1*asX5ymTVNOdgu=~72L0&xGA zbz)80(AV^=0-QfaDaK~}2hMSDI0vEU3eo4J@PHDdhV5u#ayyh!Ls<(DOq)oaW9PggKbcIAMeoVKMi{uEA z`aZwUzx=ctZsgV+1~i*D&{D65q}$TKUjxi1_Dj8DXsCnHi^!D566x(5@FeJZOt`h{ zs#;s`z-ppUC@%6)C{TluZKoBa;xPV`m5mLOmORVDxS}BT(Z4WKV^D{=UJJu;Ei7hW z)*xqFiAjDMA@`PQG4Fjq7x=^CSnce`?dJrm4A!^BO;j1bIm>f`I403#if?c z&~ge~5VNL%)^mZ1|N?ACk-Fru|` zf*zyG`~D5uHIdOxsaNr|l#k2D%#wh72Z5V;?+G4^^5ZYg0Jm2k$kEr`z8Eu+Vqp-v z9f#)7s-V{B)I#?Um7!9z7^K~Kma=I!zpnAZE*ue}KLyZ79jxAT(?A%0=4iRFxp1+S zCK3+9$?FT)nlEYIAPCPX9LdCI)}ONm^N_5=QG|=ux@B2FAnJAuaoVpT)ZhDQ1&mW- z=Ho3LzP@=~_Vtrg+&!i_7r}1}qX1!wG9%VA>>C*0WhWEF2*mUxv&~|nRtqWHkZfs< zLMY>gYu}vo-5?Y3pBx&pxIfN`ZI)Hd;VpT*p6`Rhaa854;AXUaIptCK)S4lPM(%n- zajmQacD0#6KZDXIFPnESh__b5C*YZ-erNTeLf4~XH1MAQeT+f63QSp*kKjrxm#ssV z>k`GcurRc^5Y;jZ_kN)|-sT~2#QAhTocg!-5ze{&$pd7fjIAPTq<{>W9^Y&;i{J~8 z#T~7)?^_}S@%iRpR1@D6%naDkMl&Tk?Br8&ei!=HmJMo^glWk3<}qshfD5t{=;T#? z|Dd>t-Z3KC2nc-WCvcg>z+hLy-CXfWIW`OECFsXS4QkjI$HO-2x_$A1I(XHLR3go1n-$R!x($vL`Lm5$NTA3Hwo z*lhanVaNeKl(c+gtKsib`DI6^RLF(lGbhH!6)`s17)(nMy&}-59o*jTTeR@6=GF9Z z%a$hKjc2rE{({^`oRzzUb3tIG4vK@HPKjvgXej>DuPzIpNBT`uKGN3GhxOFW+x1F{ zw$yRRcwJD=9Fmm*;I~wQTue*Gg)UX-gj(=p4G6SYwfp&{&N}iP{Wu~;}<-rOGVI&3$X{c8EWgswg<6H=UB zg@$icxwD=;c>*d>wd={i1R^@B*Ed^L=Nk~D zM*W8mAD}WB78E281DB!2J@2A-QwfSQRMQy7c0R$3^j1fMdJ}r-*=i24K@xwM?USrO zeO_$!aJKLXFL!r5wRbNyKuvsvDlhO+7M?0); z-@XlUWN802G#r8{ZkjN_Z+6yT3@A=s7=;Q|PM{u;(k*@lzW|}G#zsK6bpD*<{8*7FaE9*mX|9#tDiY2i#1hcdv=nuYRN`6a zg(dPbl0YHd=*Qzkxy^m+yif6up*+>ol9~z*8xHt?CWAIMm`!8`O9l|}@s$&YrZ2X| zS(WD8^3MiM_F8w=(x-oR8@|urDF*dU)xDWGE&pi|8sou6?y)ie<4Z*4`i~)K zWSnsDd%}2|@W_$;Y=x36E{zi3xHzUUSDMLHowZmN^(v(xPSldfCGb zA)4VV?-H8go@_?W0NA`bFc7xjT|`%T2uyUj4c*--c{gQb#;Rnb9`an4$2))9iCA_I*~w^PmajI*43p<7TEa*MoZ>Nq{wXoP0GeAs5xCyZeUW+BFG zEijdcWe5L#E2l@lIZS1-!d=F?g9AYLeJknwb>e2HXG&EB-@5@(mC(n7DH-VM^jmV2 zByTrw)uP_g(t=UVcLrYwpvXs>^LUaJ>I~YQ(o9Mh+&%(K{`T!#3jnG^vZ>M4P)~p` zr^W!CXREE~Yp2mE;ve_jnOUx_x&5pj^0G@SSb3YK$?;V6dM>~o9C4S-*+RWIUN5Uf zRj8F!69j*M>W=$g8m8fj;^Q$M9e&mw%qqTAVhe4Y2%S{cmDUT$wL+gvy) zl#Vs#-FMWD<%@$LNnGrtACbqy2=#?APgsns>NK zN%kb1E$jxEvQ6ju&|}>fG~3?@PXggI-r5+hE)5(C% zuq-$@gRl(8pnDDxS=o$4F8=tCzDq()oXo*H%39^Ab?1D0&CTULe&$2s$7>O4EP65B zpoNOp+~{N4_AH^3vNQd1krCtAY>fx0BHhzjD1t&RT%Yll~|4kks4tMODh9i5kO4{&=@O{bq!> zE;6dp?Zp3&7;L6T^8ewO@7KLq&K$e73&pv71W-WgO{4VK=M1C%zcLniN6H3uG+M-F zB7ZIwM){!AR}mb?)|_A(Xzm@ga@=y$iH=r8e}V@_Nm}x4xC8sP&gXc)D4fNx?Ab?g zZ?g{DP(7}F#im+?bAQ%x5~K8>#{ukEjkgRB#+_q&vQ78g zlP75_(>W{Qq8P#Eb?vGxj`R*4arvxv*YbTkVhdf%+x{M4Pc%`Vb)+@Tf-K8N(!{tik58HZ8P-#77*M$fK%{O4N*$BbI?Zjirv#@bv13N66c zK^_Y_S(rYfGmKN0@Z3#QTs%5H9u(i&1_m{h`}WppbH${}gA!{=s@wzP(ZCOwQ6)!_ z>?|mLfg!V=2s-wJMbqIn49kQPS;jVi9{Y2?9al~;iGztUXPKGf!3T42<_A7{3KTIe zPb#|i?c6>i<#fzN(%R$Em6BG04tl+RC23!RB`SzeR;I}lFxsKna1}4BxFDW)0|LjM zACY+M!cLUTf=IGp8VEp~aZ5py-LmHD)SR4YAW2U}OuaxPuPyBvLsL`p z{{44)$fVjSNe8psnfun=JS|=oHv#3%<;)KHRKNN6-<^W;VtOY}p6p#3nTA7B)!I_F zjd)L7e&9sQE%{y$tO7Yk#-@1{N=41tmW<~=m-mfLw>@ZUZ%^3#SrXZ zQoZG}XHZI1R2w8)I+st^w?dl|CATpkpU4+4jx#cTeu+r>VOI?H_MW$%c>uFpO#TIT zJdp9D3IvWJ69p2*CmpW)v+`5{<&kVE4)BA1%3n|!xsdNNva+YnBXgVY@j`deQ#LJk z0}(V#^PT)^ihNU?Cq+|7s!Xyt_NTrJypfwi@@@h8(|}~%C3|-~IUB(AGcYNFC#RyK z1%t+1(Na@u2aXMlhe4?f=Uy^3^kM)e>g?)rRszuk+>o?>AU=xE7W7|(bPu}NMa9HM zXXl{}l#E67U-;QRLGs7(THrZ{pzL^+?-tu134o9c1ZjtL|sna<|l`O2WWXT{L6ggis2Zobr)rS1I{vKLs&A)PosH;`mS9S>8wCm5Vyihy2#CIw_e(GHlHhtj--RKdHOK{BYzM%?o z!7O|ew%$@g|7x~&hlCl;{RZHiCm6^1WO{Nv~I@M6a& zCrK?SD*8>%aXgVsylBYybw4p!MaW1?zZ{}Fb7lzyq;uBWk|nkQ0#Fw4r*I6vES|SW zDyDOn=Om0^WHn7>QPIrDduQW7HlC!vKSb58`F8kGrsv7Y58$ky35EOz&g)*wMl$^m zZtIT%+BWn7+LYB5mFobT>A2UgH9!8l2dwF#7e!l7&vzb5V$3;HsyaHo+1O@!(oxYj zToeQCPZkQP(29%ylu<1VJAgbI^6q=Q7OHd5+YHH!!vV6B9J4bsH8Ju&US3`xpoSh0 zAd#Thbn4V8Qxg*%szba;36J-9{IzuOoP*YGc;zV*u-{iUfhq@LRR>NNVBKytT6~IG z>3x-<4bS}h2CAc+m(aspK1TNEel``~ZEFQiOWcZ`1v9cJid$R|Q*Yh!&nZ-Wxjlhm{b3+CzpIKUoG4XIIs*^J zK)P5#Pb^eE;vvtBj@E^?E0}cVHy?c~_6+!`@Z14gvG}Y}XO4wv{Fd9aUT(kOfdQ-t zzZEh%fG2PY@eqeW!>2f*3;=d3FsY=et}X+n!GJQ_r5vmVdp$s+o1<5kWHUx@HIVWh z z5AA1RNNd;&k{74=s+*hR^r6uwH95Ix61H7|2UQFIut7igCn^DXh;9`Sb6TuD@-OeY!?PY99-WJS0Torvhe0qDCNz|?j_5$5 zHp!jZDl7&Xn4x2d)1Xl<-RLgNhpnNyYSjx#cm3;%ZkKOP9v%Vnu#iChzPzrWFblLp zvE)1YDy%My$2|vWVmR1ubilBrQk!{=KF-g01{{3h=`Wf>xY8#*cIId7=6(I*ZjGTe zd2zq@rQMBsMvZr1;pZ2if9$8FI~9LuP*Btmi}a{|p$;^?=wC zzSPhD{;G0a6CO=m&V8^kvN99G{F4Sb8*htf1`x8gYTA8{%QM;ryxLIKnEO(i0ERX* z$Xu*EoM=q>cAV#0w`5?Tto>xy>!WO%F;H8bj0_Jy$IiaXgOfZmE>3cMu8kP;#dJ5D z)ms=o`0NHb|G|IAFPJ5ShhJ`(Xtl!!T}%co7-dZBMM6Zfbk)-r~#HmoHVJ?=$zx!A~e?1Pcub zVQi67&mE4+$;kmKwXv}g(i^A?!O$Uvh7{P6Fh0zKZ$_k#CG#U3a&0h4LQ+afN?g1l z;jUdjG6@%kD8rO%%iGz;;2R6i^CC5C&&yKY2JzV z;vX@3m#ZnOnrgB(%Pv0k}S8=*Mk0sJM1m|5z7-oeJMPmLkr!=xJ{u z!}cVsi#hu==+g0*gDfTzJpx%^b5A;TAcP{;?Sf5<0as5w}xsk zfKv=bVHF$YJzJFLj{UUtI8R<$k2S}BqN*cZ91eZs|aCUxt9i<&*2uYPOuMt|1l!@}um;#rbT@#Y_Na>kkPh zGkND<=Uo!I{#_fUgjKfF;yH>YI*q<{)Ez z=+wLvQC!jzW~&A01FvpX(lYIDId3J_l;B`~VK2^eN|^Tq+0-ipm#TH_r}Ws41A)I( z8c8=+qSPvM%gGrW3y{pt);;YZyb&jqN^@1{7&2*d!4?0|;V0K^d$Mr1%dO>>`d9(ZB5Yjdi8Xz}C+X8C`S{ntL?%bnV# zDpVRULobUK+VN=@_{GDNwV#%d^f*uRo>0B(LI!&(o ze+4@+NP^{CWWbvqhGkfLgce4=SKZ9XoLJapTlEo%z>J-(~u)s@|LxTV{U$J0!0pR-wn(>UeRUbrrG2mW3|!iJFS0OZ#CO z;~-)8O< z%GYtD+VbXSv8JY`D$oH3@CyyT$QqazMKATAQZ!Cx9tP>IJUIFqd;sNAitZOupt7J; z2e~_JG@14Sl1ZK5`V6Fc4C*YzOt#N~f9?gJ?e3Xx_E{;deTfH0DL=E47#l4fjJrHK zZS_b%poO{sVUv_=Whm|*+eG=XP7&L#fi&16AlHH#u+Kn2fzSY*-!HsIr15y+Q7+i0 z)N2|T7>J9z=&1nO0biQ=9q~z&fD>R&@GNfVzd$XFQfm?q!w^V0{=5f(a>2#84v%TD z=t*6!J|=VF(S0XNUwzcqR@N^tPFdyV&4*wTW22PwI9?GQhsc_tA-spm%=RbnPi(%1 z4q@>yRv28aehojhcji9>7+f%;f0*q$3 zuR+>9=JCQ_U0v4-RgWHpxP1Le1S!dib+|ML#L`$K{DKJ(AimVWgZKBZ7B_#}Aau$* z>Y~w=@YXfS3mTh}K1Nz`a%K9jQn<__Zhj3&A-Xuse`Rn{oDGQznh0yv&R#joC-YVL zW;V`nGrC8+!awTWP_e7R2;nLGw0S!whzl12;)!8>XTiWHS8El`R=n|#-~u``IND&v zY!~|mIevR8hm-9m06y5W$u5$yi4Bim*G2A7W3)6-fyW~r17=L4)7^RN@8;^`D*?8|ntgktOI_xHQEh^Smi_<3i|qtbJ!bdMJZ#x6>n zg~NpkkrAUFmKgn}Vt+R?>Wb-UB=ZSaE1C~(^>dgJ!pdI-Fy23_tFG=Zd;wD?8>28b z5CRp8s)x|v6%ch+UP?9x#maX&mBfSx-UnlGEWS0@8)ZF4z^KzOeWs&KMvT<=Qd;8oH-pPcFZuY7&2EK`^DxS(? zc*@n(h{fMntQy={1R&bnybrg)uftvC10so95wi?TFwoy$yWq_(qo%@e45AuIwhIZ0 zI`C_zvYZ@0_QU;HbZ6izjqC2Xdn28iG0J`OJ0R|ZL(d8`Sy)a(+P30o-K@+^S6#!f zGe$RmSSGI6vn4i#d}M_HvzF0XoB4BQPDkOv2&o_L?~es4{;{#jI(XJ;x*oqG*XdFb zY@$xEtGkJl`vc+G7umn;wCI(Bw+zm$SaQ_eZ+R}nac?{w8bdpTQREjeE0602%hi5A zP0Zgv4&@^%?jYLW^}|Ia);zM~Zjntx_tnJJWr7vi5@gc7vou;*g%yTL6H(XjFT0&K z{rpyWy|C)Xv51|h7T41oD&w)UBd-7SZVo!N-)BqrwOmqcd_r%0MKFFi`EHQWcfX3= z{Pg-8EAKAYOdoa>vFG02#5#E*L5y+Ea^reBbvwIou-Ys;45gdyRFpbI|^=1XbmX#vIA&`t;W^<4WPCWSM?g_Q25y==wDW!g~t?|5j z7nq$`>X+mboERw3zb7M`RhLP-n<3_+h+i;8HJm?UPgMQvUq)9X8l@uYXOoo~OJp&- z;LlyfZo7wB2Xu@F$!>IS7NQN7Ona>oZ`z)cP_WG?FK?k!UVjj8q(PNonPJmT-q6Y5 zMk+XEx{Ole+`rYkH%xe&R$5-y?Vv-%^KPvn=Q^EnR#BqDRn_>jb^IF?@PE?$*oCf9 zGRKWUc8O?$f#I5m)FjcCmFS9mn~SPn29}k5w;^9}@qYvrlR8PjWl)Cl`AM<|NOA zuZ69}Sng8(j+OW?)sooC9cgHyzP^ylQx8HrP*qh0HYq&=hy)t9rluxkDjK?z_f3z{ z`Qg{En>&2d5~bMEtdmY5K|$ybvpb=6_@0+nXU3k<-L20%5zt-wQg?sU4`ink&6=KN z+AHb3c{-ipd6lsEp&NPCjNg75X=L&1zj^xbrLa#%Vc`=D_KEC7vj;3|m6)j^T+?t0 zM&3U>kz!lhn{185pt+0JajsiOY}^SA)%)6tz{o?gvaDElR4~xQ#S52W-LDUzGyKTU z{4499&6-cvN>xa8w)upu&j?Df5LZ2Yx(wpz^MU2f5+-eaTGG@OEbUvT?+{&ZavD|}#&|e^@Fi_T~siEOigl0B?2=#>J#73IY9of~dtsvNz(7Jh`oRuV4 zSs~>S$#2BEj6fgDlg#Uw?-yq8-ePX_RYCJE07aZZ-rnB7eqCrZ;qu8^HfQE}{Rim;sKP9I^t-BJDoJ9^;)@no{A zzL|s;Uett*(qvpXaryXMc$K_Ll=9(4a zmyPUy*brbbG;6uJM`M%_SCuK!{F`>b%C~cX*9*NK#$>H|k^qNvf|SB9TwLoxWzznI z_>M}YeY}5vQK9Z}*Oe7r7?iLFqJu;t*jS`j6%kECtCF@m^(N;vFnzo`j;T9rM66B7 z^xPh{amlh}m-!@#L(TfcZl!rU1)x0SRSM(e=KkE=83Lsm>ULNHsMr@THV6p`VHN^n zN-Qz!zn~tp#<84=+c*lRLnohfOd&``b`FhzB050~`uR1e*Kw z%&9OMnxsWIYCS8$v&8@O_J2CvtOQLGl$h$u0#In{mNF1$4nOhf`MU90tXyA%7=^J@H=>l2x{Q8jv1y~`M8YwpbHpGqAUF0C~mK_XwwBG>NT%sF|58a&t6zvFgjDlE0Nos6d;K6Uq( z>1Q~=JxB_vXN$!$FPysE5f_Mt;9}T5wNj{Usr6Q((p7dTRuKQ{$01a>Bw_D?bw?WZ zs7`!0hp?;3iw{vdBz&$Duh~{jY7qu6Ra(Qh!noi%oCPd>VZ6A_J}GTP4lz-j;Yaxn{8nOf4#6n2#dyO&Sivk=9(hKULY4m3LCUk#lp zSxY3^lUcWI<6ifrk$uu$2Y+uKO{P`L=O|s#ceBeTMojzv{WbV+Av<6I|&N7Nt8fLnMlzHAn?N8**wYyBCR zVA779jrl=`w%?w-wBV5n+JSDh;gg@f*DeL4#Eby?fu?m3x)ug?@^AaM0C1$?cEKhX+Z0La*%$D$0ChbjGO!*d;CJ2k_oZ67139~54s(s0bByNi>?-k~Ds_NFfO8)hvX$;qbF=Fr-^Zz zQGanpmkrJH#YReL1!lbsJ7kvoUaNa*ki7SkHSu`k=D6XvV@nbkk<8fU_+BE72NW@~ zCv#@EESPpAr7X;)q~nhdr4JKwd$+k2xyFhNLH#8&fRPFCvXoxUSHac=(?1zP9Vy)* zl%5dyRIC`yk^_-+1JTr8C$c-G%Zb9(gB3)ZOfCECRVBdYbe0%op)P?HgyX(C72K$U z!T_`Jr0QiY$_(&gJr8yBvD}o<2|7ng|wTv6X*)_XCHB$o@*zmbO}@uR95gLL}^;p zE0^Dowd~39vCn{dNsxhYLBAd*Q=TG%k!3xF^g&HGH@AAHZZK$(SbpgZLM!`Ex++<| zy`d;ly5ye*%4b%sTi(g@X!JPH-mk9>35ZWrpn}VA$zdgwLE>LVR~M#r6SU-JscTUM#Qr zlk%$#1^n6MN><8pp_VK6N**FhkS5;kI+gW!`O;0{N_KlRi(yK2$jn!U{ zUCgX@#4H@AHo5oPjaYN?#tZLXfjalOW}3uJ{opqjYX^U2TiFvgckW!@{uBwaTm)oh zY(;ACOVot}j=&}6?#{IX52?404+SQ=&tzTmN@o2x7tn9@FkGrSUGGg8OxcBjpbO+8PS zqdC{Fr?hX96X1yJZaV#DKl$UXX`esa%{Se=zu4p9!zCJR%N>8Z%OicY>mxGS=7sWX z+jh(-jn=42VRzm?PAf{b>XZm)D}brR*J(v-)-%oKqfN#=;>`%JxkGn*TRWYY+Q*da zRFyngX^U+|ZGIPw&w9Ds3k~}neJk*ie7_F4wrbz_!;(vS&FmOsd2Sx*IGBR;el@!i zlE9J=(heZas-+$9q%)f{20eLl2F4D!8K9B$DFE}ewzt~DiCh;#u0!Z8V4sA(YCcww zw*9o*aLpESII(Q^?U?CPjg;1a--R&-fkp?4XcEu#2NXssu^f(nNNg(us877;#R>h& zg}N5bdz{?QD#$k7KLBL;(YDL31$|J729&>K)gARDX#b3Sd^Evhl1G6tP2P4Fs?J$A zf4cTqI*!N;a2I%deX=@12oidWgifq#BT~H6(hf>VUHfsJ7F|7Ef})IwZ3`X(gk4%x zr8*O*)j21zC@YGkc0T9^gl*!iY{F0t%RqBs`2NnA5G-?(I3OdF$GW|X7h>9q9mJ6e zK)Jh2C;Vg2;qc4h!jD^IDebbv$&##&DJH%nPdO~7hyC_y{=@+9lC}D4UT@^*mt)ya zx!?7J;iP9oPSvIz84uK!@`RH^vTke(vJ|2i>T`@E2-ai|$gwix9&ET1#-z6I%1>?^ zlkj!ln}1DG+Bkc_s{uke{mU!NMdLD+YNCUlihj3JTDqvbt1mUhYv&%Nc~=-?*sd=( z%2zR#dq6ZdZ0KBBBsZmEKkJGC{U4)&uPRlQgg5P>M!I~{;YUJ~YRzs+CJ?)-Z#I^Q z1c@rU>g%Sxx0_5y#eNf?|DM_UB_La;Ps)?cQRhx#>y+7$CVyqt5dAksYBqGoUHo$y zcL^Gqe0~y5!IGf!8XA`qac8k^Mm-j*qp>$iFJ5i##I{vxyDQcH4 zB{XGqF&mN?<+Dw_Ej2jdDpET2Sc(CTs=l z&8Q>S&D&v2KiFLS|3MndFa?Eud3%jW9Ua*IyN-i{1HO60=YjbPQYkIDXNQsQOrGm=rL8 zwU9L{5*RClsj3T_ISlIDA9is`&jM~VO04`X==pOls=3HeL!-ubuY)U}>Zabk?GUQu zW68kx?~$e*b%5v7$cm~Kk#F9#z?3Bc3pO~hUv2XON)4;MC%(#&0dC_A3 zg=k%)EB)E#;;L+nUj=(<%LLZ~i=o~$Zxowr+RL@jqgEG&x$I3FK~8>)AfpJfhx5G% zgaU5iroAw3NKw?m$-`4Ws3_7@Nm^)zIKaq^do`DIbjA}@V7Yo0UzA-U2A-sNV10E~Icn47c~g%38r|}SA3e_?pth%o{x-7gPW!XJ^3!izof|S&3v9=s>>~%uN0_h7 zaOMMm10WO;Ay5dc6@I^h++3zPGwGqKlb2`j%}A4n8V)9ayqXzFw0P=l;x5!IN^;5s zDif+$uQY%Y4T*@{9zL{-AiyB5X4h}JkG)hprC{cNyKVUXts^IH*2rYhR^ZyS$#RXO zqWA63f4x}qq9(vNlc;=19uNK76QXqzZ5HJ1Wl208ZZH>c=^ZC03glD~_=4WZZ2eg_ z&G%`4PRtky(BfrJ?lK4_g*$Ly7swdssYiB&due>($Bul)IWzA+ctGr3y@1yj=h@+s z^V&2z4#`JvH;x~1hyp->^EkS39+r4)S>M$Vp4DfW5SlNJY~hrWwgNyebvw~B072*6Vv=EB7X*$|tt`-q#?=@kcqpN)JSxi^B_eedm*M$8#0+Z?FAmF)Ht@tB6;o zXp~EIMTc~dLvw_Q0{9_#pia)a5=lt7SE`(_%6Cf+de_CxDjIR`b|7&c%m<)Di_!G0 z*Q-N~2Oqbpt6%!Tz#vPI92dqG%icgSp7^-*Bs+?KV~b#Sm`-|^rj?OnPnhH}34KBk z?L6bj>`6LXsw~-KRKew&UsERXrqOY~q-|7LYbvc3IXC2!Sg7&83@6K)_iWB!BI!nu)zu+?H8=xT3hA7JJ{td^9%%dRuUec<+h0XSUIieIsTDpI$ZW(-YQL@RR=Ue_fz%3N zVck8q(&FOY3I{%WZ}@E#1(GhLo;wZLIi|*YvD;v?aw)%-Drn{|zTb6rBTjVcjomx& za&9Of(s8CE`D9o{Y|oY9CDMtWKtCKc{mGLbXcN4=ykLGh8E+ob0A8Vj+~12u4am}I z>q9Gn$-5{;eb1KOYf^heucoG-#>!%B*DjofbY~)KfA{J?-xXDtB#Kk|47MMNJvqqG z;4Url8t7}TEF3s+=8>Q+ft_sSj=e6JogyD|nE5-m<$Ad))7E}f{FpEuDyx0`s22>K7RmG?$zB|lz5+!4wVEYE=z zD-n^AwPt>=AU-r`91-SsxYc#J+=;RN?dKqmn?HZP$K%J-uYW+03o6oiD?FH3D1{yG$tg8i zJ|Pwx)~?6mF(~R(|t5a8qoDB zN{Dd|=lb>0QBi3bxOM{sZ-{u1{eOH&?i0#md7O|r#Hba%x3gOsl={`iwquo!%Hw2x z#JNC~iw@lI7$NmCNkVAapx^r8XOCF`B@6OladMnqxew!5v*O5!LTf)iS$NLQ_ETLM zl%gGd&hO5am-c5AW>DUme5F>;A3F!DC(Rz?pF)(&y+Q$b$ri@-wuBwYy(5uO1zcep zc(bNk7yBLysPbuyPcgX$e*6RV{jVqt%O%7Oq0am2ZEp@r&>DxVb-Z}?woGGWg#`}{b!Dc6@E4;-DhimSB0AeYKd+5P^*L*;vzBaip#LuspX z*Xo3sXxe7$DcjV6`B%ejz)hxk4JGow{#%CunJiCNE7y#vL4-6M{+$6|JSH_V5ikDq zC#_lok_4(kgJ^e%6S#$b@XiqbaE*3gup=5Dn)e(49S8+1KqZqX-s&yzTlM)N zAtxg*0j0*^jel_SVm~-a^3YIUuN;2iFk*e><-Y;RihcL4>J3I@Y^-rwEru{zXIb&? zDo_bk8r$^U2ac8yl~@w+EIS3*jN`h0L7_>v#D7=F+46V`M2~{3M*uT;+%^uI`cA4T z*+u(6ALFu`?^N%p^$Y?Bx*2x#4KX^;tQr}e8<^w>1>97c(?W^K)TEJ4={DyFYX5{; zK62|ynP4bQ*KgbyjOhjfwwM?tGn%w3jMR$TE(UzVchGiAKiK|#Tn!8lg3vvIw(>9! zM?LcJDE{`1y%-sD%Mk|?C2cUQcerZPrkBAei>`t|y9<}*OK2*kH3*NmlB)vF3roRi z^OSe3;Nr!L2iu>)Luv^xJi>v2DPOsLp&dA^om2BCxi%>!C7ldACiI4YU=ENNAZ_S< zODeSd<8+%zC_4QVaq;ppm$G3sus#&cl^cbU)qkXzr}EkymE`n(GYIuL~i;f2LP%6SFg$Pikx z5bXf~N<2^cIam7OhN)QpcEk*0ru4s6d?cwHnpp*Zi;b~#Z3;GGFI~D6+ks|Hzm8o( zs79&eVHJeE6XAO4J$mq<{qe0~T|`&a0T>3N$&PLs{++$K_$e?#m45$z6+5)}Pt*&E zWTIUIT>K4oIyk3YUDt^$$mg>(%6hbQe=!q4yz1U;j-*KyAR|-9(Klm8TB@r>AlC-k z8Yo-z4GeHg;kbW~?d*jKK(PULEkrqnB%JQ6_%W&?l1n?Mp%NyY+%cv1tyZG~U}{(d z7v30fGS7~)fjd&>vA}1@!E0)o%M(cQLvy-mF<43x{CFR;93Kd#^q$2W}t`oU>CmlOZJ zHpl89hh7~h5TklmM_UqQnr7%drMaR^7>C0<9QPaF@zf!;5aSt(ErD#}2dwIE-Pih1 zUVN!EA;gXTF5Y#>q;x0bhd~MTW))V(cQy!9KCbV_U%4VkRGn*4s6juscT<`Qb~Kz- zx>xk)gKA@Z$Ti(E)jtvV$=1p^QP)x=WIya1-@vTBEZ<>QUhL6%18t1eyWhw9WBn&V zFOH=Ezb+kzUl;yPf1KO0KkAc&7U>$(xW-`Qj{cZO6qn3R%NSV7oR9wBF4beE@JYyj zYI6>+@c)9!Q>)CtWPD=~-M~eq`LSxK6T?4@E$sWp0OSLM`edH(Q7jz64cIHJLjetw zm1J0bsI}H_mR>C!reP|Ow4Xk0S#RksFZx}~aoqDXg2`QyjBr_;eS=oPe8ptHK%DVNoQpy*C;NdMMJ$ik4?SUzK`ec-#4zc_z)>IcuAd)AO()L3MYB_@RRYdC9#%FT1$x#SEC$B>+X zn~Ec>0#>PQ3Z9O6NO$o1{dX%bO`bD5rQZTkG1qU2ehf<;0<@1>^6` z@SCnAH3qo<{C@+um);IAVbP5&dd3@6V~ehw#FqASgSoz~prKEYZtE3glz@ zp_04{eP0tOI0Uf3n*8}Ne|goLd%I4L7H7&-{pp^5-C9e!LUX7~*t2T@B>MQjq4dFH zQ2MmbU%uor;N!tWS8ybNyi}v4g_41B|=MMJ--XvK28+6QqhXgsZL%gI}G`IGS%gSDreNsT%RLJGSPEvSSP-QKZh0=qiEX)*mODY!|oP4(o-#|?Obd3kxY zE?t^8b2{`byxiQ*kSn^ugN0W?8avLfa%*6bNs;sus6s=rZL3fMd)Z-UA`%+KElzsM ziNNAwe|&u%t8kFfYlto`HQ#$znpmXIcY&5)%Z4uO?U}|>`HDM5XIg#io2@SUYDwdO zDRTIBHe)g%eLQ}l1@#V8Ebt(q1@7%^cJsp4GB_)sjDhTRD4axyC-%2hPzmDqUP*7q z9pZc6@^9P>$vwTx3R}taps-4Zn$Cq#PaixOFKpIbP<<>nxQC#kt6ns%Xl>Xq{OHpO-m1h{;Fs5 zB`n)0@7i0N@AeuFMKO(ZTrLOoo-U`2x!Ec>dpT`s{1||}$gHsUnM+8)Fisqcl#juz zl1{}=iER1MwN;4RBP-YN2Ct*thUNB>;YJpMBv#T>6ayQDNbAhh#fCNnv ziuqudK|_=AAxFOaf&cU1R)xA|Z_-OW*@)L>Zh=s84l=kP3tEJO*B!IU?~Gn6N)Gu$ zVXasFLQC_SfE&z@E_vfT3cepn^2pqz+P_ZxPs+353yT@@=qhk_MuVIk6%`*TwuKb~ zWCwi@nh(rG74k8Kzxi3xi^#ioZZgD{ML%A&DTa*dWO(QTgf%yOb zrPr7M>L+I}wG!E8wD&Ylz0s}-2@-ea7Mp+kBuBjlV`K$>!bNrI@&g9iBDZg&d=$yT z$x5G&+;bTPEWF-1WW6SW%9!>mTPVFirA9PBxdQqeW#yO6b|&`Wcb>9wkGwJjH8M(Q z%`o*Xc^)-?*@vwC zmoHz2hlej-v?z%m9)s#P_m<^RBhKXYe9?TsRe5m?o+s)- zSYnF<+Wv;i!-u67oux3E1UZm_R> z9BQEDkYI$k{Zu2+|8?Schz4z^pLV6o16Fp^)?*AmXY>BemoJAJ^)~43Ym|h>%qGmG z;LxE%xt%n&D~7vi>o=>$+b|oTs?^)j+S(dcenj`1;w{(*&npYiCDMJNS1X`1Ia?u# z=`$=?0-YHUl$&U0=3k;(Fn4|#VY$o?gUQz#^i2X+hEg&5&hj(IcjF93)Ykn*FfK*% zuJ`GdT_8o^`3mYX+E6S%5X{~#Mx(ZGYy}^8&;*@d*f_zk0PubM$Zr|G=-cpM-?~dV zO@0lZ7E#t?{m~A6tT3zf+-`7Q89TM|PP;TM9Cr9y8b&v15`$+UwyYv-()`ejd+k8sNZ-4f&M zhGr*@hBfG^9uKHsE%R#SF(! zP}IbU;0QtngRxm?m!n8khP*N-G!vS36&MsD`fv@zp zT00PC#*;I^NH3^v<+{JR*gP;Jh?XU?)bN?Pn&vm(k<=@g^cEL(}`3+Ze_kGm9p`~L9G zEHht)9G#QVe7-RjIzDFcKEBU$YDkUZ{k>is<-G>V)6F2D#hOaqjIfy^g z7~tfcK19m1e1?P(0mz>557G+4T;jR6F79r-Gm7zFe6O0Ojhnk-{*!QNSxm~vTnJOa z0fY^b{q?W!Q^`p7pY2{pKe_t1AAa;G)lFlRe8Iz-m-ZfF67Pl`#YBQ_%g3alx+8K@ z8yn*+Lke)0X4Mk1hF$Zk(zlMn<__<4oskN{eV@{cr%#{y`uT>=1b;Z*r>c}LA>I)G&$dYmHukcXWvnLT4>DDb&nHx>2vc+fgei~|aMcsrUl7jhgt0=asW=*eR>Dm44nNwWjs-XRtkL{Qob+6o zX2`B`xMBXsqnxlM4y*X?K&)4^jj{R}^WB;tQN9*df^K^t_w7p#hLnMt4)J}XoM zVXN#UU#&b0IRKGD6&o4({Y3*46H`HNu=3p`dUz9@wTYw&Bxs>Qi8Os3|yz(l)R(ThW(9cnT3w6Y}#Nw;qrCH59`&vM=-Dp&;eZC-ULY zc%fI*-%9;-uyp-+^tS_zotvK3st=oIq68tx1#@-^8COL`C+q#Y{(2(W`bl9IsyURg zfI$cn<=^>y!m9Bme?46MROG*@=`|Vtfl$}S$43Hz1n@3xS!DO0e^XN4{C;1UOYylx znykN_ut(B4(pTm*JqTK8#iQd8PD$A*f4B`@0WZ#E;)$ctk?%UaF-?B>5fn}p36x(n zRe_*wa&42FN4xB^@89n5eGew@jn&VX=r`TpTYf+titTcjM|S2Wqn*KXQ}t=q3UPnl zXx|Mfn*6J7WiR{J3;)~ZKy7op`K3QiW%E@smo2&;>u+wDm^)oax|}eI zon%w>9_5VIo*u@sQ}6z_NXj#YrLVQpOxtrh8Fr{j-xJozSHXqhKJ{l$*l&@bI~%2I zJ+^Fh=F1wTvk{!8ME~R|_1E=j6qm;sSXSEh8V5);s2;96`6@i{4xWFS6Zost_=HXG zNLKAu$NE`C_AoT}6nzm&Xxo|d`%!$JpFH!(_1(EyDSCh0)s#D{R88LSH^zHebv$|e zn8{dr+7Wo0i90m&>#cIjH|KIy56Y1wL~>X!_1?DH(bu*#+w~!bPtisv-d=sKAhcbK zgKZa^yYI4hwea0Xh6;rGOyqg$OD zD*|rj7|X9vcKs%#et~`Blg636p6H?wpnv6WrP$W53tiV#FTwS+ur8a5hBj4T!zLy% zTF@;c2jVovbkWQW+(Cc;i@+@gyDN9ik?xA``}Bt{M^V|hr6zqz{@JOn1e|1idu4k4 z;K2>VHBGVk%Qa;?qI6t!Vw=@L{7RfzOf(L7^oR#LS_do)@ZhIMG!Yzz{^x<$j2SZ! zVVVYwI!<(~`~}s_(VML31kE=Zh70G-d>9I(ah(}Hr8W70J_7?KRn}TKs4gu9N zG-?;%mI||ingInD=yfmDl)%pQ62p#~n3zENAdC#9jK=8l({gfpkjTYw$NxwzRioEJ z4iXm0)X-*lixuyX!?Hg~_OOk1&+KH(7?74f+^)B1jkGbwP_1)QbNcBTCdDdx!KuzrK@k0 z9F)T1N3<;u8xwCctoP$xyB&Er(MOR^UmudNsaW2^q8|d-MzOk5HTfUw@IIotFOr$E z;)rUPK;;VIB{nG{3Jo_T%wW>iXszE!Yvg_}?C6*4!PNH1Mfh3dA7mS&y4(K=RD86l!q$X-^F zjW4B6@@9JJltI!AqaR^M#JjbRePK=du7AJ%8FPdeprkggkkD;e7jz%>>;AC^)BE6^ z8ya&r^^tC<7=O_^6x$qJ%(~DjT|#7^E6eFW4vBoa3$;yZ=(W|g3$0m4PJZjJ6pAJ3 zCq+KCp0t1E{*rRx6RxKCj7Cn}heOwU&2aIzrWw%NeP}8vrnJRU-oMprVQp>va?L%B zN&XPZOUeA(Q&)seg#GJ8riH4*y3L{c%?2?x$&}RPubnE$+%|4_4858DU`mY zork!Wt>=EbWa@VF?OE5}SU#Vk_pzP3-Tut#`t|oG1*0!|%&AKR(MJtdxBtQj1`6>! z>*Rt#xj)2OLl=x@Gc<`)fDvdolFudH`>)E$IpvGbZ%0WJj@>Ldu5Rv7J41xi5;($S zg?HA5qL19OXK8R=|DKccz{^Y9Vx`Wg(kVRaQnJ6OFSU`x1(+j#VZ(+7 zF&yg1V~g%Q!$pmGCFY+=8(K%Zu-pZpHnmINeJ>+6@M>nWn=#l>^ZExrDDgcfYdA#r zEBCRKA@thPl8j*Bq+i&F#uM^>3xs{Z|JO>pqB5nHzD8`aii#N4sX*-xI5t%#+AEnK z`g(8&82&lUW@c|SYB-TyNqX&GVze|+Q&ZC&)+lHgA&9^#hlb|nX5{pA!eWN?fhMcEw-~Mh;TfY&}Y$0LRQZ0-A}QyMXNaKa8o4)N1GK{?&}vdFA3eq z#RX5VDFQ2#BU8!3H=I`7bf-0DJ|N7ZqpRx%eqzhg>~LJHEFSvRjM@aiH0I%%dAn=7 zC?->24e1#mXF*ff!#4nD99&Ugr@K$5jx!`o80KUV_1@R9O<_8p zLfI_yH{0tL!jSRW%ldIbOb^Npx;ajcqtv6826c(_6ohqLXmeFRjXUuCQ1FilxK zhyWE5t_t0&Y(r3js-mJXsX^&w&138uU3oziIK=z>l|)#0@UBlQ%)hWDiM}nCLYK8l zojdfAv-4RZ2;2^DJ@u@-XIN3wf-4f7L9E){3hh9l50cC8?h7vEjP}YQKUh&44c=^v;9X@=wOV0vrUT?U6iBS}*f_WWwU@wkU64Pn6|E#SMZGQ-4 z57?JiDO;8L;dg(tI!ge-;=sdplyPPFNKlX?nyncD%!iLpmH?9i&VJeo_|Gi(BOsGv zQ=8n)w7S}#h1(?5!uF~jSG6_%#+uzb>PiG9|#w5GuCpR zrx1JOKz%?sN$im;ir^+^lFsSig@Sq+SKIshz(Ha3G8IOOjmV9_$}(iGsd*l^Lc4;T z@RS9M7Sxv~ZFkHW-}97yV$9iGx#ExH#TyBBd@S@1t$fN+UwZDEC9=Gaq&@?7lI++e zgXh=H1BHG76MpD%rP3t%VuiwCetJp$vGF}|Z25i!!o$Rw8A`9;WcWa*f6kG0zwB?w z+C~uwh%=MV6jkoNSWz44c|b#9#SH(hoVL_&(J7g8^$*YcN-t3ko`Ha-2QZ+)d8*~J zTlD?~M4Y5mFRP*g5e_hC&R|DxJ@Z{lF@sSlS?XMog|4IN*A3Ef-HJKI|UWVAyN4b87nbEfqejKL8Q4OZGs7-PP%7gcD) zhtE$W7O|W@y{Q86C63eswMl=4Zjw|xHSphaDQpGU<_|{cM-N{VQ>hReTm94 z0FCB1&-xy5LXZ2#lou4gT!=TL4a6GrbdecyhLC=JzWKW8$yaY(c1?MUOdLr#6>)m5KjePT!y}e3 zaY02vkSliH1MP=Pf5mMQoQ^~W$Pq41ahb;QgakMgkXj%n76+k4aBxP^VU`z9p0L5@ zUmNJt)Z851qmJY6$3w-_kA(Hq5OaBdtu(0qs+X?9vB?7MalWC@Um=nY8g$zXqy_Mu zcyk5^opFg?3X(^Vd7$2y_x0;$sN#?%q&FAwDq&Uwphm@(z(bG1w2K7`YmM$b<-fCi zb)oIzVt1mv5ebhQ?POnLxFxNAUNh!Un_^h5+FleA-}9ntdkHoIV_S<>;g&)rC}0qZ z`1uLpXFnNvkmSHx1js_I1~xYcNwBCn&eyjB43)k@-_dBJ#kvwgQUa$fyVYN+3g!hX z3xV2!byD@6ArVReQNHiF+g-8ARo6laJVE5Zt5W?-tg|Es0$b=?p_3DXAzN0o^=70p z9zD1g-rfXtbjU|sg@g(*3Mg3@vx@Tq-EGJHf)z=Ezod3QJ;J%jS^Gc(8|a)PS%2(r z&`K5$_Q50M&PP2&nkWhVIswpth^8*CI96ZA(z=I8X&~60Bd<6pEAm)nnDip8usN%JESS8|LzivVlkG=s{ z?=H1ZSAJhR&mp6A&XIF80zyOcWd6{J=_d`>=DufoJNE9;N{dnsN)F{-*;>AOkOQhM zSVPv~-(JQe2;kz9wk7snBa{=}w#)5zjZwv~N?%ztctBAJf#sL9H1q;RNcL_p6p{L} z{N1YV3+I@rD9J^u1LpeZh>0GDMrDYx;Z(tzzpo+|6(!k!s%4ryeD$CPEnJ$iqKAZp zNI2_LqrA-NU6Qq<-C3Hi-Z^|{mw8)#Qxh5X+)XO)&a=L-5K8%lrVUykBwbVo6yy7T z)fN?EE=CAKYv_0KFl+*be%a6@c0f@>`C1aQEOYbEb10QUokdQGvl^`!G z4ld(8aRne$U-ut|RXdpO?!#w;N8^0p@MlwQy?zxj=p;7+R0({8b}MeGx>%!$hqHz+ zu3g^J(h`ZW5D1>v7}avHdc{1HVeuI3Er1YSK+{4}63SLh>b!Ksb;?2 zujg-mEDgqJ(Nq!xnepK1z8qbL@9-=%q3dXUX5rhE;kqK-BW!hld;g8hd~*a6bMuky z_o)4~SD|Y!;)JKjG;xQRiXV%ItIGH>_w&NW z#aDIXw}yuCH(F*>%B;w}Xi(&iKbU^#LzWO0g8oN$bBBa>#lLhtr7yifBj~`ixhdoR z)~pX|grBkR%Fk%lhQ7MaSO2*5h5remvLB0lc=sw>#{Z>fG5e!J@#n73R=KhE#VU?w zD}Xg`mBHh(%^5;<%m*l-W6fW>%8Dz~o)eMRGiMHB5!NBEJOTmqBy8|~Trb|QCmzI+_E+JDO|PRYI{x06QqXGb}p6}21Bh%Q)PGy)5DPq+3E z@LY0Ra?O+lbbM^000FpNIos&8gCKvQ%)dKMhK5t{hu=+2(F_c;PZ)&bQ0a3h}w zvNYSVSegmFm$Lf)yOLg|dY%I-?#*N|*^?TJ8^xmkHHesY?E|sRXVP}@*LFj@4Rj_C zo`;J#DjYi&dA0tynJj>1NrKDOz=>t8P+`I@C1W6x@Lo zA}{awWuK?`G}utYGVfNvP+!2stmNSvdN$DpN%yD}$eezckWgM%4(SNNV>oh%A7~iW z!l=+aK?h^Wl5%X$px$F`YxlD^Z@!ip@Vwv4Y%N4x#5?%jjpfn2_h>xSzX^W2kBG%| zTqPo@+_$mFcYl+@H~>Uhcf|%6O~HI)?8I+-x9hXz(%t|~Y;*8t=A>d9--(#+va(H? zClnM6dK;q^j~?~harK~%J*(y(Ck|$LDtAFz-MTOPVsp$A?`I1>%oq)QO@;tJs`wLSq!?@rhe{)pmW!D0n!vO22aU5MhDE6?b%I zT;Y-S4-6C`(ik{65^+gH_cL45j=m%)Cr9i0=CIu-u=1gHHVhNS4UAR>ntSF^p^1o2+bmX$x+G#nnID{PRMvzs z2iRMK2R3ZTp)M&XMbV`%DlI;>=im7N3KN1{H+JdxjBInuSxkOl+aCz zPCzz?Sw`FjrkwHRwFb~w{wpO8M=Ow0_V)A4RER9=wnBNB5s;qo@H-!Kt5C2_|Av{Z zq32M2^8&EzU&r(+Ql=()ETu@m9~(KeYw$`{PevyH|?ZE!L))n=<7gAt+#o(^^?kyUa^W z`F+IJ{--ww=6V-jn|;jIR-;d-u@$=EX>WV~9;>dhZ>yfI)?(44I2Ip#+~oeK<+008 z_veGlvnPFL*$wj=LSBT(IX`(#H#;etK+qp1ELze+b3(JZnh zwwI3j*eko&I!FmjNhbY|9LE2LYU$1vJJ#IHP36x;m+9=CSVL{^4q^ywT}NH*k9yDd zepWijQCN@WD3DvYXR=}oDOUF~?X2Ya3>7{+fpeUa-P&+^Nr{n1T6%Qf8jO@${| zWgk~Eva@>K6|zox^Zx3W<0tw9CgfmYy1i3Q^vQOl$%qQe*2Qn1K0S*K**HRQkPstv zLPCNSob-q*1=5Ysg=fc%YU}HXutXTfQ?LkerCQF8OstN85`F)jxr{d)$ zncI#&zFTnWonb99oq6iNtCHK?kf`t=Hu?Q?_oPf_K`o>k4zUww&LmFwd0z6? zi>tBcYm8SXW+8Y#fW|ZO*;Rr+xktasPdRYgh3F@C(`g{^NV{h9m_N|5ue?fEl9< z=x-fU#BY4krqijh|4gnUP~FmIOJks_z8lF)wX-=o?M&qO?YycBXJudOu69$$*Z%ZA zqU)S%iA>cTuVND_pHiNNeLRpBC|Sh*TK&!a#+|~8eA3BuMYtmAX=#}Z#84P%HKbY) zvX}W^zBhCN4}R3zwE~5K4mnyi$HOV$4pB!WQ~2_)k#APKxE1r2H^? z?amTnUwJd}Co82Zr9wA5iZt-8g2M^*cI4bvjR`AU4xqXw=fUiwelyZt6LgHS5nfz&t z5;r-9Mu`#0&5?@()Jf*)PBlqrWkFV`_agD)ctUZUsAHwn7-Hm#h~lBPX@^{~ifFyxL#!=`XAu~4oeFXAia5(Ad54?2F5g!#u=5ak_k<=jGIoSK6-iM3(EK%IKbCjo;u_EV^wj=M(tE`k)y!*1|)BB-8Tc%b&k|=^<*O zkL{yaNFA$@wYsqR_?0l)!!!L#T>RLQ%w8-4OhDQqoU#zMupbP|+33elyCOViN38PKBpPqix*!;#`1Ke5 zlzX=CGkH94!J@QuCJjw1Qe>$5e>S^{{QHZf3hny-w5G7$PCR?r00mxiNCw337@ICp+OYL5u{NFwVYLxKKtA#?tLm=>Y%@7U~)xC?FC_{ zZBir=7palKPm8hgA#WdU(jPIJbEhFI7qb;Kp z*8mC`fT9+0l4-1&5^pL_F?>T1Y`>zB+?3AR|NJ#~c!XY(MeLUNddO*48cToN%&*8| zKFrz>&x&}&MBU+j>aVp_ZA)AgEygCU5HHrGYpjt)id;9#-Nr|E!!QU2;o_hMrK>8v z_}M;Jfq>HWWP#f}-*f%CS^y4R*)w!7%{3%Yr{x__UhPw|G76B$B{5J8@eufKGC zsk~Z=RDA@uf)VRfsM?jCY8?RbPWFB(Hc{6Nkl;+0kKL-#v|8cC4;@13STba|v6dw? z!F=jj1pQ`gUVU9fwSN<_LCu;C6VJi2dU{R>p~tvwn6?(>WhmU`XewUO)+y9FZJ*s3 zTn^pYwl>f`n&Y3mK276Bil$XHrOc&+>mfJWnu-u%PH;d|5TE0)NE)4QS5WwF4Hdo# z56JN9jY(&haWhVr?*#Q;C)Bl+duQg{R-NL%bvHLYQ-uj z$sMB^EEy~b#J(821C7ou)A zDszzog-S^B4hsuIns~9fBmey`A(utfMO}5T4EbtC`fBIpK{uR%6FJ4@N zzlY?FU$ECtGY}O!fRaidO!BpjSYPw{Wz$#jUy~@32HGw*J$*4ljhH=OUPkF(TENCa9~~X7e0xb!jTyg`nXFnejyvJu zw@dXX?BBZ8(AyqWB5V#O)K*z?G$@r6+rw_*EBIpOs>b7rRw zxdm%?iG3r*d+dw}tq0M!81J<7} zL*h4(i?ad44RV1dSz?{oP=_>lOGq!WME`tQEGxREw{%lV;}4ym_7{Z}*S|Uy6ZYy= zL*ElDM-Z{8`eDQveLeVe+{sg^L^j+lx|->Y=g?4%@^10zQyUX|6UOLMDJ9*Hdyk38 zteSU=-Pty7e*+)bJi8?t$!mU&#`*_Q`=g5_E+WrHUD^Co+=fUQhfVh@5@cprjeha!nNLAMAabSQc4if_CGkH1&w(DvTNsH&G&#BdR9r@ zmI@DZDip}*;kb&$gG44gZ0MzlTv6b}xY@7BPm^L_6I`Ip?xzACaG|A<@%AF}F1rBh z4J(}_Hi?w((`=45AE65O$Ac<7pI}iyIvjl?wvkn#sO7Z`OG%HAVa2Idr&=S%l-O5? zQ*OQODCWacJc+gJW8*12^_&-8mm0|!#9Z2MCCnm@y@_%A&Ev7?jbIfSXAV`gd2cO3 zjblCbiR-5>3TGtN3lih=eub-Y)pR$b0X2Ec^d|SmR3D zXlMx8Dk&voOIbx|7)7BegtAAavPxyvWu_!oA&S%1AgfSGHrZv9Jcy*GxD#>UveEzmDTQl6baFHsn4ldji1>8z|KS_bJ( zX#7vWxG{j4H8FshZKTt3_^JAFF`LtHcgF4a8X;2rZ?96*YK0){64Jg=6BGE`gD0`Mg6%kMg=(8Qr$5eXm_Ty|Wrdv=df%mPtis z8U+Q5oY9zxil$zZq;F^j-t_ z7T>qexOd}^0abN%D|os={~*B-H%G z$^C{53cnH8|3M5$T7YveMdh(3%-GU#!IfYq6L{@0QnOD^#cx!QrmFu*I=S;{T z)>28`Y^e4f;v7v5*w)x;;$V>W+b>dRX_uJOGi5tRH0+%*JME1ikq0Wt;U>=@!q?KD zGpDAVv?rfHibc+%sz$~~fn{GhHmH9%*N@~4C4k5@tB07XHYAO@l|6U_WdY8KQ<7?y z{^y64Oo!vP)!HzNYOflyLz*IqX3aaZRWA>(r#Naz&s>M7&Ls$tBCT#lx zc3zB`mFjo2x<&fZ?@U6}nolypHSPJLL+*)k7y*REw&N8wu&qU3^sB(p)P8orr`gA&Z)omHXLAdVud^cwKZALCon9f&UQuibbj{JyMQa#+< z`Tnpic}3Ng42!Rkk(-;JY{g9#&I^*@J5)<_n+SiqF|DwvFGU8WrE`-Nl@HF1r-rQFdPzo`Dvjj4aRwZ8_mqv)_6u5?g5V9n)>haZ>nd|EWq!e!UkX#BIa zRs0mgd4szLO4pyTUOxj>jza@ne~G;tT5t2GP!N?9y3CZK#bXl~`plf(=5qJ6f9D3$ z%FC|=?VrX|LFD0HzTesI&JWq$+9?2fJ=HT#Uf-_$&PKfBrQexCVVYx%0rDsn?ddaT zzK^Pfb{!EsFb#J{*ioM5lMG6m^>Xgz4YqU7ZZSHrRJA^#ruo~q?7Td2sKS(=Gx#ZJ zz=xMh8cPifFQE$GAmr`tx8nAlCydu~%!GxMDhdjSxYf?HabsDkT~Z2RGg}bQ9+YMU zAfUT^=(;y*KIsZhf`?LP^P76ViO(>r$T?u~dxpQ*V&jK-=bulSMrE5S2BkSU5)*TL z>rAs83S>V4f$%42ZjtRYA%iG~s*CA)T5;#H^YfXv>Rv5oT(!y)3j3T&{&(-)OVH2u zz!(y~)zG|!Mxll=otR#x`wB6mIY6l^0MJYTXB5#+za2mz(4QT9g(SEwzSbR7ldFXh;|A3b=7!bH5ME9Kfl|3)PCLD^tNu(T0#GLiI zoWqUw1>P6QwbRw^DL6gRqaUOFooV0d-^jd)N^}w{gk3Q?ziNTh**oX4A1#$MfExn3 zo6C{V!NcGftiMD`ciuT_kRp*V*wdcmUQ7_o2<;)i8I=_-0HF1QOJcyK#|H{ZL@)mD zAu4UVp3kyORu6wN7Gk#}a7OaE~j{Qo2`U!@8nJWv`amUwl2e?=CR! z$sNc_0qA^sdudV84l&-`&VORh1Tg{h>NKwTiz*w_2n&*nf6c1Cc|HUALWX$W6)S)>l#Tv02r# zHbN?e0&(TgsIePWny>0MzPl#Cdl=djMX=T^0v#R_4N+~KX;0h&^8JPN45x7xyGyIP z9|F>O9?e|?M+@m|Z;lVFB+V*EgTB*PH_>*$)ZRYz91U+<*7dP({xiLI@(G16A%)&h zVi0G$WL?6@k*a|_i@Fp!9EJUF1yjF1zF(YhGpAKyH+RdO)k^6tbDrzCWsFTicp9Wy zJWpwrT0G5OX4YuXcDPmOB7`5<9#X&qdP4aU>Qs|KBl13$L21jb6taHB`PJ#q-}zp2 zO#Q)mUkQ`u=4j)c&S;+hd-gx?*i6~Ko+WWlD|&D77+N0YT52@nDQL2}`R<|G>Q{~s z;ee_YD6A4nesz|gOJ4*vgE<(lNWY9@`R_9Vw4lZ%4MS_+BQJ!gST?5!$aFIcf>vr9lbd_nq+ZB zFV1Rj9lvMpS0`W7$eUn3Nh(JIC!&TVb|EKA&nu@kp$Dw7KeRaP-W@i?OTf5~ONw3a*X5`=w^b&s3Z9 z)>viX+&j)V{Z(2Zv|q=5oF#b^ty3gDZ7ChTI!8}2+}wjcOo@r3%$vc?Lnxs1Op|xc zAhl7Xh_fBDsyftpr+VU-RD%)a09$ZOPl3t=4S-#w3X*L=o8ANt^7YjPHOq^v#&MGO zy;;U?WKFfQSkHlzCr=77+PB7jNoP3$T<-66gJ~{Q!r-S@r{`TWcKDP!7zfX9f4^r_ zLoDTVPFPo``82zBtf~kB(02w>EApO8g|zM6f>iwx24q=>;h}^3^b{!U)&5%4)=Qn< z6P`27d#-Fcj@hKOV=gR&Izla(R`g2CXrK_kEz#^A8hI=TDQ&eqZL(j`frLa zatbl9akt0O86EZ%W5hy!vjH7fH8w?{LxOS+{NY#nwS7GLMrJQafA}%4z)M3=SzxL7 zao3y!!HeHBTz8qKL&eHNB$*4TYMKVWNR0GpqNja10TT*Fgn89 za!EAL5ZLdj$6j55UNiU@>u!nls-s$IVUSgagH#ix1+3>Ut@`)P788+2b}wS0CnuM1 zlg`dNF9brx<}tN`^eru7m+855oF`keQGiz2HG>(ORN!7bcGGJfzEfd}VX%e|X#NuV zo4z{sCDFTZiZ_w<3kc7m2u+S5BBa6o2Mt=H$#d^ipjC|vmp&Ol!1`JBqwd{1g|sh< zG!(=@IH?j=NFg6iL)q)1#DQ@L1ttp4z)%FIXEW4HmrugOls=^EL;G;$f=oP#{$3fA zA(2}8t^1+x&{|Mhx~99~!2}1Q_*E)-pLb|WOD3bH&zhf|(E3MS$m9u&O3+vS{CV;A zJqpVyNgkI{PK{S+$cM*Z)FETiqyDhW%n8kr^XSsy&lyLfq}I5*Lzz6HN7wRiAn7fI zZGfodvzQqEjT@hUu-RUj5(BBGAW2X$a)s_zT}WV$+&POD)lR>^~6>(oV5pg}d;J^^=6N z4jnw0GCJ7YyN+NU5irXIUP1%Xw!d`-kn!ofqrXDH>+qDhc*cMp9HsFbwqOLTk{HiO zBcU~`dmef+KhRKYXHMyHXlfSWKlA((2&wEKu#{~2+CV^p6gsc^vzMi5iHW5_UUPGYpdg4e*?IUv1R67j*W1@ui$;bf_|Mlz_0o7c z+>*h1GTf5CnW0rfMCQQJj?Xw2A;PX4tM_3#f0ONw+*HseCSLWN#Iw9=V6a+!)# zA)L>iN_CH`H!;bkqT1D&K_f{U&Z8KLY5xM^Z>njk(k>B$79#chw?dwn(GjKb9sW+) z(O?FsTaiR#S|sLOIey&bH=!vM_6PzaTf$#>mV0sZwd`co{Y?~#{b&DyUGPL#9?xPO z)9%p8o*)$R-_Nl6r?Wew541-f7Gl@P8|8S|n6IPTI$cDH#lvG0!Uz>hLO+>65mV8k zQ%3brXf%z;RH)`IqX^_sQL&>TPL(|w3CTVi(olGfa zGWscKMH|R_@p(<%qR#2!SNNU?4V~9$iQ@x-m6%*3BeLNl(}?V;BvK*~hoQ@QPIGG{ zwg?)WPTjKi_h)0G)6(meUG~7dLcF$ud?2fca&RwWPR*} zmoJ_(R5Kp^?05kEPf#M%apTxu`3fqBR*)vxB2>rVheqWQ z-fcnd9ik38w?<9j#F&WSUZ{{rb`dK+LHZ%y<5FS04MkJwKjQfquM;M_So1V2@uXgI1_=5%ATVdak@Hks>g>+{OpbrUa#3N2f5 z)8!J&(vQWv%^vte3%p>J*%^=zrpzm8{JA^qdQGOu#SP~NvU@&T+~2b1z5M@F7* zDiTjK`|hunR#o|GSo8pKH7xT^E6#GJ4zeFS1AM;p?5a;UK=(KZufj}7HDO%zrJZ>8 zs*euOrEc%U)UU_GijU7?Aqlh7UJQn#^8{( zBbXiyl^Da>|FlRg=QtEHx{MBlAvz+q&sA(F$~1Aa@eea9OT0I~Xk!iIY6MYrl2L$| zDg+b(ez%d~;0xP4dYfYJr(ZVrK#3!_!c&;CMXY~BIWU}e$=@IOJzxVX_7sV{& z(Id{)?X9hL(BMc#ks4sbLYh3nIY7uB*e`S< zc3odg=ZB^*l8Sr)HRa{+NjRidwWpo`Dy^xhiPWoyo-P3b7Q5M-)-A-qHvH*R%zYmg z!@Y6CYP3TA#B9uA{kYDeyfb~7GC?x^d#Eg-p{uukmei*eg)d2LwNlEuX{$A&=4zQ5 zZFn*#YAx%{IW#`h$$1(l`akl8pHQWTm$r82=*Y19I7K&N&I32kCbAFdL*wF5F zSy4*q0k2np`SupH=HTs}R+R?g0m|8IOAPAe<+HQx!A-CN+BKq<+@V|cNB}BYnHX9^ zz`CwM4TUWP&2u^>YYw7OAZj=GqWo!iIM3$IAe$n={DMsZlT!98i&k!W7IZ%`aS+qc zoqS|YV(Nw2Qg=A}d5yW6Ka)-GWIcWB@p9dN*lMP4l+%=E`BiShVib81 zBlYZAPws;oZPQe+UfAgD*_c2FljhpuT3Dx%A(|A`BOzo4U0IRRmF6`E(U~RffH5aw z6x|PEEJlTiMP!~*Hd*&%LThA{>pq|?b&BO}9T+n%8KM1zRt_fGb;Yf%@d4$$N2UI< z6ZulE%>Rt{gmPZ>^EE#bjm|8#?ueBte+Z4Xy7*<$_x%0O{o&d}Dt&77-q4!hEW65m zPfLfBhbKHNOx8U^^4!j)=g*baGTZkbhejK6YhjDFa>#FP6u)ixnUEsc2NnHeo_FtZ z*W$6oAXvAcpr8XvP&vV6>q={V0QSJxMF57PfmTfg_xX*|42K?-WmmY< z<4BPIt5cs_6Qw>inV)}s(?~TZI~&~)XE9UcjpgNdrb21e*3rqvjj=867%tm?f+mDJ zn*O`&8Y<@m9va*T+4Q5 zSN-lC=;vA0n4lfmzkkcQ;%5L|xGJiDxky~0h2r7;M#h<~>_&omtMw06MB4DGuc=td zRJU*5?8`XFq1=;DQd;_VU|?lM1+U;Ns{0p%gdKiF@cQx{F>OP3wW3jZ9ThD#6I5-` z=*(Z8@ul|t(@6DbT~Mh^H_aQ57gtS<`bOWgF^#-W4sGlk&P$-7y42)mt>ro%4j#nl zFZdTg7GwD(0@2?h=7R5=?fZj$bq8Fb5L6sJMNS-eS%n=?aAoSA$3NpID*leTKLw-#+!GellUtFtzBpu@dW+4U9m8@K%7u1|YLn-$mp40Pk? z*M9z#{ZY^E?DWQ!Yc!g-S9uLPfA+C`V*7xDEBPfu3dg2yq`DqiX?)>;aq zX$&k^kSX2Qx4y4^ob4hmj^#>=Sa@GrXGOp*(e8mrhgfEU@Hr6^ydRUs$_>k+go+`- z#Y0AVujlk;KA@u7?-)lAmX1A+3iM1g%y~Ka??T$PHQq{%y+-`qRvCgjO#InO+Y`5( zdF^!~vO0NuyJmguW+9e!+ChQWjEC^09G@2TELDKp?MTNbIw~38u)+O-iK_+O{hmfe!LgJi zizW%$LoIZ~=j?PH%;&N_w~Cdh+ukTPgew&sGvaLMF)i1JAb8@%=9W8PbpVZ4%Em<3 zUAX$%v^A@LD-mS74@`O)qBuP6`xG4EYYf_1VDmReCZ$WB%dtc#1eyr*Mh{O8pZ0pS4O9LJ=|Ta6ftb zXhCyg=+>rJU*_3)LiEmEgw8KJJG<9e_Nv%tx_N&{G>`kjHf1y{$swKs` z=bzYb-2?RJ=$9=!k&K8F9j&q$syTtSUGl3hB+arZk3lmMnznEv%G!o+ec zi$?6n?Nxg*mjDxozsYO3HK7(h5j%`6sDcuQjtVTPA)IKh>L;iplEHLA(R1dL|2+6VaT zU-#75(4luwGv)5XhovEl$vgctVtNLHU*W*nulud;t&Ri#*EAy~USX0=ni+E;N=BRR z_kuvGqI_%n{C4|o#@`;5g|+p2N>qs^-_7%)j!u8Q=R1;ysRt&KpoLWPVweK0 zB|2X%dBYhn>LixOM%*E)PWQOwm()FIlnt3Wxr6M+ny!y{I=p$o`}WR zOKTV=qJO$%VVUYgTvm9*@O{M&)iC;LaYzBLnFzgJd-)1Co_mO{#mvR`|E+%~61j`$LDk?av8D0FbOyaivKr$E2QXwz6{E@W=%EHGDll9+k`cYBN-hRPrxrbUfb(_FNg zAcMauNuBt zyiwabC`bo*Yt~+~(blTb2xlT5%Gmcj(Xj(zB4wnPknhw!awPf8Cr+z6NSUW|ydCRi zZCbjLkB%-OS1GVSm!>p|71H1?$8>dT0xT*A7#unokODqqX8d88SfS!JeGC^-9@@atJ<4_j5e*p3rs#l9%~QTsIO9P zTEFs^$J|m?XU1t8o_;{>JczUqU+>1!V5Mb={vrbE*lnvOS{#E)|D1jiis=}e&krr* z6ppt9IYqus2fGEsQqyK4Umi6 zviR&f7tuk<%FS{S|0r;w2htrn9GRW_bw)nxxO74loYO+Mn9uN5aTKfbi(yEEd~0bL zF3Gp&>=j4Tsn^jlbsx!QkxATP9Xo;hAwm94wJdXcrw(k?j_ zH(0(vvqr;3iF|XOdYXr%56apS z7*vdzpwf3;2apUxe08ZaPOVEEEpc9sc3O{H8K}fD`!=_Ek{V*!?_R~G) za2$ZIRm`H6`)@#YD_vctZnKp-NV4V2uhTHw=kk)AjTw;@9@^gzj4iMG@E3=X!&}>$ znrLgm0~(sOawxQwZs=eOfpP7H4^Q?!tMdhL)=Bb!k9kg;lu zo|JLE6w^2E95g*z$8Na_$$~C~+bF@*O|~2|Ij9BlKo}4?OyC19_3zeN*m3(=nQGo? ziOkJIkMbLr={@eC^JzTjc&*tio`(hsU&+O+%C}m)+`hn`KqX^FFQU3NdPDm7#3QP1b~S*QI+n>X<^F@*dp3|Mr&ZSBwV6+S=Mz za`lAV%@nX)HET2BSCA2jDer18nwG4vtI}&N^Df^ZS|)Npf1*2M-&H3+G|Sy_ou5=Y zZ+c02;5JkLBD&SKZ|4POvRYiT*ZfW+>y%9FgQH%-2@C0NEvI~P^!O+D3^qq`#XH0m zLKudAP~t@s9`vqVZik+#Z|z`Lg+DYJHMU^)K#kS)C>Ue>nY3_g&czw_QtP>ZC$u3D%#758#|9;bx&8CXW3PJo*K z;8z+4?%MmtrX8!z&Xr~#jp4>j6xfwzeiB0^`7JiBnNE}TFWa@iv6?@om=a4M%C=!_ zQ~K7*89Ya8TtcF%vHRva5p(didQ(70&`P#6!D_0iegx)C>)^rq{uI+9`K!dx8`nTc zK?0RGicGz|G&Z+176##v5GpfD;uRAEjq4i_-qEcW9XjyLZs+BFZ{KkFa7mn@Uwpon zQ3A)tjf(b0q_S6_0O8@>@wI&v61Lp|?apOL=k1Vwo!IhY-%@cs_kAJ>iiOhUTBfEk zLbpbC)ljEyN#C7xq;j{f4P_(DpJSssXm9b6Zfe|?k#C{IZQ^_0h+AIs_T^hO+ilOd zF79ibArRf?y(UD6+VY;@e^9D=viD)+x8WHg+>20JCq$1Ip?gm%Y2_3L777H+a1va_ z#}U3h_`kqxv2cT(4l`dKk$S+jfrEjaHq$7j=a8O_>X!?nZ4cR|owgphWmM{QSNug` z+%PsMr804fTVB2X1!bDt5X}3R<$Ow3b4i%kmq-wa7=FzC*Uio;j`OPVaA6nOm4opQ zGIB^5oB;^0r)NlPpGiv1g5P>IA?&?|gtG*3jVI1&DVSvIR{FqE6~9(QDxEY9>e`&q z$$DcnAx+^d$0Iuv3 z^f3Bfw_r!naX^jV=j5#6*UthfQrBVLcXSML-@Sthr>>Hc5=>53Fr<@?ni%8qu03Xu z9_!Fey}a~P-V7ishz#UyT&T3yrfu8I@Wi6Lg1vT8w+xCQ_`)F2G=Km8H#}59veQ4J zZw!?bFcIw291sT~(&*8NnS_s*7sy@TF?hjDXF-KeVu`0n9_J2tzF7_aIJ33#*JLLW z&57@y59_R$6O_*|e{M?@edBT|EWUK%qWfe0$El02TQz1p$GhS+P)BOuBLNR(^9+At zV-LEcDJ}vR4&jw0%`GinwUR8y1gyKdx(K!$(Y?4|rYdODb=fc+lCfHT7{H|2W+~e0 z5#o|31UK;TWPgC`3ej628s${!Vx_8=oCTp*sdS^vNMLEZml=5(_KGlot z5sW}nrFE?R=4^xKgyuePeFN?$@bS4>S-1TF`M{EXS@*&Cn3LR6e1*SZA>5C=!2J!Q zQEweaehb$4rU%?wE7%6$y1co$Ic5Tj4;?t*+@8?;23ig7oND0J6%`cl)^jeutW2@N=08y$VRk@6Ll~OVY9r~3c9BByX|U=! zl%LBqhLNp?ey!o|bc-?wcor{BAZ+#Qf<=fATE&Xzk?3uXX)*Gy2+{WwGzPmfIlmFD z22|d@i+g)|&>VmfE+-e4^4%@RfyU==D-!1>t#t6US33!eMWI{ae)1x*QrHqdBoXz{r2CgqsgZ%aloWD@Jh$ArfQ~S-@R_xs#4Q|*Bp)RvucvGRd)YY<8l8yh*z8fWuwxz`#mt(Ybu7I)>2eY%;WXm4|-8S}(<)@ZK^{8I@tqZM!v(JQZchXV_y?ShNF>cn@@ysl# z&v>}uO$e)2+n}xJef_n;J^2}6Yj;$iqCQef5nq^G4tD08Z=$8)* zHe)&x9i7H|!p<Gonx=0uAN=vV6PgTkX9!J(GSxw!O@S9E(g(8$b%~I zg;{{xRqfRDVLlP?$IEESE;`AH2zyRz!;R+{7Q#%uj15EJB}^2@K-$?FN32ol3-q8doXM~bd<+s6hc*`~D~T`6o^n++zARwgH`Zq*t;*-;o{=LFaB&7J2l=5xd-ODl&Cj{ zXi>!OkU(U`Fdv34rj+hG|6NZ{v$`z0=DP+X`<@U;0iuN@xoZ6Q==}PBB(SEjwK`$G z{Qlmy%(3P(?L5kJcctzBWMHjw-*)odvvsBs7^M$Njd;k6@GMATmcPicoCO>*~5;Lu$ zhL@hk5fF}W&mt) zHp2YSr{!KxT2NKDNTax)<-P#HKW5QaRw6h~kD{ymi6|O>Lbdibb==AKr$@dG2Gb~K zuwQ&Ty}kD{nE%DQpHGkoALwhl44>-|i&_8e>t!0{8Pd<6xxdt+!i{sEZ0q^_v2inH0uE z%1wSH$}~f6glqlh4k)d1g!N{b&qtE}$&mj$L34p2Px^Y8mm0#n^pDY!fCK#3m7w4u zq*2DpQB2#3e1A$HB_0IWf>0{h#q5u+3Petx#-BA{AYO2qYx-&}e^#n$pC*CM!r;7$ zj~_wJ1)^odY1csl8}Thx*9}peN?u0l!0$yOQH2rU63Q{nv-#f009*U>wm3ZO7_p^3 z!PN5p{Z~vXyJjwnf0p-NYyOT4g~ZFPSOvk}-0=!!LG#w_K96M}!TM$u2z$kX%3KRkZ-Zo-4 zQ$uf8@I0$;!6c|555aobIqRrJ1;;$yZB4?IC@gJKhOX0-!%=Z<+r}pEN8LF(-)l1J z-Fi$HqCNk27{{-i>})XX-*>Z97`ok%^7F=p!eb&(+)mR7`Xb927{KoWwb=lGQMZ-6 z9-SD40y6mov;?%>Ak9<$Nm;@$<;YTYk}tJDmLhdZE3~Ge4DLAsG4L*cz^KI<5sfpB zhbaYkIB8fkcMr_Ie2GyJUwLjToxqn6}FK9~NCtf{{S-2Lc*M)+z)r8JCo1=G*7zA8+;h;{76jgEjj23iqlfa;f<;Q=Ssj9EobP|Bh& zZe*C4BKlZLduNkj-;W=n^cSRRNr+?F^|yyF4#9cdFP4K&2S|=@}gG(Iii!MLr2sZ3_J*DWR}o= z!ET>GB9f+(C8xrnREUa#fJ0Hrb;<#cu_kL%ah0JRa9-_P;_)dR`K8*%tNJ^ZPitTP zI)U5e=D)SN9WJWQcl35**5E(ey!)%)8Y>OX!Mwgv(7uB-$@fP-r!vIka)+TU0gesS`}D}=2#JVsOYWn0DEjp zUTY=!)jBmRQM5lLEyjyqD#1d#hT#vSxDHEnz$F=-f6LNU#({XhHNt9OL^|HA`@wtf z-v7G_^w+KYLoof>vG`LFJhg53=PQ{={Ko?9D(CTK!4mxpuvf$I#6Nwy*IiLj@yU}X zQB`9yDB8gJ5eLqE$EtQ^MaAc-CZuUFH%q0dkA+FLj7Pxw2%$E@XbM4-iHDch5Tf_8 ztco)S&1uQ0Np=l}Hq*Mga{B+-)onsXjovk|Kku@#)CqW+>n1qI?AW-`AnW#e-RDtJ z#t_T~_=1Or2Q2iyp^>gQ}lwL1gS{2`wUOD|g^rL@vSJpHUBkr?jP3P|R z8LK+cL&Un}5OY1*n`=(A#z6NBGA)Yk>kI>4C71JODT0fzZs!F#_l%LMXT0_1(C$bG zi;L@pteUBssXzOqX73+Ae(JtPJA4dLmPxl<16m1_aAdZy76ZzApS=8?P6TaDK9aW0 z`;mGnj>T;sd&^cjUR@g-TAugZfjkN$@|&x+vPwue04?l(SRUjFS$D>0*>u59BD}K4 z{5hqVeQYa+6w)Bc6oiGgj5mm9j<_px#v-o6w}yt3!s3=4!95#>Jogdf+45G=k7#(1 z49k4(+*vQUu1`lrWqqn+85@LDpavW*OFWD(9VvzL*w_~WUs-_0Liwr}pD1__3g-k+(EYVZ}8%QwO z36o8*m@6B*!6-$_s5oz3AM=dht{UY@ZDRu0({!D3xi#Jo?yWF^dFWknyF^Aesj*Sk zWlJ*xnI6uOc8~O9IIv2%54Dyg^YpIl4!I9Tiaaa(dR&=Q8Kew1uH9i{C0qdQn?XlGOp3r&$C!^6!nn&ctAO2E&3=x*6}1*G*?yNA0wh5bu! zj=&OauF*fGz;6CVka_fBQ@+HSgGOSayQd`*aR=glc4Jp`8<9-XY~~)BGx53iwr$+D6!17hzKlv zt3|$GP;{dxX!J^0C{@Pf-$VxEHS7>5IrmpXYwXvvVysoydN7rAymum7H7K_P@lL`X6xFJX0k*2GCStRr3GUZElClY<@7#*&P!#D1Rr!9(ZGT;> zf_rXqX>u(4|5*Spq?I?n&dT)<7qeVv{X@iWkjjNOV@4AhnQAXVJi&_rmVUX57?*xN zq;x=8{B}+5g7MneCIm_duz^HLHV8yNXnSudhmXR|tTy?A7DCCq{>EcyHKQEa^8=98>Vp;{ynoS!v zbPd?zz|&5%Pa&pN(+l0VyL*45?4{WbbioY~GGtI`IRxTSqW33Cv8%7IPbenovg|k^ zdU}ZG)mzsn&Dag_-%EF*8)$;D@3(zW_574FTEsZ% zRaHHNP61^jy6Ln>#=6bw&m@n?B{OuoKV1adq2xPTv{5TtT#sLEGuKFZ??oBLKPBnm zm-)AC6ErMEJqX`OT`><_tlE(yw(!OSqzM}OyqYiM@ZBJj`}xC%uYE(lyjnZXd}Lou zxKM(a2iPwJqJ$y+RS@BylcQkLk!@d74HlTj+g(3yF`8KMN-Z88j3R5keIwNR#eq-P z7?^kroQ32M9{g-bsyL*rZ4VT10#Ab<@l|{`X{c&yS|?V)tMla1tj6vXzb#Vk940&H zUdUE+rXP?^pPqqFN(2zd!y_V~{diSOU0uDnv~)uz@Wkr`&U!JrwOlke&oKe6-I@d0 z5-=zpwP?EInh{)wUKN>1t{;?jkMHS%Xsae4P3;REa*A^bM$&imOz25w^Y+EWA8$6< zV6wYen$pDza#uZ8q^|c3q^aP_RNcE76t2R4|2l2h)+`3ie z?K-hFHe4{k0*CIU$z8|VW6?aBMqJFRXa>8Y95jXF!~1H?=`&9ju)8hNAb%{dzmGA4 z_k!9wb$W+Wbqx)rZ0DEFKr$pMnn^7N!A&KTf;wi-J9qAk6bT8^D&yeei%Lk)aEH(L zszmSFUUhIQR9R?m*McY7%#JDYojLO95e6>ZvOI%06Q1DJ76wV6Zz%cg zct4mNhC2fqgBJ5_0XVgCDEegegDU|Q)rP(y!g)RNbO2!l`hD5a=CsEbBcN0FZg6q9 zpO~RcrCXUiqo0)LCZpoPb3C|V$2t#ZK;)xN9YmSu;@o42!@TjrYhEd+p@2Ps@&F4@ z5I^;wMMXs+eSG$8i?Fa2gp}a);%!2Ta~tu^iO_h?d0`{rSV0&!JPBH*PS%YK4?k0r z?o#85lOZxHill_hVViCLc^i>403#uXt1*R~$kUOkR~3b4*t)dKm1>F?|E+K01i|OM z3JR{4e1tRCv*T~{MQ3QXCqQ7~^(RURcqH|FcETL0nl+RI^Oc`Hd8i6$y}IAzL?SkJ z5f#4bcyfuB(!|dJ@9KJ z9Y25P$Oj4I{r@7fm~9*}r9ySDTCX|82^d_* z4EpD9j`!O48Y#N)4Un1BClZT`tIEh1M6P3~cSB61QlP$sTypO_x%5I=UDRl@9x;n9 zecbBMtlKe0PMet7^C$4uFMjoAQjyFsMr`xY?0VIz6C08`xTdQbIPc-+D~I{WLm3A8 zWsApH+rLmdQ--_@QZZbyANsNmF9}a~5pf+I{!!!*!z?*Y5E>hrcc~m?xk3TcOsk5= z`ZK;uc!p|KmQG;Ty(S(d0vt8gzKN|nOfy>0iM~Mk)}OuUEozk1gVo^+6V=4Lmk6Cd zt7ziqOTtsnh(VusduIfP&oIoLaZvrgZxj4H#HTFdB!KqxH25Uvj8{VBx>TD0F~HZX z-dB^R6mY-kw?Vfv^F#_qMWw7#H31Bx37s_D{)W;ezc)4Byz=}Uet`=zkQ z#>AQUREv~1#`uMOz1VCPqp3yWwA&n}>U^%fJmLA{$B1%JlC~Zh?BOr|@`Z$)0mNi5 zvQg|0jo7bdAeU(9=tw}v5|ccaWnAbNXZHgR1~T$p5OX0UY2EX^z>p{UFyl7R-AzNEe0!ZryPyW03DGAktqW4Lv6G?qfE4d3t_=mQtoi|Ez=MH^k`s zqz|l)mR6aLo|_IlxRzy(rYHP$!D4}mz|%}O6iG1J#kmYZo`7*>GRzCIkLK<w15VHI~1SOqoQ#kk%jix0Ha4Wu_p(dh8}4 z!357K1_+z&anF!1&s+WDEx=e2nc?XxA{N2gOt=(gmBPX$OfEL-|GA-lpL?Q20nT_T z3}k&w%Jib1ziEP9R{p^_#^M^`TCTpB(McF2_h@`l`ZYq8r+6&TqfL|*&2BG=Zg{M0 zjkfa!wnN+se>iQzJ}!A~2R0W0-O|_3dOyZycW-uYYAlk5`H_Kn+0u+UHH2GtCccLI$D^Xyl!cn6+uyPzQT?8_i5 z`1tzf#%nFYO&zcA8St?ye!PV*!a2J*ulSIQ&-xQCppG})gl3oqvzL)G?ZG`y_j~;s zXX4RKP5rINo`JK4gkgP&gH$Ej@2nqv7Fz4!vM6Cy0cbkl>(O-IvYKY@Y`{JZ4Gm47 z$Hoeb)e~Aqvz@p7Is@j6jP|AFXJ<$6n2h_v_>NWs+#6;)gIsX4{6&JtOBECtFBxIJ zt_$KNY%ty2+#H5$nMEw-DEI^C^~unaY*IrbLC#FcvvU^zbw*Hg%&iq|YKSxv@ay-}6bUwOR$2Z!{NzR%m#NTX-K<*I2O zxlgmi*3vcPcVru-r#*$xy!CkdUb`M|p%0i&?{=94PMJNK`|KgHC6`(d!L`;DfQ(1; z-maFlNd{0$a8y?7?5BvTq0b}agNwyiY1=mV2JPs&xq0W58Td55QD%B2^Qd6S?b5An z+2pVq@?iWkT?&W(9lgAaik93fNJ~4nZlz)36LT%jpszd zj#M02UVeLY(!n`Gf!9!9566_!vx+^vy{R3q7h!B|QE}0CmGi!4(Vw7b=VJ5%2nUD! z#f$xA>RgOjF?R?d>t^?0j)oNZy!kA{h6{fGQI+IhDcUEqC`)km%E6DMh8hbfW?;8;lDeM!~0Vc<)C# z{eg8ggG31^Pfs2^c+=j;2LV_p`SQa-=OYRyRNVofe}m0%=u}utjB2`a@3Kxp*KlY4 zyL;P|gT#3Sw@s<*-RsP0q+FKyczef^NN#J96eYsT1mWQpM0*7@@*j3R3ue%a(U(7S z=G!-W2s7Tj*o{(^c0R&T9_N)_XTRT(eZQw*SrOdVu3f{J($u!MrG&7pi|)^koIr{_ zn1mG52nZnB+U3@Ta*XGP`%Q#b!rUYfh$To0tKTtbBbNUw%EmhN^1Eer^RUwLMEy-F zdXM%08HP=E{mZFQ;#lrl^E2e0EfLy*Y={zc%WHJi+%skYGv=*#{C5a#4P$1kr28n9bn5|$PU(%Q zfcZB+4}GK*kf@(cpSg%YLQHF~V{;@_+|}FGbS-_KW)Lq&<+Y0)gzBK)+r`2@gv&OK z0u3*KETmgY=DdE?{*XV8_vcu6>e-dBB6P+Q+WgjtZwX51x2}^1-poHBDT6K(0_l)# zDp)v>Aj}Rf6PsIauIK(fn1SA+=iRn^O!krcCij*ayt%%_vs03V(0(W?;uM7V4Ek0{ zNlEo5Pft0JPS^FNk?o+_7@IJ82CkJPJq~sv&XY^0QhqOn?YbwOI2}EIjM~ObltP^7 zH&VT$LjWqvyguvr{{U80->bOt^0TRp>1j-o5bheq9bxG~u9-=k5<|>_^v3^s-0wf< zMqlR^nE=x@PVt$VEKi6#5Q*3ZcN=@>75@@T#kk{kyR?3OumM|w zY9$cU#3E1$nhZZ(IcZfGEj1TjT8I;#_%i{NB7QFTFp2H%E?0dfoZGdwj`h%t_3(HV z7)-nSjLid+FJ9Ns*|0Y1vu{kD|Mhuj2)DbZoBD@;gO$x&Sx+_?I+tHdF1xP9DX`S) zJW>}oZRPKpS�v{p-TsPT@8*-fqAB>`A@Xj;#IJLGOk-^3JHV%4Z1vWC#^ZNtr0& zmEE<=Df0n6(`_B{_EswaF`hslSJtAn1Rv?QN`X@2%cxY61RmCX0Y2jFKzo`{{}+P{ zb#wQS?Ue!pB@*Qdwzw?{Cs+S#h9f4ooIryZMhTuv>Kh^&K(9!g4xb+@*mgLUg>hSI zZg9_-jB2}J0t&mhU5sfhQn@X9I|IU_CJ=wPNs^0--l*N&#Z-Rx$NYfsj!79&Q$6NW zBMoWE$yVtPEIJ5s|lUJ`eVXcXJnFmE zKir+upbRr4BUg{sgYLO<#fkyjRG1A%>7;4TL+RzirxS>#p@#>*!>BC=^R+dyxBds% zc}16$n!5xG#;p>(cAx+uhnAb4udJXz-#DH@MORi<5-HT8`i6!`&Dfm8{S-nPqarBC zD+Ph|V=z~tMbq<7CmXI9!ea2y0?!I-t$vFE6W!nNeI}g`G65$?_j}l$9CIR3)VgE`xiD{SvQ(>HM-IL$bnuzyLUd(mh_d-?bTw)GY3-O^ydKo1@>o zIi^ME-OYgPmXd8YBdI|>U$;GP7!@_(6rmBZ8G|>|q-AfO*1NdO5!k*x>&(bycnT1@LSdn3 zTr||xHQ5X|?&LYYk{pB0KQI59#eNc39@c!Gd?2WLfXP zo9$vtu}WSqI!PfRHCF`Ilz88UT2-8JmAG&Uyf&0`R6V|?w^Z^97?sB}Xj@7tDI#A3 zG1w_J)8YNzg)Ql6oBon~a885hw@P;31f%Usy(6_K4qtE|%w?u&hr>raiLbiWJnuEihJ$qn`;lKL|6qV=BrwA1@ zT>LK6bLU#C3$&yMJF-wg07;RFfS(texbb5lYeEYcxAHLr|N8u*6Zu4 zVm`%hy2@l(eHlhVgt5pjr=@nV!n)~p|5Cp%A^BmsIoVO6B<6sn+9S@c`Zf2lCFTvM zuWRHAiI{EXqa5(Kvb)$=;iECHLZZD@*O!qfc@B-Ok%bqqve`K~XRA_2`cnH+pd(pZ z!?Km@uRv!94Oc#mtBtQk9((aBUK2cVmQ^Q`Zl?sD;Y;Om6KsJE1*ZmT--`$?{riCJ zx0}1=bvCB{xb9K3YdA{&DdX*G%}nGV{1TcUgD#fb=` zhny>9%dNLcYb;#Ao$Tluvt0VJZNi==?5|l8CYb{+r=LjbTV?n>Y*7OLOVv>&*1OMB>niDCa0ExtW9IzAqsWnb?4y6!-ZPv~nuet|oDU*-n{ zIq#_EY7@fuUeWaYNNj6Fm9nMnfUlAEC#wsc93$a-GZvAwZ%p35m*%v-SIgi{i+lR{ z=7C4Iw{R=YnuYI%oqcJ}Kts5P`J4V0Gxm8uec`CitAr^XVAfFAw0MydPc?t-uYaL> z%r**7=N*d!7qthg#%)?r=@srWZEY@XqNbI1xkKy%kKO;a3t|{0**W%bE3Th~=UK%^ zn)~*&?{~YNik0WzZUg0`=yFu$B(X|fp|lI@as{NZ#g_EDh~z2DX#J%xd`V7$SPhVu zUzz=RDJdUCkMo7ja6I0##-bHU7Gy;14Vgk=6?8;YG?C7 zkL1mfR|m{<{|Ui=>pjQ9R4A1 zW3Fxi=E(NF&x5j=j)R;|BieE4%^{zbLo1zD;WZ#nff)tr67}L$EJL6JcR~-v4hFDr zrG5p9b!zxS~DO>;=| zA8mSYEAhfN*03at_Q;ggvU;SLP4nP70Lk3}G=3V_RGsq<2;imBAHSJkPjQ9;J08og zfLlk1x(V|tsc%#SNTg{f)?&`;&1ty1U`yJ;2|hN^thezPPmZM~liax{0d?Sg$9i56 zvdU#qZ!2%x3xUQZCwR6(i#%lEfSm%Rn7Z0p{e)ft_48Ao zEwt+Pc#^RyHuVn;Kfc*!Giqn#9}(e#mr`c)RQmv#I1J5;!m`0HyBk%vN2nj6gBhOp z!GD0sIr?oAnV&TWW*?x0OK3WPprL3sgZ!+#2Iv_@Ta_Qui-95zzMU?@PD>+8t26y{ zGicm`VsXtl6gy0@M3(&ir1$);=)(|b&>soRw%dMwGH!c3&2s}Nj411iBb1g=h1=xYN8NQ-B;^gKzAo_1VCN@2#v+yaRHjwZ2>6p zs9J_DjHM|2MY~1~TyF=!P-&{^+l$}Qc`4O$aP*qR*ZIg`!Xw?LjL6BfeAB2?WWzOP+4!wXI{(n2B~i&nv$mn3 z9cUDGKkQw+xhF6N$Gq!;)iD`Lpxr z$2Y?vT|B%N9PzpmAshMqd#NKW z@v!G)-7X1bJ$mpGW%`GI@yqnDv6bDgHCHG^V$$l4%gXPcqiW#8gNJ`Pxb!hDOF`3h z*9dD&1KWZbiCJ;OvQ7zXN17KGbZ_0UqbtV2i-TPEfyiDfI5Z{-u z_VDXQf!_%xU;@=QEPY?_-7Pdjz5c=!eHY_yYcQ8ET_fGbsZ2+~>p0({h&cxa0;IkL zMD!{=ppvg$KxIwhCB%O7H&5|uK&=5&d%Mn89Hhzi0}dh~`Zm*CrqI}AB!-3G_)}iy zm)7;8^I=U^>M2EfoI@Eb#^4#{RIp7ju|;&Dp+a-AoDNQk?apkH6aabXC?!s6xd z%lCgu(Kvsb#SjKkT+`5CGTK-g&xTGpC{-znVV9u)0y|@mN=f4dU18uBSnmUsLE3XS z?oTy*{0Ob!k|0I^+Vv}K^u-bCA3i&a+PpuN7i3}r1XmV`5z564cF>pY1pjD~t(hjVWEM8t zTdamE46ptXJ?W`m%+jj&FFc69^l>L{sI@@a$EIuM2=oGS1C#>!AKg!(jEd*iXnHQSp<7x+p9qvS4R<^Fbo((jDw@8tzns(km7GE^UKk6PM*cH^m8e?t>7!EBa@V}=1IP@Wt@FzB?{4p>fY$jBl1fw zXA2HnzDe^)4E}JL@q;4WNC^Xzk> z(eED`h6>n@Kwm24;6RqV?MdA(f(ypq?C;uZ({8N{-z_ESF1D)tDQs4t&kv*3A^Xn+gSr9p)9 ze#{TW>-ynyZNH7q^>cFbrP#ptD>W_01jVB-{S034UR*b8?=R`VBaNb%+Lhs?NBTT6 zW_1CC!MFvmrpj`4?IChp^>-E%yid$>*orl0zF;@qG|`(e67-ArcBm&2{#`mkKL>JE zTZ^v`$TV3nK}HD=VcPVEL*h43(Ls6f0(?BZghXl+J)wC}k)?~7p zP`1gT;pmq`qe4V|KfptR6A(ZXkWhS&a^d-x{eWb?EiU}w711{&1poMdDDiXYgy1~l zyq%*y(9qV`*KO`WJ_eLfimV*7A9HtIxU!St`zMXH6W8p`G7OHJ;>`e+AVtM?9b4#U z-#K#H1rS6K96A0qnfpdxt#jU8O^Rh%GUZFx8M%yGRAEX?(xp@>j^h>UjN_ zO;yTk%9Y@H4#9U|*qx}o+?D#`1)rr36jd}eyPyDJ3nP)!kUKbteB*Ei{r514RpC`h z2?9l1etj=AqC(mvxY^$U+nA=s=YsFHJ)#9@`xnSqe|IcL*~bckyNi$hBBP-2Hkr?O zK`vqw0fiq>or6AhxhPSWxA%8-F&IKCAF$OEw@Ee^H+M~IS-Vw!&y#~MOPV1_^$r7J?_giKpVp0g?ltQ7!XJZzXF?;8%Y$=|A zaSz5`c2R~(;2_i<&f$;g-PaKm_k9M$gs8pouP?j5NB2MaU0Z5F@rzO~1n(w~IZq@d zzckO2aSq#CY*jSd`bPCW1H|P10y4tFL?z`r-Ou? z$(U1U`Q4md1#fq9=f4Y<5bo(fxy5vX$S(xfDZrLs(Ovga?aWNsFW~Wy*KS3*j6)^` zW77n6i=ic;?Y~Cm2ZmckwlW;tNl*-k|C6BjAABo29i2GKL8!MOT$WpbpmewyI91Tk zUR_U_xyDb-}Tsm!?R1k*|}(&0C-)v@uJ65kNt9UQAp6&GF7)Q;HdP}NQ<~(Il1~MS_t{8;LMt>c9apoAUK@xcX)E35wjYkjm|Cpx#F?}#D zt_Gk%UVN5m2Q&->>%KleEbtfexAMpDG8l$L5i281na1nGx(VlpPub?|sv!D)C4M@h zd$l3@=Or%AkL^9jd`-vNgO{M;XY>C{EvrjX-2S`b+2=bH&v*%le}3DJ*T65;QHif6 zAODZL-{2Meeu4TMK7xN-mHM4={h@C3pKRQ3svbW+&L2u;|DUrAncjI0S1F72J~Z5l z7Wkz&!dTYPYkiLH8}`TNq<&W`I$CVJp~1YcsBT3)4IK~cEYn}VI6w$9O{2&S)PK zEFO#$ur&nwC!Ve0UE!*qpnavD(WE^oB3t` *y? z=?jo?7{k5t8;b%bmlp+1SHL=~?F2doh~tA(1Gz`D*;koDgvGZsnmF*%+OIA?f<}?b zoHaE$Z<<1%VM&c6EOoCBZ2SH!^o$G}NBw&Enc_XiFB#eQFh)5mZ>9=6ugw9H&|jVo zce%o**pzimH#qBH7xahnLe{AEGG5p|&vgeEz+Jka94s;XmcY2Dx$=@P&Zye6ZAiHb|uuHsu2`$4HGGMxOo1i_?$ z5t7KRDobCTc_kns0t=eH-pIUo#q^>4`RkNGz>)p)b zD1NRP8EcSG#%ge!+gcPxjM`|1zsPg_KF6@Hq2K4x=-)B@zhw>uBtUEHE2ly?sNWJ;ukyL5%}Azz{~-z7YyOv)BYK*Xt9=EXz!Crrzq4 zP-M+5IbF<>Tf#jme#MSZrs&)qyQT@c^GzB;$kMM3vHA0rJtQ!4C+z4Buv#hz7mVb2S0 zOAr``A80mKD_bB-lhLzR3a(I|9vf(4$5=4cSedwGXX)FYEFbTrhAE2en-)yP&t=>K za;8d*HEL&imGQIZGh&Bx{!URGi7clkZ20#a_z=l7#KizhZ$p*@h3)-<$a1#>pFkp9#my?Z(GeEU<|{G z^n{mH!QdBxf6uB@#kc@98)|AmjgO0u*8w^_NUQ~8?JDOGnXc0k!9x$JtAL=ORzTdd zF0)Rsn?6)RXzlsiIGcDnA}<^`yP2FEpy6}lU8~X@c_1*$`>}w|FpK>A|GyM&qz9d{@O!j4EO%u{I zKz#XgOOXsn7BSx*<)#wg#c6v5rf>B)ntsx=@OE}!@N5_OP67iyQFJaP&dk<=#dJP* zxMt5SwE)4YVmw1Rh6J)n99_RF!9^jRTBv>56scAG?I66w%4!n(n8@$Hdg%K~8a^3i zY3=@FFan^f2GTjcnw@rIS#>CO!4U0xkHmPGbV@EUeP6}`QWpIyU^vt&@BMR|EEV9f z(x{e4kzb@FH`cvSM;M4(h%0R-_cYNE@M>I&4`W1H^InJ<{EziB{TwqiZq0s>MkCmH z%SAcM{KQgJN*D2Jf3Na=<^P<663_8v)huOSU5O#rCBYRE1SbhaKY+Z^WLV#sH%48$yB!_0sSxAFGN<XFe_V5mV+`wobwvBCh0$SIg-^A>24 zeV_^fN{(o|Zgc2?8=c}94X+=rBV#Q$sBsp^LfbF~Grs;aL0ZYY$@#E1HWI`F^JtBK5VX1G~hhOcdcur-&3dCbJnwy&GSenSM)xvRz^NcMi zIYSgaVsV=dI94SuylUoS`1ljV z;mf2piJk`pyq^6Z+*q-0*K{8fotVOvTkEo}^hZF!v;m22N8X+8YF(dPKPWW|Ia%26 zWm1}uwL8SubCLnGI*S6fP^XE#8!*jB>}5UU26B5L%Lnz#Y$(E+R^=8`6-RVGfs2D3 zSuvv@JNCM(WzZ`H3~N+)A52%j_UNQ%GZfslp^`Y9x^qIm-iV!=xyK4nGb=a1NysM2OLni9~kMUG;RCD?cnhR=p3lof~j6CxF`weS23p#4yuhF0!~0rX520KP>3qmsQC* zXopdwCv;`b*K;@5hi{qtBH2t+fs1^& za-Mbs-ONWQJ3wUa!qeE>Qx=waVAti}lDAsyVdh*x&<@3(^RJ)+vdTkD7P8j1ytU@ zrx$Z!@*|{Eq0iq-Xt7d)QQ2_n(*2*C6SntKCWGrD84BlxeposC9!L{i86SY9FA7&c zL?c{qtBS~4F&(I78Zo3yTz044?vQeS!prKUsC6$iveNS1fPFv6j!vr$z6gnXzcJpj zH?`uJ3ViB-MGvsLfE~-nP?U>XJ^b&tKOdIy_Z_9Dx4H88I08H3j5Vz)j3K4Wf?lDM z_|+lsl-(R*amA6L{`5va!Iyk=pE1d9FD1cAB3SC}7k&Smo=&jLQ^kbKug)c2au0lR z;HQp{ogK}=$stR2M%!{~cc}YCGQn6@#BXod0g4oJH?KY+Vmd(abLY%<^-l)~%+AMD zqQz_9Kl$x4Q`9PZ31Vx|(6GZ5D}RvL*63~bxFXKt;{)l$fPtR(FV2q!ckDZ)UG^}* zr{f7bfBUg;@?vAcWXXVgi6>|H91t@F#vhP9bfn(oA;wjSOkow?c@nh?l3Lv5C-)|5 zSIBe zI3XQNCdqg3;r%Yjl8O@6CpY{Raah#7iNSiwoELdmMKQAB>=&_*WBfi};2~>@e$Ugy zs)FutY~*r4Zs#Ov~< zJ*_HN-1oO%$-do~K*M}>`Y&JC%@p#NBs~6CytxgIT`-7*8m@n6UP^pAvE}^IYFdra ze2OAR7bIr@%mGwZ2OgNVwz(YKi}d&RGIkB_#qN=m*w%IL?R$|7J%6w#I{EoA_Q*>7 z2w)fp5XEKWWBu+)sNl)aBGHn{W^_J7RApU!y$%%iK0Kp@Cb3gARS-ga2z(y+ocqX$ z#P~~sDg~EA4ZKB{uRbv4b66VG&8>c%=qI5W%T#`05pcBV+LuRo9yRoIt*u78(=>%- z9yIuaIAy<5n@DI)IjI6Y1+fg|D5G3fI?d7xKNNhqRKbUEz-+V)Wjo9(8p1$!5KmGO z_5r`6t)XG^KfAA3$h5%|0jlFXv^;>UK{aJZo_4Rp5ySSr$4$qag5zZ4ekm$31B z<#Cbg#@qY0&!q1GVSLFu61lYw6iUe0L8jU3Vtl2V984@k!398T8%(lYhGPmbWF?Rq zVBj3YAnAFbyqRE&C@l)2p`bF|ogW2Z2be^)!o8X0J5TWmaf3tu6N-w6)0c}2n1HVK zl<5@HDHD3`tMX6oBw9V=t|NDuh~C|5F*jC)GWCOf=$Uinn%e_zC^-OwRXkzLaln)D z6HhSFlD@Av=S>hxYDd`C4~OVNTtK%1q2KXNjy%o3xt#E#-ockfk>h}s3TLc8S>u9@ z?)H!C#vXkg=v^`3F18uHxAese;0){`7Jjvig1qi&KB#}M!*tX#P&0sD8?&2aq(IGL z<=~+3b_IPPAbJ^oes!^H3V3HZtvQ}LrT5Hx3Y1c>YG^n@_X8+b>#$#%n%mx!*7Jjk zo<{TD9AyQ!JI`}=!NxTHA1)oVY(Ubq{_{F!G6NoVt4-I{dh4?cCG#IB;aMEUfocN2 zQKn7HG6KM4UMnmxZXpC^ypfsW1oCAHDNY{9;ydFp5%(9jrayU^2A)ZH6%I9^6kY5D zlnN@AAeAe0cY^-jPzP!w$>uUQCylDpp(2`&9d9!04B zscKzB?d&&l?0+{zr{$0TgOLxWnl zGf0dLV`Po+A22_lq=U%kXW-_1KRJE9f|J^9Wcu#7mQ{Kqj=VV0Ee8MiPR?6cjgn*6 zRvFLFyHKq=-kDaf7c+k4-?3R|-9B))$1tzp;GUXF+WB##Wf5SdLiVQAs(X7I;Pw{$K%g%TFeyr<02;bW=s(JQOGGU+i3J_+m3EeTEpwy)&&W zi~{RfA3p+%7*%G{d}+~C-dwtfFg(!T#xaEb<*g4rCHgg~*+%xEe4P2=;VFF%uiw6j zg25lK$5Lr=YeGoe#_Y#1?j#e&@$24O8dJ{$)e%XI^(5(rmpRBoIzL`J&zEt;bf(1E zwvU;-aWVF|LiajzXqT|GRE2l8=@!jYC}Vr#A!4q@TI<_xizXxEp7k56sIDGdZpO03 zNF>$-?*NP^4q51=y{vzuh|R31lbm0eau=h*wWBO}EyvO&#kQzV({(XMm27=^gzGD` ztx`>6C&P&@eSWMc)@!I!-}ABEOs6V8p1OB|n^j1gZ}b*VlB2&w?}d$rj=A2v_F6os zn!yts7ph8XB*TVPgCo+z^72P!c`-Ln%G+;N+x9ofa$q5{%aN3hZ+g&%x02B4jwqP3 z#iWH#(zuBYkT%QrqN1y;ww^zoZxY6rP8j6Q-GL@8e0%X>QfQbaqIFD1S6H+OM{};e zwIx!-VK8Dn4!6*rq*6d_j*0H~MXuMeq^M#|zr1xF;R);dNX%b|8yJZ4MpL;Fqb^c) zl>|lS-tJUpxiP-FI4Z_L>a+%8XC{5I((>I2YOu{R#g+A}<082&%gMa!mR1hZ=`iL< z*E^=DOEd(GICs_Gj*O)nsFdbK65G|Mn6+q5Ddeu#ciWP}M9|41$Hmhwqw(?wBop!p zy_B~7Soxuu1_xd$Nii+8Rd<&S8R+4)B%)m5S#+DxTpC25I?uz?y|IF2oDW@j`{4S3 z`|GvU^x_XolObwa8%~S0ZX;HuTYS@F!3j4z$BP7LQ>5zy=y`G6`irJw^5!Fhp-!P) ztgAQRZjuxf>q>BYeDzXQW#=VyVr-V0P&NXism0u{KFjIv~`C&EAJ&Z}i8 zUq^#k8YnT}M@w*J18Px!@?f>Z&?)P1@P5OlF|i736>*7FL85_wC<2b$l=m z7(y9)bD|e>+jWXZLd}MjAiKhXI-3b77{ejMJ@odw^*PQp1KGGYg}S_c5l;>z=o{Px z9>9|+job;fc0}{wLR|w+s#6h(dDNVMZc%leDKv@6zMdj$Yb%7>T#$1o4DXl-*1~v; zn~4M$%Qtb(j+PU*+mAhupVfHtrO~1{3rhP)m^aUvhIZcFMRdsBqa)nYn2WV>cj%225qx1@^E!?H!rBd2TiY}+U zRkfI<=r@ztJ(xtjFV(V|}`Y#@vPCwIu72d0I}r`*~T$=(P{eWP?98t=zVE zBP!1Arayve5&x5jufR3dh0~QPYK~}6lg*GBU%bBK)%k$4JUklRzBiNFZmkxa%K^9` zt`Dsbfl*?g!QE7xsWT~FEcs%JYov=*als75$0FDtu52zgFKGz~zhYhGU}P0%3+weg zwn(%bVW*fdLR)w9ZHLIa(==xq7yNT2+0$7DUT|QC`JY}dzRypqCgjDdeJ#6G7bR?c z6+64`m-4q*GwVJ;r9;RKmu$gex zykk!8?lLc)^4XwQcW7C}O_IpSW;)kVQDphEC#hD&R=$Oc#o8R@!HvKja}%D?GD8oO zITC{oN@$g=)il1g^y0sKd9$MaqnvK`NPCoP(e!kFh-Z{(mhKdC@PkoBL$#u)Beu^8 zHw||oze1@X7j~ClqG_E&uKsX@-VKifg^QmK4^|<|Z7iATTTY#@vwwbNGDh-RuC`~$ zVd!L0MJaY3x%>QBDC78ZDQ^3&KiTx6BCgzW&XF8IvumHvl_V09E1GLew$#yn-S&~f zjK4^nd3tBl)mj{N6w<7Sfkub`bxfO9WtP$EOiYGrZrE7M-y6;c;uIqmF^kSFWqxkN4Ju>Csr~cIJ zTsjcnCbpvMayJq&w0YmA1Erlv?OO4|K5;h3Nswnz{nMTgGyBNr&w2)jq_WvqYkyjA z()H<8zq{9?+&b5A?65oO=*bn1_{sp9&TL8+iHS}P1g zq%{*$NS%p$??irUd@f28Gs5R?ENfYknZcjP%i_BC&@roLj*MDbI(E(k>33ST#8=Qhb28 z_arB`V$)&Hh}he97ri@-q;ekY*+|n^tVAYywdVY^vXfSK@GEWdJ@{6R>{g7S$z-X zrghtsHFvyCL>h(J0Sn@Y{0?WB)YICZxtBu`PBdzw9@w&`Kx(ck>{rpCD=@uzziCzi z9iUHZ>Q2jv+neXNMUo}Ce!*5WINXZnoSotu4=L%Axl3XRzb@@eXVG$DO1de^yc@&? z)k@b#>bgwr)DnoFMBdEzdvoZ~+fWFXAZY|TB)BzwvWV z69uq4=S+mDwI}i{R-W*gc`j32($OnwSDZyD>1Nw(*!zrJ;8gI+^oHVq;bD2XYX9^+ zpWfqwQ*9Oj{P8r*Rt4{J{S1xm6dgu)neZ(fl4@-5>V+JDUQ6Nw=)*a?s4VW=JNW){ z3@xnfd>*So!MjT0-QQMnkOaSwBh(W@(~%1G!oiF8Go2nv3d_U{pE2`BAVO@*mO^gv zsZ#GAutKE0bdm@Y!yV2{85mf^S;wRq+TRU!`Yc#3ba1z9xFgWap`{E&UVxTtB1+Co zzH>y8jba1M4PH^Jb{tGpIsSnPuVD^#TBTBuP==Ur>9n*-|EM!4#dOR-E1dhW6Ox=H2I3ZYAE>z>+R zR2znB2K5uf^(!8){~2z~hjxxw>)d=tm=P(8jprvUwlc{kazAH;F5#=Id+Hi>N+wa9 zn`-9O8CgRY?BpoU$a11(wl3dwj4$VDWcd?!vuzXmrW+;xnJVj+I(fCnc zz6N8S=Y!m6>bk2e)a+hYcQ?k!dr)A+*50AK`qN4!WwrFbI`FUd)Llt`{jp`!{hVT^ zk7m#Zpc%%irpexlL-=$Evma!`%+Hd%jNdvORX*t+8y^@qMRS#CqkWZs(}UA84sSMJ z=9@NUhH~Fn0thKc^}gh6=%kmZ?%}Y^2@(?bLDTvkQj<-p99fRiAx}n8TsD7Q>&K)_ z#EHI}1VNR_tYiXvgR4R77DILaXjWMzCSh7q zY@w%LxW3)YVajY))z0C9z!aK_B(17n>Zv1(OG0mRP095DpVru2uPr0#8~aH{BpVSS z;$Gc5iQ|6Mj`u*@+1va$5 znh#oa+0VYvxt_Z|=*#DoF!ALbj+Mf5+}qV5vI9p*Tf*!ra9FXZzufY3neIn0jil$q z{=grq1LVTY32N!#Y(&gNX%Y8V-cpNgQQG$jTq<$JY@m_()`MK7_lAv&YikN8g49M# zX07k?lg^v97TvY=rA5=Ph*|b2rWdD&^y}+yN54l$S?=;nH*Sqb6@84v(#>bI4U!z^ zbZWgmU3vGqn&rk~REX#6>Md97LWP2#&GBgK$?}OV_P1I8&(GHQFV5{ z2gD)SB_wf~s#Gzmr+DUs?c5jB`Os^dEIMc}E%l9um)sY*bu;=iGlGbz@W<7DZ*Dn& zX;q9uaYBG&3IlbD^q$LTVd)sy$KEl8?C#FT?To(}(&P7tXmz^FT!hvWn7i{)FJ8WM z9jew`G{w+B7;3b=9jrAbhN5cQ^`9}Drizy~ZRAm-U5pzB#mM0Jb}|Kf-F+JN&K?l) zl|i+=Vq0s-k>R_gpi?7gCr2r;FgdXA48_YRp@G8;kuq?yn)0SYEDPgsZigC-p;#H( zl{=r31v^Q9Em?_4VEz337fKNfL#E1x;_cDi3B|Y0=QGL#XS>W6b2`@-X%C}{bnlIy zahpYwH8kVad^evy%}dilCLwVtYLlWb>?*wyDrS#_Ae!LlfVjpT8r~^002XB6rpv%f zE=YM4O5#}UuGC86@v@}TfTxqh{-xRdJ{?jFO~-bv4`&Q8zO44fON4py_ZWoa9$CjM zXW+Wo5tzjsp~vf*IW&tgj};Tw{g*`m5E~DKtaG?9Eq7$Lv+@P9og(y%cJwDIMDFR_ z-W9CsbHXaL(>+t#oCg$FZ<=ct}Ll8 z0dE?bUpmI%w-&eUP6JYnW!a+U8i`?7I6IkEY}wkR-&^bV9N@cIm{s0jP+avSdOPO= zR@b(Rkfwl(CFPOBy*m>v{qG2ISQxj9q_wV?2XnE}B+G?as*#s2L@bGIObudQcZj^A zTFH<%mPg-OrTb74RMtE*uHToORORLRb}WohS}rSw?R-Y6e7Ck*g{ZC@wct+p)$V-M zy3XTb9PU~hK$WDm{<9crUT%&idpG-I+)>A_Y}@q{6b08eLT&|3_I=*kEeEvpL7BOS zAqGhSNM_wgu$IM#_RMTf`Y4xs92JStWhI-u`qE3tJwdF#`)i``Px@%HwoT2FCZ5%l zw%z_j?kf21szZ^5f^`C zEj9P2A`-2+v|R}yfBe{Wtsm1@TCC-2l4;7ecl=!bKGMru>kV~aJ#bsAPawA4+8kXC zYxgYsbMb^pFq_VAuW|IE2J;aCZc9g#4|6de_P2Pk5SB(F^H(Tt=xHuhOX^W)#;cllJ-SLI6WRtNR?fxN98?sXt?s|=45>CYTxo7pEX zBU3cs6_{&=TGKZO(&}lx*|~pz{OU}v2#iDK$e5h!6g%%sYkN)WT!x=v=RnYJ^2DE= z>se)8RJCx$mLiB2^}AgT&5vudDx-L&&LMop4rBF_5b~)UdFN;L%8$xxFx!k|gVdRX z-bZAyk%eBwqWEHMSe`{!38Znnyu6^W$b3z7aC}w^46>Z^rN%49uR`~aU+u>~91J8B zl6${E?ACgkH0JUV6TkR_k4Bn9;*vS^5v@bnT^Jm8boOWjE+#Kb?5ty-^C>rmw!>y# z%XJv}wV~I8DqNv8VUS^E8>Kyyfe>j6KUc@lCLFcEh-CXKE>%vFOZCgCf1b{>$A2X! zJtrbdhZ~Pk6j6EAb%+MLrp9*ulqtO*xk>FgV^MH>Yfql0oRB!Z;G#TPG^$4H(CSPU zET78MMxm94`%oH0>sn3wfXAKBAc^ik18Ubq{j8Y&r(gq8xG3GS$to7L^uVAbpnPSG zG5&AA`O$&nYNel}9qYVLpc%%bXipMxr*o#CDqzS=K6V7?z#%pI=do54~p4 zSrMv-4JlhL2l0-O;9!g1qU#6c`_2vjufMh$Zg?UbPD=91sMw%zD5{r>FEgmaP9y9c zvL{O(K@r4cU&16?=$fx`@!~re=?wC#&~Cw|Y}~`4=azu7g!BWa^ghY|TGtCz)D7;k zKQ)*vxIoQIX&HJ$bp34D<{wMf(T@5sn^PseXFi^FcqTh8SYciA;>8Ef<$+gj8W_Y` zqdboPub!p7LDDjIOZ@hAjl2%$1ts3hv<`8lb|ui6i#|W>!)L%mfv!+nin}Pu!2yDB zXIWVhg*L;$MPz{j*oNfC>IB?YLevq({hD%Ndu{#)HPDSjrt(+oh`=~goJ}e;6sqQx zjc2=2kGz)*l)ufga&jeGf5sC3)TTP#sTJ`A!4>qwsit^zqx`;*n9y>(z;)fBsg^Lx zzn+STaa$H=6clv0H|~>N7*||OlU!tFV1ga`T}@6NVrU-AIpYtg(4CrG%$VE7#lo|) z*VB6$csm6b*lqh`q~)hBP9@AeFahng$@YvT z$$H-f|06L!REA@rGQ9Ygui<9O@$Jem%To*R2CTB?MUMV2I-DEiTu4QN&AU;XiHS^6 ze&+2%Mn)W4pO7ZV{dUhY?wIXws8xPnBNoH~bnuq7NTx;IMbF4~3t^mSQcNA;(S}eh z1OlO=Qb9Za=Q{`}`7C^=d({>#!57gg$%*}J;MUYe$J^CWqScwkxA+X>fX z`0PtqWh2Z|<$~^Px_z|hRI9S6M>#dulUAVgH0>ghQ7X6OWk~i7(ltE#0;N7^WFcwr z0Hp_3@sA#JUMIb;(%#-YCVcAzyM*#X-^YJ>Z5zCsNz&j90*l^-ac>2A$$31NU^N>! zXT>-(8?1@xjk=1qsv67+w3@1R%5bsQoDcbgGM{*8SNV3pa_j2Wbfj}i;FV*o6(>pf zo;nD69Q6n@qSL$lSnUsu$C`g^##oNiGrg0#v`2EgaQmhu7EG&5?-%(KI2ow%UpdXY zedl$`PJ^qR%91#d0MuW$RU27>$Mp4b%zJYjhZRRzOILgrua}$6s$)7hymWM8$YO(G zwat{X0;|zFA3{8wuU`*xxZoi)dh)w3roAuqAMcUsHr%5WoP6k$qJ-QQlyPxbG)_C; zA|hHQ&e3U*ml)-l;m~(lRH&PJwoX~^x#zC@;`xk+A-L{!zU)%&mB@#eIk^3svlwzg zlK&ZQZKLVfGlgxY$n&>w=pMIGwoA*tR@<<`Cf15bMK~Sp1-cG zjoqLXVe`*{&xYipE@=EH(Q#&)wlZT0BbBy0iwOv3;Pdu%$8Y*}SQ&(cr4W~xvI`4v zXKvN<@z5H!HR zkC)ZFymOSdgzIXWdu5(k@vxNc%Ol{+8rowqif7rGnc3NOvh+Py(k$S5GE!5yY4-9W zg5!Vyb8ToN#=N;cKq3;(#JxokuI2iYtd4~FbK9Rw;ImT1E*l}Vs$uS;fdbT?n^j-m zxXBZxE>)(OoY*}!@IGWnD-J|C5m&Fi(Xa7`Zl&I0d$sG=j}z~qWUo^`KitPFaDV5s zV%o#A0_SR^s@jo*IvSxJ=Zr5IbPhC}usfEl6eG3d9cPofI2(YGlAnkR(P_f{iH3as zx@!JdA*<}X1=IFtko@5krWR~!Y@E0`S6(l+e%*TVUDia-rtBWw29#5kJ#E+Gnqnm! z?j`4~^iU48^VVYQ37nl`Tda;TeLT?2Q2=Mat&6@ndk?No$#kT8=i02=HV4E3E5dvg z+WgyE%MnsuSyCp*_PZDBXs!!yNi8_7F7Yr;H>`s3KV|n`D=xI+QC6S_%tovd0~QL1 z;2mp1tw+8*Z9AaLSvqscjfY2%QBO_J-T9>8_BRxqU8m4quzYh+@~^>UUXTX`0dzdc z3&axf2Iu8Q*n7eR3Wd$tGw2e_@l{&L-=q#WdV@p@`S$S@Q=E80p+nNFN17SkftY%% zd^rHxKDIx4-}|fj=h*WSGUR~l1yh&Fpf7*((Wb13^yE<%kJgIXmRJNdK5W1CNQK{l z;}k+o%-6u!sRKV4Dk;OoF(7KH+j2NN<{ImfU*ADUah|>Gn8wTG#KaCDl7WW>nMoO0 zS+#UTp`6r5FNMr2sV7xQS*``xJOdpa{LdAVDu8}ePe7><2Cae51fA*`AYTAJfOFNG zNM|USpiZw!lA=^*OhjC(OcaW{xwidI0;S}#=hoIO`quD(U?47@boR>U1zCjN;Xji` z{=N-~@+VKd51#{B!tEat!vDX25Sz7)?l^5n)JY>8o0gl$hwwp7x`gq1um+Ix7nr+pY}!hj83 zxS7$okC|azr&TOp zuT_d%TQU84GjofVCp)dEFDtk#Nsh+e<_&8Yl?4Vabk%eEHz@P@Ha2?ewm38o{0#4A- zd>Xc4L~n6FZ5PQao4%^e8+PbXEq*6lsFXP`+goUtZ89BvHb_+M8l~^iu$H%PFBlma zHILYr_qs9iVVyTsjyzw zt^Rpxqb}dFxepaeV+|IQs5vb}yzn{v-oE<4zeaiR)~bSK4_RDO{iK~wZ;|$kdoCFr zu@B3nOSOBYqPl4eBNNjL7o?CczFZfD_uStfC>JaUR~6SJZ&r1GyP*snaHJ`w3qm>-A5G2s2fo6W$NaWh2EsE~$Q zfim{D(Y8ou;irMzNjFwL9t}x~e)q0TO>FN5w`l3Ijhdph^!RQ3=Ld(J&sH(4>Vqwo zc5gBY#pr(xCLWn6+pc?;7RVjix{Iw?%y91w CredDefRoutes: POST /credential-definitions +group Endorse transaction process +CredDefRoutes --> Ledger: create_and_send_credential_definition() +CredDefRoutes --> TransactionManager: create_record() +CredDefRoutes --> TransactionManager: create_request() +CredDefRoutes --> OutboundHandler: send_outbound_msg() +OutboundHandler --> OtherAgent: send_msg() +OtherAgent --> OtherAgent: endorse_msg() +EndorsedTxnHandler <-- OtherAgent: send_msg() +TransactionManager <-- EndorsedTxnHandler: receive_endorse_response() +TransactionManager <-- EndorsedTxnHandler: complete_transaction() +Ledger <-- TransactionManager: txn_submit() +TransactionManager --> TransactionManager: endorsed_txn_post_processing() +TransactionManager --> EventBus: notify_cred_def_event() +end + +' Create the revocation registry once the credential definition is written +CredDefRoutes <-- EventBus: on_cred_def_event() +CredDefRoutes --> IndyRevocation: init_issuer_registry() +IndyRevocation --> EventBus: notify_revocation_reg_init_event() +RevocationRoutes <-- EventBus: on_revocation_registry_init_event() +RevocationRoutes --> RevocationRoutes: generate_tails() +group Endorse transaction process +RevocationRoutes --> Ledger:send_revoc_reg_def() +RevocationRoutes --> TransactionManager: create_record() +RevocationRoutes --> TransactionManager: create_request() +RevocationRoutes --> OutboundHandler: send_outbound_msg() +OutboundHandler --> OtherAgent: send_msg() +OtherAgent --> OtherAgent: endorse_msg() +EndorsedTxnHandler <-- OtherAgent: send_msg() +TransactionManager <-- EndorsedTxnHandler: receive_endorse_response() +TransactionManager <-- EndorsedTxnHandler: complete_transaction() +Ledger <-- TransactionManager: txn_submit() +TransactionManager --> TransactionManager: endorsed_txn_post_processing() +TransactionManager --> EventBus: notify_revocation_reg_endorsed_event() +end + +' Now create the revocation entry (accumulator) +RevocationRoutes <-- EventBus: on_revocation_registry_endorsed_event() +RevocationRoutes --> RevocationRoutes: upload_tails() +RevocationRoutes --> EventBus: notify_revocation_entry_event() +RevocationRoutes <-- EventBus: on_revocation_entry_event() +group Endorse transaction process +RevocationRoutes --> IndyRevocation: send_entry() +IndyRevocation --> Ledger: send_entry() +RevocationRoutes --> TransactionManager: create_record() +RevocationRoutes --> TransactionManager: create_request() +RevocationRoutes --> OutboundHandler: send_outbound_msg() +OutboundHandler --> OtherAgent: send_msg() +OtherAgent --> OtherAgent: endorse_msg() +EndorsedTxnHandler <-- OtherAgent: send_msg() +TransactionManager <-- EndorsedTxnHandler: receive_endorse_response() +TransactionManager <-- EndorsedTxnHandler: complete_transaction() +Ledger <-- TransactionManager: txn_submit() +TransactionManager --> TransactionManager: endorsed_txn_post_processing() + +' Notify that the revocation entry is completed (no one listens to this notification yet) +TransactionManager --> EventBus: notify_revocation_entry_endorsed_event() +end + +@enduml diff --git a/docs/assets/endorse-public-did.png b/docs/assets/endorse-public-did.png new file mode 100644 index 0000000000000000000000000000000000000000..275b4ab6de4f715c3ab8bdeb7cd299b277f9cc08 GIT binary patch literal 93680 zcmcG$bzGF~);5fZ4M?g80@4W5A`B=JQqo=0-940oN{cWcC5(WS#84y6h=4RmH%NES z07DG#iFn`7z4yNNe%}3lzmI>=;hO6@W36Ky>saf&f)t)fUcGel5*{AjRjH?9N_cqZ zuy}Z9vo4$lpG;M}G627roy9erjqL0_tW8Xv@gz-bO&krJO^ojtdfYL0cD8@W!C`N0 zVC(E+W6f@4XG3&ffD#YybeM&ThVvhvImxMIxG)ZM_1$UP7r;T9?14$4gDglS{viH4ZzN zqnGwv(c=tDOk`%nsxx>Dy?B~e{8-pKBEd_s4SnhRHsfN=H`te_AtYy>ka&;8alhH$ zFut@hN82~+S3(@tYIs|Ly6E&L@tj--no#lEO{#sgmveRWc+a^Ca!hN{n;)3Fbya%D)V-T3y|$BK zTx{pESrTF{QK!t`ad7&UQrUE!=a~Xqgc`mjPg&&o@J~`I@AqvUYgJ6-2A{KrFK>%0 zO_|2!o2(qT)k4p^**m(s`1ICv!9#gIiH%;dm(QO+X>XayYig+%64W#3Z<97 zT4Z00)8)|`o)0sgtx@Dz#bj-xY@|{TJUN{ALxSW|_pVm&5gTw8O_yCaSsQj9ea${s zb3>ITk<~kYiXXf2pyzw|)d61-+!LPzK}jwvWGru}#|)~zLT*+0Xd3eNO}?1Tw@tSW zXrkU+j*|nI!}arJZ`wWQwkSEeXuM%#8+ej@cQf4qIHu`wW+E1aK6VTtFW*3(?VraDB7m}2PFJm)zVH7 zqQ4cY`n+%x70&i1`Q7U4k)I#Rc!b7-CWUX;PN8L-rQXQ0e^6(ADl3eY`vRSDBHp|u znohVZ`eo`n9^MN)DY3^Y?t06k7u`uz`_?z|Ev}m|UU*_G^~jGl5PpUw+;h)0r2DZLP&3ydzUH)r1F_UO<{JP9#gNv6U=nKW9F33c#MP{2Q z_Nr#-eg0grbmX}T%h>D7UHLFfy7yz@Ei*nae>}WL#eto!zy5`H$53(U*eCGt0;zhS zC!eJR=pvH}l`{y~4jtzNo%pNoD;|)vq(};0IQcsb>7G_cQ>uusAi8bP12sjM3-EZtHZ*6te7>;8b@7$^%4Ub7WP*R?c zkOL1jly#lz@fLI=CnI|w6=m*r_SckXN2%_{nzh-OJzZ7w!Tk$AEdynCJV1yhE3)ZF|jcI~XLAu%>x2V;Y6VL2NrDWMr0Z$0!DVM8LI0TPxgly_!2e z3B5HN%!ujgcL`R#p?x`;g(EOHyhGdbPE9&Ha>yqY{5R{Kz$GyEm zFhmiA*-*ZEcX#)UwkLQtcrs#?J`c-U*DxOMkc`|eUoC`mBCJT9L;TX!Solig;Iii3btjFGkkV(&`p1s zpeU;`l4@c-M%%PbKjB9A1}yvh{86k7+RJMu&JAsxG%R3+F3riwX>FCoy}A7B33pzJ zYA!J&n08uiou}gbp2;?N$O&QU^DbG%gNuvHI9akf%d9?_)R4F!*;W7KIqQ=1RuFo6 zdSHA40s=Dc+2S@bXuxV_j2)OmUrUir7qX#S<9TJS5c}6N7(Q94KKa_uaPY`x*0CX2 zOiT<6#(k7z)=`O=`_hmh8@Jh~ZFiXAr{TW8Qo_x}1?H^aP$>I}Z=|ehjiTWctqH#&z|Tx3H={CT zL|H>)7d!sl_`9=?kb7M`ua#;0hd9m0Y^`zJjNpf#pI<~o|4QnQGryikc-^%Z`UwH| z-7IfL5Jd)M0^fad{> zX`#BEX1iBo2p_6FnZQ@-Q+-gr>ieAB=|<)W0~y=*Pd;Cle1W7I>;L`OH>Yzj8pQ>Z zD-PlE7ar#}oE@({_v@0McmL*!kNT7J;9D1E0@FCKdEPJ;4I6qav@ic-G3Q!V(mRJ>B5owjct{dwK1kA2E&U%b6cUB6KqYnqw1S&1xpj}Pq!SX1ESHYMu`r@a!*Wu_oI)ZzB}KFLSnagg9!F8?o#`5x$aJ@kG%3^CqJhIfGcuW z3U}k9n#joOD^vzIkSCD&(|=F=u%^f&z%nYj$qmhBK3HKtOzUm(3k}^1dK>tK-=o~@ z3=C*U;a?SgN2B8vSQAKaoge%X-VwDnU!XUy*bLbmg|y%{puZ7032y*ck*-eR{j{UF z9sM*kdCPD72)whX)zJUhvfOHGIUXl}dGhdc59E&U+@Shx?(d-0BjCwiL72BYOJcbT6~ zF636)1d~02!H6aPi|5Dnr9XapR$+-5n5SvMYVw zs(*H7MiaZq@iZBMneNzU%N~lRrLV~Fxl09taG;l$AdL*#SZ$@)e0sMxTOFZY^0uS< zT4bfSo%bQ~ObeHV4>H$C&uMd(B3VeVCP$v2;|*9G2w0pEV@(2?X}#mcNdk*wB~Q7u z)ok^9aSr0n(-1w%W%R{K7c>Ry?JTlB@Ilh^A80O{Z4z@Bo0|F@Y%~CD1qd5O#CFLc zckVcZB)N^oWvzF(HT&K}Qwq7;cO*eBulees&Fb}v4ytlomimI8HA8CNmDp{l^o!7& z&)OVyaZ_Jd8))Mc^>Fpmgi|G5jgV`DPM;wxYbvCA-&iFP-(d>DTzJ!;^q_Apx}TQ# zaoF9i=Ht=PSV3eeu902-g=8rWX4=>WZhd6+Y7NaEY$yA02S;NlqKrG(X@W6!05}Ec z2-rv^vLQvG4;oxO%wOWb5fOTX!@>(CnbFH5WlnAJyiRsrVmXP5sD`;M5wgU>5vOh8 z=cFZTFA^#R*l%(xwl;nEKn?AG@kS8GCZ<;?6gF>Ma z65qeQzFdzXBOx)DC#rsx)917~-$9LpY;DN2l#6IVH&s096QEI|9kXo->Il6m%X%}X zya!tbLq#yV_Cu&Y+s=Al(pUTaYo$#IQ)u!oa9N3QQzS5*W`e!0Hv_}}#m1Z9Wc}aS zZ+Jtpj}Bf9bQJF&MI0r)FE0akoSvJrIv!(spE7i_r*Jhqn8&Nf^~HgZJ+@z4 zI$3f+$zW;-r2@_-E!2Q>yBF(6Sp8toT zy+bC2*?Q@9H1vok%E^?$i{oHm+PkelZRoz=QTxp)@ zvgh{lpteB+xqh4NfO6>3^m|m0k8mmD{gLW3wwtpLuyGME2D$4ylGximBV0WH?RqKn zL;Lb{D=d3Io3y<5-fUvLOvd)0Vsk6O?tRXcrX71Zk)zffdy_CYQy%(oXB;lBU%ckWH zMPs*wSw$*-(2e(`vZJF^-kquTu={dgxs5a$%NQZXvmHOhA7J;EsJV!^bq~F69cXRY zM@RjlQSLn^Uu>1P%ZqO*OY(l@c3pumqLM7`g5})^wb>4y3qfBkAuVusRcj=Og?n2i z17yyXQ>`Y~c5fk*zMfTG5-9aLL^~mfE;a(-b-etVsw|qzdL^)=pT@YkQl&arWS3gtz*=d9!`o3MP^U*A_UE zyS7JL-8yuPR`N7q=?Y61Wq%@*zTNXETEY-sZ1mW~IJUf|lDbj3`I5+xfZNh-c(=(+ z!hZtRxgsba;86SKMsw{#w?1h}@>t`MB{W+ZU zmgh4iC0GMB_4-tJ3;NdiA2}VYTaAIP(~V~G!XE0~>0y>Vne0B-Zg8#7cSi3S?4_gh z4k;Sq9i8DB6GhxVO?}vw?x{$r$Q6&z2dEC69%k8P2+yvsEdtP=d z>B|5SPHl-u?b?3KfW7Nz-s@qq1;n>cw@3Fn(eY-&oAgW7zQ!gdCZ?vFgRQ47EEvP?t?m3~`_VS$-fZ!(O`l zte_;^)=Sod^<9(NyHMNltvS?2fQU|(5D6-XQP%y7&G38aj(a7}+WF5vXbhC|?td9B z@6T4Ra9!CFpR4hgnZbkad=zlfLoFBf%_Vux9>w6W9g@$IbQtk7E%=CR*2cm)N9De)f2tU7Z+|t(jRpBM0{L$CYSi)i@nRJ@vM zKq-7bKzDyWe|1qdfp4f@D%+wjBOsd4BECa8QjVJ4q}La>rL#4Q+A3={4=`*Uwj+>{ zt^6n*&9a%i$0M{`7y8lIA|8_;tCQM=CeIcqZ&GfZZ?cWjPt}c9eKmP>h~g_Km@Bp& z^;+zSa6Gasl)PcbB@)&yopXIdUT!7l3fao{TZr~XirnSF`73g+R>e|r*(ZjwD1>_- ze;BgVkH}@W3^d_ny+)~_WEyV^-;$2oj_mNq?M@Z$?rNBb#3QA{J7J2WzkIWD{oBv6 zstWUm;^C#nW!BC~X1Xf~$fWR(Jl*WlQYY~rW1eg0Flr3)zVQ8+%fe6YC3IfZK|OKF z=yVdx+lhnOFitgp^wfCQ}bqlk<~-zr$qRwc0To_ z)Ihc0 z4T31`&PNn}t`0Vdg3TxRYW*no&8LYgG6k(V$D2Xnogj>iKlwfIu)dwK_I1{2c9 zDVktv$JJg)yeb2u6Ly9fQN=g?BYDQ~h*hm}xM)r_-n)5r9T3qQa`}Zt%1#^gQnb+` zhsDy;v zvW&+p7|@F?nmS5LOt55UjP6s9mkFv&7l-^GIXr*^0}E9twC>l+i;~{ z->fO=!-rNGb}6#N=A^LpAI2pT38e{u${z%!`ZhN*_{21ErCBSdSN3FdxR^wz4V&xT zki!)Q?_dIBV*K&P^C}UNzS4iOvSORz&7zKC#9-ZHjw*D_Q3j`g{2?YdBXlpl+`QAh ztodNUuFyACAz|bzMo`7=CS=E7=soroXSv8LmLOpcO-<6TE^%`*-U1R%y?5_0G^oa> zOHw}j;6H%U&?}lf{Pc-oQfvBtYIgUU^5~7^?mKt=vM?7Ux2X?yzcz605?Kg<%v64YChWfr5cP#2a?D4*XFqd!FwGCWes8(KX|~M-iJ7 z%<-Nnn+jOv2yMG6Jxxzc?}}N+Gi9n^{fmi$`m;_Vi&SkaFj$u6me7Vm5w*9|F36!| z8DbyuF7=xokPQSMEk?EU>v@)KUe@D@a4U=6wTX%Bsw{S{T1KswAMNz2cQ@-K7RLjI zrg1T%JnloJVyW0tSDL@OYEI7eS~k7Z&FkvIQJ0gv4J8x`dD@^FvU!RYdm+36<+IGD zXycMdeN0|n`R7se0%Ebjyj{-2T=*DcY=v!xTAD>l*Gf~(OlMgaA=wG_A-qF%qk!_w z%738HZ)Ooze|G4q5@CY9+#)n8u_e8#Y%w%dUl$m+J?e%MYB6*s)>tUmG%glKw9*mZ z@_dVh+H5ANow$r%Tzlys|hN%=6;U)=ig6ckkqAY}mpWuBzvGaWH2! zmAgl!4@THz#L%~mr- zmLya9l$l|}J64FR9tRtz3@5TOR-ei2Cuq3OM5CI+1wAmmjK<;S9v;-e{hk*uy3FY2 zXdyor>iZH{{`mM-gEd|uO_~($A01#xuA`tovKEB|sp~^$^Ri6^AoRfZ6?e?l;y$!; zcU-w{MGc+*A_c?6!HEtAcljN5L-P*}f zr%f_KU~1hUBWI;i;)f}7U5s&mOOeYpn5@Chj71biUy1qVFW-7W`hB`4K}ZuXi=1yk zd}@MFeQ46M1eSLw+MPMmHz;!~c{Uk)!1Iu`>`vboJKu=X-arO2<7mwrM$T^-D*iMJ zO8W44*MnVh+M><+&%4a;O<3BxZFmqol8PcE6{DpNvvT|{W&5iMXe(ZbguH}H(J#{& z(h8qlptCJ<96VS+j0Kf9-0flDw?Dkrn<&w7cqiOM*dTP3AXz%40|?UR>~&|cO8Rd* zb$BlZHaSG6uMydNdu3LiS6JBIvlne3p+4tCAZ_;Q8DKDleadaW!UkSm@E;SkQr={>rp;FD;zo$8ZNWg6Yo6u6D)*F;oq_7B^w(Y#F~ zxxjNm7UP~vWGU{%R&F*mQh2xVn&IGc*g5K`qc*+S#b5lqRywe!Z8}Q!Tbd^Q`!l5y zGs$M~SfVI4xgshun3YOAP9r`^8g3}ekB$ReB4{uT?BHPXUNQ!owW>%~hgzfd_;gfD zu%kRC`zeR(;S<>CO`A#EMWQQPjBlmho+qgGo^EWY`zFHyVPS|}MzfAeC_fX~44@Xy zRnAE7KrRkD#?}168Fu&ibr?{>h6h;~3do_{He!{t?B!C4(lKrW4(!5)VhHULjTVDF znOD~=V_(9uaK~mdoZ=M7>q0|JNa6R6jhBed2b;|VuSI8Vl<|oIJ|9?Cyzx3{(#+!{ z@ZIw2j~^d>m;mw$&>UhoS4CiOFNe*KM{T7Tj&3T-cr1E8XM@-zNK)-ss-HGP2VN#= zITq>%be3OTf9XP#jbS>S-N`hQbZ2Mk_H4--h?2g5slR$vUf2q_Rm`OR#mVtYiyak^ zwaQYsj~0DQ42Q3z?~)?fL$_*cU|zlZ>hYzX!HKK{yd|G*3(oc<^egh$W7d9F?k#Q) z4jM%vi_DI;$DVP@9!mIgpjs7N(*#~MHDwOBCvI8w3+s>T&+dYlA18bN@Jc6w%cJA- z?m6b!xzDN#w9$*>SJ_ zLw`XH8p?p`s#|(ABQGjz+p=itT!cLKNaY1H?t~8+I4Cc_>I?)0_#W(V=}Zbb7DQz2 zEWxm({k`0Shwt@?B`knjHqoH7b97>)s?mZf@z6hE*=u(KRVTvHkLcJPUAs=keo)eR z-`mnFFAUH<$Ctg(<_`89Hl5P@+}uu^(@hJg_)jU$!KVykDP)rw?^NM_%(p4TyA0E2Mztb6+4jnXqK${^Vw+4+d-PY zWx#P6dR0RU3mjx5Y=ZXBgG%jOm$p62SU`Y~qz+RbE6qgFkNS9q^N&-q#F({7TMZR= zcBR>|!sXLPaOba-ZU8b?k23Yad0-;x?R32yA`4mDXk?#BRKC*4e?9{8k$PN1_)!C? zx=xP3jX|AC0SQ9b#dJ8$o+>Se1YDdkKGwadDQL0eeN-901#jCPI{!MY3%3A0%sz-b z?4A~nX2GQHMh~G#ePOg!&xaQ{FQ8%kH63W^LaXeNblaI9X9GbWc`dpWtd%rMH;3&q zLb90l!+a8ATjgOzU5Ku5&WBG9zeL;q;lLhm*~>HIVhJ|ovI?=$wrLIKn=x6qH!@ff z4cKtupIkpMwZy7_am;gncD5bjV3@n>G49QujdB{D7Q*yrmu0aan&gsj4Ey{SkxhW) zN0)ccmm$WR5k^>EgDA4kcjGBVQO(URanmwIPg2V)uM@(iSme5kj2aH!TwTi5gJk7Q z7RBA-x6OB&^E$lm67CaS&J8@fk(s)j){(weCqL zvs%y}MnIE^iTWT{F+?4&d#fE7{coVVzIk2ipcTlj_pV!1O_Iah0r z*IZ$)Pa6@#@9gcgF-!3nu(ggGDQj^LHn6j#eI-B?u?Sx&9G*Nx@L~)9Obuq{$nU+lao)A=RACK9Envm z>!&01*;#S9Y^O7wZvv-}hclR^>8DYH7`$i0I{K-#_YZCsg`|@ZV&Tfe4Zq{^?aD>Z zGonP7MS$@GF7Z%p!z9Wiy~HGyF|}M1u09*VoV}}E&zMxo&AD-UV+16Dmy^6U^HL>! zJu1xO7CaUa>ezOd&ZBn0+FnUc=w8CIzNSKadBgnqym9GK3-n08-OO4+jyy%$YnC9+ zYNl+of3!*J5({^GjA;OaQ!mq<*@j+faUqksPfADhQ=g=cNKl{|py|z?>SFta#Pc+6 zvs-E^#o4UNN{XvZEiElgO_~LU^+2sIDk=ij!K*7SM3QJ2O>yTjoq4roA2T5%|Qa5OT%TEtu8MuWs$cQ?)N_u9c%y7twG9X5Wfi| z<6Df!46;#7fi9?+%QyXTW0r`s_>a6VA3seZJOIzDzc@ENougS~dR*;P`NJwHd{>F{ z0Yfk}JQ$8fO(oZ?2>zU%Y~%DB;IG+UJuC$hhA_&CoANj@(+}ja`T13E+(+&7>J`~=#q)6ZFobJp?CCxd*)JRG*> zj<#viC;9*jOc4EK2VfD%EnLFFeDrA^T0`TZ!WK+j37AIwJB~0D2-{m-P^@v{{JV09 zrDQnOvGSY%fHqa)>V{L}JDNqqu-Tci&TJPK$4iuWO=e^GU4u7$knw(^>LU)0ZgKUzJW+)Lsh4Vt8RX@8)Y~g*ovC3?a7rCwsUA@Ie`M~~X zxCo9(z2+<^)M(@JOA>4@pDhxG2;HJhi2rlxB0P`x6AG9V0^$H96@)m0-%Y7c(OB-Q zBplVdhHGP0yoo7hb%8`Pq#RAX#^FsNXfRkk*Fh7YY1t|%MGn+vDQ6g?1}rD*g20dJ zXO##D1Ljr97&O~sYjzJRTDE}mPU;=Z(3c9T6=bp_~;UfAObz4c&Wx?BPaC+TI~a_7N9lMoFIx?8?KOL=a{3ZY6h=G-mUeBWeZGK7Py zNcjq=IH9)M1@W&?-1+}7Pn+Q{x@C@O>03n|!WHN)nVG_LF~dz-@7~Xo;lmPW9O_E<&JKpbp!-K zhP`YW?7bm&eWI3J&pmlQB0H~Ydk8EFGabN*USz^O?Jtcc1<|txi4R?(44S{G>wDA3 zygq|}tp}m6?{R_dxC(P>gc8#D-_;Tn>b`%c0=iWb2*MxdNf~*T^paz$;8aZi5r9JP>1k-&Zhm> z7pI(NTO(R9i@iW=3M`cFbDl2LU}9xeR$HK6B$t+vi9WTcdjA+-$iB99`v14?0_rcR z%^P&Af{{{?WZThl3C!88!&`}@yk{Xrf#`*9##w9rajzmB zlgL|gQDIbz3&4?b=*FS`m?8@k&*B2L_m=`PP8Z*1NGCm_WWp(>X3y(V)|!| zKghXo3mHH}|MpF^6G(XqdV}Nk%!dpFn9G;E3DDCko=dU)b;^me>co$?c4p@vCnY`y z;BNNBShZML!?_NI11-#XMc?&nI8tCZZH#ZDJ!aZYB8;q6_alep zfdT*}qn>M3!zFei9-E(z3D-^*$_c{EY|)t_-mL$0K|hie5<6(J)P6tOyq$W60hzSDG91EW^-$yx-QB`o zA*T3@WQu-&AKgIJ7e59OW78QM)UE$CVKZD22EW0aw8bNH z&$|$lkc#i&?WY+_rAGit@*BC=`;_40E#s4OaZ{VSzn5yFDp00Zq9&q2Enz;t;N3F z{Q!}J$CSDv>0QuoKq-ftw#7F=zkwBUYZsVfs?Ax}?@kr)jY{Z&547+tov(0f7kdQ>-kS<82|`>i+gYs6-8ph;rAq$ z@#ggOG#hAY6BN`I>KzX-q1B-^r*)=%0h$el@+Z~DK0No4Bd~h|6i#trJfe6u)VJ%& z{TJW|(qMnH7~kq|H7iF1-MLG&wl8q?Tie%{XZWoL6jW5u_2l}+R{g8J>iWHJAK7PO z#QaA_Mvf&0Mr@_WR(dTL5jznN(L-%rUc?v#KJ@y7(N07U{TnNx&|%!<^)V1 zq-{c&x(zEm4;C(>S9cA|;oWyeWaD|V0Kjf+Y%nu3gXlkz>MGj4t}kKz`!R?zp4L!OO1aAFGt+n0$ql#5 z3mLf-p+6Fq;c~pF+w?7c7xqcX_VYNs25zo^MF){nJ?VjMn=xuOnl+-pU%T^55I;;K zi>bvi?I8ZVZStL;WR{S8qCv^~u_DRhLMsxl62*#%ZpSt~qaD(F^uCr*%HcASY9Mf# zj-nO*gEq;P?i=_^;I&p`Mj{)IJ1qu*pGseA5m<|D6J42=Hz$&>ATj`d5yLkvgav*f ze>ZbqOlic>GdCdIaFeSr{P}%f(Gd5cunU4g=5!D5!@$F;Ua$qwL{5?oTm5b#v%m7Y z!|28Q4)wzF>uiBUpT+TSR9x!8ki3Yt6?A?2H=!P>N?Z+{ADTR|lDc@|Il%6>Q%y(QerzRX_wbG2d(x6wcy zt$K^k`l*~81m&l(yFF|dt$NHOXXDHOGLVv!$Hc}S_d6oAkkVEBye6#mfXu*PFl1F9 zlOVdxjQ0>Pb!t)4G83O-~>kfJO+Qtj<2Dk`d_ zHB@~GI0q4dc!tg}=o7@Ja=*jRU-IeGT@DVr#hxrc5F)ehZpGIxAyC5Z>(6Zvyfr=0 z(6`ytS;!41CiPP{MCo9%dtAy?`oTUA$b}P9471N!i8vE+1m}i zy}G#Pee(U(^FJtY^v~{w7j@45n#7$Cana(U z-=#~B{#YqEjk7-9sOUonNp*qNob#!v5QWe(Jrly-< zNXy=bj?;}`1Y8E+&US*eOad|-4v4C}`XmAP6Umh-5n+Tv*+x}5vH-2#7h5a!l2%KE3${wZPz(09z{57vk~I4^JsBcAokk22wtGkV^C(g18UTAYt%-!-+Y#FDMme zX_NxaP0prakLgtpG3lh+tN&K+vXqCy?SWs--Pvln@gq8eMTLaql z$)vuSn9$j4(LVTpXB$$MVZmG=763Fqew zARc+1(!BQMZvDOL7FrtPO+{G=@f#BfcGYDY-*0tf{{XIvK;g7BkpOUThx_~%U3dBT zzKDmA0iT@*Mx&B&bJcaWHEt2L-Q3iarB_vcP+I`dA<$%oiP~q^0Ax2-J(-=w7Tpe0 z-xpdqn?)@wvMAnx#tv#~-+EH@)hzlEe+jjUI>U;evZEX+B#=X5#P z!p5|5b}V*Vzs{uGp;TCU{PP;SzDV}0%W)e&yxU{1tbd)4`q#I0%zm+gU|Z=j$uBoH zbbIW!X#SUAN+*NIRWr%hqFDApb)~sgzXej*mg90rK%Lt(eR-Sk?7t--lVZ7&!>^ef z!BFEIbd9~upkdi&z12QGTh+sQaE9^zC;y4QfH?nB`4XgP(HTzUwgMV+EQJvP{|g~H z?L6yQYN?Z`nFiYn6S`?kUGx0k8XRCW(;4M=nkjXveO#-k_V_o=-+7<)|F;`CpttlG z&5TQaJ~uolJl=THjz1#)H(cqD<02^mD@ZEg;HH*FVZWMxOFgU+m~~PsJ&FHiE&em- zN$xvyE8;(m-o2!cn0RU&xi3gHWAMRS?BDO2q30p3wMcN&_)r*X>Neq9*I#^-(_FWnTTdZda*=bu{UPjt;f} zibSwDtd#ykMH}lybEiH{sBGeoxBOndLzk1Vod4O}!7jPFzDvwWk zRnVrUCSnFzh3_#@QCFyhG|-In^kLK@)m?{yr-&J!$;!&+D5V82E5iMu(VbMGZ!_80 z^eRiA1YD!#vKcDI{`4o9W_e9%RP1!PkQIak*i*eb88S6B1%%gTpyCC#v+I_FRxP<3 zTcd8{ytX46cg3%ey|f(ulKTJ#1SaE|=IDim1%UOzVPT}hwu6N;C5I>2;Tan(zMLLK z^XmVJKs_z9)X>t>+F2cSTN!cz1o9=g%?&QGZ357V1eOWVq|qWX`4ZdFsErQD&W8s9 z2bcIq?4P9V6P?-HUSU1o5)kjCfZ!VP?<9(T4d`*l2Z62KjVbLf);I1ye_@(yT=ABDvpz$-dKTAgPzw$Jvbi|(heXWY0;ftT&(i8473jE`Cv^1O5N7RXf+Mj8zbnz z#tdf2!xIJFnghTtCR#T9+TpKcZ%iT-II&C&X%rD~xUb|nY(8M?4dK*BHaZ?2V0Qu3 z&o~+wOaPKHRANVc>sCTk6uL)Q-ePRlNNEWukY$_r`px-KQBk?tCCxL3`#bzVW@QJb zjO>PrrP%ET@*m&exc0eHT>MI=vxwo>)zOL>$Ffz*p6t-==gwj#6W=a#Q1aW3?2P+z zP+Rs%O9=LV@PDIGVK**ZrlX=lLQc*Y<9oC}-oj5fGds&?(Iu7>=??@ae3N|l(u1si z^zdo2xDf~;-rwgfS6XP34L3=xI8l}Z`4hTsqb}<|9}jX+#e;SwMJ=sz5cND)%QC=1 zE&9lXJ+Ybpk$k)}I%;p~>+6}#XjK4H-fsB*orRvU;A1HHA&C5YKB@jz%R92t$RA93PoyMLmsVzb04oLP_bn95X`m4bpvwNogq&^ zasT;9sY5)n`H9OmuhmQG|1be-U#v}}Rx0_?ne$hE`090lDWt6`QVKYJ0?Gd(RqE)a z+k&n-1%|J|kdd~d;4roPjntehBYydiQJ1+h#$REa)m%2sVuG z80Q=+lPYa?v5F3u)Bwuu{{Hw8_^wmhwKQtkT>P5*WuABN{yuUK;5LWvA|*I*z@Udd zKKAD{`pz_7OHXAx{6#Krr6;Q!bn>~bj(7mS*R61cpnp68n77y#=IRMt16Wz2kcX16 z%A2si<4;Gx-k{sdWhH(TA491hI%jS!2pB#}+gF5#cM3d}@Vx&prSLdyKveH70uKSR zF&`-{SeAYIRKZep=DQ&OrI=3~<4i4aadDFqAZwg*%Jq8se={8h4jc+Qn{dmna-;GR zU~UHh9-~!3MzK1M@&$eUK<+%&=H^qwkJVyi)`YED`-d!?6^Dt2g~dXt!xX4OOadq> zoeTz&f3pv!M868$uX%T>AzWnlr$6ZZA(f=&HkAdXiq@S}Vd~W1yJE66SY!rfB+Sm9 z2cS+ShCMpb!otD@B(i9(uy6DWE&R=!p*Og}!hz42;Z`$GEFl-(G>x7)b?0yOkNWMH z@rf-(Spu-~0|iFNsv0m3J5UZyUg0G>5ZLt;+dtR{_ib%#{IQ*M&#N%}Y286V%5H#P zqMaH1fl8)s@{_+6UXB%^^@ymbPhJSMcS-J(LEih@E1;hgpqsof_LD2J;6(?`Pr9Mx z;$n1e$=EUMZX%cPbesXr(R3;I6}ES;X&xUteGwqg*CN2`E+zvYV_;z5vmRInQRvf{lm}-$J7;;MNyAFO`w8&ysEj>x%`Ex@>a(w5 zU&;dVmZU{CxHH(n)uQ%3PUPji>|ykxdJr6ttD}ijUdJoTPySVbI$j9bYQwhLU-V_8 ze{8TNUA;m{&BOG&zF>u3__?MMmbE3CzulZa#i?-pC{cOW+9d8tgf92<*x4=U-)l#I z5*VXV@SMWWx^cfb+~51A!qUsIU&AVx|G9|)=fM!Zbm8a!KC}3%bkz_%Nqxz+I&ozS z(8IsKHQ;$Chr3@G{l}x0`9STG$~_a8;P-&&#$ zTm&RbdoGI8+p!El(IolTG)oQm{hMvxR>U#uRFwV2I{W9sOFhBSe{cd{V{r(X{rk^& zk6uM0ldST}W-Y$LrbX_6zZ2R5Z+!w7?`d$LY?5@%;47@|pkMengCRUWEkg>_L(7|8{2?=nxzJ^BTm7j12yq^gnNGj}3$&eleouTWmL#c#G z(=yS=`~~1MIEJhVQcYW1TX1@u>+81f_cGkVXZvHt6wLBe57fB+@e{#DzZd*hl0pK$ zJoAm*qdLpM5NZX<8>fEp?ys38!)BT^sPuxKDsT=thD{TcR*w&wU&%j&JLLIVap@e3 zsQq*DUy)Qch$B%X9Iw>fe2I4QZt5dhihigLN(7Il<}X^I92g~_15z*sa6GFOq{ToR z@bK_x#v4AmA!%6^3R0B34)~8g==A5~z#gOHD5aw_k`|)}+Jb3KRsd9Sl+UMtx&{IO z%P~z{n`@x<^_q!RuhS|3q&Qb1oa!F5XWt7*AL711x)SSPgnK!H|Mh=X&3=~naJ0e= zF5r|-=`sI<#vw3-qxylcu-+B2gg~k?X}MK}|>Cqz4(8 zGXXk7)7YyW&;h^M!a=SL>Kc&Yk{9`|IR2%B1 zKL&W~)*EzxqNs1q9KQzA>f}=Ul_FZ}QXgMDQNO<=y9rFbwV=o66CgXjd*}tQiA`;7 zs^@<@>%`tcL292?enVG7??O{V~N(g&o_L%$B)s6Kxw`>QL94_t+nAqqc{vQjU(5u6f_Ztc1!t;PIyKJjbyNnH84d0&|J(%xE)llRWB!}KxU_E?A>}MHHkx-9iZ+3 zP`3x=pdmBy0sBK7-m9~J5ybfbsPTiWtQLbMw$GJ(Qf1klE@y}Z8X_mDol!(K2&Hwk zKuBs1FnnBP0Cv7^6OiKi%{NzX)fIxu@O1uhF*raJ2)&?`MCp(^USbFDV%Bl)6gQlX z(f46~zqPqp=LiPadxm83Nw#N?q#`R56QLC`G4ae3fs^fyH$65`VgeM%A&Mk#Bz@A! z<1rMLmw)oM=Wd!@0^cHXF*cU6XP!ki(vXBT75C%Pk*K@}#mGN<(dwTCfHzB9j6b!) zzm)iyg7Z!~Iv+ID^r}M6VTJ|3if;!1lcb922+%Vmzqxq!a;Ly#3HQ zmfP&@vItQ6W}b@!hR}11Wm;=1NnYeoI{%)q?iF!8vh#QK#^HE^o42u zY;uPa_h3S&pdn91?;k^HKJbR1!cK<5SZ|giyZ3}v>C$mMg-Y@mB&RiU=P>>H;heb2 zB+wYao<8aS+;wg2p2!8$o=nBP#HYZM$vOIWmIsU6Hw+6{1OzmdC4_Dn%%s|%L%*=d-$G?{#ti8?HZWCq;Ci=yo z_6^w9;}UJ*_`%&=N!cw0xxC0@mDdU!F|M!mJzUZUo4&z-&s%;8_RFb7?lPglsKqV; z+$Q|^!U}S8Q8hlIxwg2t7?6FCDIjfzK|}P$A`-l^fD*7(cU6!n4HufUM9@iER;x-6 z2HgiV5eWaFC{O{u(y!yx2E_DeRgms3|44pvY=PIWgUzJZ8_52$-)4oV>ihWk3?>O9 zshR)$<%15Vb^%6CPDP)u`JK-(Pk?ARn6M7YE+EaDnJk7yMvg(3?hRV_ME238dt@@G z2k8Vlg@lKb?<@MoBYmF0{Hgd#gKNJ_aRx^#`mUc$hVa*9@GK#LH}@=`k^Fia1feD{ zivNjw1V}25t!KH6qL=?BY#bYWFz<223mANWR(OoZr#|B3zqK?F!R0^%xA(?1-eMxG z2RcRNQ#N!|K+WN)3m5-VlCuVymMy4Vfg4JWJpv_#&$>i$I^kwL|61M~c%?-!auS)q zD0g4~OX<##c*hFjMFNZY4KOB1s{W}Pn13zH@j0NtD<6nhntuglyp%Jb&UbD}w7Dxr1l?L54tlR#aPZpieT;yBIIz;Rxj79tg;c0QM>GXv|5!f`t5I5%Odv z_3?k1iTdP?S!5Cb1i3ga@lhB!RsH-co=dn(p4dRqKUE$70Y06$p1bfDp9Q z0HqDcE&&umpj-w@WZMhfW^?U{o5_til+@(p>Yy*6I{;YqO3j3zy^cj)JJA?skpt-iRw3H$mu&HJoD4v00K$#anHwxWcv%@{ivG@dj zX(KI>_7kFQ$4kg*2KSeb*Jb`YjboMoq(4jmuoj%u@emWlo||aG_Q>k=*T1 zAWa6Gagi#kYX|f6M6NIgVaO{JdvH57(aLoardTCGSpal}V-5*OW}ZrF=y@wDAcM*u zxT+we<>gURZv}gfgUzE-z%%epZJOqqff@q@iUy}@ofH*2hmcm$h`YbU1rG>F^?UlE zK!w`}9UZqte7tJ>&VgFS`^1YEFB+;lmW{a2f#UHi@aCe8b-=YY%KE00cdOjH<@mGI zF{`86IB^Q_D8ett$>@tp@Jb`#TKu*;($dmiUI(CmoYQSpXV4d{B0jZOJ_rQ&w2CQC zQ~+aCAr?e9&@Tims2|j9(qv+q+kr(tbXjCF3U#G2&NcfUdIQLn;De|RBm)8Ht-Hx% zo^LbEhqVWe3q}Y+5bEkZ4vrJ`o$(s!XTjqfjU2pj<^|p{F24XBQR%?LhV*v+O!~C| zlgvC;V@(R~L(%g^{cs16q7Pz@*0EnF&atBU@{Top%DI0w6JjqR_}`@cw?ISNICLo& zRKQvED{Nu8NBl0Jpi%T$uWWr%#KzM*zM}+tmfAo=@*g`n3WE0kOacJ~4l2}=U*Dh< zbhS1#Gz9FN+WqILHudNLg;0*6$EfSs;B6|^K<8Sybj#Wumzxv&b}Iq5(Ve{pSFNbP z*$CH2QuO^(qsL#ON`Mvtk8zo#n4@drQjFgI55H;c;{wmWG>`lxy2zt@p<*z*R8kSm zYwnt(v;0eRKJA~o4+FycjLRWLkk|hV)|L=^;*&J2Q+=t^wMHKQeO>~QXPDA_oX;w+ z)6F#SKA@l-%!BZM{vwE1n@6G(zayoXW^Tj(VeY%*sqWwZmF}das8D7^MO4bjs)#g5 zWRL7o2-)M%LS|%ToOUWBd#~(l%3j$zw(R4a-}Od9^tr#^?{E3%zI&YY9aGyD&O6Ki;Ak=9!UsEJLTyzw0sTaNV>y=lr|RP9d4*h6OVvy6wrTnD4Iw z(iT%T`2HlC@-Wdqn28^m<$ALf+GqUAPjVbITn|JI#oXL=QyWZQ&>za}hw(%PBuI-EOA`X}dIr&|J zrx843Ci|*I(~kmj0MPscu~*76`{O9Z^EuAn;)jhj=%m(T>lDJtfVx1qU>;Tcl*J$fTIXD#^&6+Eg5AXkZi zO#srYj4nULty{NtCT!&?(KSW0(lqqwL4^hjnc-f3a-}&Z9j%sl|Y zI$|=W!#(vcFSr5;U8p67&Df&>hrCv>`Hk!f7~(_yRjkF7I*@2P&^S4qLsBem{2j9W z((-bDlIp6e6pNt@sF8zygajnnpgxbugqyOe?l{~awc5|P40$0UDle_CQn|s)N9RmEA27!)-|3vmjfx;suImRG)q+1|QjyJBnTE^n>KO@)JZcV(W-1lnc zK;%GUB1_H}7ThD2`Jnsx@aIp2t3(BVJ}V+4w?T4&+i~*Jw2(d&??8Q-A__{UeVGaX z|Ly{50XKz3vB)z|lEPrnCr@aW&%kGL9LpKj9>-pt)^uai<8tkc z`7pfZn)v+$x*;NQr4GdD4PE<+hQqwSj)vbF*oEY2+x&fDWtY8PXvi+;y{xA$7tm^Gq+fmBj_;?@Mu?Yd8){5FFTbonf|@}i1@ z3{%AT5e!djJM5T(!1p`U2z>W~OU^72YCBP{1H1tAGGT!x{~~_X_%+CIpiBc2Ou$gG zO2&EM>!gJTcvS~!cEi zI-&O6$h^K`#~L}&``sO`=GTOY6Ss)Q@pJfJi89v$KAKk5xIDa}7Sq}-^q<1@!e-AM zhXhu${N!3&lRW;hpvfDVrB16u+=|$^`zo&X@l=sXp+5(Uc6bK5%fUa>ZZIPJL&!-M zbruCUi1r>h1pIL2vnz>1Bxf%!6K1NDt@x@+bNbiLcXZ*J2&CG3_HRaA)8_|9eNMe? z7%u4-7tWXetj#+CS!+-KnVxYQf2_V4uT~`lvZrEb1*gz$N70ND4 zjP2qwfS8@NWsMXR3EB?FJ6D6T4r{(wH0wmcrRWPA#~&H{|Dhl|^DCa-1@D0e#AHu3^uC<(4b5rKbLG9;3;MlU2q+{1Q)dld z^5FL!Qvl#0#CExTGK@PiKusoj^| zr=4VI_7A*+iaLi*86T)I{f;R5x` zAg%Xxf-M++!nb&Ka>%GAJTT^xq?}wpK)_-D)A6J%K`g8^XLxw5fIl&j4@SL#u9!I` z%kK|?cbLV^4s{8M#rgpA3O2ZWZl0d9|K|69M_@d@zj?DrJUJI5U12uj}G(ur10Ux`DPdiGChqVL4hjKo zC)J5JYV0Qc$5V%xB(?ucYSPVqQ95FVT{%G%QlEr;6^_;K=3_n+7*u?tbzigWWbjAR zO!UhzQ;F6=n_0YITTQutBSfxvPKGBJAC-b)w+oBpk>Kgr-+!A2Um-*yFOe8(Q$E*6 zti@ONV&L7e&6FBi{Dx7cP>^b9Xn@uP0Dugl9^9daF1)H7>nU&4$wi5RR(616vWaE8 z;5CB)N8fPLZ#xN7sLr0sW7g!aa2d?mHh^Jfl$QXC`ZLD5KM3w?|G+?e`9(zkD0mFY z{xe*pLW1o<;r7J?^oB>>XfFYd)x)-%f!UQy?Yyz z`?&EbDQ8A#C1iPGsX1 ztWPjd1ZF@-M~8JEFLc_B|I_);KmUNfbUzi<9tnI+Vult!i=bfZR&{Sa{ue}ickS%v zz$q`%V;8Sn`7=Qj4xX!-*;#g#w8Q2J-OVaU*9u5vJPJAn1_l7ynN%6$+6B>|>W<}n z_~VDZ6@w0M;?^7z=>ys~4}BDl&v9TQ+I4lr1s6_4z;rk#hK37mKhp6?>v{1FV zRXxy0%-{Bch={0mzAPxEq{J%TXe}cuDth6g z!ds{cp{{;alRx9j#oXj-!m&}MO{L5Q66w@dZ%=EqWVc!kcA`1A+mB|3AWqp2f^gwZ ziYFdYHan3w)OxDPdrnyI_uT6F3cmWxQz@(AM6^L^K<%02_GWfeqj4a~w?~w6MMotE zjz2HnAS`A3QV{cx~0Op;HwZ zgnyp-lUfai;Dr;}UThs#TWu+AnWi+`=RzpeJ%q--o;y!N^kE0bC+{0C8`RGF6FR@0 z5=KE5Pxe$zp5!>atqE3Iaga+ybzYIVO`e3{wM#hBwcBkorQ1?>9!m2t z@ET%pe-K$LPv%{ex6sYWlO(Ed57+b!GpDHNB6xX3pc0K} zG}S~VzdxK9*O04ul^&Y^>hn>_^>!7faH1nG{`bGWddoP9Ir5`Bc)wsq#*{9JeUfqb z9U&nhGqZRq*&8=bb8@!ex$|Qi97I-~2|?B-EjWd$`#lVL!{d3PQnuVLGBd$orhG=i zOjcHQ{=v$cvjL<=m$vvE=pAUa%^lJ2m~Gd%4sQq2`0d+#0jV1|;(@B2-456Fv)jT$ z<&!W{D*D-P{Oh-G&{bL>P~Fs&X3}+O*?abQhxY);@9OG$a#0hWcO--L$;rt9mNWE( zhKRZO1h^Q?&dzqF9lm(^axrj+u`w}6j~+d9=1eEHqM`z*c*HmD`t=x~^E;;t+uGW| z;sD&lj~g_cIf|?eBt4wU62;yw8C^~$!AHOK zFiz11#9Q6n-4Hww=JT1dva(6ygA4m~lE7TWm~KQRmp5kT9(NHNYwJ!qQhfbBW(_o4 zma_qU<4F?KQF zX`h;^=wSo}2JpHljy`-*RTWKDpnihI;qZ|oJT?>IR2FydGM0-zhKS3nC>zd*9CJ-I z2s}0T`4^8E>}LEsGD`z66VHfUS;gy8hDE6=&s@{ie#?N_9V9O=KS7xkp>tDRZCUgo zmM6LUa(4Lq>h2Ib3(=8u^4V7}KO05`ekyD~TO)WXmiF*|_fSEUipQZ5^Z<0=&u(w3;W!?>e3y9; zPIPAB20viLJQ-|BZ)|B%xps|?3rd~!jg2;~Gz?8KZrm)LP0I4086{tsp-vqQw7nKeU4njMXqvM8T;vxfn zLb7cv@`pZ9Z@$app!-ySuyB2oVrC zIbyij`_G)Zi}8S7i{s>rvBndmb z=(T)jm$8`ZqG21`aFcT>kdYO-<3}@Fa9C`Oo7F7p7S6F;uvANcVEbDD6EauUj#Yev z;Yp%6Yu-^eGr$wSz%YcB4E19rOEI|foys&?afG+Sm^RTPysmQ0ZH^)_CY18P3B#_T z9klrY2gJt4~;K8gvfoIkj1LWj1zYU z|N04w^-1(J0=Fby#V;a?11V-se=dBXn)^?$sriO+mSkO`Q9ao^JrdW7M?T1_Du+Od zfl17ZUzp(c)9N)&cSu_9&^%@+s7y|rpqXl_osh7vO7SX)CR~2j(?i<3#g_nh14fVU zDC7tTHtXo}>3G**a+I2Ul>6E;n6|-z_{%4_i3AMbCWK(hO7c+xhwQ~D31N_6qvi@fDB~Sz>Kc;YZq&*Kxq0l9a(9b3+wwz%R5s;7#3=OqHaRaOo zy09PuV_{(d^LlIOlSD!HwdUzCN2$5*=3_^R8sY&t!^! z3H1&qU~6l7eB}4zJrA)LpN|`tt`ERe9N3(dGi>Vxs{QZ?WW~@p>)YYs;c+516ij)Gqb+wQ)?Tqx2L`|9pl~n^5v0p`354R zSEAnvEv)J3>0rcXSQg9^o0vG6!7q~3brMbuBB_Zjig@u=wS0lO(>SPg7 z92Uh|KQI`Op_-UIuxwA#u z48ViMMff0~Y4A_-L2&fdt5+e+!cvE*2Th-!g5fv&RXyY3pKx-dp>ljQDDbxcHSDX( zUa)gLt@ULO53Ot7=;$Z}V`i?@)Kn0i5N+PvseO^L@8sPV^ZCymmbb@W%_FfBte8p_ zi4MudjajCsP#*=ct5WxCLle}LK=;)pw|)PC0})Q=KNqqQRcDO{k(*QRPcbW)&N>X- z3xpejfaj3wK{7@O9%$b(GBS|bmqJ``e65P9!1luRT?e1v>E3q!9pzo3U*C?0xu>Ao3I_bs46#)72cck zK{w$mq>!MAPYo!x-9!It#~~mnk~fyPmCrYV$?EAb4fNc&I^|j(B#yf1yQ;PmNy?jp ze!;UNX^efc9~Jh}j1d!UPi?D-B-{Qr>P?U<&-?jGDo5y=K?Pw3XVe{_l%rXo6 zS&Tt)t1kO=j%jNva})+HKRw2AF-&RlUnE|Uo@$MUKkun{m5Dd6i5-dxw`huTdONjt zEL_N+_s+i4prr~Jjjs`tXkhO&e*X8b_Z8r=|8VS`$_PXITNB*8can8pQ-5>w5`0;mhcADj3O&C5YWt0kArh%Z@B2-l{dp`1x3bRD zy2QQw_4Q6wsaztnp0BxID=MxjD<3PkapMMn!AZeTilxM4jri+@x_W{E(>F9!UR)e| z@>yhLB1bki+wZ-Kb|RI+%62m$beae^p3kcGLjAA6roG3Laeqs z_cXBXkQ6I2(=KM#Gmuo~9f&Ju6kdAF32dPlT?2|9IO z#s&xiF39&$I9r}f;N*dTgeE3H2wEU)l2N*@s>(x2()TWl&TIPaixst5{Ad@N!icNg-uoV+JWHTqH8 zUv{;f&l#@<@|bEH8AX}a@CyOZ>ifl=kBLQMo)D&{S_~y5CW7rw;nhUbi-iNZq=sj9 zcA6_oC@VLUpr(X){GJ5`!Hhw*`D?wHjW5Ud3ws*jVKFHPwKDSZU|vA!ng<>SdUoSU zijQC!WWscxj|{nF5z|~PmN_Y^<-o546ES4@GR}5-6iAMOjuBw)!M}^A&wM9wa!7y% z$9KnEF@;IY?xD{O(?<9SBmZNHOa<40SThstVJx(MPBzc;lE{->KfjpU{KD_vr?f0B zlR)hX<`66_w+5S%p_1e`W=Q#X?qfK}F7-;`=Rmq|)v)EnCmg?jhZ$5a0CkYL2*X$& zPikH!=H@bb`b09*+f003+#kUsjvnB{e)kLzg#(@bWGq^a6|A74RBsZSn#v2K>~7vX zYshqNi40zWNq+5eBD2uY_p&l9J{erz5dB>=#VJls1(HKA1TkVJEKC%CT}HZ(yFfHJ zfWd&TU=@gARBQ%L_v9RbZF+%9L{gobS14V$EiN^~ZFv0nF^%2NN^tUM8R5t1Ex~yL zf-Q8*xA|o}tV*zzsRe{xt6G}z4vq4NIe)Sf?znK#efz&JP$27YuGT^7!-)>?ENo|E z%l{AWe95IZy7zCpHWWM70a^>i5U-ZEca%Lnem*#;i<$gny26Q%;-QyEb{VJ#5W!SFKtp_}+d zzPkuwNdP0+n_!=gMH_#fgIB}>>Ogg43ZT|I8;qy@$+@5ipKj`@h-utE{F6zM1+tsT z$u$<}wACPPF|681bw1=HI*GR8#nc0@K!lo=SG`XYi(KIh-K~HBY~>!z`Oy2BV@;6Q zKc>_==b1f1`?u@urTYaqi(_Ndm|C3E(G=VdL9~@=B1JKG{=1Rro*G=Ybje?X^Vg$M z7%Pk@QkNj2wKv3fK9+e{J5B*S^!gh`VawAXW&!L_>r7eUoOOnHQ^ylnW-9y-@BHnu zJg>YO%{C@E%&)Y$dikQ?Z_v59{jW>~ly+V>cR`8Y&)*+{I(YmIG^ffUM5Ph*d;y+^NP=`l4-NT` zwU4R&=Q@PEr=@iN7?_tj-+xS7D-q0H+0>+@pztg(@Q6z6$B&l*OXO~zuX-}v8>41kc*ks&$`mx&bywhST4_?}M~> zq6o^vgM)@V8GzL~!0fdEF6yru!WBRUziJ4;b%4i!vPYh4Qo8nMG8Q(r1UBq$(YT#Y z0|WaCt#h?yWc=(z(&K{eb*>+X^(?0U=bS>PDuUA_*$3Xz@hnmCD+mR+8IA>P|7A|0 z-~TtKFwV#MkKtbl8Yp^tnS+zRsy&DkE%{oY1|cVRm>KMzR>GZ-WJNZq-*Bhk5Jt>W z9qINp%cL9!t=&(_Q~TpNL3$i>x9>X7zWe$4A{cs##j-tYrN=9G zG^bRL``he9MI-rsB13t=?w>Hm;3Je?{MnSj1)vgo%(`tU=+7>t6ah&YdZBb;IVl z(fOYlIfTzRHhCbI|L7s3tyNH)_#GaWhsmr55b75h9)ewGe+LMH4a;)EU1$GF$NWOO z>*y&~!Y`=#tbd6XYC`|h*VlWlL%gO~ig_MN?%%&3ifUa+5YwWgM{otc>dPvhzjZeR z0>t88>A5vhcj@HuH^Y3Wm7yB5vE#|Or*-IN^KP+PFi21X*a9H7{e291U2D@oNDfjRNb1FD(VzWyMMp+NFF(iCW0&(gwbkR{6MpV*U@9{X{eFAIM&6f6yPS-WF3Aw zj6;$-q9oY}lmdvr5Z!$mIS;zClWZUTch}Squ~Yj8`ud(|h`~Y_K<3HwB~a+kwx*tKWM3ZHBMppsogUD93MD3C3}ul}*>xX!)1=Cnd8Qv+ zxH#2rs6)EEs4wy+PWEKg*=4$|wRWH*1NyFlUtx=k;RJ8Ql+@I&DJVC9OLGzxs_(LD z4BLU|qybhkKj5qiXl(is_j4{2`D>^4`x2Sh$ef3pD+DKBWX!rRHpT^N-(b-9C7N4} z1#<)(jT=!E-RF&QOx^%~A=E@R~2sD3i; zTnfqZtlK5MD9zW7>lOj4A`_;B>?Q}5Z*eSf8amqA`5nf$xQYyV+)!e2@Ej>9D(2mU zI#MfcBGE^Xg+4KaV=!~KXS|-E#ECmWa;wU z*aJCt9sPGPCLSxEJGYr^jT1Rse$^OkS=o>?40}3pNo4<$FlaS13zwI7^d6ihPznt? zuR&K*38nvm+v(lVk39*#QZDB%T*w2)r|WfNEU8Ij{jqKkP%NWG*xdi1MS3@2j_~Yc z480YS#@$Gi94We!)KH);H4GF4XqN$w($Z2Eneb2l=EyeQgNB$JGBW1o=K1Q;U@WH4 zn%`VeL5J&CO}dk9pGlJ#G7xn{z~uDl(=N^7l!-crIul|QOC;xE1u4dls|E|kL2AvT z+TTAv1Y~HzC#e7){ct)^nKK+j+`=^TT<0WjeqC@1Dk`N|?W^MA;=p8aOWYEm@yrA(h-wjM z$8m|5R8iG6HJl*vxX5^2Ma3SR-R5~A{+s+{G>0`hZIXaS;l@KdkwRW6X^*_;^YLH)!bA&|A{Bm{hr-p$sRq(J?G?|9cOE*uVZX7X%S?-$7Z zj}t6ns(@Wc29N4VMvL4cu^oJ$h`P0L6F)-EY$feU_P8_n^x#kw)LwOAw?F%=(At46 zmaTKz_}n+ocXReg<>S`*P4gB85aHLtKACdy-`g0rDRKa@&?I%TYj@^^ptNzqh|vXx zT)70qB+D%#8hhG0G+GRpoVl4GvfWfMyc8BQv7s`cGRR$087!6|B?my3DKLXdNbIuL zzzgk-7=0O9UTzeW;-z>}W8?Rs9luq0s~@%^l>yY5iR{XE=vF}!$8yIb61yjF{{16* zcjF_9ZF7Z;bOeN&?h~v1yz~gQ{=+O5kMFA^5=5Do^78oR@^8stWTEWvSlXi2PB5X& zNYb2Q3fcgWi-eF6oIBZlDW7`{?IJ?uaCj};1?2xX?OKAx1ZN2rZ_oS-UTexrL8H4; zg9&K$ubJa&UD0Zk_uqV-NjN@Fm9FdvpTna^k3dMw6A88a+qcv3#;>k8``+HZTw}9F zA^Ht5;5Hq8z0Ap9@J4?byb07eAFipfh6KYwU89>?W6G+1c5$DpnUt5W~goxo?BX zQH0Q<&;g|mXW@hGv0uS%061z;F#xv?DxQ>-lr3SJB(^ID1)fr5w~z5k___%*;L8RHr`EVzX(R(yXc*B9_WF?Z@uky<0fFKwY>d z@_--#+7!NsC6ZGr=RD|PAQICL8ou(fQddI46ZC!>x5UI;>^2h+{90*awv(x?dr@Ja zX`Gpvfm!S<%oq#?5gfR|P_S(1vg}NoI`BLtMWMf`wY9aPVi+12YrlR49oad<$C(G= z^s1vg=CMfiGynU*HGq>qFKqPCZ=nKqMLxR@nN;#E@3pF%pKLqU= zur1=T<#?_b8VCz!*7Cmpz9np6#3Ag!qw z@Rf=%Sg730(T_yOCnePhXn3y(xqlGeda|j|7Wnh@5wS@A=~D5*AoYs+`XdGHW+H-i zP^SPo$>cWkP=tnYBXWbOdzi}3+4h40b`8=I2>8EI;#)?L#u%(<(6m5GU{bwsD7M!l z43w2arb_ftVZS7gt1i&$bn8-T18`F9B;%lK?Vo%$uV=LR@=MRya?JD2?Z?^B@z6PE zjp!FM54ls_25ZJN``;}SA?=9B@#0g>#0OO6X2FW*&SirE0nl)HymQb%aay}3_KzYE zvfg^l1>V=l*eWQ47-Q}Q>}eSD%5E{6Lkv6C6TGiLDXJ*5r(M+&g2?P9sG4rwwhd6q z5$2AOsbcm_K7$7oa`^5-^ zmHJGp#MjMud7sA`Eesz)V}dX=Ahv5X$wYJ+Wd+#p&d1s+q5H{?P5Eendd0jxOhhck z`^4Zx>C>|UBz4*-kS zxzWT{E6r@ja(=S`W_)QfjL9suK1pgkxQT$^{4WY-KEXytV27q0uZPoblQ)QZaXCE zSgSt-N@CJB!`*mX+rwi}UWhcSii!doyu! zY2SwKn2|8uTZvFse|LWPL`4KIqQ*(Gubw8@#i$Xf>Bg{pi&`Tgs-BP^_8sD&5%WZrirCVdFRC<|jN!>hh2HKh+ubK#iEEQDD^*Vf)q8iRtEEk;r zZfm)xmsaKX)2+6WItpZHi8|#p^UY+?ATYoI*Zksv#bs_^{AqU(0D$SB%;^J|N?L3i z5%>!DrpCv`m4x#1vao!7`SRtfS2+zjaetIL&csmMlG+YK%v8X_EhuQV>M}cAA^9;d zDFXKs=3I<(`K}czNahL%$SCT9)Ll#9!bfxwToC9jGDv4Pu+Z)nfbmT_XghNl#i7n> zPp)x-Ve_wsAeUPL7g3RsgMEFwMWHWlh3Jviy)ue{==wYq zn4laH1}O&8l+m!5D_zTa;thT64Ex>fdw5%SXSznmODvQcqB~y{{U#^SSG>oA`E6u8 zkS+yfYldzZzyfs%XG7*Tzi~}%?K3en(SaaYU-t>Ut68_HPD!N7FmgCupYUAHDpmbzbyM2U zqkO=`&2~eNzMQo5*P5D%Ja+ra$CsO4`Y|r+PZ%I?0FI`f;RI<^{( z`^lm@{Us|ai+?ig(2K5Oe=zwvL*tnUT8@4Dz7#=U3*wHQhRbs$2dQemTIo`lPE*U- zd`o4gw~+$jZ0fFDA`hSgFeo5JDmoQ;wkcqhldBA6lyA}xb7~x z>9?mpWxCohSoDB-{IY6=fATj=vDHuM%1uH;#uDNibee%4yw5gEcvbJA&1vm;N8%N& zr)%+#iCgg|H;3^yb_G!QPFSpjkT4uG{I;2C->OY!K~CprjfCYf9TigCCogXiHX9E1#(%qMq?*`xTSl-!*^aB zbZrGOE31|+TaiR~aCX*SMrN|(-iMrrmlZ|ZK_b9WX7MANzlP(=ihh?lc?lL+^6D!D z`8E$h0Xj_nk&^}+($jDmH=OnKowH3oHHLCUm+DF!D z@*<-ehfvb~H$#1+R@XV03~&*eZX(MF^2NqxI>xoNk_8}0|9jVfldsl)T`QSHeJgJJ zf#_tAPi8{kj;5K*Cb9nn9ZevSx{1Tc2p_9P|224T2qR&{nrzvcqMn*;`6dwA=yUWq+JWz7(0W@M<8s8Z5*!?3`c>1DsNe9*kZs0|9wf>dBZZt*vjWe>;^ z*V6(TkU-l_)Wh%u*XSu_w>-Bd=I=q5F7~r}vN>nNu%9D6%`JML6aBEkhY|^SF7=o< z4cSkowzIc{hK6Qj2vF)Zo3iVD_PN5XS8h$Z4`yYLLvgEC1g2)xH3*Wr_ele!PKoGBNX22Q`LNkNL$g9-xSg@)o0z>+)BtS=SK1gb8q+CF6o&?E0)v^r# zv}ySqRg;IWSBK=tnM&QCysgt7a0(H2q#5rY`C96I$~@v{9EI5Nsf|FVIk2`g7cF|tt2&tjMoYNPCiJq7VN>cDhfI63G( z0cu5}3RWL9;t=csvC~>YY;S3s1uCFSeiA(llakKx@^(#u^JGn0;|*qy+U32H>!Z zD5ybB06UxmXcY2PcKZ=mnV)SFZ%Ii#6-3F63`#K2bD?M&eB-NBM{7kuwyb1lR24By_{WN(Z#mFFGGUVvf4<&px#jM zdu?qkyL?P;O0wH&eEtC=ISU{8q&weXJRY^gQ@_<}i-oi3>giE*5rZegKNH&p^A21` zBv3QeQMO$yKx<8kgj^XV-vcM4nr0bohN7BZ67}U@+#4OK-ocW7^l0-4`OrLy1M145=V!s949-J6zL^cPTmhgC4m)@csMuw{PEq zvIexrV2eN_U@KRaI`;r1t8ZWuio)3PuF4yomLY#~1g3X*WzH|q3m=+#Jn>VWFxo|n z|0lFhf@|7LOPG%s?j9wBs^?yJkv!sX<6`jmF5d)>>6m zdRSQ4r%%_U#l%y5PeBnaIL>2G=9KY9_O#*i=H?09rjp-}NWCqbmSY9Y6%Zmk$@R_6 zA!syvEa%YQPxWPk-bI=UHwG=D(Q;8*J7~y`C|zDFskAO+SdNlbnGpa{Uj3Z)o}~G6 zR2kGby zIRMlB1ift`p5)OVO2wZtibIuZq&42Z-58Kc07~`0tQqW!;KaL`wDZ8a>#j=C zAYIf+Oz!e;|BBTzP45_8 zR)Gv`mA`s!1Dqoh@t*k6ujquXB@Jv>FJyu2ixT7Gj@toz{wU9wEF<5SppDQeZm4tR z{EOg4-|Caxco;%3_LNsbdHdA8y*>>yPD5Q`VJ5AKi z@rI&U!cV~6lRt5S>)l4ntkwg6xwv5nhtrdhIaC;q9~&N$N%Y66VJb%smNB(RQ9 ze5Hc?|DQ&oH5!e)@9850A)zkS&c30C(Ehq@Qe{4$;|Ysv?_a!l0p%5sj~_ljo7@n} zcH^~A-fBx6AJ9N+Z9$_^4KjWs0HqS+>99e{)L?51Kx^!k{wEJ9jF0FdT_KlG-I?6sAOY?w!q5Rp26~fqwtt1LBX56j-Ew zJt@cUY-4ZgwtLg{+*~6xD}Z;Zz_=F(G4K_x(?OavB9#ypCQEv8gS&Z<6m?rsK|#Ab zG{o2U2G|i-R?_!??GMbE^o82mys{@vn>i=nIz}3ptr_j?oBzc zh*t>^5;(kH0+o!ox*FhJ;BRsgI1W8s|%)#*$sFui39_M`Mo~7d6()>XkW7m zMje~JszW^c-b$cbzJ9$sJ*su9J9HLwHRc;@vJ9LakkX`l{D_Gzpn*A><+*ZL8h4Q{ z#fwQ}{F4eiJ3~O+tNUP~ZFrOF>Rn<@9g|pmb4*Z4Ny&V3FdZi~FE{7Fm>_P%6Qmo{ z#i}?H=!k<(*czCkB#Ql(3%pF*zt+VN^ zvtxqzG0@U6;!PqKv#2ns3v6U?ydP&`FVsq^>AFGU>$dssV~1GqT7wxA z#nWJ!$5Q-hXvi|8rbgz(SS@O6uH7e5C4k; zrCTP`8S-AR1o>J?>C|D?Vm~_oJC{{;Ad<-c;YK!OF0;_L57R$DrG^-Y1?XVbM8nX6 zwke-4=YcnsHQXmi#Oy(}mETk!A>$#Qx0aI#k0hgf5rxHakh7L(=EujmQX~5@QNfuf zru!5OmK%#q3PzT$=#c&)YZ9=W>R9B7&RWB{;j2+I5)ynP560s$Spf^JxubRW$OUs0aq#u?Jql`m!C)Ch{n%?C0I&KBgqfZDD=0jC?O4}1Ro1Isz zKAhGFor=esN`SQ)K^E`Yx|qH?(_49-r7_*@7Wr<30=RE)_!;EOqwKHRU&q=HKY3W& zYNt9#czQW-j)TdD^naReUD?Cs5o$X-Hm3EZ+E3Z4xAG8ZpYh#Ilaoal>*cOSt8ppm zxBLfm_d^lb#WxoOX;Lj}#E1*Jsb1I-{XKu!Z3{n%h^}Ut1@$t$3RW<>s~>qugH26Q zhDE_rJTu^3w)~6Nsu9h5cbwzAGuI9Bds3Ccj;vK>fM0eQ1Oz4XVs>woBTRuBM;vhp zc2TrD?Ln|oL9HnK2mTfi5?Jwj;zyEEBfgNH!frSU8i-=#Wjcb4xd9{mu#+IR($oH@ z4^(7f_`(p_9@_gsx85SJcHwzVxga)25R2^*6ujH(H5K&Ji1}?gYFMZ2h#SivbS9R` zo^Q9{YiHUUxg@O6Xd7#9k9^mHrJd{92sG$Rr@*ABeW(I{kC}*xNm8O`PyXH&o$QFw znmw6ds59;LT+gHJM1J50rXZND=NcyyMQhEYkvOw4e#$fJluBuv{&&!YMcl8&Dr|z@t6p$T>%dt2A_(F zCcT*#=X>n@l!ludi_h!mBgZgjl#_P7n;{{6i6s-cQ2aBL!KLnAVnnl>Q)S>2?lU%M z9*)*>bt|yLD=!Id9b*1Gt#wN0%Tr`nt@7sN{J}QATh52ugJ!z9GW9aHb+_W@TA|5X zV7ikm+7giJWU~c2DHPEUgR}>XK>+GQBx{cc3Q(2G!YiBH3YNV zMp#2Q-WV{zgRU!(zk;QxA~@~`tHK07W8;zO4Pa4nB4uwr9dty4HX9HP9D07pk$jb+ zrE{MyM+sJ;fPt#N8hpha}Una?b_*7%AuYfaH@R!ksmYXgma}%9}Fj} ze8hFF=*(^dLAqi#;p^=etfGjV&mX6_ef##)j+q(j4tp>7L8NH9(Xg%Yq*J38DWm-);8N|;d)tbVjVIZPIcuYkM6(%m36D2AM37Z@Jm_*?x zy9|V(cs}(*HuI<=v-$|oer|c@QSOJ^FFc+EbA()EuYz3(o&>b7oX_6#iHa2w7Q@J|UJ=M4@lpUi-KcSLpURt+((?&5Sq^v*I z-j+Y)>|#n;aB4G{z`#73N~qI98D=L9pKd)%JvCon{w?Q1lfxQgFzODvudX2 zL*WL{2->GXg&MplSXeHLYinvc3l)u+&EDr#ebG7m78%UOiSIVTF}1hQa~{bT9wywd>apq7LmquEq1G)yz2oZ3|BgDB#gn-GRh}xmi$lBCqj)DF-u8F!R19JoPoXV7psmgaDPU%ITCGj z%!0=VCetx8gD4ZGBk6=k^Cpe$y|mNl2h3_LDK7RQsisV`C`9ht9?)+f6C;qTD8!b) z`|1$g_dDGD{OR%WHw+Wv;xsiiVP_~TRa^h5H=fa?AhXSThTai4_t8HpE>ot_^hI$B zgSh=zP%F&9kkjet@x=9d+Rs#@6?DjQk+M)}5Oab0HAzY14%~|!@`6c6)_lGms*vn4 zDYUPB;)%1u{g~6c;J=eLcCZ@8R)u2v`}yo@MdqwBX3(;CXb2C}dTm?nCMi`@ z2ZeTy-#@%d;+yj)ap40kXAi+%HRr&Zx=YN3+?xNI?wyG!m5eIv95qqKNu$XyqREys z3{38!uP=tXQW>vgwQl{8DR*f%cXC{KLm_6VAul-|ydDrt-n5)*WJqv+r!OqW{BnoU zb8oYQ4fbdKl$~Gax{j)lVEaFp3)ET6_T%+Ou!H5HC#r++GtBKeDsEj80w7caC|?o~ z-I`B85GE8wq_3QA`)1r%8HJARjWCX#^sv*MJe#Vc+7{GQi*%Y3IBflC{ag&W@{@@* zX_Qm<6R&U6n>T(Zc)lf_Mpvt;JJ#l_kWGQW`P6wF;!t-Tc7Jm3xf1%>QBE}6tT>yS z1uyJ({j=Ul#s)>njYZSPqQ*ykCu;R=*zU?(5afi>HtkvS6X&Cdwmz|Q+cZw5xb9*( zI7>nNgL&S7OiHo@WZg}9{}zV% z+?rRwfBu->k8{*&qY7JuZ&zYY?0f3x2d?-zX&+Nktij9jUwz!E-<1RpIu_#1_o2vz z3{{O(N!Kp{J6(QWUK^NPsU8~@H3W@+V2u}iT)Ytz3bz`9yUct1ipj?iQmh@0X&6)9p zMMY)K4yNWkkB>J4&m91v7EQ(l&@&Et@I&Mj%q9XIZQEQA7YW?;)5_1XN=&Kc-2Pi-oQ(cdhK36>^|7Fj6u9{`nsS7OBU^ z##YyzvL@Qe?oass`ExK>iFt&9L1T8JytNID>qBP% zEcNpRmbj>_%&#LmVpl4M0vqc=rwY61-h7`kQCyoXTI~{@y{ULPT3Z=aPEK7j-v*PK zkEMeK&)Zu)^aS@;l9y-sZZLcX{ANk$3t(=7FsA=J;~|wtxSPabbOPAPi3#wvVPs}L zFa}Qwho6g8s#gAfr&6^HzFk`w1Knu&8`5g~@mMsT1K`YwshQeL6|hw-Wbky34%j`W zJ`9loT$*@KpT0q(Qxb4u^hSQ4n`1Mj`#(Fq`-LX+ULKJ6k=8;HkkeH{D%T?vNDjdf z7V`D^b+zgKxiQ2Bo8Ttnuj2tz#Uf*Hyl4x$`dY0eKqFCa(q#HLoshk~J@ma=|cwcSUzx`G$Aq9X6D8rI~?RCu7|l0cUZDi07a@Fx+fZ|^VDP5$KEe_kg;>n+>> z+bU-ak?IX-u^k@5obafxs5p*XFc{V2-a{p}0x^l)Qz2;=vYkTnT1479mw;@%KlC-w z;`=)sd7*g|kfcGbmU)mldwF5u@iV(WBPiaAwPOdxO329yOl`s!wZ_^ zDbO4PkAN$oc;HE-vuNRxe=|bOHiEN}0GKjCHUrMRCMORXd+lYBaVFe+|6H^Qayk!k zz60n2`ZT$|s>=JxlS;4%<(dyX3_U-eoMf`xF?#s z_WU{3J7KiKj+P%p9socX-h5P^`Q-qSX8ps9m(mX7D+*=9rvrs1$kNqzi{gkw*4@Z1 zOgo8O$G|gffyy~}gcNhoH@t_2t|#KMZ*-`=gV~dl>*K2~+Qy3!=>JKmM(_-T##kW^ z*~e%DdBXP0X9ey3nW?)N83V88Ze2Gtw?Eu>a{k6U^^Rx7>eA068C&ON0oqZ=Sam*J zXE{Bcw4O>E&>1NeD94E19f_bFqF-}=`(ndy+cFV5rH#&%Ayu};6xghZ!Tuw>KwxKK zzbfFax%h_AZOS#&(OGuQb8^qhva9OZY1Ot$s3h5Zy$?X`Wn`r(e^vbxxx$<4BcQY1T%!Q@x)l@4 zTS9)eWjr-HYJiM(CLm~&faCEqW++#*C~2`NW=Qp+`C8Na;^F+JHB1ku*NY%fK)PC} zf~i=YlAqm~II)~)fxw7>2GpxZbMbdt6jr zC?Ilg#%0}GIlq7j2n>4XhfNR=SQ4erbF>5mL(dm*IzbyXEYN@PRWJ~7nzFv&;x)l_ zE?w{VS$ZxV!6QdZMjA^+)e0d#f%`hlH!K9hK4X}z5?!3wq$KF#yyn)jeMJ6lCp`Aa zJhSHR6dEmt*~2@m4r>VP+4|<)JD9V0-VnOWbmo&u+vuwl3jZp(h5*EQwpzILf9 zMHlO+s|^LV3ekL#yCx+Sdoz3ESR^<#z_7zW9oxe)BHOMfX+e3K8#f?Nd@B!Ffq^%9eE-5jf8Q{jN@dkj^x=Vki;9okRZpiH=$h$$l#|w ze?riTamPo_-yaDzUF>g;(Ykz`f(l05NlEaVjsLiz(GpWa!43EXrr+p+!61yY0{LFv zvf&id7amJ!DcluhlYjSRn@Fz%HT;qK>BneqlhP^Ec}?e zkw^5PkdZCRe8>1;-YWbYF%Xh=)@7ngX1$2nk;9Ary3Fc9ICGmnVyoR5^0lq)jw;XI z!-x4BQ)DlwZq5W|r%G_XQ;@eg{q^9H#)Z)0V?g0;(nAB_6d`S!*9OaeP+MFhRspsx zwE93b>KYn3`LI%j12x^rWox#dzkTsMLD@Zhx=Bb+M`v662cBx)4`pTN+Eex@h{s8I z+`TI>GHl_j%D-*fGO^p0SK!A0p_-!W6KI1s9`N+iw9=aU{3ap?Xb~YbTeuxyVkL?s zFEHC9C+C(2N)ufF`~#bnC3dOM8f;I#A;Q9vs^$J^-~RnR1kf6Vy|giMBDN+V$?&tO zH;%Ah_*TqexYKxSW9`tJTOYiwn-1R`)&*RH`5Fc;pk+g# zz5n(BJGVBO%|jo@WJ@pBv$Tk<^y{r7pEYrl9L!V&-q{fu(454OGC1&qlEjGw*?sF; z9h)4nxmH(fM@uHU<7oO_#pUmC@Sty}3xd;dTC_()mn!Vzzs`*GD=l z75ay}6;}zx)?81iNhXC5`*jE>KS@*MD_#5=;Ch;twDt9Fbt$=x zST7$~S!U;l2bnbZMB0>1wAp}#axMx6e7L57a(JVv8Y8u}jkJt>(Sx^ve){zfY*d_P zJ*VHbT;8R|c(qrd`Mm85{#qB2)pN2l{}oUHDX9Jw-hd2`cpN4lccG&G=)CO!=0r`G%9DtY3pmIMQf-BM%*&=>%Mr zWFFp1V(4^;rTf0|=vYa>5@#}7y5QgP0Y~NtMJh~6byIP~S{R^D^8Zw;ulhSg}AP~F6eJDu%eb(l*wTn76R&E$#_6J&zbGQ4YJj^Ggdpx6pRfSd}3EQ~ILAtRele z0f`QI$a%2>+mTFLs}9TRm6qwjIB;PNljc0=RXsM^pFV;FH7$>*af@_ivBXx}496MP zp;5cPt(@(YQnHoYy;^_EcCb6tPU#2IoGa8fQEF|K@hM~msB+3ia3h`#Obl#I8uQk6 z*V^lUX#Y&_x@n}c`bVWea_92S=cv5FZRet}(qnqcwc+-6H8-4cRIqopPqeQijOsj- z8odPXMG3!8Xf`?Ms2I}Pc!F^B(XSXfPFlOknj;jYEqD+Rr?*xn^n(`?H>|xDzSBI+ zPMSyZkYt!<_N|R*ZzDv^Yi!%Jl|2$*rhg(_2q|{sJe4zI1z_sa%rIQ2_ByDf$C&E& zub10k8!}jwwTy`Q3M!3k;)4!e=*?WEFHtb|gFs*Xgz$hTjav80sq* znx>{EU)nW!f&7JD zg(@ZB8~ZdgHFFQe3)YrLCGhj*7Lg3l#~Sc>GET(Ma%Aq7;;e0u%ev$F_@Fm$dSWU4 zN;{9wNlWb>e@f5M6N^!M)Pzw-BW={Pa^5r#NI#iL;kMq##qYdcxMXD4)ruC8 zZfHG@Ezlep=p+h*mM*Ri>fz?3rD>A8C6?8jS@q?;bY8v@`{iP;673PJ>Ft7*u58hk zv=sZoo$gdxEnDm2--7Gao-N9ft~aXJ0!`2!iz;`iw1U??6cK+m&FJ8>$jIX~Whl#~ zffhO_#G${%y+2Zr&MNN>A4pdkz66st!sH$DLT#lpayn(sX1Wy8kqImrMzXni-&yzv z1Q*5U4kr6^pA_~lZdVj!+o%#k->CYwqB*_**`m}e*`g%1h3MZ0Plq+#Ii<8O!d9se zQb~_R3=52hdd>|pd4X10E@qEGl>-2?C=~W3^;fM-Knc7d(aI-}ajcUuK*T&EI=UBI zAUl;(T*v0YSv)qC1^`(X?>Cx*Ij54DHTpWL^%ZV~zmE8nc+M1;>>=~Yd%N~YXeL4I z1&j>_6JxJsNu%1e9B)rSJmAWmaE{08V5Yv?cxQv{A3WvUj0gFTFCd=UVGdclXNRhs}+@$^af9I)G@s+*G~~e7r1bH zTwPzeKEZ6Ee5ls?IoX<+t)idM7US&f4DY@q@aj)Xt!v1|2P7;kUJYhlEj)%PC!yVF z_s9n?OSvZIv`!UPy4|C{iJd&`qHncZ!DQ>25tQaQ+5EVT2B;%-e6kgpX=^_pJ9aG1 zFi$3uVPuz`he>&&lio0C)!WoB60ivEj+G9Xtx9o}bu@`#&lX1|N)c59fy`89{_#Q# zby3T{Gd&g~dE!Y7>qs%%;COQ{>sAX5gV@$ zO+30ooAkkkm#lZVI(Op!aPJ5K>g9_?-nDF)yV%6OD~#=OQOo7RqqAq*q^0MqL}}$Z zNAmCZj&wI)jOV~Zq^H1;PO}|UO0AbQ^^TTemm3OOSL~hgEFwoiF&w@FwNcA4*vi|3iBym-}f;0@cLhJGoWx=8Ra>iQK}ZDc~$_Y=;> z)ldn%LfiK96T!$turD!g_dF;6B9o=ej%FK6$z4yPK3*u=kMs`8fFv{=7L>17ScVp< z70VVO8yh34CF(}Wzm=krc-B}w zx$U!R_G4gmaZwTVlI#3BpSG%IEECc3~jaiMT8W|;rgdEiIU(Zw$b~?5|R(~0@ zf0j|%<(*9Q6uz2i7D&?fDa^&gl2!=%+0HXG_X?_wg(kFv)ZPK3BJ+spMHeBH_T0Vb z6~$N*BL!t;@i-JCnV9W1lRO0t1a&T)Xr`%*o!#bW@GjqDMI9gt(cu4vN9)SdP~CNQb?}4m34-EvG8tT^@1nZ%IM=!!k1Z zy1I9K(QUeR?OKc+HTvLZ!F3@z<)meA*Qyp*i8cZztJuthtM$ezsMXEzY8k7y*~ZNFD;MVw<~rC)nl zw^@i%Zj&4}$j?Z;V+}IFzj0)X z$i!Hhu!+X?A8;E@6&P*pVB@YIe}%#7F;X%`9H@30p)`zj>dWoNcr`@KM)cLitN4aC z@Oavu=WBEvOTL)dx>S64XXu`g+1O)Z>-twB%0!W(jDgA5@8O?2C8NJW1!7TkaPX?_ zU;uZIQuC|BKhoCC!X%8ZLJQIF{AJDNEiv8!rsh(j7YuS@N3EL*^Ow%SNt3pzR2Vq} z@pS_8ZC?&&(3x}o$BRK2d=H1#0}snqBi=o8E;4({GNdfaJR?f`>a%k2`T_ynxdNp7 zK)(eQ5#5MqPqq_U1R}bV{PH{&xa^9hoIztj`4>Tf7l0bw%pbZN{FdR>qa`7m7D7{xP6)V&h9J7DzSxyU=14@ z6tvYOzQTanS$4y;x}764k+rZt4Klx9x*GzPZ{IYtN_NvrTI=pkHW2Kz0$Rhq*7sY_ zQ}?-*>%AA{^<7}pGRF3ApTfAO-&lDzZOL@~BJm)|k9O}2BR616aJ_mqYvH#b;a&q* ztKqiT{@z|WO=+F1<8({GQbIU+rs@QS4rrHhk*d^Ri~*$9Br)uCuZd;oId1x`hTdFI z`{CC5vn~tKum%gDtsO9m2jYB2;;_F9jqjc_neT1;1DudDqP@kKCeV<$ zCPY5^ADj^a0;Z6xW(=W83N>M;K?0L@W-?+>!7Xy0+2tQ@S$g4u@_- zz7o2t_G}S}&+PNG=?_Pt%?2kw-DYxQv*QIz|GKW#xxZM0R>pI%KvLxb@cS)F*`Y=?Z*s z%F;upaojbv=4DaJ%LM*}et@R@78mpGr7ZsJwFxm>!wdzBND2>Xp+t_Rg%A{bD!F|+ zxNjd%VVvjgbmqqCI+2;c*={I2YD?aWYjAFauTg!yn$YU`-HG9&NTlwGTAEx`z?o-id%?uS=k(v`_A;vY^hql@EYnM0OKWS1lX<5= z9n6Wu(Q-zJqpP}DeMr>J;G;AW|n!vC7GdWyZ!H-$VoVcK^U{|4Z+HA>LbKS2Py*Z4NNx(=4E` zqvk{Io?&(LsC&2=A73Qe@bMESC0Rq>lwl<$b)(3EieJBqQ)#2dxOR1m@)EtXv7bU0 zsuFuZ9Z6q?>IHOCCP7=9JFo5V1)3d!|vl^F%8mQ8j3-i2v}P z7mN|(n9d;zjRc4~_>wiAd<)g~&$7TdtBbeSgvf^PI=F}g9oDu$m|2O32M~Dpo?!U% z=Q&=Jii@)9YVJNPLVLc$69`0%`Pb8h+PhGsJMS78sgX^^Uu+!JyKOfe+LnU-U}-1f zKHpBDArXK$FfcGSCMG8zGsuvuVjf%kSEJb;`Si2&XmO#zXDSEg#*U4SW*OZyHAvbsj zP8ONa{0dILAMS+6REjA^I`XAbKUAe>UoxmbP{D>5>vVjQ2DXs(6$`Yf)J~1hCt}=@ z@hoaSS$pSTrRZh-_QMti{Xuqp&eqpRV1|l&d=-%y+~NK#I^VXhqHAj`nykonrXYrz z(Hx)1RUHU5h1-V+$28luZUzTMRJZeww#GNqT>q#Q=Ij4pOO+~p%sT#g>@ud%z(NzU!Y5tYNi@(62pi| z6M{+S&%{sRHkSHk*4Gm7at2!9+cAzzT+CS97XD7I<5^rKqVRZGUd3trMdEndMb)}ek~-3(|kxOR@R4T+U&?lto*YWaH{I=HE0^rDoM zl#EOv?SUVTb8!??Qj5k`-;Udc;&tcY);0|+IE$YPWh`WwMFa7Reel_cu_!Q&Me9MM zBB4F|$qFzLu&=7U*J|i%AWlFqfzXU$My%64nhsOA96?Ij^x|)@Ngew#q4)O9PX;Aa+GZV+@gfw+nFT>sFV_d;__Y2H{ z+=66P$4lZnAA)pEtZXsI%!%~VC{KV1R={9s;*wD3B?b!nVo*KW&|$Rmt-YNvW%>3o zxM5^gVW#ul+{3zOGK`B3|%?eqd~K${B|gzfIyQ*?_Lysddj zf8fE9y$5|XNh|Hg0qsczLStds6#HcAe0@f5f&iLf*@Haeci z{D29eqi*7UvDKMpCZA@TfpQ_O(;}_}^bM1+PZD9J(A4d8aekqOQiPg=c}BpTx#t9D zA!BYHZ>Eh-KhLIKuTq_=#CUkrJbT7LPr)c^Q9Pj1HK}0~>+Ax0q6^D`2-1gxVseRr zROLYn2_6j$|8vZ9c~JdFT({J_B&*-LO5B@B;=?#GKs??!@PD|R!r#?{@88p7PAn#j z{=?3`ci%p5xL{wNPhLMVU6{BqBN)b*L@$WW=OmGbXZ`W3K<@f8Uw4xCRE4e03)ygH zVDL4|981?(rq&oheXgw?T%mLBTsDaY9IU-#({M!G{;xT+PH-P*V&KgHx+6P$&AA&QF-{`y8VR%9gC zF2Ep}h_}C9RDG+>U%-R05+8#CyG#=)%Xc7XrJZ4cHP_o}8JoLKZ6ozn@YMOP6VS7; zF7*1EpdQBdRl?l2Fu7b)d7zBE6va3zPJr^k4*orF=#m&(XtwH1-kgr&$Y5As)sVTd z^oY>+JGWx_oT|65>sqVdO$@X99|AF&@G@`e^t7%Y-mBQo*pZ;y80Pt+V@H>}`Mepa z9#JWRD6(ntr;r^?EaK~P2Q|W5ouw)<1J;Z`r|*-guiz#zTUKNFqAwr>X25!4pCC0F z54gA-H-&NK220iNpZdQ?LK5pkI^mdNCVw;hIX$pW{Rif0STsE0jFDrV>BuVYmikKyP3q0 zP*-uWR1vx=l-J#TZ#6JYO_@n+edGplxR`{o2?@o=#+LGORCOR~IOqRl$;W;*7!`&Mc5$Ic5#(@{cXy!UZ#Vc6p_Z4Wv$k_aZDd7p}g!KQ1f$A}}qs zBfQ%8_j(C*cXtc#*pbI~OiwQ~s1ZNBExe}Dl$#M;|Hxz+0iFf|^~P!QMRg|{Ee&Aa z=VG2vw-=Bg81lBfyxYE>E!$vXYyy$?JUX?7Zv|b=`Lof*PmN{ppxiuL<)3dw@n` zv>Su!PE%2>Y8PBe%OzqnVg_?c^p|4z&%G#xlmQ1n%(V>XfD=#_xzO;nxuj+!kc_2C zl0S@e7h;!as;h$&KXh1r9oKAvIa}mKc$NUQ!sr$We{FUGgEOHKaXyrAS9)9CaWHJZ z!fh_U(h1aF(9sxfHl9!KR!SZHG0bT81?8Tt7rzznwxh`2m)mPMTWi3N*KA%z32pu6`GNs2akYLDyf7S9?x;sQ`wE;LWJ*+!uG8m~A+ z+0*AF?V3i!k-J6A8kZ+n%euF%Y$T$c#eB<+`nN;-w1{JkelyxRExQl6IohsE7OhDU zNRnOO$7XNOhvvhBW%<0HSueX4~2y@7j4NJK`^9h>zCr6_AqMfh7~9@R!rd zQI@wDXl(nccBIyjVzx4ItRP80XLdGWfc#aWt{dUpzm_lgnc;_i)7G_o*8MxfAX%TH zpJRM9{+4-UOgk7EhrZ|67Y2OpHn(lcb!%fBv%6?IW{6}rjv%3#h}b?>v|-6~)|q}c z27k5vov!~>r0>mp-rR1B8IhAnKNAKWqqQbz-APOkVrA8wnxx8DCi-oI9i91%cg>8q znQ26h8@OtDGRXHCh+#$z`FqwBo!#`N*rO91L%>`LwHXxjpQ_S(h_FOD&!|pMFcZXI zXCz3KnQ>X&>Z$)g{i|&9Cpw-$51UI!b=SsLCj+@a73r^U$Uj)^c!rbf@#8(NaG%2b z9KDJ#-^QU%k=Yd86IFFR&-bJD@2^rm9lWrMoo2s`jKHXg)~`zUos&K-nccG~Jtv5a zc)9R4H(A%!@4FK-%z=dkSrMEJ~l~?xcU5m^czsu6O$7 z83{|vgbJEFEV!92h=K@PGfFQ$O;^K*Bx8sM*%i_&J;wC_J1ZtAieJH`PfUF9;EkC%jotb|OKM9P zV=!gbGMQvLuTe96li?y>>zv^*wV{!;lhW4X*!$1gx|LV?O30nqF)g*0b|>=_C@sFKR9vM84^-1>o$_#@)9W6=*kCn(8SIq+cI2uZ`z>)H{04< z;L@DNV7z9&d4`5ws*VZpT&lZa==DAq<9u(vOf#;+dessWqzuNI`;u%TkB<)LH+8iXgIw$-soTtNxR+mtzmP?dVxttf=R}g8TuOq^qZhi z1Z(e<;mQtq(}YFSdB|`hl#pXH@<%6C`pMNBCX5XxKE~lNk=XzJc8GtGENx{XnjHbY z2>ERFV1X%Tb!7WLhR+x&r*Mp}5BMx?;?aRt`H10p?&*r5oX|J1gH#i?Eu{J{6tJ^q zfkH6_t%D6!JRkac&b019kNe5p7>`9i0G7rqPQctaz^VubR;XlXm7LnGaT+$I}E2TJ1JLJ9C?2bmdB z)gH$=WcTw^#(HQY;#o#pWwty|GHy>$9$wkrGVhD%&$xNUOYM!WOxh zsv7Nhe0zFIDNDzbJv6iG(^RvxdOlTV?Vhm0n4vS6X`P_j@V-V>-^I7hiiL8bS9&@f z|I@sp-m7_>*dY-F6FwIIDPjBn73P3yVP@-`N28XLZN}-q;?3|W;NWntLt`48gy{M$ z0~$G11@}5m@CXC+(--`XbFML?N;2cfL+tC%Dt$#H+NBE^NjBQRuL;cq<4_$5XoVC|+_8~9Yb&q|`GQF~hbU}op!>3RCViMu;fVef?&P_sU zYdd!x`z2lgxHO2LmdFddg?0bx)vJlIb_ofVa7zq66$Y21>L#bvTfQ|nFTTDDGk35n zZ`@N(v9X4?EEHHo=RWA#4VI{BZiMWwmZ_nkA-2+_9Ky!W<>d`e2WXh?N+!NUO1_$w zHh^&}QJCFB6658Dv$}5D1QJ8!>d(qgkjWo<=A3K;OifI33jMs^`EQ=Tn2y2oN!jLUV?^SMpk&A#1Rr;-2zs5FW_^;$9G(>v8l=Q z!i9>LDwh-{Q_mt6{1v1CzIbEVj&J7ccf=N)LMbL|FdF#?e1af`|dm zARwS2kw;x8I~!*@2pW`;ucy{Pc)m_qQ?K+cR1m(PY~ttDUg!DRyC?A_%*56dwoq$s zTkS+?3F@5G@1JJ&Fj7YF=bHF~vWKFmD#dneJ|)~77J!ekXnwar2(@Qra56`Z+=Orh z{bTT-$TCkzb{=dAN59%?4<(tjr7dl3*1!2J4H^jE^Y^d%{P`aNxD@k}oLcIi5*HHw z(fC0ydc5IgnO(p{k=dwR0_lE8x4Dr(z3^-{B9AVzRGd zTC$`|tT(aFar~vwP9m=;Xqt_|dIh&koR_yfwj*2E1C3->vDCtKdHR2?aU ztr00o7XMnJWEHJMc&g+#Mif+Y($w#Iq_4S3;6x&IXQ!zBs&Tmyv6f#)j|LZ^0}m{J zVt>h6yRqTO0_EK6vg5xB@d~nvuf#{$d@TZAsFz~+0rcsfl4ZwkHZHiDE!%BDGd>}F z8~bNs#Qir>VkxS+F zJ**V$1(wj%I9#njE1R?{Jy?VSI87Zsyt;He5f^cfdrlahJB8)afptGEFg&6JKk0W}f2N7{=-11RDj~=nmEYbb+@+=57k{XWaJx2yU-c{~w zvJYJ-UgE+#^9TjIV8Nsd(sbRtpsML|YKQrrw{N2);II^UYN}gfdXp(|fdd8&pH@(? zvUyFYqMe{&t}&2x-DtQC!Z*^5{NkM!ZDwcJI(N>!4zr04!{gI6J@CtfDWg;!%Y;=F z`np_9_7R?hbOP!23sE-rRIT}?lIJn599_q}f;`cn<_f*j(#V7Yd$tv{=PCwRLQCQX zfL9KLN$Wg){MbvAna~4F7&M4*Oc(`0^>2-w`ls_^Wg@&6!@m$_?40z%COViMf&f^E zVR2^P@863`xMAPQm51=Wr}nwCG8dn+)npF7W5PIb&$zIel=Pb&8+QP@Sdwga%h$KO zxR{mHeV+(NMWZt&a5RMI99>lL=kQp|6lK#6?1)iRHJl~B`&wj`D7 zUrCFfn2+sTdR`}pN>5_z!Cm(u2$AEnlQ*?*Jp5dZ05x zm=5(c7e#PG-p(G-tg~S>n!(9UlEwW$OovE1iL3__9nLTthuMgrXUH^)>cWJL<73LK} zVDf~MAiChD7QwxvyD|UxNxAzaE6)^LP#NYino`SzQJ)p3*1a~LuS{l-@RBT}znD23 z3DG~Kxe%f-qTK{SPsEx|sp)c$3F zJ^$Ow-uRyN3)O_?=_Z-)-}gS6cYCdJz;JEzn4tSh2iq6qu6PqkX0=n{+JK_u_;-E_2cj6|J&=*V&B(uoEM!Z^@5x_bM^&utCsqYFSx(SofNPd@fFwNH*gq* zReE*Lp7BqhQjk`z2p_7YO9|?TN<4hhe9!5RMz4jtZKCx{c#G~ftZ^GUt?%FU@!7Sd zzkagCQ9~Xl^v4Vg&kdhGsc7&+ysFrE_c`AORo1M=G-)X%ESt|8FfE8ijb3)EZH1&r zlGHWqPHKugHF4@TcBb6F_xjzIiAfG0%t$z;Xto>6Czw0Y-$;hDR=I`a?~c3o%(wjU z-2pTWx?;+SZ`jPK8W=Rkm(SckMOSu-KupCpbH#+3+m`fy-el)_IeOWGT{#t%Eqkt+ zEaW`e^7q#BqV|HbuYGqKH{5M%ld8}kf6+b1L{?1w)yU$U48CWQWeJ~V4tDmN zKcMr40LU*zjAF$<9A|ftRufb0ESr(_U(X+5y%^#U_~Z#kN*f9Z(D9a*l=!FIss5kd zN<&MFO1PBht$=_O=-GSi$B4HPkBc$AUUu<$ba;fH8I5x?vv66AGmVAbD;sCQ(VIu) z`cg{x(25467Ay_gX465h`wX4RyEbk-h*fl%cxuFB+Bdf0~et^XaitER~txOl>iV9MtrF&h0(x(Qcn8ih{#CVE%J})**9-) zK^u4kw?p+vdUlXB)s)}dYztfx5%LiW@%9|LvlzK3jv+Fr5p|$<7m~?)sQMCD=RzkC ziWW2&dE)w!=iaV!6B}yA=ZX(CKZ1j@LDtO;he|A#ELlRqD8^~R3!OqPSAq`MOQ1`4 zr&2u~R1YlHa_ z-`-SIR1lf#JlMw*y6tuL!iFxB^n_6Grwll;k{Ol-Lf zr?Bv8^Ex-dher2-xemRM=0peG^XU&j7s7+O3>U%~9H(_&;ndcK}34OS^nHKy1tKxA{O1>(K?*x3SaL7OfB#3rNS~A6E{Qswes= zz{l^n_>GkoFjo#P##O6b^1kuHRlYjKHl?)#UiWw8-5v|^ME3VOGm0yXeC8fL29`-T zw(=>~nDf5N-iYaUc`Y%Awc9e|CfF!6nBKZFUn&jkN zZ=z4aokjdSD?{`C7n$GFFhM8ON(m1wB7ESw?Gyw=&+Y2*m-gIo`?4QrX+X%1hzr(j zMJvC(L0c{QTb?|AT;I?@xQ$Yv3w{=0ba8EOsVaa`8t%Eq0s7tl74fJfe%iO>PX}s9 zq!kec$NIgWwCM{a>)tg{Jd@O0avEK>Iko7Mmqux6_#vNum5BO@L|L|lu75agZ^(s; z%U%4gxjENJZ_7EuN}qbV)T7Pb_!S|SsY2rpJeEWUzFhtM(IIUshrQx|9iEpr<#xG< zXmiD%OI@`vSY9c%J;L_~cMt2@BE4cOU|N8s)^`t5`;+@c^ZP^C*7;j7Ov5!c$Eva- z#dOnN<=P>v(uM7!{IY%=Mi9HFRd}e@b^G0le{WsT-RIPjJP;fHBHrxGpp0!2DX!}C zJ?M~Ota?B~e8d~>%RTQ)=|BJGYOs9ekuEp+LhXf=Vx?zUJ6a^XrfkYRyHhW-*(8N` zd)<1>UG=5g*Ead85#?J}AmgfphPltnGhPj0RgE8NUq#=o*b|?rd1IeMD-mo_k-xV@ z;)Pu+%j#KBXGlcF0Dr)X-R&H@wo4lr#N{?F6s|8GPKRu;0lQIy&Wlsh*6gsO=BzlG2J zL0W8wuWvLgU_FcWA|i5fU(e0W{Xohi3afL(|K9Gg+ie`bBfkH>-*xTfWjvrW%yeG+ zh#|X>u=06gktXvKUfDl3yGBA75@|fY@YnLAE$80DU?2K|PzH{F{_%lqe#X0qbtLAP z9X=z$@~G2tVhq7~3~?Az-|Mo3>71&YC~~D(u&l_@pJAMx9&}~_@O^y*{lDLii}F!g z?)2NrWm~%u3t{36HT^A?D{H>KdvJ9PkLB$RkuK>Qa1RKxem*`s&b+>qWZ9q733A)A zJw6ZyfjEr`h1jtcA%s(qzD$RcRWn^5vaG)IBZ%7;8571}fR#zI=(WHXH}GpToypP= zSx(8yuvx{|&#w~R7a&$nDLB!`3Yw=ft!b}lJ%|!B5gUhZnaOcJ3}oo^sPkS%KAuxd z18JA8G26T(eC;A4j&T(Ph}(i7fe5k+2^d_a1$94f6B>n^iBmud&T}!C$KK{R9wOkm z$`f_>G1%MOVtI>&0FL^6C;M&0BHkWD_6zkXS}Ix<>dLR+S=C?8V^Gi1Q8l#3lh3w6CHt6fb5+tX zjCxpxYp>mrmmz|#f_b%LV;6{i1$eEzA!lS~Y04{nb2Hy;9m8IclS0`5NgQnF47+Yf zY^{hd^M*gO_IXhi9@M!-|03I2`)J|}G`KtP=JUIE_c_7Jus(~@DLdv0e}dAFA31#Y zY0$C#c=Q$+Q5nt{JK%Ml_@l4<1tYGl?s2!cy|moF0fEh4E>4nVGCRh)Aiv$NR}lwe z2Z&U4K1c1N@bSC4A)g|jLEDTe{Bq4c`Fn}2TC10v>X~dpC?YKLtoJ2y78DembR#3X zIWU=zRy=t|+XD&0!-u;>nW4aJt}a!L36Cg5aei>9C5+Z@-uy+aRY|8Qd_;3@mSt$D zyCU2}JD}VL6RAMw(}~-qOp4*AJqb4Q3y7z(-)Fu<=09xQ1tkT;7HXLWMU5v2jFGtM zq#MnuRj60#exoKMTcJEh0Lv^Yp;-)j0zs>R?{NMDJ&AU|8Y?Ct%OHzT;qK!jw~>2Sr^H z=TnYEr14fYKK~o!`_GRsV0^L7P z9{soPSe5PV7&;rPa;~-=thdZQTC8wPKX{|%=fU(_1bHu=5Ysi_TFxNn!Mis^-eZU0 zo28=rq@w6Hu4s$rf3H`3FuY^Kf*!uLRU1AZ-RZfKYG3ExIo8v~sLhT!O3gyakJB&K zeyo30vb!`kWg|ZB(v{&G6MW0Gvysd#s5*ZmxApa zSJQKUe574AaOR_awC`VJWIu_AogRmEY`gncGAiOr^h5ok9vl~I0BuBN0ix==*pA=8 z_>k6S{cgFBU9UdUE)S{r{#;`|)4Z>myotNViNag8?m+ST0pkN_!uZuip8DM!4ZD4) z7p&%68_F{I5Eql~^{Tz#3pJDX2EmwwCo-ygtf`|AS;D>R7UPZ5XfAt)q8 zS8TfMmHj|sat-{({Q>L{ig}oO*V&G$BqOVFAXLk1+54>*I#3*U ze$A>{W?~Vl)TdMVvXSRn4ZLhUP^+$uEd3+P*;=zrsq=mwNOOQ|Bl zU!m`VnVcAjfIlhemapYi13)oS7@^@}XQ!e=$SCyYYl_I_qQg^jXOXUdIKcBlXA{Hd zQI;aOrFrsl38UIiaMnO%rf^rnt)Nx>P~_!ajT_tCRyhG)ya)-o?F6U*BV}z}-8PK9 zut0Upx<8@%1V09bD8Rrv?i$RODCq@{xWb4Uqv&HW?Km+}!F;7l+2LiXLE?;M;r1_T zydeig1urX^m7nVBH}|cEA|5qllia)XFK+1O5sXcGka&HMyPw|=3=7IimJLpH+A~5! zC=y!{SMAerSsYIL=k0RdOj(U_$U`*|9=^VoUtZ5E8w2_SDfGN-CvSP)&E%M_(cz9@ zX&NXIyuH06^iBRocF)DhIr8NyW&J~c3$`0P#Q|=uvS}sX4?+XFJ(?lh%g5(A1Ij^@ z>|gu-!-qqIgCp0_tPf`&*63ZD|FBtBTm)K{eNW&-<+D6QfbX$+h^&s5){)iKW_2zP zcH04apKSkjWhvJY{^K!6x!X~mLdOplTw!*0S=2cXz;B1M%kujwDn4n9GbKs;6Lnzp zM&KN>$!yh>1Z_N2I;%Xw_<5CS`~WL*lRvqQ*1$9`Bq->n_4dKC9ZMqfmz`q>77{R| zs8z2<2o@h3>4rS=naqhKZ;fzFw1$KSe;XC);BL1G2Q+4wVC&Ux54IqNKa;4FskiF` zJ?_98i~20;Ud?b!?>E+ub=Eo*HiACpfO+_?eJ^s-SdTmL`!1Lwo{m2M@yP+&aGA*b z+v&gYHa=}>TW@GIeW>_UQd|r-C&FZ#i$TzUY5Y_TK9ri?dGN;kpJ9gx8ey`=GlNF4 zT2zb7$@OxtlKYzPDz9wkI8^o*58SG!B9@^e{}I9D_6%#4wWO>!I5k|y5Qv~)Vx}_>0R9kcp01Izc?5;u~H*FGV+3<22Qu!^&?pxpXNIfiNq|o$s z6#aL}J8Xj)?_*L&ID5##za-a8)S)K0_*s~O6-A@pTtv2nt_)+5JZ<^)5RC;DXNADX zM7eMr1$-f}D*X9tnw^A8l*D?Rh6*-bRevBIbz%#7t7R)kr_ZsThRp{6K0gF9K1*&T zJGyfslAd`A&f`tMi;ChFWdSu;n+KV(``6`)2A@{6?YUk$7lN@Iym=^TR9E-gnH+pA zQ_mzK(m7VuNC2nc)0I2pfv&E}0=A9hKmM%<;G~rsJ9s-Lt*p=|tUe-eeYH{{IqxtVN!_+$BtUSd3=?e(JaP;7RRw@vb<`bxd zR4gZJfB2ImBfGR{7GQHJFimD{+sy;mrN@sPVHyUzji4xOOPEOBbD9M|txj83f4y!lo3{LI(Wn_ElV!y@}ATw z^_uJfLm`CmO(W>S5}z4Un!V{rm52=-SW}a`-+XY!YMsiN!>w zOv&hpOt}jjK!OFU6ve~ktW0w=UB;`=J`rOkh8BGP@`h+DweAdScA}2YN2d$4=y2oH z#K~vyWoN?%3upk)mjHKtXh%*}?SCZZXmRwy4NAtJ2tw(|5rcv6XARSk<|>$!siOf* z$f(|4fkxcFXV`r`$2j*P%FMvN=pze{(P~|$qF<7L@jd<<)-7NTd3MWcq%#w@Mk(5J zbKq3Z9bMqNO~Vi}mPmR`DS${g$rqAX4?MHuRTeB67>@MMaIL)t+nJLs7~5G=Y>}pN z{`_-1l>$7KbyJ_puiD?4L&rB#YhVDR{XeM#;_+aH1u$vB%q`m zAkxERmWeutD6^8enx8nn$H>`*}=5!s# zh+uIrD>_@UmyWozr=dly1V2GkYkCRx!Q$vwtxy#rPOIBlJPrzKMgz@p0vx>NnScko zxJ5-3pT(fWr+o3_2{b#b{DJXDqdwZ9aE%t#R65VI4g43#Ixsa*HK?npWxatY0&3jT ztz%eSfP~JkWciHh_YRtivPe052^l{H9xQR%FKxZ*irE_;&C$3eN`=9l_C+=yFT=1Ws^XI-IEK*fy zXDL0t$X+}(%R9RbKA^CmL`^EsSD<60H~c*BXLNb~l%Pzkx0&^!)zqT~`6Z`rRG<1h z>RDHAa<23ZwUgHTd2i?5>_dwKM(c|iJFIlto(SBDiZf~3C=h?Tc^~h?O7_H_qD5Ei ze+n^vqZf)vUq?H9ko}y{&SjqJZkAlu*AlKXYz>%FiT{l%-o!lek9=$$r!(h>6O z4BRtcJ{w2;xr41h!DEN)v#zX|O@kR-0i*H@lzY^dG-p=bb&8n3VBHiI%ak&TJE5!i za%B}=g0q}uS$;5AT=L`alH;E>)^reu|0So1u(&vAWqGyLj}p6+Y?ShrU-KuUBN^A^ zVtIEP(GZX4P!|&sP{|AyPV9~v1EK;o9Qzd&s0S5Lq+I%xK1)GCoA>S1y1!3Yw$}X1 zQ$;&O;xKDeouSeB?~MNcQ=EX+CY!GFb|Q66;Z#mDs~D7Im#QamiB+TKkMu&-H-=GY z5r=)i?Nf!waDl(0qobSj7m{5H!}F~JK%_9$SQ%~YoTG1UM0vAFN zIL!3!MX>~9Y(BEMnhSUUXpyvAb8H-FsH!B(3*~S0hKH1998mGU6PV$~m|`_()VmkC z#kIdz7SH&Y-?MtjC@P;V>_SCuZJcFM7VUz(@4cNvb~@HogNz&C<*R7$cC zh25MH9cV{kPo-OqvKSHA^+87bMTj*fX%d;V0AC{eZk0M%2dZmo#_Cs(qSU{NjU_Wb z^k3U^K9RhtCifqA@76S_K*-iXNkH+{1?u25oBI25dwbc=@dw?DlzW%!ie0wJ#kx$v1cB| zt|% zYH_Y}BU&dd7(*d%+Svzh{W@rgbCL(#Em}YEK+o`Mb4oDy?;n1qRqj7fiuxy|23=~w znRogP?U$pzY+XhR`{!vTj#k+l+qBbd5MmiDnl}L4Y%X7;bbCX1xKR&L^Ejnu^5n){ z4eNqiWtX=-;1kvZX6VsB!n^cY&AxRM&$UHwNUUr+uB5~@T$^^g4RGrxEnt|ZvMKfA zv~+G34p5C+w=>vsLMPD`oS<=ZWRo)7+=m~k@^h_LSJrv=?j6(cTihn>w@XnDL&peA z@v{j+T-bBNkI$G-;`jy*m%G*$={&Nv_mR~$lhinM-t1h7-OW6T+ZSw_^IU@&PI+A~ z{?59b{0?8K!j%Yi*Wi)$-aCYU)lF`Z>LxQ1z+VvlwdOdB@Xpm|>_#;nEWs{};%aUi zMw1p!)i$uH`PM4mZ3v$}?$(WQT!_1e}Eq}Qt&Wg1Yj{3IbS6pze~O@kMdT5(1{^V9}Yu9Lk5^g4K17&qNVr zyPb)$OMRTlhfi8=MSESf1(w@mdOV0(lje6dmes@ljg}>%E1r7a8R(!rHjGY>omP`Og_TK+aL7nIMUW8HcW06ifdf*)$xCXm;5F>>3sJ09J*B~;P6XlKU~(Izux%8Ew;+21LqKD3(Nn=@~k z?P62|o%u;+tGeeW1qjGlsV-nC-#kwt%{uj`QhN%=1>D7=xf zoTkW(7utL}y^{0Q<27}2m>8=FTT3eC$=4m)5&k2;`bT*WDr%+C>kkub(jE!?kX_3Q ze-r0Ec8kHx_gkw=U+7(&-@vfoip{1};i>T_@EZ8Ue`XY+5P$~x1KyjYa`ZEG{yFr{ zD3+2rJkIuBMmsk~nWh$=?Pr|%g4wc?-&zBvaeYLRt2p_z@W(Xm?KGZg4rn__)dmeD zEEVk;(>||K>KZh#IOM<6gDx(sP<4ALcn{RZ)x<#xVWeGCSZuDH_p*i47S}8%5a9BY zWP>^v0odUBr@S{d4AWz0J7cm*qmJu*7kzCghN^Gba>!g1qhBx}T

_%h8iDW>W$5 z@5tAwh<`J?g3Vw9AZ&@NMP93#)rb1OQ6bxq|464jtx-el-MxM2t%5k9n2?4-Uh%Kn zr~2B1QXX@kZ$>+iRA{z4)!zrsCnmxAqVOyR-3NQ1oUqf3B6* z{0)HHfM_LWFuM;*ACH)3{h+@e>#BwtpH7LZi=I_SbT)~Pp@pmtsQQFdF{p-FaQ(hb zns8*A0pky+e%EREZ``hC^4I=Mo%y@TDH#$WS4_`Sy{3e4i2v^m}qt-uOM_36AxzAI^CT*4T+PhKhi!CBRAKQxS)~m)B92N!m=<6 zSkQe_Lx(UihE;Xb29t|37q?LZJ_^&RWBOeVVWKtQRD)wzkIA{N~0>vuyBc)ngr%?B{bB6^%)A5my(|^F3GLzAB+udYWCgIxtZfx2z+V$Z$OAlbo+_7_I5l`p^gdH10@-jYZmd}md@grzxK8&Zr zka#my=y#CJ-my#ma%E|KT;tIp(uKd-y7)$3=nnX-!dU47rhJ-XB*1C?dyj>AQaiB- zVMg{$I*o9?)#+VKM&>_3qmXHFMJku(S8=e2vq+sCXtCbCdpA_sV@MOh zqH=Ux(iSu*=kVdvgjhN(JMv?z^ol?Thf1CuXN%V|p}3KE_uGs z()Q~mwDJFbojA@yt^O)__h2`pc|Eld&cjd|n&NWpKCCTfDSxZ3eZBXq0QJ+4rcq3` zrt!s=$q}RJNh+xK+stEpeqeUmY`thZJv6m~ewGw_P>a+{>OHBhdgx}pN~j#o`i6HP zGmnTxW!+3a;Z*vDP+IAFj9+>Bfo~`+Zg$#*iF93MpUB7tnC8$`N=D_|C{ArhbGtgh zdRn{ejtoG*Ji<$sj7_)!B>xx|4EY{P+i11Z|MXJA)z|MM%2B8UR3OJ|ZEb~7AL)*6 z&!k4jyX%NMYQ9dZ)Ql=R==fI`zA_g`O#UJh<;w%~*08TD=?IF5`-ZA>v`$$Uejhy} z)kwagdj_-iy{SaXPRqLIP=$=R^`(sDrVy6$!j3`Hs*Zt5r!9NZpc_VY@)`N^L2-{q z&T}X6;*YHHFo^kIC|Dd#Hd(LrzEd}0fBAA->k&#B9oypxhtmtam(5*^9C3~rkgLFv zj{UQnFDS5zsYt6Z+TDbuJWBZdjEJOR`x@>VlyjH z6Y3r%DsK98{erFr7V9C0YUPV>F5Sv7a(hqIEK#~Fm`ClNdg>w7Jq_5J>Q0Q2m&_l%xyxC2on7$hgvm+DJ7AUI$N zru^k`Js!PvK&>#2tr)lQx%cx{6{YvVFT3t?{gq}reA6m`vsV-R!|O5r%PuTnunEwr z2>)h4YE;CGg7q@)clU|&7{B)~`r!|HQapeO>Hj-F=_m})6hJ<*YW14He@>YE8{F;h zd?nizP;$Z3@NS^WjPCs5yT|{Yh^HN)1wMAQSS-)ox#$NWZkJ;{hQvaYQ&-cOTaySZ z=*6kY1K=|WDJh$kquA=J-v8TpxwzHT#rk5>Zc|X7$p&a;&<@#WFR&j0hQA|%TS5^a zPl3h)LNYr9IhCzl0 zeBCM-1{5MUF*ZJ{u%``h$P_UW8m$1fd@9z|z+l<^ew`l^RdMyP+utDmcX+0jz|hDm zDpCd1Wn*KOh)LjoIt6l|o@#{unRXj{RHoZ+^wj@I`3rkpf3Q-(C+zQ;20P!oe5II8 zKC;iOK0*|WPW9y`Htora(YPJ{&guESGyA}jq-DfbE$)W+kHwklIuf1PcvO#Mq}bGH zcs=b)7l`4A(IN(opWNqOJ{Ky2?bViZr5d8lH0Hx?5SE%!vcEW=j-l#!=3Exa_~Wol zTCd;e5#LQ7C?zChsKUMyRrO-e^yD#_nAE$}TrJzH;|3T(U$&K;KCa zm?W9D~hvp1t-NEu7){a0QMG_=Zbbgh~^Hc-WVWfJR0+5$W`P&kNHC}C{d zXRLm|TUeG9Ug)r6t5`7K;_<*LX#{@0rIe5FV09y^9+PF0FMZGAN~Y;T?ZgFKy&**kl#>wIDHO2R*&DB zka@r@d4K1n$~ImpgGMZoNj28=aM*1FgI65z3eErPV@7I!>6DQK~BV@Qoq_v z@gqb`W$gDZ2nfvC=ec&SyHh>3n`X~k%wsz)B#?Wp@GF5unAxI6$3z4?KgqpG&mK_|5zI z0eGBJqcKYTlzTLu-cxE)YAKa70*dPw{`0q=?#gWgqymLt&~drn*z}IbCi-Z2gUk%* z(d-Azro6P2lEjDwsh%a4T?C_m zY!7w1<0`cw3&dij>=HE9?zLxJZi6uE$|qQJ<9aiU?}HhTJ1`O2pd~11{8S_fN|1i5 zo=S^jfY)SzR^!GdU(nHWNPSRLvRoH1%mE_Kr0>$Z13awTe#kxg$nSmo?Gb(&ia=y; z0+EURQZD#n)RHAKp+IC-ibXSgxhsE&%&VR`Pd;8QZk$V81Bv^C{H((TQfSr|0JLFi zr&XROR^c{4DoVTi4uQdBhGaN!#{gpG=~Pw`^alx=?(znT=H1@E(~nI1dT;8}GgZAU z#cYWmi07YLq~A@`7vmKxX3Tu^3+tAm9^7y7FG&e1OfWpst|=TQ2F)HpK39XIU8z$E z9x>sIaYJocwUpZ;9kHNa#s&A~i|&y`rc=iKB_)elO^P9`uOJn52 zc8@UK_*_&IXfO=CDMl5Zp~shBP;gXurFO808FHPk%IwDklZSA%-fe@m-cjXPKMGVs zwYP|tFI(<0(l4NP(M2HJU58lB=JZyF9*UH~X}|o!8MKrNZi+WuOQEn&Im%vvO{sC8 zY|32@eL3spybb-^dKieB1!qi61CH&{_i(-pTPHN*P12pL*g!SLx3RDCQ(WI?!b3Ta z-A@SGsv*mU6I%p6F{ud=AjT!y@%CP)b>BoD88Ax3W2szLbcs%S2=Z+aoMt zTGuM@+muQ+!L4=aI~OaRAHCY}?ym?>530F?PsYuZn&P88^ptD9IUPH6(Vf&-J~F7A z>6{s;iHF4z`?9P(R<^XYPOe<7)im`LnKm6wP)Ziif7OQ}T=!zQ=jt(hcHD(ESI7d!7|2>}qck6JpQ?ECquU+lGP*yfLO=cfwZ=Y7KGqHH;_|TLmb~K_Zq2v1CBgMH~Y<3 z_ST1oBQ){Vo4t{at%6gM&T2qgav6}=x2aY5cfh8$V}21!j~|I4WodB|y-N_3U911G z{0^xw>)cYrts^N7Cu!cRn>94OR!A(=e<&fmV!gf{{KUO+1z zPTZYvgmH@hhW@n*?~$Q%?t{J7y711N$hsG7p{)7s+~#d4c5ZLQqLk-SBw7BQBl>uh!@c*Z=XqC!!^v=#aN zb&vdJ*C+ijdE!#MZCB`Z7zJsi+F3ln9a7lhb>=Os=HWGadsYYCCQXWy#?oKc(99yM zbB(u9xu(IeW$ugn3oFbW1^JM%bY`{bgB4Ana5pKXQ%#J*FGuYBku)bS6YGR9M9}sn zJDU#Na@28?qNLopzBzU&FBj_b5NwV>*_)uTywwe3XZ7JhT%gM%gR5%|MH}OBQXDU4 z6knPWDiYZ_>EQClqR~ZjS{M_)^Zv30DGXn`lgTiQDDUs=o$L`u;9mfUJ#FgFJmgw^ zV1~!Y%lLStW_wzY-wV0{4;1l<(Hwm*&rYuP3E4YOK;D0$F-Q`*-^L4ZUm`ptv8d?5 z{W}~lXt%7Q8!QwKhEd=NS!WotvnN3uaV%pt6m+{ZS2dR^(Z@q6cQ55W>1W080JRci z*erd%Jp<5rR?&gNf`a%;V9S7xJ2fD<70Zv{OJo-0YXQ)^qHWXj>(bS)fq_sp_yBpK zCMZReydS^OAXDQtFHYXiGXFGr@4R-kcVMEA5NY3EY8JEYZ=It`xx0;B6W-=#80K1X zuV;1TGS6HaXH2iQF;55ey>Mwj^+=fqoptv`Fvu?JL+c)(4xduOKtv8EaI?$WX1UZS z1FM2M24{RnjTH8MzHhLld)9r7Fjn(uwPDD4b<)bj*o~JNfy2u2tKU@1gxSGmdoAKN z>$$r0(*{MU%-KR{)Cu2r_UYxywaV9``u4b7fGE84cR(6#&wG?2Er7V${&WAx^Fqr2 zc+5s6Xl(x?+}ONIdcHF|E}(n5Blv|>O8%Vg)>bXK1pZU>eBB>Y1A7?@!}9YNRuROP zJwPKx#Zq|F^HMx>TlCXv_~?I@Ni#Ago)3|^Nquocl6d>tqXvW~?E&mEu*7N@#Z@NA zULxU?yqKs8X|-#vs9qdSKI}3Q;F)lu50g9YVnvqTo1m7G`l`X5C~$NH=m#Le7>3sr zmoYb9oV7E@Z7rX-ad&nUAF^L1xUMM4gH-*NpR(^tRl+)D8mjZ3PMUh4Umdl-3w;^h zURXk3VLC~+8Y13}oz24KQX>vfmOrvYb?LGBlSlzD3uutKhhc|f{Csl$HQUkNGJk`m4Ju~b10_@ZB`yzn% z0xDcPYaKc6tajNBzR4rA51ch-L^Sr(b~B!xClz!MjZY6+$%Paj_CCzGTJPcxSBej0 zsA`-#($? z3jnB!SOaG(@Zbos;~)>$ja2lVT9W|oub-K-@r)0_r3OLok)kWof+9iv6O&#l{$7bE z@H06GAi3I?Tw5OLj=dZNE8TCpux z-Qc^KT5l@5L!T2T=ReZT%5Bu{wJdPDi!5&)WW<&eK0Ja=T@O^Y_v{h+gQw9L9W>r! znqIR)I5bcNNlgu$IPRfZ=xXovX!d5Flh?rEJg4VPfPakzI2}OQglv7|1c?a77iduC z(640xS7*diwJ4UBL}e^O52rG>7gZ+Pm@*x-Wt8A^LGP8g^cxAnnZ`zSe*}$Ohk8gX zTJu`8--;$LUUqVFtMC6@`pyuyu3uYLo<}OP@rUCLNpIc!XaUr@2CrN72**%nanIM+KRFd)jilq>~PuHXJ8-4eJcQMI@2#w3S^}p6c^2o;MYCd z7&jh9pYXNZ+7}-Tf4O5b{JglD*m}=;*Mx>cn#{Esh&U?t+pYgqH-uaLM!^dec;~?? z($ei6L{zNZ@IlPe?h2XV(M7Tn14;k&dWxXXCO}igz-Y|{0N-pUUfkuTSSqh&AQLgP zFi_>SHhtN9@ceDOiJUdn)<|h{m2gpZf#fF-6@;fG1d?;{eqzAv{Rj6D753I=`IEJ2 zp-G?t*;G-k_TneCGr_;4?hU{3G5~NuZy6ebwmQ6$X79D;v8{!-p`RSm5#1;DwB(Pl*76 zlJ%C#NjFDz9)WZaE#-GE>DN~mrj1Q0z-5gnH(|<0sU-f=&VT+^!c;Ar#pjN%VM_Fw_+FFvmo0;|=VdTH`dpB_`Uh-o$OoV=@ z!~hmK_g~~IDx1$~<*fb2rB7SvdEFLiMrj!OWC8Dd85C80EBeRAaKbC@<3EhwNyhN3Lv&D&wm)di|KSzoLDmlD4+pFH)%PGLY$OR zk}6*hapx9#GDDij8nteDI;+`35lBX%mZbYAT`QlBn6?RA0kRnpeNNX?BAh3*y|mo( z4t2HXuB@rluhwvj>=`X^4<9B)75sEA&y)xiV%Em9faHVi_y-e z3$KqUI4Kaf*WPdmJ;GcNLEd-TR@7LVx5%2@<72(|3=C}#k>u&klV_C43+t&jvN5J4`E~MPgkoY_q%L{N!`5)SEQ)&1 zQJp*hBE|@E%E$>>uQ?*2DbGL&ur8M&%vBS+(=syN+gr?nlHujg$J?m03E6ncnbeVO zDlZ_MN{gcd$Lb~01IjfAl|tERH5NdlP9SBZZw+Jf@FIbm1D_HllchJwB;sOcb{DDm zdP19+p*w<0G~hdRV6~wzndOa|zuhYLUUaoHMPD_08M(m?p!iE0%f;4ABoE^af;o(i zi-}6fItj!k^T2)%8FjuB0_rn8|ZdRI#Sm$i047>1_j1;2=Wxf26ny6bW>xF`HCY7NT?!`}<9F;os%F1r4Msg*)?^T~g<-V{6ctQ@J za=-@d(8+#K__+##usAi@iGwxQV#enJ(6+dTDnjB-T5s9p{K2A_8!{7p&%a@=2#w?E9 z8W%G(yd^Dd9BA9mI$hPG>zT;g;hU`>RK=X}P@COEVs*fOMG{|E0R1Ob#s0O9miQoG zzF({!*?&;l@vIVQB-YZ;Dds{%ITpzJvBa%|GIf_$??jF*Pz9L=&(ntAJ|bUdW5+rN z&jw2=ggTys@WD$@sBhIY%ivE-6%(s)3P!{26yj8h%_Z24bD#J!1p)QhzUs5b9}68> zn90KP4U0Z37j5pVM-M8h_lM-YsKHOl?Y_%bB62+kh@n%ap!K{YoJJ7GoGcJ5v2qO? z{u@c0gUEmd5xsiaFZhlpHb?XlgdP(zz@^8~oZu#OW4c>Q>rLiYQNhl`y*u{o%hSSy z{zidXQ(Fs0)hQ05PL*tC`qqIm=G>fwpr7NdN8b|-6wQWT5Yr-OcS0XIua-TybM@b; z$zig5+(G@*gV>BAmaVn z=c-E)MxH1;#K$wl;?tsh1|~B~CUpDWp1Wx>#6wb(1+Ys+s6t4D4kjjyHD;j^Pco)ASa0Q$SoVKVbI&;BH+z>X}xKrQ}r;8>LVw16T0@l zg@12rxGvA|5n_3^U5Aq^fkTu$IOtAaJ}m#QcpcUP#Or7;|1J=3ioHp8a=cLCNTjdD zh(y_?Rwi-;6^}|!cs#b0*ck3_c8@KrSG2xf_RfpbQ5{Uu15$^5y?jzFy%$0Gm&7w zRyT;v?tYKR5`g5NpO<$5KDK8fZE}n{kEI28vY$bTn%%n939gkYbIVlF8tn+Q*3uab zx`~l|Ei_#qKmSX0^$^Qq#X$KF`29x!a-QEQL@N4k6DrSYNQRHp=W{Nm_jWTT$s=cJ zV<~9&?Kei7j6gc|o>NDSq7rgmX+m#{+}N0ZwEko2?Y3n%t3h4MI_H<_<{^5nG)e%Qzh3yE;RO|{kNY>me-_z zZnI0c{M1TbAF6Km2fY`JH+$B>a_q!n^VHyTZH0N7qL-phck-jwIL@+n84Itu(p~G-Mkrz~86d zMtxtWfU*>zU4}KtP=Ql(gx0mdJIM-;vu>dHKg)WV=DDbCw&e~PY2r@Wg0oAR^1!(9 z-%t7a$#cZl2T&?jDMMmj3i9=tB2sv~SfZ?=`X&OJd9%>0{+$wT7RJiZ%zCJ@sehVj=(i&I&3y z@MA$rAa}B@QTMh*RS89pUTRu30@m5;qS>5-<~JG8O}PRzEX8%@!v^yy`+#^cYk5rE z?ZK?u<_Ju~>6$T)Tc^n6Oe!r}0eT7qBNsnDpE8N1d3mzug+b4$bP12K4qtfP?#s{@ zsc-f$Scgv^cnFT_@VS;k$7?%>Pj^mlx9NpEijg}0j_P=if%ojeUy06mJ}ROqk2G3TeUP(KbdfI8!YPH_Fr9J z3zUUTb2Wahi*U64wOdN! zAkiREyN#zVK;DM0Mt{fF>XIuW9=R0iXm8qZ7gj(vY_7yz=KobO>9e-6!P~OVQwawY zU)kC1mcI!baJ!s!n|a1u;_a0qf1Z|`dT#Y+lhd!K;M(jLvb6@-Zx_bQJpHZyXrZxz zr0x=q+GW=ITWoFP)NcceiIU`i^}$yG>5bak(`o(z>Q)LTVl(q;>Fju&`1xHJh$`W& z#&6d675<1S%v@r&5A)jK3r2~SI$<7ZgRn?2w~N@98s;qa=~4>8Pu`N+JA>!DPwXO^?Z1&uchSqk&&Jr z5xe+UfyZvQq|c(`^@lhdr6fMbPwn!QS0!LHejCdJQ>eh4NXXV^O(Balg<0fxM~ejG zohj@-6#C??Q(fs)cYoA_)L?R&uu}Wz1Cp>b$DIo`3s8z2&+S|9=zIsaBoy=V&1Z82 zW~5e>O*MpK?q86!S|O20eqTi=Vl#DSgpxyR%$p<5Pbj}X!GT(lGEB2dJ`9972odE;kD{BxAhmdtd z<7B&ATB4qY2H|u29Q3_fSwSN$fJp*A))ylUGC>vV+sucxZ(#KZmp%OVbd7KUkIMyxK_e0BxT+I5@6_xPqkV zxW44+=^b3rsC!l-B@D-FmB5vPdwXX2Lkd^dOC8Hj+;ZVeW!jcYx*_M*L|+U4fdtAK zhjeD{+D)J45u384wFtkb@veZ;Sl*5~@B^WwE>HSK2XHzDgz@g>0JZqQCLC3vDqARY za;?RDUWpw)vbahsZ@&1<%yUSQSTv=(&Nnl6)~b=WI8i^=yKkXnFlH0|)vUyc;jr_L z@8$WrTm5e3r}UOowfX7}4cR!zCRW6}=p0U>VDUNnL)}>~mZ-FVCzsyK6Ma6IB+8se z!%(vx-d4_QI|Dqfy{ryHZ7fd2WHpQTK(|Yt71})M4?kGeEYK&TWsF%nsb+H-3Si9( zdrGuVe=OHmbh1|)WU(gz_r>XWiwtX5%87K9?w4AtKO$X`8sulBs?Ji${`2{2=Mcl zuLXKf9GX$|O`LzbEGdN<7kY5E`VXl44&Ll8f4;JsGf(tHV{PTk$)-iRkE%ep#+6q$ zU0ezmdwN|Ix2q?UrEuJB0$V+Tk;O9LI=vDSNLbkjP^Uzu&CWXay)YctP`)F5MdPj@ z>__FWq^Dps%2MjYXEm_C=ORIvm0{DZAth34$geHubfY~hv~SUKlZ(+>nHNU zJnfTLNOIv@f_KO+GE?!6oFvAfB$&y^uo^N-E}{) z8P?WQ)v7dzKlhGm(lgv`)((h#vZ|^l%+@pK))7uYZR-E+FH&G_l_=?xY1J)DKG4$q zTmI#j+uGZ~=3J~>>wU1+e)YP(z|8Yh@dZA_&xuWYN|CFVm&N8WrhEb(>tlqDpgf$C0j2OZ>tEfLV2-}ca{eckW9vVTNYI+DX32yC{f1us(oSa z8BGn;b9SOGNw9_a|9BaT7*mp5r)SPKjuz5*+_zZWPI%hD!7k-l)uK-6$L)s2RLaTZ zuk1fQtUu$R7QWg6PQ@{S(rUN^!K*#X_IUcn1a(b(jxzx92D7do^GaqcXM^y4euT$UrxSXC33 zd{C`{laskKC&ZF(+tNi}qaQyZ zB&)!n(({k6HK>(X`~cKus8&CxoNmMjczt}3uUh&8jeFDG;;&nKKk;ua>vu%3H7DQQ T^NE$LadkBHHA=4Befoa@d*{tf literal 0 HcmV?d00001 diff --git a/docs/assets/endorse-public-did.puml b/docs/assets/endorse-public-did.puml new file mode 100644 index 0000000000..63de78bb50 --- /dev/null +++ b/docs/assets/endorse-public-did.puml @@ -0,0 +1,53 @@ +@startuml +' List of actors for our use case +actor Admin +participant WalletRoutes +participant IndyWallet +participant LedgerRoutes +participant Ledger +participant TransactionManager +participant EventBus +participant OutboundHandler +participant EndorsedTxnHandler +boundary OtherAgent + +' Sequence for writing a new DID on the ledger (assumes the author already has a DID) +Admin --> WalletRoutes: POST /wallet/did/create +Admin --> LedgerRoutes: POST /ledger/register-nym +group Endorse transaction process +LedgerRoutes --> Ledger: register_nym() +LedgerRoutes --> TransactionManager: create_record() +LedgerRoutes --> TransactionManager: create_request() +LedgerRoutes --> OutboundHandler: send_outbound_msg() +OutboundHandler --> OtherAgent: send_msg() +OtherAgent --> OtherAgent: endorse_msg() +EndorsedTxnHandler <-- OtherAgent: send_msg() +TransactionManager <-- EndorsedTxnHandler: receive_endorse_response() +TransactionManager <-- EndorsedTxnHandler: complete_transaction() +Ledger <-- TransactionManager: txn_submit() +TransactionManager --> TransactionManager: endorsed_txn_post_processing() +TransactionManager --> EventBus: notify_endorse_did_event() +end + +WalletRoutes <-- EventBus: on_register_nym_event() +WalletRoutes --> WalletRoutes:promote_wallet_public_did() +WalletRoutes --> IndyWallet:set_public_did() +group Endorse transaction process +WalletRoutes --> IndyWallet:set_did_endpoint() +IndyWallet --> Ledger:update_endpoint_for_did() +WalletRoutes --> TransactionManager: create_record() +WalletRoutes --> TransactionManager: create_request() +WalletRoutes --> OutboundHandler: send_outbound_msg() +OutboundHandler --> OtherAgent: send_msg() +OtherAgent --> OtherAgent: endorse_msg() +EndorsedTxnHandler <-- OtherAgent: send_msg() +TransactionManager <-- EndorsedTxnHandler: receive_endorse_response() +TransactionManager <-- EndorsedTxnHandler: complete_transaction() +Ledger <-- TransactionManager: txn_submit() +TransactionManager --> TransactionManager: endorsed_txn_post_processing() + +' notification that no one is listening to yet +TransactionManager --> EventBus: notify_endorse_did_attrib_event() +end + +@enduml diff --git a/docs/assets/endorser-design.png b/docs/assets/endorser-design.png new file mode 100644 index 0000000000000000000000000000000000000000..1c4b9fc55575268493640eeebf664f3796359300 GIT binary patch literal 25582 zcmd43WmJ`IzwS+UO-X4cNQiWIO#~zbB%~XpyQFIZ(k&f=N-9by-6{>zDc#+*PwxAE zp0)N`YwYpvPw)Q3U|#d=I{v@&xMrl9iX1K$ITiu}0skl51xIuUp&#b>_0f~67K9Ly~r3`j@N!HMOGA6oFOYtc(tG8D@$!IU(wcg z$iawLT-Nik!$xMHOXN#Q{e09gWxb_Dw+zvZC^fB=hRy9F5{p4*RnzYZ{qH*wH9K~g z{5mA4FH5=Y^t%;IWrJ444rNz_#)N+o~H-9nKWe?J4`{OlgeYtH`5cgi{(TUK*z zUsppt1Fn<>pWn=O2x>1|9$@?&6k878^{yP*%JQsuNvO)H!{f%DKb>@AJGs|tCK?vy z7(KS+g)-)ahpKLG0ADQqW6!1eF4#&{g_L>GCWg1+t{QbA=viNQ6oD4b+JTCbJ7o41 zI^i+_>Z&u3)m%s7U?W`ZX8lZdu-fGA5kk{_Mkm4pyuN&9m6f2*p9lyh9SSfhO)takY;=91ZqjbH07AYlETpI? zwqP#u2g-JzOkkZ3Rh>j;k$kDVzH-V$vV?q32@zA+!Ac*W9UsAD~@!b6dYPYi?x)5?F4gh&w- zY39MfgU4zx0u&ZvW++B*G8PqF{&tD!48J?i#`NN$>f{EG6X)g2OuIxWbSTou;`2p9 zUgd2m7>MxC6W8>aj{w!mZ}XzcoAAIC2x2A&NJ@PohADD^*r@Hl6zd35w!mM+fY_ntVg(-@9@KP-cOrxvI_2i9}wC26HIf}wseysuZUaB{zT8@d1j!sJ> zoe408{OT{xrz7%%TXu|NH}b;8zXYWo#)_MPZt{!7V1_$Jd=RHm!Mr{rmvAQw;cuc* z6`yeoe#4_+=`Hj8p&xvyX?Nb1+Rqwu$AZ$Y>%ImM$V6ugISMzu^#GOs@cSAu&uih= zG(2YP0bMI5+`lNWBrc7+oAlI%*JXSKqa_?a^JY8>F@!N#UKvhwqTC*J8N3gET!)+2 zSa8rXitKxyWLRdM2RFKXkxIPbuoi* zekts~FNHl(#cQXNE3Bb`?|)$tKwmiJ6gTx{LmT6@WJ^~BKK}T}7fbU2X)kqn16M){ zm*SipFV4+M$@xB38i-Kz2cz-XHpyTOy39BA8GL(^AoPb(uP^6{ZeZneFbm4yeaLIa8SzJew*?~RO2MTZXv^Y>MLeF+ zl~_~Lrr4mxonNd`E$ZvYwU|aYWQ2Wtk)e4#^z+F>Hx8(;UhJt6q;&va*#%=e&}<^; z^prQs+xt129)I|6#7V895*oy{LODokDoH%AlV|YxLdopBb3eG>sR!zPZ~b$YqIM zL`&Ry-CXW+G{3HXeW9uDFkSO5`d3tms_OZLJ*cgtF{l017oBQL%V$5OygG~mZb+U= z3r8>snzX5rQSZosMQ2h#bKEM=H-!I~9nmoBs#RXthF7*An-3xo$({fQwIZmu_e=e@OqQ zV+`HxkZbk-tVJ*ip^zM&$=;#mj%*m#I>ctrS`}UYV6u;;x+2ArxH;z2pZ?O9Kz|pn zhk_yTg-#{@?q>>XYcT3heCkjc%Xl<~RJB0SyxO|&o@az9h+A_vOEM8PL;b#Iw?vavk8-Q=hPL6}7g75vHdaSD}PaVX)M|p^HY6!7*iaM_~04O#C9OTpk-A@AY1jNLs(*z z$jf)z_uI*z7qyzK37q3cZjZR#H!-Gi5Im9h+Y2CC=|#BuYhz=n%OqTuN)i(e1Ym4N z)_-lYYAJ=xqT5utu5lMXnizhUWb*vk&-l8GjBfdd$#8c2_u0ZLSl^kLTbf9BrIpGP zkC^rE$0B|G>TR;eBP6jw1>31Fbd7q;YjW-3NFJ2S1g9Lej?G?=q%RIP-*MDE78)Am zlzcV(rb5`n1p67HBK65E)+1uiFV7|)h3v|947en}HQt{Zu)q3Y3r%CcXen42 zD#Q>z(VUvDb;MOb7Ir!LSlM{9<}{h4hoZmFq#~Hqg^c!kb>&2arCqqQ+ltD@>H1uy z_>s=tt-;x@JHraQfq3Y?gr5&mP3vCseAb*~UOOC4wGeT&C`Y6F*r+M}ZYj>FH5N0= zs=rP-OYluY%Tt;#+xwG<@WyY9eiSD0z78eOH6LBrrpktjzl^ku$e144Hr}!6p?s-i zH{E3sPX58Ep<<~uFCS+7Tiz?M>G9cGCc1w2xQTE2__0_ozvxe_7Rz^YpxP*@nsxfN zjy7E}p1AarAMPs6H)RXhU#9ebKU`r^+G4*zqSYbPuSNkViIMeqfFp{@N>j-IA7|<4?34dS*%G&Bb6;( z9iYMI3dWW7IAy6Dn6HD#|Epmkk4oOXL)E{_Bi~=I%BI5|bz@D|oEds-k#>J@EXx?q zkvtY>k*tou9$_3^+ij|8KhGsh2)aKXuLmN}YcGRo{BQ{o`co{A?d?UJYahS6KiYR? zmpiRU^h(crp_$?wCQ~q>ow>Uqr&}g13**1#K>nB!%FUlXm{uXmd8gjWTFS2uYH?9f z-_tY}7Ic#Y5!Y2RTiajP*Vg_#J4%*6^3WmAbCfaNk8JY(G&7A(Kc8on>f8BpHUDtq zZ)G438yql|R4ByXS$whqYtCE1u}0t4HLg598Am?98X3M57tO%0sauq4nLQ*6_UhE*^W}+f2b(-@C}k(zCA=P4k!78{45LR-iwheHw~5N2|Snnoc<`X5#JK zol#<-Ux#sfwHT=s{MD2_=wVxw>8_ktSARwTSStvwde%4r`8YYGdus|yzrFs_SHVN$ zZ9vTxz%kx#ABO+)A2Sr10W`?}_@6Lg!a44i@vFhmH`fFWcK`mCFd>9jS&83Urgh!c z{<8vFcBFj6$V;7(0!-cO@UQ=&+kw#EU8>M2pG_L)PyZiF_&=5Yzdr+IUKa)WBu&a8 zGn4tT-Om{wh}1}jd6EFpo2D_wO!L7E_yQB^XlAcCxg@CAeN>Pg+2c1uT>91ba0*T1 z?-&D77RGSD0si0q2V#>4wbT+UdHv?6LkRx_CZK_Xyj+vZxTuAdUw<$@bfblW5QqlW zL4a-0JZt-5slvNde@(cBG9^EOR-xbH+fRB&=HWNue38Y7T=fhgc(71Kb8~Y^$v_Zr zxauNLwtr(l?#1tY(EGyF6eH69HNl0Pz&svDm?U9hLT6AjOd3&RNxfL|w2IvZ zfd}~rxf;baB`d49KaY++dtHMe6Ai_iAtY87CDr`-^XHr_v=xRHSmyX!iD;ZWoD!Uh zoZ2=vHrqJl+{2OLvY%}vM~K3JT5;mS5M)sjc>W(qu8#CrlX+?5fhz(Y9-fDXN6}2BQS zWsJem;-5=;kDdk~8~fDM*3N&mB^|GJUF#nzl>6`B#jef{LbA5s8Y?f=uAKX1)*VgC zA?|aGG3L0jv2jeaKU!%-BMQch$Eu%~kB^UwD+uYP-eYPygU5=T*Lq=j8D*uQpa94n z9UYy~4;of8b)MuUl<-viUoc z-=3V8{?=5k;xOp36K&sHetVyrOLuj~7HQJ1mJ>1*23$NUVRL(XgYuVrtgNMCzNhhz z4LYSCsbK@LY=ew{d3&`-BjSNm@gtQ@PpkYT@nvWqk_{s-S_-n(#+$e}8u8bHZ$5$t zG7)&nii-697&ps5l3MW+Vt`NX;yz9Xa)hE)spc?-~0;%(D#w352kBifIp;FB6i2f1;bRWA==K{G7!G zsk8Lsq$G!1dV71jyP24n&?^jmJb%AZI_#d+w>c z#f*_!349?uU1Lu{O`VgSot==t<#&0ENytcW*A|9tB9VBVDDHPTID{o;Y6GosfZ~uh z!HbHE6S?~E_KCVV`1gt1W3I}N=(ed`QvwSXfaIY0Mm@iPm;L1CLE&$dqW4Z z&dZlwot+7XjlO4Zd2OUi&oTJ4ghUaj--T+{>dfMOLua9rHAs0j538wlCsaDD}3pKU)4 zO(LXyNnS=`{Ch}oWl_BDZq7_nFs2PQb*qdK{VWj8scX_YRQrRK8DXZsKfdVL0IM{H zuF?1ceVTIBj~gw&OS)a}i*-VIIh)^3$d%iB8jptKulOhQYb5D1W@ctSVF>YQ$Y?lu z5eVD+8`0iqC<-C1A?WQ?NDe(t7a18D34bj^>1@sjX)`q;P(w9BaNT9 z;$%n~5eAu%Uj1u{@scL&OxLy-;H5H*B0In3G7JBqm?ug4>G;XS{7YS3HlwENi(hHq zr9zBLF)4aZcc!ndu15U9Od}zuKI1zr*Q<5O?O*N7G{MArp;4@~$gcl8PJca!l8VZ9 zyh0hDt{pJHKTAtL=(Y>^?$#5EXl)j@wx%l#M09@W>FGs8L?r!}8uu?O7}vdf@j?Ba z^HSF!@yQEHY9@ z_)>~k-X%fdSrP%%MWH8$gTjA&20A}W&rQ+?!+VgyBOlAYx2TG%MMVr06q%FNR@J}1 z$<);A)J$qf5!)52=S|OdoZ|lS)=kgI}p$(P5~2Y*RHl z7smfXXWQQgb9@}!+)#O$ntQT+6zYOHBKdlm9_k$HOAYH7lyNu>q@`9KD zBH-nAYz{ZV&Mv4S4myl4O|9XtAVEQoQ>=!5j_ro|!jo9Z+}gQ^9>~b3!HR4dwEqVN zF0JLmxh`CbMf{i(mFlfEDD!QUJnD=6+??|+9GR`>LrgC!4cSD#5ByoWIk)HKDo1Y- zNMtosq(VkQYHMo)Cp@je@bGYYd`iwC<*}*Q)WaP)Z7LV#FCBvf%Cezv#Tno67MC6j z`VSA|alGXHq)}3H&HpyNqO0(lL))WIZ)J70SdE$1V4(*2jFQp(9eN0OlpRS6g))SP zCu_kL_V=1eGcq$Z%XA=kOMi#}5g9GlE7d9|mx(xiy-%CP@9L~O^v%6`^LMGo_IYa< zwqZxW{(=!;9lt*|+FSf~J99w)L$t{tAwkZ**A=6zth{w|v8F%F7WU9krT;?)Pq(BZ zVUmwppv(#VqIhL*uNh~N=~e#D23>$?SeR8RNA4?=p*)rBw@Y2PK9|QR2c@t7yh+e^ z&fZ$s-p1KLs-@CtVq|3GrehT&b9*&P&r6+O;o_>Qnk|epCE)1L` z83XpL)8LhOnJ`F5BQDzDv>2)?Zlhr*@t7v(?C!dE8*wi5UDlKNB6BteR4rfec7L_b~t$bn&2mjCHD&z zJhOsF;o<9&um^-87kw?<+_6DT!tJIxjgAu^B3W5c|Kn zK8TmDw#4NyIHa=>#NNgK67(}ca{(bJ^a)E1jk+=3!{&GII)9fo6=l8ksZt>1QD;Am zWR>kD#t|lU80g^jR^Qi|i737q3|3>*^m65n&Zc|^2lG~#Afcdu%!iEl>vw?9tBmOn z-s{&V4(b*;t-hgH_i6O``m)B3lJYv@QstwTA2!k3>A1H!^)dMf5u4LVNTO+>D0dYG z5;Nb&Kk?qEg)oF1gdkQKhBbFo9!ByozN|<-$}25<(Zb_{%g3o|E1DAj3pnU9RDO&zgcTg60KAovQ-MT(lYE2t6M(#bUb-=};p?Z_o zQ0BZLGvT?J+9BgV#Xf=VfARC9a&=Jw7b1eM>u>`eXUI(8c6HPRxwI#@GD`a~v1&I> zP=_Ra7vtxzkO}GD@oo)1aP!5fO%B6m;>1mt>ALGAZLD=L$SbCqz-dn<&60Nr;wiy=@$n76xA%QA+Xof9+W2wuwGw{6aTC^f+cJTima=*I> zzezvJGB}9&e}zqES{qRW{nJ~ZX?Y6WupUr1y0?FyU&~gaegqJiCvG<$mG5G|ua>)H zZz0g9hOnLYRa<(-(P&rsvrMaK`>jOm=QaKhif!X1qP+32dB~uh#FQpX!K@n z-CUiolTH*VhWxJ$hNb4~T?ZoQPmeQNE`M=>ek31_l((~c!kh)JYJ5_Z|SfIu;KD9F5DDM+0d1|J!<~h#@_^K0iK_USBSX?WpFoPC?xXtfCLsV%ptVc z@T*!2zcOZ8r&%-TB7#}+2`v<061yz7DD#kAA;%aiF^bCO`^04bB>sYnh7LuKe#hwz z5j06C=tdc1eethiEiD=Ei|xy!nb_HRQKG$G955Mm7?U2EkLC_VYDe-8IcW7@l``ze zZmt{eIL+GguaQn{xt1ZKB_T+dnbGH05ta>>iu>;LWBxFjP?5iyc!m1!VqtUo^TmQQ zzQS9wMcqF*NOLbOA8yGq$XpOZETl}FSg%HrOD3^k&pI5BC+>!{!-Tx;P@xZhR>BcH zPs3$`#kC)Ur{to7{f<>59Hpn}s-;;fK>zWx+(ULK zery)5op2E|srIko0%ndo_ch`=|7f+3g)Z?&OiL(sVB?_4VvjeD@@d2hLGmgoARKa!dLTbB)jOu4%xK2mEv+!_0>gV@xkS%Uhk8NT|Y=llM0-p&h z!Hw%L`vA2g6YmYx2`t!Q9=ri@+xpMgj%6b}$NTl|TTt<(HuoANE&!(H7CNUJpZd9| z(|}y(0)NY?Rp-}=;#CpDg94##F1Ozt2ALXLDf)P0hFnuP>JWi+Lp(1&A2!D+!fkm`$Aw9;|!e`g`mCY~8evzdg; z4BbRPMWkxwI3K0^A=Qmcj@zO7rw5ZL!4ytezt9$3UPd|;#e`XVqjdRadW{=dwuo#Y z2h;mbkUytf@|k?=$hM#TQ=|D}V^#uWblD=W2L{1s0mmty^WwqdHq0yU=Y-my3_JFv zAVY9^KPyAsm0q6{r7$duhg9h3=prH_>bv8W^Fa2^mvKDQ9;>6gcB8zIwi6TMm@3b? z9LJkgDdbHUbW_Z*s#OQ=2Cbir5*V_-+^i8$&WlS=NwF|DPmYh5#H%!Hd}U_`92Ga; zY!u|hoTi$k?zX?<_vGi~q=Egvc7$2Ch;h!JKBkPaGb!S}iIhcVeA zp0U(oZ?}d*+qP#0@w>V2B=btv9)Z^W=6y!_L8(@1|F~S}+M(8%cMXh#PM*74e`jU&KKHiLOVNlAps z(NR%aWjZD%CeKS$vO!$i8l2(L!z3>5f4;xy4hY|i(y_sOnUP^bzbG1?xdw07YRf** zvjTvoH8g0`gyzRhe6bnX8lOLpqSZ+faGc8m^(JlZyg5T0h!V0(hAZb7fcPWS7p7hY-;k@1!hcvPQjYQp)o zw6uT%rc=R~1QgDMf%6^bC*jmkhJRl^bb53K^8+)fn z>Li_0u3H_>4_MqunQnDnmGw};T%+&2x57Y<7%7LLl1NZYNPV!Ym{;YtQM#2COT(UM zH86KRN$Le%SoVoR)v|va5UsbeEUGO68lCn;S2_nZL!b$sY+bSN1We1i)y3zOl$3LR zX9g3I0D6W{dhE?*^YZZU9333|`0)c#H{UiY44bq~{L#fDfaQjEc%RV_B|LDo8;m1e zbu?+@SX^~}_3AN;40%FB&=*^fg;XmxVsNul=Cfzdg1Kj!*o~T$$lL8x=RV~9pK#o0 zQbvZ2gM-S?K_0&j0OOo#5{MQ~f0;{Awn+zWJs-Gf=i=~>7TNgC;PhNb2&dQjMNNc> z>sky4^wC>ZLDokrzd0h>3o*p33!ldD+fTV&o!Yy|2VxAr!@^OB7_lbEgD0nFU2WW2 z-i*ZNnz7CiK~ntdjJ&v?=-+s*NNJ+pbqyfQI%>Komm@s4^V7|jt$W`| z-cs@v`psVUogVA9_(aV&!CW65Ge5@v4}eGXGBVkc92YK7j5d_@KC)CL&a62qCAa&! z{cJvfW~{v`qa+6vn`CetwxESWVfTm0s+FIDJ^V$fq2JiZj~|%i?VZTjW)rKNd$in= zsjNg;bqZ~B_p5kjadK)ZaU5ohKhS`M+|ns(+=8IaI59anG!FUgdik9=fu6L=5KlU( z(DF;Ao+}lKz08UU z`dE5?;(a1dSigKFrMR%hb?0<-x>HpV{s{9#@@aoMSNrlKwLC>P9}>I%^0Km2j(~h! zSy|aUsjBISa(D36o0}UOiPym)AyR=KH9rj3IhzSai!=Z*-fsFGTmjsJyJi{`Z2+pD zzrHTLBa4cT9@*DWAFnyzzB1-j!qaP&vUVALbLwu}Pj@Dk0I1*LblTsee|&OcKVCs) zmt0m}-rn9GNhr;K&+Fi=$rXUtZY^89#x%g{Eh6A z25d{R1^29m&o)j(>}0+FTwff~J~~#5w&4|wQe}kU3f$q&oZY@}v#K1!3lC<5s;ht2 z7)I(vFWQbaXZ?u|cFJH>Gj;VuK>D{QSGMG`-E);zVl?KTEHw*4xrsGf`TyiKX<yKAP0D+fabHXqchj)>;#){flqRuUq@N*D6`w*z>SA9Jq_f*Q3h9IDibr zaInZd;|wY1d~JLJ8MRwyi$k_URW}3~ldi4Vi%;0RHaRW5vrj_*Sx^9u|E}A*1duZq zGt_5!bX+kG_f|odIX8`(|i-MFTZ7=6)k9(+9k=s37IPXSZ&t>KH1)e0!I>POxT z`pPb{JKRSNcB}|w#|@32yLYFAU08mCH{VcY(@x>K_B=y4EfEo%R?ZTw?50mPwim$saA_*_tNE9V-QJ5?p)n3cOUI5rR%s*lGWu4 z%{{H;OgTKGo7cUW$T9?UYOtS)Thl~{F=#GleAL9^Le0k5C6Q!BR3hkczIXc%nb(TM zUmqNCpGq3h&EZUyv2S zKr|fuJqkl2sR>`z011MKZC$W?w&fmM;7{YFCObP2@)GA)z?*q5crp$V0hHmcZQ-juexVm~oL-N4{{ql1Dlhf>_l~|c7qK&6y6tRH7K5PbU4d^q$~~vY zeTuABRP?D1KFld+J!+Yxp=_MrP)W9R+%aq|Z>qa+%86z@G2|Le_{-=`Nn@#p*r@mG0$0vvt+)4JIR>g;oa_k+Dl#{UlT4}+gL5|Q znI{NS!d zheW}#qu_aPO*Pc!jK7(2Yvgnd+p5)pT{EHdSO z38EXC3k_!&%~ZPr2=I7o9JLs`Of^^K%boky>cqrrBxJ8+X4CD7Ti4Y$?`~X6(rjTc z(X=vTh_kcv!Qo+WC&sq*#J?KBz=(E~8DwEj8KTE*kESioVr3!QHXM>n!N)yWcaWx6 zdwQU{<~dv-OYc)!(nW*M(o%jq{D?+9y!x$xKnbp9Y-pCCwVEdZUqC=flb^_u2vhNo zqX)0>3z6)h7Qo*+KRodVqkt~n^-7CQ}xjy za6N;W8k8bb{vv=xpD8Ga0^BHk*h2{oZ+>86_#`jpcNt8xrUaF>wAgrg_?=2V9p(w+ zsG;?v;g2b(+mUq!TvUg+vaq*cW7dL6V!ZaFTD0`N@mT=VxYN+CYB4U-ImDM64hMIs zmnvEnuMgg02-a6d0uy`vYj2L`HB{`b5g_xPS2-=B6eGI6KXmz58Prc@jR-$|^}Bi7 za}RE<7b9D8){i#~s!G)-CH`)Qyp1davo+z2W~t_d5t#WD0=Mehoi(17X23+)YD&!y z0W)#bGck>nFW}b8)s-+llTB}xaP6DB>#K49v^z;!CE%y!=JGz+#UWZBduq+_m_vJ$ z=wBU}#E2VWuQF1MOGOnBiq>JIz}%ta{gVH|8JRr>UETN?-6n#wC@q#Pv~<$D4Hdxd0|7e zynojSI)tkOY_?}FH|uQY9*@`K#eX25yq*{R7I#!OIqTZ$XqRI0P&5J1Ql?(Bn+IRA zE102)1r5;{q&@OIiajbl&j(Bo_V?3kI;wMG>>Qfq@7D0s?D)oDAx^8ZRVkSCI`!Z@R`Fa(`#+b+5du|5aQLG;1KIR28jx zWMrfrEyP(0;;jte>C7Y%7AnL0Xo3Ly`H?M=xNnUfs)j9EKZ0jZCF*o1>g}dmwd64| z2tqM~QRx&_R4kDr!D-QBYivSEOpN*gvsRGfh1c!psluF?wKc-VEYDBw+-=qK=5ZeM z4KrdBSU(UDga0b_o~Hg?Ebhz*Ez9C6TJ&<2fYAC<4cUf{DCJ=-oN5Nk?cvGs8P0yX{ zwQ42vQ0FuN*?D>(bo~SkiV=l@|L(A@tr;m3??;_ecKPG9nNvJ$Yf?e%VXt z1v&K$^aiKAyhih^L1LNDgNv#HUIK)&!Cj#pYTMFex#k_(jmBB7DQmook~e7rz{XNi zyHrsCrXmi<<{t5ET-^)Opk(czU*SdBg7{cS0e~E{-J{%3lnFqOfI=UwXv4VKXZLw_ z&{-ihNJD|uJXCSV*&m*?3E0LTK86Ba1~lfJi$seS?c4k(PlRWDFY9ypK_qlhC>ka2 zFtxOxb&46_1)8VOqZsgD#>J~ZkrNck^D`UHjE&X)O|^+L02_Re^*0Ko$@$iyMY|H| z!UU~Ck{AvkaaeVibs0QBI6|CcIX^L7NjrQ51)v+9p9*Qq3A7v)h*H5(cj#@UWc@qJ z$eyHgy{c1{xJWbB_+i9f-#}E7bJKbsm7uT4!T<+2_2azk@{j;+LG{MxK_bU*yQ5&v zO(uoZSM66WwFg)^q4OWgQHt5%NO3=PT z*;Qzw-&s)G`+*S(Pf&=Xx&F6&&YI4EBQ}X1eOLigxv+?>*p}>ZFPOY0*pa!EEKEJR zum#I?jY3}!ZvS+34d7H{qyY?TdwW~H-l*9h7y$jNZ~5fVd+V!=S+{;uBqSsN$fqgc z--jNTjNzW-u-?MWV0rm+EE=Qm7TWvsCpS0uGkLEQ3SRIo6dyl-f4|lZ@xIX62v`tX zS82!_f!{rLHn+B}ELSL!t@q_4)zWy;ltEo(jP(H!EbTdybj-#Gown=1LRkHNwdPXu>k3NPW9lSll#u@`vX>8jslW=#bjeg z$7b7c_6n=D{%qioTJ<3-Xg2cO%(N)0CBleN>rovt-G_%0@tlPJ3c{P8!mtIGm4mmgao|k^iaTovg}i&(IclZ;AiUV>tod_JJ&y- z2@Vbhb}>GOnTgR+BI;;C69izbM2U-x#3Wibc)P*F!Qr?$`hoM_?bLof=u9o_)-yFl zzWVd$&sP9ubxi(4XkSQ9P_;zr|bsaI-@sTnB74O)s>u%C2*Lj?@ z)?t>Lhv!#M&)|JO^k~3x=IX$;PTZBEV!R>n7REny+7S{I1aG~4uc~VV@5Bgg&ozqJ zi~(S}zwb2hSY}E&SKO~H6!Z9)P9~gzkx}~UZ;zy`AJCPgZLfs>qMP{&d+Y1gW@fHO zt9{4E$G}F~pTQGx{sPGgms*s}hc-fb4Zy$8a&grAqv{$OB}y66S+uOcQP^~pm?P%P zZrGq>k*k>cB*@ZpZ|+gwy5M5lKN!{fU9Ih4GC;EvnJspKNn49Y%$uKc6^!!JYQq?{ z`p%7VdxeU^1_#v$nhNfkHchV24}f*kyJBYnq8wiW*VkQgvD~DPs76spO-z*j8L#Ao zkwz(|nV|%1Y);*bD~UA`s2B#b`c1DhitFCJv(TR@4WMuG+%rsKRF>C$aK zxE74rF`7ycV#`t@_vo-5`v}u{J*X!I-zzX6#o!f|kyM1Tsq`%ZJQLK`llRMqQ2|i# z+*|z*=B%Mipq>grLd%yTZtId-s#Rmw6IFB%tH(m*qGWTRpYIAQfqBYFO!`MRh&%bK>9I{Qmd!#(k}R`Ot;HQzkKlc#Yfon;~=zW{b5XQ?4l&1DE3{R z(ujBp85>D9T8qujT^$`QNcjM{RxM#;V$!KhK=i+R#3W-nrL_I-KY5Rm8XECUN%Nvq z`xufT!?(nQx*ETAc64M5=05qGSNQWJddd!O&-tNG@qU@!D&RPs87CLw$=_7yPg(4xOsNCPq_R zTg4j{6_u*r5nCi07kZT zf42!`YGew~(OfZKl2im*sHLT42d0aVaZBJhlSF`nm6e>42U{wI$Tj|#yYLGIr~F7_ z^5##UxL$}k_!iUPdOL12Fsa5+ z2!y9psl}2^Q?oZKBr&$~qiYRqjaNnh6|Jd>()Wao%|u|X$!z1GciS#czI+lUB%Cn}&6PrK>Z6L zG!#d6xvK9=vR^`3?Y3aRt*AWa7M~K}<-eN)0@TB$6(odmQqrOQcdNLRf`fg1G0v$j z`*4EKgphYGj`o=i5f96Z-0q!^SJD5;`M8o2D$$7Vp8dYraXR8;3T!RFy)84pB6}P& z16$tsDiWj|Q8o9u1%U4wGNAi&1)ZTst56x#7gaIlA>dwZ=juF$y39zfWmH#32PP*6 zu9oNG^*+yGN8{~XY<2>*9yr)u%(%5$ir+vN9yea*T^`d-642%+|%%yke~0|0GC@5 zBzZki>3Q08sgDeGX0NA1t4BoGu7%4C`&;Cb2r4;xpl)?pL?1Lpo#8^63*oD(Al1X!pGU?#%I3Ki>^U z$!7$YZ3A-abYorDqYS&hba;Neb$1BE30`h7Yu)pEo}AgOMH?P?pQw9}8mJu~$Pd>) z(eOz=6czQJYx>gWs@khXCLA8XWxn?B_ZkaLZ z1t9eee`vS@@*{?qV$P}?8RlrZfIc0I`mkR?BeP8@vC6hW?U0#{BmGB9D# z1wg|ZI(mjRkA}?an81s2>o*-wyI9N4i!;r+ydaAKTI5>L<0}!u?2zHuY~N~XE}R0U zN};O|da>m@+J{gGjv)*ycLiL0YikXc(uASK9%rUdIuT*vs~R4Em@MUtc=51UE`Q-X)ULQ+dRW5QY&&*9V z)>Wp^4$JAPd7TJo=X(00@KO?7E>5J|*gE=B2@iV~pNY)U=8>jd_vHl2BV52S_RsZ$ z0qm5ifoPDOnds4mOOEv|lYSqH%JtFOTsLT(o_pCSFR2=<+rcl(5C|N&aJ}!-)h=@K zf@(=-GX9pOkoLuzH7Qv?z4>pA8wQJ=-8zsBIsph`o3Bb(=uzqX+2+)&9hIpXFbu`y z)J1Juv@jfM>|li8$c8r5Z=GmaU8iS=dU+k18ZSxZq=@X4PbQViFa)~&i-mrv&YZq! zD0Nqy4R~wxPNu!GQBBnb+B4B8GsGmv5lVqfbpF-Cl*}*6O%~-3OrU-E3y86|^B{P` zamF?{g{leP;2rilo_AFdqfdaRR;kc5!39OpjLZ-1Lo`N*(g#sizVD>FK{lFC)MM`x z45&51iloD^l%z!HZHT`4`IMP zZ0_^$-2MwsG3!)74Eb;0lT9b!2fSr@#P>!#WHAyR(tiAq=V^kAc@g$9x;45`mbl;Y z=5LApLX{Xxolm-@O^6DG|E8%u40ZIaJ8(a|6^eWe5|PGtTutzJ>8D+vfS=Uw`V;Y8 zaw7TD+}3><0~gmsN0FhwKX3AQMfS~@-ixvFOk|7wKnWHWl%LMdQH$;Nz*6_3R4XQ5 z##h{nK&5{rTK?Z4G?2LO9}fD8rMHlf-ey^NymfN&Avfl(JTNXODVhJ>wwa3(5{j*% zhI;Sk&vAr8?~S1Y*+-fWX@^6=Yw(~*U_bt9){W1uzYkSmbeci;Ib8Po{n7E@W~My^ z{SF*!W#Fd`0Brm2DdIG(*1pa|mIDuT-<@FxoQp`3mQnTA6#T@18XnHf`J~lYkrY7F z?QJ!{AA5MPR9GdN!D+&LaO8@O((_*}u^DKOV~P^)4Y5!EF~o`l^Re9z>nrGc7axhs zo(B~s>f%nuth#*fF$d%6#2n{vkaTswxw)jV(|p{{ptiK6*8BSE>3Buw{q6tZsbOZe z-{w!^`Juuqm%po81i!vmn|WSnmp@o#asy~-s}XY?<)qct80vLbRV<%?0G&k>9K2_v z-?*vx^TP-9OkOTC^7p(-^vY_4tyrQh7u__ja&OMFc&lpfC=1FoDe{0D_}+YrX^Xx| z>XQe4iXm5MiE;D|5&2(4mQ?<+1Ka(i4k3a})UGr*NBPQa%Y7GQ0sk9jt2R&=&9Y*1UXbLrMY{=1;o$W;g zNg?8;kfXzCbXN&d-&MWen7c7M>65fD4$}BHjcv7;!3E199*Q(pX+#5_*uW0kl9)a(vCR6vio43FD5Gerbi)t|2na(X zNTW!IFqEQzsDLy|NTYO04_$+lbPLj*(nupx5~E0W=K$}H;;Z%kzqR-SGxvPw)_2d{ zXYT`2k$6~v;yX+w3;w>DWcCnga&|D;%m3;G60Y*z_t|@Vx+)qAwP#GVyPl4VYrqm>@Bdt<}gl*bo2HpUMy|NT`jz%1XY z@VN#+#yrPOWnR4*LQeaWy}}1RwG>Syw)Dc*xQ4WEyc6&U0=v90wH^r1xEA5N+rl`c zq@gWXVJ8Nx7&@9-T2ZY)e6C$-8U-1oQW@BiV8T^|2Y+NFQAx493ylv!qSCGNcwK!)8dt1#1lbh44F zUwxDiOCm7tnRLzHsO3@~tU$?|4CvdG%;e5#=!O*}F5iTV_K?U@1KK?KlRri{pI(o2 z%BeVXMiQtk0PZVKwrYujsCO1W==gWpxkeVp?}s+(1y?UyzX!<>12D1(W(``feF10V zRh(P`<2>Gn4!#p20QFOY?q0SBD4K}>n@s%w*QTJmfm*!n))`-{5rbHq^2I{_LnaZm zaD@rJ(C^G=fBdFg4*EY~Tr%;z@h9p^%)+_BKf5LvDqLOq^;(|*aC5^f{F9D&4zKZ< zi$KV^r-C}HS}KC^85tj5Vh-Yq&^IzGEQv*(+v2V4?(d@$nu_Lp3J3%(Y>OmLX$^8A9LBbZqnvGP6b%+NlcY3_fu zd~9CVIjPf?Pqks~=x}B(HYr8=_xZ&yLcqCY>b2skh)+!#LePQ3tOU-m&QMfm%#wy| zBE-XC7!!3iq4*tN@PkW&+xg1#0T4bo9Q#|Q;_{tfwUR+1oZ%Ep5(PI}GqFN&<*;A* zK)`LQ^tYUDRP+hom!05NiVw@Y-a-9Hr-sStvWGe9)=BjvqI=;<7R)+6L+sT4a4BD@ zWu|AvyGjw{d<@i7A;fZViHSX7H=NH=6YH*k#@K#)I=cx^SHg8B-{p?Rd#ACb4YiC@ z-QSYQRQFYdXd`Td!z@1mwKv+GHmR>q6ba4vxjiy)wbIn09Q=;zho={ltaxW0Ssl)2 zs9g`=OCes3yk)LEo(*RJ%H%!S{Rx}Xrib1pB{2{y^Ug{1jvqEJJ?e2Nrm6+J%$};S$7ZH1qT>4_QLtG3r~UPA zf(M>E>0Ucq3WJUSyIB3d62f2BZasF zz4Q{bk?IiO)wTq^|2#;_NHNt4vhKfEWL^W!G(ClwtBlhCCkS6H{5V)@{o&bqcxd~q z_?|8Sz+MOdzfnr3My%PzRM4HfB9i6Pv4G~u4z$-+ zT{VXsU)-d+8`U?X5L6SKZx~-+eng^j?V8_VoSW@eq=nxSfMy$JR?@k-09xGO1&qE2 zSK^z?VrtOX!j@d+4IeweUIhb_Sq_lnO=PO4)sI(|=^^8QgA)=w2GG4bDzJ#Bsz==H z)$?0*9`fTsrN3>8@(x?l|9u7#e3kTFG8Q6g*346N2AsG7{5ueb!|_dpiiz%1rANgr zn^T-`88*OYz?_xCG*zmL`j!BTpfbInrh0Dk(W9y|U6?gcn7_nuJcSSBlw?qOOW)GJTdWy=?P)BI&eoUvj`T z4kS`=s14p9U%4Ee*B{TCx2FaYHHr`bt^c7_#ttA|oFKfX80ZVu=z7w-O^6l~D?(Nl zM9cro{RAA=E>%K}qP;Dby8KcV@{6b1=w<5nBHc6N1_FICHE_pt36?qId+2jb!hE}+Cy4z1oy@77y%Tzn60qONV%kez`B9Ny zRJ55GV;%8-k!+1`UGpp9+2z?vNNvw5WcXpA=sLQg8B4|LYNg=3aH7|k)2P|uA}q7S z^$88|>Xq2r+tUk~l8s?j896#S76vE-Vl=9kdsK@~+J{?$bzQU0MCS!!t%hkno75*_NVC&U6G@SEu>Hh_?bcGmh2D6;5db1D6~_D!0=lK;kc+Yb$=@??tfy;@ zjE#XjAC%+z!;Vc5(3tzfj{QYpDD3SfGn*z80C;Kn&-0Cqj4Dh97@j@~Xr_}k0AOfe zf-qWF4;t?5=r(*xFOB|B{FgeJ`=hMuc%yqA(0&5g_$$+X{#wodhLG0`;d1N zQ@{gtBy2t^b@lc%n&?We#B7Zt7zW#DSBiBLp(V`mC!bbOF|iFmRT(PBtslT3H)R!>k!-)u}RQ^`GPV^s<8M zooF)?w`VnjBqMZSV=6%Uzlf<;pA(t~oAqk#0!eg@*m+)-r>^o6>_s3DZ4#N-F%nw; zQ>otlLt?cQ1MF3JT@V~_-O~f4$CzH)q+4;=aptUZ9zlcRykhM?;8cn@)L z@h@esI!RUl&4Y&TXXr(CSy@>|krlyJ=nhEcQR0v?xE-u%Rv)+STN@k8nbp}Xms(7e z78Dc!N;L_s5L}EJ2 wWkFV8iSQnmn5~V?(ojYxKsMvXUn+}$TD%yH{*%*|Ipe&Q+NEjUu(?wS?SwN+qfscp^jrj$Q+x_6rfXvkqzHP`R0aTTwb-_-yxh0B z+!{H9M5Y{7xQ-fO2jQhd0&h1%Q9^BByfCMyrl=PI#(jez(US z(9pikzS@L4ptp2n>^WAU5|9Pe&#XK}#16PX&cKOo8U*wuVyvP24@pr^p^<>7AT*yl zy(Z?W*8r}H1f`|!Gt2!h5cIif+8y(}zvSIV@Uri6jZ5QOX}$dW!ykdWX*a`+8on`u z>@up-ksobFR`b~$z_|ca>ZuyTtz0yXZzT$(Dd;1O^D3alwTnL7YaK&?BlysracFZh z5^x?B@T+fnu%TlBbwjYB-F`;f{yiWS6jitka<%{?a+H^K<7OECCV%{;EgJQ)4M}to z^A;i^F^Va})8kF22Lw$A%V5&KcG8JKwA1`~?WSzg zd56q089G2X#JoJyKLA3n_~0+CB$QB*6#zG?3L^7DkFeGEmO*r-&SIYgB z8;cNh6`FGPTl}&H7CmRSIGkkP4abMgztKk3gRy+rUePy$`@@qtuL!dH`OtZ$mFQnc zLwECFbwYEM#$+J|*1=4|+c>)BnS}6*a?O(K0JEOO3W$AQvSp$$!`>zV{fjNBEc4-m zdj&8MCn%$Xwymu>SpXW(-6_YP8Ikl^jUR@w2;9~sEusAvqWR}H)iA7xr}43e)44o5 znnR&@tVJL`K;izoX)A~bEM#Uxch?Xhv;6J%ExWPMS*?6^)s0(^A)!E2^duw>EVMtN z9#KR{u@y4q)Oq+F6k1_vb$+8u%^w}7U>e~AJC8u1;j005@5Bul7Wa`vNz*DN z%Nj^;{c|=tCT6DYN%wdLiq1=Ssa2u@ zDK*WqlZ;c9dLOduW8%T)PCSe2GmjMnC;IA%nmhj$Cs2j>*)@#(yk|X#tdk1|0iOqE z(qu!iAgCcgGj}k(ILDS*sjPcm`fD!6X>_+1Sh04G$5klMq6dED~ zTeyh~;$@-6HIV*w0O;+3rzhC5uo*cRi{04)Al8hJkNf)i&dtsB_9}^xwqeM^fOxZO z5)OCzZ%7{oz2c)=Wrvw6u-6JD(5TpV2T^rUyYHn9Je^J>tGJ;Jp(mULayRLl?jA?P zmU8%e5+vA#z`Hl)1P_5x{c0Qry53cm!PBmHdW~ zU0%*IW7C8rBb_u(g(NYK?BhiBXYU`8bJ9W}1FWl3Fl}w^`uci(ehe|%%O2)ZLR83* zBx>%GAIXn4g&S@7a4g;KC%b`4Y2P9Ev|!;l9zikcr~yvljkR9W8cQ@%xfrGwu=&ck zcb=X9<;3Mz$jjV$*9{{ege6mfR{rKmAB&Z^1gDgVxJgaTI^WFfJklEt|p|y#!=<-iGb%y}siY{$xQUmq) zj<3J>cQ`$eyq75#s^sQ2x;>f8zoB4fxVJp$^}Il%)E^n+#^>&*&cTI$_NJQ_jrBx>;_Y(iae`i(E(q#Y?XaQArixIoAU z2JV$4%+NIE*yAvp*?b>==v$kX+HuicMRj)NT6CG#YL6}h?T;869V$*oTYT%d3XjBq z0K~+6tRJT)?|Hs|r0P1o*oO$URI?xDkZae7&A#W793B9?F>=^^)Xii#Td%M0vHtYc zaHWw-DEIz!zTAF3s<67Rwf;4A<=8_2FG_j zPac*hX&qa32OIWtq(sR{2^5=C_}HXxu6zLUeXHr0T)25}Uw$!sB>`~n(|)lLmxDE% zjj3Cl@_&1ES$9<(msoc0Z`sw%{7B|g3uI%QE|sWg&WuYw(BAOSn!SmMk5^snUxza5 zl$I%D<5(4_<*?ly9u5Z~v|(RDyvZ(4CJQ;y@>(F3O= zq)N5p=3rZ3YvY*%+3-TMkIf)&b@DGh!Ch)@xPp4Gf^WiuDx3E6^Hc6EAIg^fqHKQW zd&NhyBt$>H=o|OZW-P$Q{U#>d_C`Pzzl4EXzPw(A{Is=Wk2}u$g@?9KDZ7vK zen_?$XED2SSac!6It-Revv)kKtf$s=0%k*>Gjon8gwL(|vB9t4ZM>wT9$qB3mWbbp zap2u)kfo zPTps%5rb+<+9eG<3Hg1O+CntL@&yZj8$b1^T()h=!VAXXEWyeNstoFqjZD+FIR4`2 zJ@-FYF7t%s}3{mpr+? zd^Q{E)})uR=?;F9H&2hn2P7PwBHA3Rtk?udN~XU*i%&ZH$UXY)E!MSlk;}RMmTQgce2{#IE!%|-MLwvB9wI!q zz2#>sCMv3y=T$&m53=*&1{_@vT0T}gtUoL|go`&i7GXLvRm$9YvtKEtAZ1Z$!UyBx znP$}sV3RJ-Fop&~jG*fyi$-kTIY#=aiWcGF7a^}F7f+w9L4N@)rVC5BJZrhhZO#cV zO8Ohy+oTzZF`Zc=*8Q@)JdQm*!4aIkyn@y<%nCd6w@*+>VmZqAp97zl)V#Zyph=P6 zvg3dVGJ2gE#HE=8M-l#Jvn3NHoGDC5AZ@oYW&J#JEpKMV?3u>KHyGT3l0r23M2_Vh zR43(h?js&UxVJaAT@!T|Bkn}4eS^h>>HOJQsG->Xi}sTff#ah3`s-~4uU~wyJlwbw z5THC8Ddsx*TtkD=m`we%OJ)9QF1K}6(=#nsl??(;qtVZ<9vA1*MDJ9h-A_Mqaz2_{ z;w)!-(GEgt67w(H!PlFj-x0s?n_uUkYQ1$gsVG1cf{z^~JZGg|Bw{^zz_oLCyt((` z%LblmEBQ|KyYTB?247eII=yV?M)oyFP(t?RS7nnygL`yEtZb6CPTLrE{Z-b0MbyD~ zlU{&)N2|(ZsBg9;^5SLn7C*m(6td3t)j_&!IP-nIGB%loU8nKa;S$a!1-9!y@N2}( zX~d1U*t8)r6Q!)Yb{6C>!DL^*EtHCZkH1Zw{83WkOka!YLsiDJwchxItVbzC@1Q$~ z%uMX9;ND&;>fyeW6r0_Jy!e=M7cK2VnKy5gGotRo33@JFrZLOUSB^JIf|rM@AOvRo zn?KBBr>(+rFSLaq>Gbv!r?3>0iR@FYl9i%@eItvXydiA}Qz%UAQ=n4vY2T`uM!JXN zLg9)9YQ9aaXbS0Q!r`7CNVeW91nIgsNbZOc+c?vzx7^V4?6v*q8i1S)6&(WeYb+cY z*mU}2{$BWGC9-ET5H+_E;A6KScH|1>=SaXC>MvjuaPE{paiFS;9=p?&(;u$jd;{k! zrh>Yi5jV}JEgS8^3pS|G)JdqLUxRae!-%FsY5G~fK)>rM?&K$oX}zObq{R2A%H~+B zMR5i0V6ea`W?`4{JHOtRi*$?YeB#InBx}H+B16Hig`OJ&Xe<{nqVzwEgj^ht-K2Myo(bYzeJGhXq4rY8jjS(B zKwduo4U~lz5Y?Q-8De{$%PL_-%DO@3_4#r;Bwr4~h}g;SQWM|SO`BTkQItt3K{VU0 zt1{5iK7DnQ#}w-C!#%cnn30s$^oiNnD-K!;=S>$E5J>;q6qlazCAACJ>-zOJZZb94 zwQX4hkR*?c%M&KRf2CTcg-bKH#yP)_!!``+f;Pf;#>eAmPfZ_CF!{o;9{ZA!LxdpG d3NRGrHE+w}E1KO(;6HC%QIvfGE0osv{tt{f_mltt literal 0 HcmV?d00001 diff --git a/docs/assets/endorser-design.puml b/docs/assets/endorser-design.puml new file mode 100644 index 0000000000..39883ea66b --- /dev/null +++ b/docs/assets/endorser-design.puml @@ -0,0 +1,31 @@ +@startuml +interface AdminUser + +interface OtherAgent + +object TransactionRoutes + +object TransactionHandlers + +AdminUser --> TransactionRoutes: invoke_endpoint() + +OtherAgent --> TransactionHandlers: send_message() + +object TransactionManager + +object Wallet + +TransactionManager --> Wallet: manage_records() + +TransactionRoutes --> TransactionManager: invoke_api() +TransactionHandlers --> TransactionManager: handle_msg() + +object EventBus + +TransactionManager --> EventBus: notify() + +interface OtherProtocolRoutes + +OtherProtocolRoutes --> EventBus: subscribe() +EventBus --> OtherProtocolRoutes: notify() +@enduml From 71d41e99c13871d63b225e7fe37989e7dace26a9 Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Wed, 31 Aug 2022 11:12:56 -0700 Subject: [PATCH 455/872] pre-populate revocation registry ID in IssuerRevRegRecord Signed-off-by: Andrew Whitehead --- aries_cloudagent/revocation/indy.py | 12 ++++++++++-- .../revocation/models/issuer_rev_reg_record.py | 2 ++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/revocation/indy.py b/aries_cloudagent/revocation/indy.py index e010fe4534..eebbfb17a6 100644 --- a/aries_cloudagent/revocation/indy.py +++ b/aries_cloudagent/revocation/indy.py @@ -1,6 +1,7 @@ """Indy revocation registry management.""" from typing import Optional, Sequence, Tuple +from uuid import uuid4 from ..core.profile import Profile from ..ledger.base import BaseLedger @@ -44,7 +45,7 @@ async def init_issuer_registry( create_pending_rev_reg: bool = False, endorser_connection_id: str = None, notify: bool = True, - ) -> "IssuerRevRegRecord": + ) -> IssuerRevRegRecord: """Create a new revocation registry record for a credential definition.""" multitenant_mgr = self._profile.inject_or(BaseMultitenantManager) if multitenant_mgr: @@ -72,11 +73,18 @@ async def init_issuer_registry( f"Bad revocation registry size: {max_cred_num}" ) + record_id = str(uuid4()) + issuer_did = cred_def_id.split(":")[0] + tag = tag or record_id + revoc_reg_id = f"{issuer_did}:4:{cred_def_id}:{revoc_def_type}:{tag}" record = IssuerRevRegRecord( + new_with_id=True, + record_id=record_id, cred_def_id=cred_def_id, - issuer_did=cred_def_id.split(":")[0], + issuer_did=issuer_did, max_cred_num=max_cred_num, revoc_def_type=revoc_def_type, + revoc_reg_id=revoc_reg_id, tag=tag, ) async with self._profile.session() as session: diff --git a/aries_cloudagent/revocation/models/issuer_rev_reg_record.py b/aries_cloudagent/revocation/models/issuer_rev_reg_record.py index 2e9d7b7e42..1e3d41a9da 100644 --- a/aries_cloudagent/revocation/models/issuer_rev_reg_record.py +++ b/aries_cloudagent/revocation/models/issuer_rev_reg_record.py @@ -202,6 +202,8 @@ async def generate_registry(self, profile: Profile): except IndyIssuerError as err: raise RevocationError() from err + if self.revoc_reg_id and revoc_reg_id != self.revoc_reg_id: + raise RevocationError("Generated registry ID does not match assigned value") self.revoc_reg_id = revoc_reg_id self.revoc_reg_def = json.loads(revoc_reg_def_json) self.revoc_reg_entry = json.loads(revoc_reg_entry_json) From 83516fbef3f53978768f0b163d1ed1a110eba4a5 Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Wed, 31 Aug 2022 11:13:40 -0700 Subject: [PATCH 456/872] ensure rev reg in 'init' state is not returned from /created endpoint Signed-off-by: Andrew Whitehead --- aries_cloudagent/revocation/routes.py | 14 ++++++++++++-- aries_cloudagent/revocation/tests/test_routes.py | 1 - 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/aries_cloudagent/revocation/routes.py b/aries_cloudagent/revocation/routes.py index 0e897beb02..3cba351d03 100644 --- a/aries_cloudagent/revocation/routes.py +++ b/aries_cloudagent/revocation/routes.py @@ -585,9 +585,19 @@ async def rev_regs_created(request: web.BaseRequest): tag: request.query[tag] for tag in search_tags if tag in request.query } async with context.profile.session() as session: - found = await IssuerRevRegRecord.query(session, tag_filter) + found = await IssuerRevRegRecord.query( + session, + tag_filter, + post_filter_negative={"state": IssuerRevRegRecord.STATE_INIT}, + ) - return web.json_response({"rev_reg_ids": [record.revoc_reg_id for record in found]}) + return web.json_response( + { + "rev_reg_ids": [ + record.revoc_reg_id for record in found if record.revoc_reg_id + ] + } + ) @docs( diff --git a/aries_cloudagent/revocation/tests/test_routes.py b/aries_cloudagent/revocation/tests/test_routes.py index ace6f54e9d..34cb11d323 100644 --- a/aries_cloudagent/revocation/tests/test_routes.py +++ b/aries_cloudagent/revocation/tests/test_routes.py @@ -282,7 +282,6 @@ async def test_create_rev_reg_no_revo_support(self): async def test_rev_regs_created(self): CRED_DEF_ID = f"{self.test_did}:3:CL:1234:default" - STATE = "active" self.request.query = { "cred_def_id": CRED_DEF_ID, "state": test_module.IssuerRevRegRecord.STATE_ACTIVE, From 8cc9a6fdcf87929e744bb71a7d95b387dfa5215e Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Wed, 31 Aug 2022 11:36:13 -0700 Subject: [PATCH 457/872] remove indy filter on tests Signed-off-by: Andrew Whitehead --- aries_cloudagent/revocation/tests/test_indy.py | 1 - 1 file changed, 1 deletion(-) diff --git a/aries_cloudagent/revocation/tests/test_indy.py b/aries_cloudagent/revocation/tests/test_indy.py index e1a79ab585..08fd5eb828 100644 --- a/aries_cloudagent/revocation/tests/test_indy.py +++ b/aries_cloudagent/revocation/tests/test_indy.py @@ -20,7 +20,6 @@ from ..models.revocation_registry import RevocationRegistry -@pytest.mark.indy class TestIndyRevocation(AsyncTestCase): def setUp(self): self.profile = InMemoryProfile.test_profile() From 2fbcecf77869b0f73cb1374de03626559d744eef Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Wed, 31 Aug 2022 11:36:35 -0700 Subject: [PATCH 458/872] adjust rev reg record init process Signed-off-by: Andrew Whitehead --- aries_cloudagent/revocation/indy.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/aries_cloudagent/revocation/indy.py b/aries_cloudagent/revocation/indy.py index eebbfb17a6..57c2af53cf 100644 --- a/aries_cloudagent/revocation/indy.py +++ b/aries_cloudagent/revocation/indy.py @@ -75,8 +75,6 @@ async def init_issuer_registry( record_id = str(uuid4()) issuer_did = cred_def_id.split(":")[0] - tag = tag or record_id - revoc_reg_id = f"{issuer_did}:4:{cred_def_id}:{revoc_def_type}:{tag}" record = IssuerRevRegRecord( new_with_id=True, record_id=record_id, @@ -84,9 +82,11 @@ async def init_issuer_registry( issuer_did=issuer_did, max_cred_num=max_cred_num, revoc_def_type=revoc_def_type, - revoc_reg_id=revoc_reg_id, tag=tag, ) + revoc_def_type = record.revoc_def_type + rtag = record.tag or record_id + record.revoc_reg_id = f"{issuer_did}:4:{cred_def_id}:{revoc_def_type}:{rtag}" async with self._profile.session() as session: await record.save(session, reason="Init revocation registry") From 8b9fccdea1e28472e02b009201e527ef928c1b54 Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Tue, 6 Sep 2022 21:41:58 -0600 Subject: [PATCH 459/872] fix: Close session before sending credential delete webhook Signed-off-by: Colton Wolkins (Indicio work address) --- aries_cloudagent/holder/routes.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/aries_cloudagent/holder/routes.py b/aries_cloudagent/holder/routes.py index 614a3eedf9..26032000b5 100644 --- a/aries_cloudagent/holder/routes.py +++ b/aries_cloudagent/holder/routes.py @@ -274,10 +274,8 @@ async def credentials_remove(request: web.BaseRequest): async with context.profile.session() as session: holder = session.inject(IndyHolder) await holder.delete_credential(credential_id) - topic = "acapy::record::credential::delete" - await session.profile.notify( - topic, {"id": credential_id, "state": "deleted"} - ) + topic = "acapy::record::credential::delete" + await context.profile.notify(topic, {"id": credential_id, "state": "deleted"}) except WalletNotFoundError as err: raise web.HTTPNotFound(reason=err.roll_up) from err From 04a929c6131bb26345aefa154391d5134495db1d Mon Sep 17 00:00:00 2001 From: Ry Jones Date: Fri, 9 Sep 2022 08:18:45 -0700 Subject: [PATCH 460/872] Delete sonarcloud.yml Disable automatic sonarcloud Signed-off-by: Ry Jones --- .github/workflows/sonarcloud.yml | 41 -------------------------------- 1 file changed, 41 deletions(-) delete mode 100644 .github/workflows/sonarcloud.yml diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml deleted file mode 100644 index 1a71527c05..0000000000 --- a/.github/workflows/sonarcloud.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: SonarCloud analysis - -on: - push: - branches: [ "main", release* ] - pull_request: - branches: [ "main" ] - workflow_dispatch: - -permissions: - pull-requests: read # allows SonarCloud to decorate PRs with analysis results - -jobs: - Analysis: - runs-on: ubuntu-latest - - steps: - - name: Analyze with SonarCloud - - # You can pin the exact commit or the version. - # uses: SonarSource/sonarcloud-github-action@de2e56b42aa84d0b1c5b622644ac17e505c9a049 - uses: SonarSource/sonarcloud-github-action@de2e56b42aa84d0b1c5b622644ac17e505c9a049 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} # Generate a token on Sonarcloud.io, add it to the secrets of this repo with the name SONAR_TOKEN (Settings > Secrets > Actions > add new repository secret) - with: - # Additional arguments for the sonarcloud scanner - args: - # Unique keys of your project and organization. You can find them in SonarCloud > Information (bottom-left menu) - # mandatory - -Dsonar.projectKey=hyperledger_aries-cloudagent-python - -Dsonar.organization=hyperledger - -Dsonar.cpd.exclusions=**/tests/** - # Comma-separated paths to directories containing main source files. - #-Dsonar.sources= # optional, default is project base directory - # When you need the analysis to take place in a directory other than the one from which it was launched - #-Dsonar.projectBaseDir= # optional, default is . - # Comma-separated paths to directories containing test source files. - #-Dsonar.tests= # optional. For more info about Code Coverage, please refer to https://docs.sonarcloud.io/enriching/test-coverage/overview/ - # Adds more detail to both client and server-side analysis logs, activating DEBUG mode for the scanner, and adding client-side environment variables and system properties to the server-side log of analysis report processing. - #-Dsonar.verbose= # optional, default is false From ad2148af98082098babbba07c985497a65818146 Mon Sep 17 00:00:00 2001 From: Char Howland Date: Thu, 8 Sep 2022 17:08:41 -0600 Subject: [PATCH 461/872] fix: remove layer of endpoint nesting Signed-off-by: Char Howland --- aries_cloudagent/ledger/base.py | 12 ++--- aries_cloudagent/ledger/tests/test_indy.py | 32 +++++++++---- .../ledger/tests/test_indy_vdr.py | 48 ++++++++----------- 3 files changed, 49 insertions(+), 43 deletions(-) diff --git a/aries_cloudagent/ledger/base.py b/aries_cloudagent/ledger/base.py index 8d1ea4e5d0..1a8f880174 100644 --- a/aries_cloudagent/ledger/base.py +++ b/aries_cloudagent/ledger/base.py @@ -98,17 +98,15 @@ async def _construct_attr_json( if not routing_keys: routing_keys = [] - endpoint_dict = {"endpoint": endpoint} - if all_exist_endpoints: - all_exist_endpoints[endpoint_type.indy] = endpoint_dict - endpoint_dict["routingKeys"] = routing_keys - attr_json = json.dumps({"endpoint": all_exist_endpoints}) + all_exist_endpoints[endpoint_type.indy] = endpoint + all_exist_endpoints["routingKeys"] = routing_keys + attr_json = json.dumps(all_exist_endpoints) else: - endpoint_val = {endpoint_type.indy: endpoint_dict} + endpoint_dict = {endpoint_type.indy: endpoint} endpoint_dict["routingKeys"] = routing_keys - attr_json = json.dumps({"endpoint": endpoint_val}) + attr_json = json.dumps(endpoint_dict) return attr_json diff --git a/aries_cloudagent/ledger/tests/test_indy.py b/aries_cloudagent/ledger/tests/test_indy.py index 767eaa50a3..f1c9cc72b3 100644 --- a/aries_cloudagent/ledger/tests/test_indy.py +++ b/aries_cloudagent/ledger/tests/test_indy.py @@ -2308,18 +2308,34 @@ async def test_construct_attr_json_with_routing_keys(self, mock_close, mock_open attr_json = await ledger._construct_attr_json( "https://url", EndpointType.ENDPOINT, - all_exist_endpoints={"Endpoint": "https://endpoint"}, routing_keys=["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"], ) assert attr_json == json.dumps( { - "endpoint": { - "Endpoint": "https://endpoint", - "endpoint": { - "endpoint": "https://url", - "routingKeys": ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"], - }, - } + "endpoint": "https://url", + "routingKeys": ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"], + } + ) + + @async_mock.patch("aries_cloudagent.ledger.indy.IndySdkLedgerPool.context_open") + @async_mock.patch("aries_cloudagent.ledger.indy.IndySdkLedgerPool.context_close") + @pytest.mark.asyncio + async def test_construct_attr_json_with_routing_keys_all_exist_endpoints( + self, mock_close, mock_open + ): + ledger = IndySdkLedger(IndySdkLedgerPool("name", checked=True), self.profile) + async with ledger: + attr_json = await ledger._construct_attr_json( + "https://url", + EndpointType.ENDPOINT, + all_exist_endpoints={"profile": "https://endpoint/profile"}, + routing_keys=["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"], + ) + assert attr_json == json.dumps( + { + "profile": "https://endpoint/profile", + "endpoint": "https://url", + "routingKeys": ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"], } ) diff --git a/aries_cloudagent/ledger/tests/test_indy_vdr.py b/aries_cloudagent/ledger/tests/test_indy_vdr.py index 13db2c5563..8d646856ab 100644 --- a/aries_cloudagent/ledger/tests/test_indy_vdr.py +++ b/aries_cloudagent/ledger/tests/test_indy_vdr.py @@ -612,51 +612,43 @@ async def test_update_endpoint_for_did( "all_exist_endpoints, routing_keys, result", [ ( - {"Endpoint": "https://endpoint"}, + {"profile": "https://endpoint/profile"}, ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"], { - "endpoint": { - "Endpoint": "https://endpoint", - "endpoint": { - "endpoint": "https://url", - "routingKeys": [ - "3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn" - ], - }, - } + "profile": "https://endpoint/profile", + "endpoint": "https://url", + "routingKeys": ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"], }, ), ( - {"Endpoint": "https://endpoint"}, + {"profile": "https://endpoint/profile"}, None, { - "endpoint": { - "Endpoint": "https://endpoint", - "endpoint": {"endpoint": "https://url", "routingKeys": []}, - } + "profile": "https://endpoint/profile", + "endpoint": "https://url", + "routingKeys": [], }, ), ( None, ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"], { - "endpoint": { - "endpoint": { - "endpoint": "https://url", - "routingKeys": [ - "3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn" - ], - } - } + "endpoint": "https://url", + "routingKeys": ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"], }, ), + (None, None, {"endpoint": "https://url", "routingKeys": []}), ( - None, - None, { - "endpoint": { - "endpoint": {"endpoint": "https://url", "routingKeys": []} - } + "profile": "https://endpoint/profile", + "spec_divergent_endpoint": "https://endpoint", + }, + ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"], + { + "profile": "https://endpoint/profile", + "spec_divergent_endpoint": "https://endpoint", + "endpoint": "https://url", + "routingKeys": ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"], }, ), ], From de4c36bd201489411e4faf7e40b3e035eb920a3c Mon Sep 17 00:00:00 2001 From: Char Howland Date: Thu, 8 Sep 2022 17:11:41 -0600 Subject: [PATCH 462/872] fix: endpoint value cannot be a dict Signed-off-by: Char Howland --- aries_cloudagent/resolver/default/indy.py | 145 ++++++++++-------- .../resolver/default/tests/test_indy.py | 47 ++++-- 2 files changed, 113 insertions(+), 79 deletions(-) diff --git a/aries_cloudagent/resolver/default/indy.py b/aries_cloudagent/resolver/default/indy.py index 3d65d03a08..e9b4df347b 100644 --- a/aries_cloudagent/resolver/default/indy.py +++ b/aries_cloudagent/resolver/default/indy.py @@ -3,7 +3,8 @@ Resolution is performed using the IndyLedger class. """ -from typing import Any, Mapping, Pattern +import logging +from typing import Optional, Pattern from pydid import DID, DIDDocumentBuilder from pydid.verification_method import Ed25519VerificationKey2018, VerificationMethod @@ -21,6 +22,8 @@ from ..base import BaseDIDResolver, DIDNotFound, ResolverError, ResolverType +LOGGER = logging.getLogger(__name__) + class NoIndyLedger(ResolverError): """Raised when there is no Indy ledger instance configured.""" @@ -46,60 +49,83 @@ def supported_did_regex(self) -> Pattern: """Return supported_did_regex of Indy DID Resolver.""" return IndyDID.PATTERN - def _add_endpoint_as_endpoint_value_pair( - self, - builder: DIDDocumentBuilder, - endpoint: str, - recipient_key: VerificationMethod, - ): - builder.service.add_didcomm( - ident=self.SERVICE_TYPE_DID_COMMUNICATION, - type_=self.SERVICE_TYPE_DID_COMMUNICATION, - service_endpoint=endpoint, - priority=1, - recipient_keys=[recipient_key], - routing_keys=[], - ) - - def _add_endpoint_as_map( + def process_endpoint_types(self, types): + """Process endpoint types to return only expected types, + subset of expected types, or default types. + """ + expected_types = ["endpoint", "did-communication", "DIDComm"] + default_types = ["endpoint", "did-communication"] + if len(types) <= 0: + return default_types + for type in types: + if type not in expected_types: + return default_types + return types + + def add_services( self, builder: DIDDocumentBuilder, - endpoint: Mapping[str, Any], - recipient_key: VerificationMethod, + endpoints: Optional[dict], + recipient_key: VerificationMethod = None, ): - types = endpoint.get("types", [self.SERVICE_TYPE_DID_COMMUNICATION]) - routing_keys = endpoint.get("routingKeys", []) - endpoint_url = endpoint.get("endpoint") - if not endpoint_url: - raise ValueError("endpoint url not found in endpoint attrib") - - if self.SERVICE_TYPE_DIDCOMM in types: - builder.service.add( - ident="#didcomm-1", - type_=self.SERVICE_TYPE_DIDCOMM, - service_endpoint=endpoint_url, - recipient_keys=[recipient_key.id], - routing_keys=routing_keys, - accept=["didcomm/v2"], - ) - builder.context.append(self.CONTEXT_DIDCOMM_V2) - if self.SERVICE_TYPE_DID_COMMUNICATION in types: - builder.service.add( - ident="did-communication", - type_=self.SERVICE_TYPE_DID_COMMUNICATION, - service_endpoint=endpoint_url, - priority=1, - routing_keys=routing_keys, - recipient_keys=[recipient_key.id], - accept=["didcomm/aip2;env=rfc19"], - ) - if self.SERVICE_TYPE_ENDPOINT in types: - builder.service.add( - ident="endpoint", - service_endpoint=endpoint_url, - type_=self.SERVICE_TYPE_ENDPOINT, + """Add services.""" + if not endpoints: + return + + endpoint = endpoints.get("endpoint") + routing_keys = endpoints.get("routingKeys", []) + types = endpoints.get("types", [self.SERVICE_TYPE_DID_COMMUNICATION]) + + other_endpoints = { + key: endpoints[key] + for key in ("profile", "linked_domains") + if key in endpoints + } + + if endpoint: + processed_types = self.process_endpoint_types(types) + + if self.SERVICE_TYPE_ENDPOINT in processed_types: + builder.service.add( + ident="endpoint", + service_endpoint=endpoint, + type_=self.SERVICE_TYPE_ENDPOINT, + ) + + if self.SERVICE_TYPE_DID_COMMUNICATION in processed_types: + builder.service.add( + ident="did-communication", + type_=self.SERVICE_TYPE_DID_COMMUNICATION, + service_endpoint=endpoint, + priority=1, + routing_keys=routing_keys, + recipient_keys=[recipient_key.id], + accept=["didcomm/aip2;env=rfc19"], + ) + + if self.SERVICE_TYPE_DIDCOMM in types: + builder.service.add( + ident="#didcomm-1", + type_=self.SERVICE_TYPE_DIDCOMM, + service_endpoint=endpoint, + recipient_keys=[recipient_key.id], + routing_keys=routing_keys, + accept=["didcomm/v2"], + ) + builder.context.append(self.CONTEXT_DIDCOMM_V2) + else: + LOGGER.warning( + "No endpoint for DID although endpoint attrib was resolvable" ) + if other_endpoints: + for type_, endpoint in other_endpoints.items(): + builder.service.add( + ident=type_, + type_=EndpointType.get(type_).w3c, + service_endpoint=endpoint, + ) + async def _resolve(self, profile: Profile, did: str) -> dict: """Resolve an indy DID.""" multitenant_mgr = profile.inject_or(BaseMultitenantManager) @@ -119,7 +145,7 @@ async def _resolve(self, profile: Profile, did: str) -> dict: try: async with ledger: recipient_key = await ledger.get_key_for_did(did) - endpoints = await ledger.get_all_endpoints_for_did(did) + endpoints: Optional[dict] = await ledger.get_all_endpoints_for_did(did) except LedgerError as err: raise DIDNotFound(f"DID {did} could not be resolved") from err @@ -130,22 +156,7 @@ async def _resolve(self, profile: Profile, did: str) -> dict: ) builder.authentication.reference(vmethod.id) builder.assertion_method.reference(vmethod.id) - if endpoints: - for type_, endpoint in endpoints.items(): - if type_ == EndpointType.ENDPOINT.indy: - if isinstance(endpoint, dict): - self._add_endpoint_as_map(builder, endpoint, vmethod) - else: - self._add_endpoint_as_endpoint_value_pair( - builder, endpoint, vmethod - ) - else: - # Accept all service types for now, i.e. profile, linked_domains - builder.service.add( - ident=type_, - type_=type_, - service_endpoint=endpoint, - ) + self.add_services(builder, endpoints, vmethod) result = builder.build() return result.serialize() diff --git a/aries_cloudagent/resolver/default/tests/test_indy.py b/aries_cloudagent/resolver/default/tests/test_indy.py index b53c06d7c7..d1a17dd357 100644 --- a/aries_cloudagent/resolver/default/tests/test_indy.py +++ b/aries_cloudagent/resolver/default/tests/test_indy.py @@ -3,6 +3,7 @@ import pytest from asynctest import mock as async_mock +from pydid.verification_method import VerificationMethod from ....core.in_memory import InMemoryProfile from ....core.profile import Profile @@ -116,11 +117,11 @@ async def test_supports_updated_did_sov_rules( ): """Test that new attrib structure is supported.""" example = { - "endpoint": { - "endpoint": "https://example.com/endpoint", - "routingKeys": ["a-routing-key"], - "types": ["DIDComm", "did-communication", "endpoint"], - } + "endpoint": "https://example.com/endpoint", + "routingKeys": ["a-routing-key"], + "types": ["DIDComm", "did-communication", "endpoint"], + "profile": "https://example.com", + "linked_domains": "https://example.com", } ledger.get_all_endpoints_for_did = async_mock.CoroutineMock( @@ -129,19 +130,41 @@ async def test_supports_updated_did_sov_rules( assert await resolver.resolve(profile, TEST_DID0) @pytest.mark.asyncio - async def test_supports_updated_did_sov_rules_x_no_endpoint_url( + async def test_supports_updated_did_sov_rules_no_endpoint_url( self, resolver: IndyDIDResolver, ledger: BaseLedger, profile: Profile ): """Test that new attrib structure is supported.""" example = { - "endpoint": { - "routingKeys": ["a-routing-key"], - "types": ["DIDComm", "did-communication", "endpoint"], - } + "routingKeys": ["a-routing-key"], + "types": ["DIDComm", "did-communication", "endpoint"], } ledger.get_all_endpoints_for_did = async_mock.CoroutineMock( return_value=example ) - with pytest.raises(ValueError): - await resolver.resolve(profile, TEST_DID0) + result = await resolver.resolve(profile, TEST_DID0) + assert "service" not in result + + @pytest.mark.parametrize( + "types, result", + [ + ( + [], + ["endpoint", "did-communication"], + ), + ( + ["did-communication"], + ["did-communication"], + ), + ( + ["endpoint", "did-communication", "DIDComm", "other-endpoint-type"], + ["endpoint", "did-communication"], + ), + ( + ["endpoint", "did-communication", "DIDComm"], + ["endpoint", "did-communication", "DIDComm"], + ), + ], + ) + def test_process_endpoint_types(self, resolver: IndyDIDResolver, types, result): + assert resolver.process_endpoint_types(types) == result From 13110bf6019cee7feae7ebaf65aaf5d100593431 Mon Sep 17 00:00:00 2001 From: Char Howland Date: Thu, 8 Sep 2022 17:40:06 -0600 Subject: [PATCH 463/872] fix: flake8 failure Signed-off-by: Char Howland --- aries_cloudagent/resolver/default/indy.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/resolver/default/indy.py b/aries_cloudagent/resolver/default/indy.py index e9b4df347b..e3e66688e6 100644 --- a/aries_cloudagent/resolver/default/indy.py +++ b/aries_cloudagent/resolver/default/indy.py @@ -50,8 +50,10 @@ def supported_did_regex(self) -> Pattern: return IndyDID.PATTERN def process_endpoint_types(self, types): - """Process endpoint types to return only expected types, - subset of expected types, or default types. + """Process endpoint types. + + Returns expected types, subset of expected types, + or default types. """ expected_types = ["endpoint", "did-communication", "DIDComm"] default_types = ["endpoint", "did-communication"] From 981cc6dcdfae52e0c5e03622ca6dba17d82852fd Mon Sep 17 00:00:00 2001 From: Char Howland Date: Fri, 9 Sep 2022 09:56:06 -0600 Subject: [PATCH 464/872] fix: endpoint attrib json object with one top level key "endpoint" Signed-off-by: Char Howland --- aries_cloudagent/ledger/base.py | 4 +-- aries_cloudagent/ledger/tests/test_indy.py | 14 +++++--- .../ledger/tests/test_indy_vdr.py | 34 ++++++++++++------- 3 files changed, 32 insertions(+), 20 deletions(-) diff --git a/aries_cloudagent/ledger/base.py b/aries_cloudagent/ledger/base.py index 1a8f880174..06bd362d95 100644 --- a/aries_cloudagent/ledger/base.py +++ b/aries_cloudagent/ledger/base.py @@ -101,12 +101,12 @@ async def _construct_attr_json( if all_exist_endpoints: all_exist_endpoints[endpoint_type.indy] = endpoint all_exist_endpoints["routingKeys"] = routing_keys - attr_json = json.dumps(all_exist_endpoints) + attr_json = json.dumps({"endpoint": all_exist_endpoints}) else: endpoint_dict = {endpoint_type.indy: endpoint} endpoint_dict["routingKeys"] = routing_keys - attr_json = json.dumps(endpoint_dict) + attr_json = json.dumps({"endpoint": endpoint_dict}) return attr_json diff --git a/aries_cloudagent/ledger/tests/test_indy.py b/aries_cloudagent/ledger/tests/test_indy.py index f1c9cc72b3..bda1890c2a 100644 --- a/aries_cloudagent/ledger/tests/test_indy.py +++ b/aries_cloudagent/ledger/tests/test_indy.py @@ -2312,8 +2312,10 @@ async def test_construct_attr_json_with_routing_keys(self, mock_close, mock_open ) assert attr_json == json.dumps( { - "endpoint": "https://url", - "routingKeys": ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"], + "endpoint": { + "endpoint": "https://url", + "routingKeys": ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"], + } } ) @@ -2333,9 +2335,11 @@ async def test_construct_attr_json_with_routing_keys_all_exist_endpoints( ) assert attr_json == json.dumps( { - "profile": "https://endpoint/profile", - "endpoint": "https://url", - "routingKeys": ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"], + "endpoint": { + "profile": "https://endpoint/profile", + "endpoint": "https://url", + "routingKeys": ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"], + } } ) diff --git a/aries_cloudagent/ledger/tests/test_indy_vdr.py b/aries_cloudagent/ledger/tests/test_indy_vdr.py index 8d646856ab..f20781e838 100644 --- a/aries_cloudagent/ledger/tests/test_indy_vdr.py +++ b/aries_cloudagent/ledger/tests/test_indy_vdr.py @@ -615,29 +615,35 @@ async def test_update_endpoint_for_did( {"profile": "https://endpoint/profile"}, ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"], { - "profile": "https://endpoint/profile", - "endpoint": "https://url", - "routingKeys": ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"], + "endpoint": { + "profile": "https://endpoint/profile", + "endpoint": "https://url", + "routingKeys": ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"], + } }, ), ( {"profile": "https://endpoint/profile"}, None, { - "profile": "https://endpoint/profile", - "endpoint": "https://url", - "routingKeys": [], + "endpoint": { + "profile": "https://endpoint/profile", + "endpoint": "https://url", + "routingKeys": [], + } }, ), ( None, ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"], { - "endpoint": "https://url", - "routingKeys": ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"], + "endpoint": { + "endpoint": "https://url", + "routingKeys": ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"], + } }, ), - (None, None, {"endpoint": "https://url", "routingKeys": []}), + (None, None, {"endpoint": {"endpoint": "https://url", "routingKeys": []}}), ( { "profile": "https://endpoint/profile", @@ -645,10 +651,12 @@ async def test_update_endpoint_for_did( }, ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"], { - "profile": "https://endpoint/profile", - "spec_divergent_endpoint": "https://endpoint", - "endpoint": "https://url", - "routingKeys": ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"], + "endpoint": { + "profile": "https://endpoint/profile", + "spec_divergent_endpoint": "https://endpoint", + "endpoint": "https://url", + "routingKeys": ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"], + } }, ), ], From abcc96deffeb0579fac74755c8014e875b5a93d4 Mon Sep 17 00:00:00 2001 From: kukgini Date: Thu, 8 Sep 2022 19:33:47 +0900 Subject: [PATCH 465/872] fix: failed connectionless present proof on some case In demo menu (2a) connectionless proof request, If a mobile agent (for instance Trinsic) does not include Accept header when present proof request, the demo would fail. Signed-off-by: kukgini Signed-off-by: Ry Jones --- demo/runners/support/agent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/runners/support/agent.py b/demo/runners/support/agent.py index bd82314937..89d38c61b3 100644 --- a/demo/runners/support/agent.py +++ b/demo/runners/support/agent.py @@ -781,7 +781,7 @@ async def _send_connectionless_proof_req(self, request: ClientRequest): return web.Response(status=404) proof_reg_txn = proof_exch["presentation_request_dict"] proof_reg_txn["~service"] = await self.service_decorator() - if request.headers["Accept"] == "application/json": + if request.headers.get("Accept") == "application/json": return web.json_response(proof_reg_txn) objJsonStr = json.dumps(proof_reg_txn) objJsonB64 = base64.b64encode(objJsonStr.encode("ascii")) From e0ac68776b2ff7be141857a6382747beac0aa1be Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Sun, 11 Sep 2022 19:49:18 -0700 Subject: [PATCH 466/872] add RedisPlugins.md and modified run_docked Signed-off-by: Shaanjot Gill --- RedisPlugins.md | 266 +++++++++++++++++++++++++++++++++++++++++++++ scripts/run_docker | 10 +- 2 files changed, 275 insertions(+), 1 deletion(-) create mode 100644 RedisPlugins.md diff --git a/RedisPlugins.md b/RedisPlugins.md new file mode 100644 index 0000000000..da4ee090bb --- /dev/null +++ b/RedisPlugins.md @@ -0,0 +1,266 @@ +# ACA-Py Redis Plugins +# [aries-acapy-plugin-redis-events](https://github.com/bcgov/aries-acapy-plugin-redis-events/blob/master/README.md) [`redis_queue`] + + +It provides a mechansim to persists both inbound and outbound messages using redis, deliver messages and webhooks, and dispatch events. + +More details can be found [here](https://github.com/bcgov/aries-acapy-plugin-redis-events/blob/master/README.md). + +### Plugin configuration [`yaml`] +``` +redis_queue: + connection: + connection_url: "redis://default:test1234@172.28.0.103:6379" + + ### For Inbound ### + inbound: + acapy_inbound_topic: "acapy_inbound" + acapy_direct_resp_topic: "acapy_inbound_direct_resp" + + ### For Outbound ### + outbound: + acapy_outbound_topic: "acapy_outbound" + mediator_mode: false + + ### For Event ### + event: + event_topic_maps: + ^acapy::webhook::(.*)$: acapy-webhook-$wallet_id + ^acapy::record::([^:]*)::([^:]*)$: acapy-record-with-state-$wallet_id + ^acapy::record::([^:])?: acapy-record-$wallet_id + acapy::basicmessage::received: acapy-basicmessage-received + acapy::problem_report: acapy-problem_report + acapy::ping::received: acapy-ping-received + acapy::ping::response_received: acapy-ping-response_received + acapy::actionmenu::received: acapy-actionmenu-received + acapy::actionmenu::get-active-menu: acapy-actionmenu-get-active-menu + acapy::actionmenu::perform-menu-action: acapy-actionmenu-perform-menu-action + acapy::keylist::updated: acapy-keylist-updated + acapy::revocation-notification::received: acapy-revocation-notification-received + acapy::revocation-notification-v2::received: acapy-revocation-notification-v2-received + acapy::forward::received: acapy-forward-received + event_webhook_topic_maps: + acapy::basicmessage::received: basicmessages + acapy::problem_report: problem_report + acapy::ping::received: ping + acapy::ping::response_received: ping + acapy::actionmenu::received: actionmenu + acapy::actionmenu::get-active-menu: get-active-menu + acapy::actionmenu::perform-menu-action: perform-menu-action + acapy::keylist::updated: keylist + deliver_webhook: true +``` +- `redis_queue.connection.connection_url`: This is required and is expected in `redis://{username}:{password}@{host}:{port}` format. +- `redis_queue.inbound.acapy_inbound_topic`: This is the topic prefix for the inbound message queues. Recipient key of the message are also included in the complete topic name. The final topic will be in the following format `acapy_inbound_{recip_key}` +- `redis_queue.inbound.acapy_direct_resp_topic`: Queue topic name for direct responses to inbound message. +- `redis_queue.outbound.acapy_outbound_topic`: Queue topic name for the outbound messages. Used by Deliverer service to deliver the payloads to specified endpoint. +- `redis_queue.outbound.mediator_mode`: Set to true, if using Redis as a http bridge when setting up a mediator agent. By default, it is set to false. +- `event.event_topic_maps`: Event topic map +- `event.event_webhook_topic_maps`: Event to webhook topic map +- `event.deliver_webhook`: When set to true, this will deliver webhooks to endpoints specified in `admin.webhook_urls`. By default, set to true. + +### Usage + +#### With Docker +Running the plugin with docker is simple. An +example [docker-compose.yml](https://github.com/bcgov/aries-acapy-plugin-redis-events/blob/master/docker/docker-compose.yml) file is available which launches both ACA-Py with redis and an accompanying Redis cluster. + +```sh +$ docker-compose up --build -d +``` +More details can be found [here](https://github.com/bcgov/aries-acapy-plugin-redis-events/blob/master/docker/README.md). + +#### Without Docker +Installation +``` +pip install git+https://github.com/bcgov/aries-acapy-plugin-redis-events.git +``` +Startup ACA-Py with `redis_queue` plugin loaded +``` +docker network create --subnet=172.28.0.0/24 `network_name` +export REDIS_PASSWORD=" ... As specified in redis_cluster.conf ... " +export NETWORK_NAME="`network_name`" +aca-py start \ + --plugin redis_queue.v1_0.events \ + --plugin-config plugins-config.yaml \ + -it redis_queue.v1_0.inbound redis 0 -ot redis_queue.v1_0.outbound + # ... the remainder of your startup arguments +``` + +Regardless of the options above, you will need to startup `deliverer` and `relay`/`mediator` service as a bridge to receive inbound messages. Consider the following to build your `docker-compose` file which should also start up your redis cluster: +- Relay + Deliverer + ``` + relay: + image: redis-relay + build: + context: .. + dockerfile: redis_relay/Dockerfile + ports: + - 7001:7001 + - 80:80 + environment: + - REDIS_SERVER_URL=redis://default:test1234@172.28.0.103:6379 + - TOPIC_PREFIX=acapy + - STATUS_ENDPOINT_HOST=0.0.0.0 + - STATUS_ENDPOINT_PORT=7001 + - STATUS_ENDPOINT_API_KEY=test_api_key_1 + - INBOUND_TRANSPORT_CONFIG=[["http", "0.0.0.0", "80"]] + - TUNNEL_ENDPOINT=http://relay-tunnel:4040 + - WAIT_BEFORE_HOSTS=15 + - WAIT_HOSTS=redis-node-3:6379 + - WAIT_HOSTS_TIMEOUT=120 + - WAIT_SLEEP_INTERVAL=1 + - WAIT_HOST_CONNECT_TIMEOUT=60 + depends_on: + - redis-cluster + - relay-tunnel + networks: + - acapy_default + deliverer: + image: redis-deliverer + build: + context: .. + dockerfile: redis_deliverer/Dockerfile + ports: + - 7002:7002 + environment: + - REDIS_SERVER_URL=redis://default:test1234@172.28.0.103:6379 + - TOPIC_PREFIX=acapy + - STATUS_ENDPOINT_HOST=0.0.0.0 + - STATUS_ENDPOINT_PORT=7002 + - STATUS_ENDPOINT_API_KEY=test_api_key_2 + - WAIT_BEFORE_HOSTS=15 + - WAIT_HOSTS=redis-node-3:6379 + - WAIT_HOSTS_TIMEOUT=120 + - WAIT_SLEEP_INTERVAL=1 + - WAIT_HOST_CONNECT_TIMEOUT=60 + depends_on: + - redis-cluster + networks: + - acapy_default + ``` +- Mediator + Deliverer + ``` + mediator: + image: acapy-redis-queue + build: + context: .. + dockerfile: docker/Dockerfile + ports: + - 3002:3001 + depends_on: + - deliverer + volumes: + - ./configs:/home/indy/configs:z + - ./acapy-endpoint.sh:/home/indy/acapy-endpoint.sh:z + environment: + - WAIT_BEFORE_HOSTS=15 + - WAIT_HOSTS=redis-node-3:6379 + - WAIT_HOSTS_TIMEOUT=120 + - WAIT_SLEEP_INTERVAL=1 + - WAIT_HOST_CONNECT_TIMEOUT=60 + - TUNNEL_ENDPOINT=http://mediator-tunnel:4040 + networks: + - acapy_default + entrypoint: /bin/sh -c '/wait && ./acapy-endpoint.sh poetry run aca-py "$$@"' -- + command: start --arg-file ./configs/mediator.yml + + deliverer: + image: redis-deliverer + build: + context: .. + dockerfile: redis_deliverer/Dockerfile + depends_on: + - redis-cluster + ports: + - 7002:7002 + environment: + - REDIS_SERVER_URL=redis://default:test1234@172.28.0.103:6379 + - TOPIC_PREFIX=acapy + - STATUS_ENDPOINT_HOST=0.0.0.0 + - STATUS_ENDPOINT_PORT=7002 + - STATUS_ENDPOINT_API_KEY=test_api_key_2 + - WAIT_BEFORE_HOSTS=15 + - WAIT_HOSTS=redis-node-3:6379 + - WAIT_HOSTS_TIMEOUT=120 + - WAIT_SLEEP_INTERVAL=1 + - WAIT_HOST_CONNECT_TIMEOUT=60 + networks: + - acapy_default + ``` + +Both relay and mediator [demos](https://github.com/bcgov/aries-acapy-plugin-redis-events/tree/master/demo) are also available. + +# [aries-acapy-cache-redis](https://github.com/Indicio-tech/aries-acapy-cache-redis/blob/main/README.md) [`redis_cache`] + + +ACA-Py uses a modular cache layer to story key-value pairs of data. The purpose +of this plugin is to allow ACA-Py to use Redis as the storage medium for it's +caching needs. + +More details can be found [here](https://github.com/Indicio-tech/aries-acapy-cache-redis/blob/main/README.md). + +### Plugin configuration [`yaml`] +``` +redis_cache: + connection: "redis://default:test1234@172.28.0.103:6379" + max_connection: 50 + credentials: + username: "default" + password: "test1234" + ssl: + cacerts: ./ca.crt +``` +- `redis_cache.connection`: This is required and is expected in `redis://{username}:{password}@{host}:{port}` format. +- `redis_cache.max_connection`: Maximum number of redis pool connections. Default: 50 +- `redis_cache.credentials.username`: Redis instance username +- `redis_cache.credentials.password`: Redis instance password +- `redis_cache.ssl.cacerts` + +### Usage + +#### With Docker +- Running the plugin with docker is simple and straight-forward. There is an +example [docker-compose.yml](https://github.com/Indicio-tech/aries-acapy-cache-redis/blob/main/docker-compose.yml) file in the root of the +project that launches both ACA-Py and an accompanying Redis instance. Running +it is as simple as: + + ```sh + $ docker-compose up --build -d + ``` + +- To launch ACA-Py with an accompanying redis cluster of 6 nodes [3 primaries and 3 replicas], please refer to example [docker-compose.cluster.yml](https://github.com/Indicio-tech/aries-acapy-cache-redis/blob/main/docker-compose.cluster.yml) and run the following: + + Note: Cluster requires external docker network with specified subnet + + ```sh + $ docker network create --subnet=172.28.0.0/24 `network_name` + $ export REDIS_PASSWORD=" ... As specified in redis_cluster.conf ... " + $ export NETWORK_NAME="`network_name`" + $ docker-compose -f docker-compose.cluster.yml up --build -d + ``` +#### Without Docker +Installation +``` +pip install git+https://github.com/Indicio-tech/aries-acapy-cache-redis.git +``` +Startup ACA-Py with `redis_cache` plugin loaded +``` +aca-py start \ + --plugin acapy_cache_redis.v0_1 \ + --plugin-config plugins-config.yaml \ + # ... the remainder of your startup arguments +``` +or +``` +aca-py start \ + --plugin acapy_cache_redis.v0_1 \ + --plugin-config-value "redis_cache.connection=redis://redis-host:6379/0" \ + --plugin-config-value "redis_cache.max_connections=90" \ + --plugin-config-value "redis_cache.credentials.username=username" \ + --plugin-config-value "redis_cache.credentials.password=password" \ + # ... the remainder of your startup arguments +``` +## RedisCluster + +If you startup a redis cluster and an ACA-Py agent loaded with either `redis_queue` or `redis_cache` plugin or both, then during the initialization of the plugin, it will bind an instance of `redis.asyncio.RedisCluster` [onto the `root_profile`]. Other plugin will have access to this redis client for it's functioning. This is done for efficiency and to avoid duplication of resources. diff --git a/scripts/run_docker b/scripts/run_docker index f16334ebc9..31703b7555 100755 --- a/scripts/run_docker +++ b/scripts/run_docker @@ -33,6 +33,7 @@ for arg in "$@"; do done fi done +ACAPY_NETWORK_NAME="${NETWORK_NAME}" if [ -n "${ENABLE_PTVSD}" ]; then ARGS="${ARGS} -e ENABLE_PTVSD=\"${ENABLE_PTVSD}\" -p $PTVSD_PORT:$PTVSD_PORT" fi @@ -48,5 +49,12 @@ if [ "$OSTYPE" == "msys" ]; then fi RAND_NAME=$(env LC_ALL=C tr -dc 'a-zA-Z0-9' < /dev/urandom | fold -w 16 | head -n 1) -$CONTAINER_RUNTIME run --rm -ti --name "aries-cloudagent-runner_${RAND_NAME}" \ +if [ -z "$NETWORK_NAME" ]; then + echo "No Docker network specified." + $CONTAINER_RUNTIME run --rm -ti --name "aries-cloudagent-runner_${RAND_NAME}" \ $ARGS aries-cloudagent-run "$@" +else + echo "${ACAPY_NETWORK_NAME} Docker network specified." + $CONTAINER_RUNTIME run --rm -ti --network $ACAPY_NETWORK_NAME --name "aries-cloudagent-runner_${RAND_NAME}" \ + $ARGS aries-cloudagent-run "$@" +fi From 70ddd6739d93bb1b6774658cf7d54937234be1bf Mon Sep 17 00:00:00 2001 From: Victor Lee Date: Tue, 13 Sep 2022 14:58:19 -0400 Subject: [PATCH 467/872] argparse.py: added flag --light-weight-webhook Signed-off-by: Victor Lee --- aries_cloudagent/config/argparse.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index c3cc337f6b..a0d5515b82 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -1250,6 +1250,12 @@ def add_arguments(self, parser: ArgumentParser): env_var="ACAPY_MAX_MESSAGE_SIZE", help="Set the maximum size in bytes for inbound agent messages.", ) + parser.add_argument( + "--light-weight-webhook", + action="store_true", + env_var="ACAPY_LIGHT_WEIGHT_WEBHOOK", + help="omitted client's info from issue-credential related webhook", + ) parser.add_argument( "--enable-undelivered-queue", action="store_true", @@ -1313,6 +1319,8 @@ def get_settings(self, args: Namespace): settings["image_url"] = args.image_url if args.max_message_size: settings["transport.max_message_size"] = args.max_message_size + if args.light_weight_webhook: + settings["transport.light_weight_webhook"] = True if args.max_outbound_retry: settings["transport.max_outbound_retry"] = args.max_outbound_retry if args.ws_heartbeat_interval: From 85a24ac0a8d2620db58cf2599993487f899ee330 Mon Sep 17 00:00:00 2001 From: Victor Lee Date: Tue, 13 Sep 2022 14:58:53 -0400 Subject: [PATCH 468/872] transport.outbound.constants: list of obj to remove from webhook Signed-off-by: Victor Lee --- aries_cloudagent/transport/outbound/constants.py | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 aries_cloudagent/transport/outbound/constants.py diff --git a/aries_cloudagent/transport/outbound/constants.py b/aries_cloudagent/transport/outbound/constants.py new file mode 100644 index 0000000000..ec2c899806 --- /dev/null +++ b/aries_cloudagent/transport/outbound/constants.py @@ -0,0 +1,2 @@ +"""Constants for --light-weight-webhook flags""" +REMOVE_KEY=["credential_request", "cred_request", "credential_proposal", "cred_proposal", "credential_offer", "cred_offer", "credential_preview", "cred_preview", "values", "credentials~attach", "offers~attach"] \ No newline at end of file From 34c9805606af5ce3002ff774217973dfc37e35b2 Mon Sep 17 00:00:00 2001 From: Victor Lee Date: Tue, 13 Sep 2022 14:59:11 -0400 Subject: [PATCH 469/872] transport.outbound.manager: remove redundant obj from webhook Signed-off-by: Victor Lee --- .../transport/outbound/manager.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/aries_cloudagent/transport/outbound/manager.py b/aries_cloudagent/transport/outbound/manager.py index 094f537f32..0742932c24 100644 --- a/aries_cloudagent/transport/outbound/manager.py +++ b/aries_cloudagent/transport/outbound/manager.py @@ -7,6 +7,8 @@ from typing import Callable, Type, Union from urllib.parse import urlparse +from collections import MutableMapping +from contextlib import suppress from ...connections.models.connection_target import ConnectionTarget from ...core.profile import Profile @@ -25,6 +27,8 @@ ) from .message import OutboundMessage +from .constants import ( REMOVE_KEY ) + LOGGER = logging.getLogger(__name__) MODULE_BASE_PATH = "aries_cloudagent.transport.outbound" @@ -87,10 +91,13 @@ def __init__(self, profile: Profile, handle_not_delivered: Callable = None): self.running_transports = {} self.task_queue = TaskQueue(max_active=200) self._process_task: asyncio.Task = None + self.light_webhook = False if self.root_profile.settings.get("transport.max_outbound_retry"): self.MAX_RETRY_COUNT = self.root_profile.settings[ "transport.max_outbound_retry" ] + if self.root_profile.settings.get("transport.light_weight_webhook"): + self.light_webhook = True async def setup(self): """Perform setup operations.""" @@ -309,6 +316,14 @@ async def encode_outbound_message( return outbound_message + def delete_keys_from_dict(self, dictionary, keys): + for key in keys: + with suppress(KeyError): + del dictionary[key] + for value in dictionary.values(): + if isinstance(value, MutableMapping): + self.delete_keys_from_dict(value, keys) + def enqueue_webhook( self, topic: str, @@ -343,6 +358,10 @@ def enqueue_webhook( queued.payload = json.dumps(payload) queued.state = QueuedOutboundMessage.STATE_PENDING queued.retries = 4 if max_attempts is None else max_attempts - 1 + + if self.light_webhook: + self.delete_keys_from_dict(payload, REMOVE_KEY) + queued.payload = json.dumps(payload) self.outbound_new.append(queued) self.process_queued() From 594e0ac60591fa5975c0cb066c907362babfa636 Mon Sep 17 00:00:00 2001 From: Victor Lee Date: Wed, 14 Sep 2022 12:29:15 -0400 Subject: [PATCH 470/872] Code formatting with black Signed-off-by: Victor Lee --- aries_cloudagent/transport/outbound/constants.py | 14 +++++++++++++- aries_cloudagent/transport/outbound/manager.py | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/transport/outbound/constants.py b/aries_cloudagent/transport/outbound/constants.py index ec2c899806..ff4adef700 100644 --- a/aries_cloudagent/transport/outbound/constants.py +++ b/aries_cloudagent/transport/outbound/constants.py @@ -1,2 +1,14 @@ """Constants for --light-weight-webhook flags""" -REMOVE_KEY=["credential_request", "cred_request", "credential_proposal", "cred_proposal", "credential_offer", "cred_offer", "credential_preview", "cred_preview", "values", "credentials~attach", "offers~attach"] \ No newline at end of file +REMOVE_KEY = [ + "credential_request", + "cred_request", + "credential_proposal", + "cred_proposal", + "credential_offer", + "cred_offer", + "credential_preview", + "cred_preview", + "values", + "credentials~attach", + "offers~attach", +] diff --git a/aries_cloudagent/transport/outbound/manager.py b/aries_cloudagent/transport/outbound/manager.py index 0742932c24..efd49fea5a 100644 --- a/aries_cloudagent/transport/outbound/manager.py +++ b/aries_cloudagent/transport/outbound/manager.py @@ -27,7 +27,7 @@ ) from .message import OutboundMessage -from .constants import ( REMOVE_KEY ) +from .constants import REMOVE_KEY LOGGER = logging.getLogger(__name__) MODULE_BASE_PATH = "aries_cloudagent.transport.outbound" From ab6d8550fd7f47dd58f7bd76dbc169aef5b2e821 Mon Sep 17 00:00:00 2001 From: Ry Jones Date: Fri, 16 Sep 2022 14:10:00 -0700 Subject: [PATCH 471/872] Update pip-audit.yml Signed-off-by: Ry Jones --- .github/workflows/pip-audit.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pip-audit.yml b/.github/workflows/pip-audit.yml index 1f068b8738..0a2a0296a7 100644 --- a/.github/workflows/pip-audit.yml +++ b/.github/workflows/pip-audit.yml @@ -17,7 +17,7 @@ jobs: python -m venv env/ source env/bin/activate python -m pip install . - - uses: trailofbits/gh-action-pip-audit@v0.0.4 + - uses: pypa/gh-action-pip-audit@v1.0.0 with: virtual-environment: env/ local: true From 147262998c62a8366fc8527ed2da5c7757351ca3 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Sat, 17 Sep 2022 12:32:08 -0400 Subject: [PATCH 472/872] docs: description of images Signed-off-by: Daniel Bluhm --- ContainerImagesAndGithubActions.md | 118 +++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 ContainerImagesAndGithubActions.md diff --git a/ContainerImagesAndGithubActions.md b/ContainerImagesAndGithubActions.md new file mode 100644 index 0000000000..27d67d0363 --- /dev/null +++ b/ContainerImagesAndGithubActions.md @@ -0,0 +1,118 @@ +# Container Images and Github Actions + +Aries Cloud Agent - Python is most frequently deployed using containers. From +the first release of ACA-Py up through 0.7.4, much of the community has built +their Aries stack using the container images graciously provided by BC Gov and +hosted through their `bcgovimages` docker hub account. These images have been +critical to the adoption of not only ACA-Py but also Hyperledger Aries and SSI +more generally. + +Recognizing how critical these images are to the success of ACA-Py and +consistent with Hyperledger's commitment to open collaboration, container images +are now built and published directly from the Aries Cloud Agent - Python project +repository and made available through the [Github Packages Container +Registry](https://ghcr.io). + + +## Images + +The following images are built from this project + +- `ghcr.io/hyperledger/aries-cloudagent-python` - multiple variants are built + from this project; see [Tags](#tags). +- `ghcr.io/hyperledger/indy-python` - this image is used as a base for the + ACA-Py Indy variant (see [Tags](#tags)). This may be moved to a more + appropriate project in the future. + + +### Tags + +ACA-Py is a foundation for building decentralized identity applications; to this +end, there are multiple variants of ACA-Py built to suit the needs of a variety +of environments and workflows. There are currently two main variants: + +- "Standard" - The default configuration of ACA-Py, including: + - Aries Askar for secure storage + - Indy VDR for Indy ledger communication + - Indy Shared Libraries for AnonCreds +- "Indy" - The legacy configuration of ACA-Py, including: + - Indy SDK Wallet for secure storage + - Indy SDK Ledger for Indy ledger communication + - Indy SDK for AnonCreds + +These two image variants are largely distinguished by providers for Indy Network +and AnonCreds support. The Standard variant is recommended for new projects. +Migration from an Indy based image (whether the new Indy image variant or the +original BC Gov images) to the Standard image is outside of the scope of this +document. + +The ACA-Py images built by this project are tagged to indicate which of the +above variants it is. Other tags are also generated for use by developers. + +Below is a table of all generated images and their tags: + +Tag | Variant | Example | Description | +------------------------|----------|--------------------------|-------------------------------------------------------------------------------------------------| +py3.7-X.Y.Z | Standard | py3.7-0.7.4 | Standard image variant built on Python 3.7 for ACA-Py version X.Y.Z | +py3.8-X.Y.Z | Standard | py3.8-0.7.4 | Standard image variant built on Python 3.8 for ACA-Py version X.Y.Z | +py3.9-X.Y.Z | Standard | py3.9-0.7.4 | Standard image variant built on Python 3.9 for ACA-Py version X.Y.Z | +py3.10-X.Y.Z | Standard | py3.10-0.7.4 | Standard image variant built on Python 3.10 for ACA-Py version X.Y.Z | +py3.7-indy-A.B.C-X.Y.Z | Indy | py3.7-indy-1.16.0-0.7.4 | Standard image variant built on Python 3.7 for ACA-Py version X.Y.Z and Indy SDK Version A.B.C | +py3.8-indy-A.B.C-X.Y.Z | Indy | py3.8-indy-1.16.0-0.7.4 | Standard image variant built on Python 3.8 for ACA-Py version X.Y.Z and Indy SDK Version A.B.C | +py3.9-indy-A.B.C-X.Y.Z | Indy | py3.9-indy-1.16.0-0.7.4 | Standard image variant built on Python 3.9 for ACA-Py version X.Y.Z and Indy SDK Version A.B.C | +py3.10-indy-A.B.C-X.Y.Z | Indy | py3.10-indy-1.16.0-0.7.4 | Standard image variant built on Python 3.10 for ACA-Py version X.Y.Z and Indy SDK Version A.B.C | + + +#### Indy Python + +**Image Name:** `ghcr.io/hyperledger/indy-python` + +The Indy Python image is used as a base for the Indy variant of ACA-Py. It is a +debian based image with `libindy` and the Indy SDK Python wrapper installed. + +Below is a table of all generated Indy Python images and their tags: + +Tag | Example | Description | +------------------------|--------------------------|-----------------------------------------| +py3.7-X.Y.Z | py3.7-1.16.0 | Python 3.7 with Indy SDK version X.Y.Z | +py3.8-X.Y.Z | py3.8-1.16.0 | Python 3.8 with Indy SDK version X.Y.Z | +py3.9-X.Y.Z | py3.9-1.16.0 | Python 3.9 with Indy SDK version X.Y.Z | +py3.10-X.Y.Z | py3.10-1.16.0 | Python 3.10 with Indy SDK version X.Y.Z | + + +#### Nightly + +The Github Actions will also produce Nightly builds of ACA-Py. If a nightly +build at the current hash of the repo doesn't yet exist, GHA will build a +standard and Indy ACA-Py image at midnight each day. Nightly builds are produced +only for the current "active" python version. + +Below is a table of all generated Nightly images and their tags: + +Tag | Variant | Example | Description | +-------------------------------------------|----------|--------------------------------------------------------------------|---------------------------------------| +py3.7-nightly | Standard | py3.7-nightly | Standard image latest nightly | +py3.7-indy-A.B.C-nightly | Indy | py3.7-indy-1.16.0-nightly | Indy image latest nightly | +py3.7-nightly-{{ commit hash }} | Standard | py3.7-nightly-96bc6a8938f0c0e2a487a069d63bcb6c8172b320 | Standard image nightly at commit hash | +py3.7-indy-A.B.C-nightly-{{ commit hash }} | Indy | py3.7-indy-1.16.0-nightly-96bc6a8938f0c0e2a487a069d63bcb6c8172b320 | Indy image nightly at commit hash | + + +#### Testing + +The Github Actions will produce images used in CI/CD checks for Indy (Indy image +tests require `libindy` which is not available on Github runners; these tests +must be run inside of a container with `libindy`). These images are only +intended for use by these checks. + +Below is a table of all generated test images and their tags: + +Image + Tag | Description | +--------------------------------------------------------------------------------------------------------|------------------------------------| +indy-python-test:py{{python-version}}-{{indy-version}}-{{hash of indy base Dockerfile}} | Base Indy Python image for testing | +acapy-test:py{{python-version}}-{{indy-version}}-{{hash of requirements*.txt and indy test Dockerfile}} | ACA-Py test image | + +## Github Actions + +Several Github Actions are used to produce the above described images. + +**TODO:** Add descriptions of actions From 412fb179809986f6ee4a687bb6c91a255e190dba Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Sat, 17 Sep 2022 12:53:13 -0400 Subject: [PATCH 473/872] docs: add key differences section Signed-off-by: Daniel Bluhm --- ContainerImagesAndGithubActions.md | 35 ++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/ContainerImagesAndGithubActions.md b/ContainerImagesAndGithubActions.md index 27d67d0363..e5e8ccf277 100644 --- a/ContainerImagesAndGithubActions.md +++ b/ContainerImagesAndGithubActions.md @@ -116,3 +116,38 @@ acapy-test:py{{python-version}}-{{indy-version}}-{{hash of requirements*.txt and Several Github Actions are used to produce the above described images. **TODO:** Add descriptions of actions + +## Key Differences + +There are several key differences that should be noted between the two image +variants and between the BC Gov ACA-Py images and VON images and the images +produced by this project. + +- Standard Image + - Based on slim variant of Debian + - Does **NOT** include `libindy` + - Default user is `aries` + - Uses container's system python environment rather than `pyenv` + - Askar and Indy Shared libraries are installed through pip from + pre-compiled binaries included in the python wrappers. + - Built from repo contents +- Indy Image + - Based on slim variant of Debian + - Based on `indy-python` + - Includes `libindy` but does **NOT** include the Indy CLI + - Default user is `indy` + - Based on `indy-python` + - Uses container's system python environment rather than `pyenv` + - Askar and Indy Shared libraries are installed through pip from + pre-compiled binaries included in the python wrappers + - Built from repo contents + - Includes Indy postgres storage plugin +- `bcgovimages/aries-cloudagent` + - (Usually) based on Ubuntu + - Based on `von-image` + - Default user is `indy` + - Includes `libindy` and Indy CLI + - Uses `pyenv` + - Askar and Indy Shared libraries built from source + - Built from ACA-Py python package uploaded to PyPI + - Includes Indy postgres storage plugin From 98f553744a92f3bb27112f4bec352709d60367e9 Mon Sep 17 00:00:00 2001 From: Ry Jones Date: Sat, 17 Sep 2022 13:17:09 -0700 Subject: [PATCH 474/872] Update pip-audit.yml Signed-off-by: Ry Jones --- .github/workflows/pip-audit.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/pip-audit.yml b/.github/workflows/pip-audit.yml index 0a2a0296a7..486a36e0fb 100644 --- a/.github/workflows/pip-audit.yml +++ b/.github/workflows/pip-audit.yml @@ -16,6 +16,7 @@ jobs: run: | python -m venv env/ source env/bin/activate + python -m pip install --upgrade pip python -m pip install . - uses: pypa/gh-action-pip-audit@v1.0.0 with: From c2ec492636d85793e6b001156b412b1e8b3091ef Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Tue, 20 Sep 2022 06:04:20 -0700 Subject: [PATCH 475/872] Add support for endorser write author DID Signed-off-by: Ian Costanzo --- aries_cloudagent/ledger/routes.py | 38 ++++-- .../endorse_transaction/v1_0/controller.py | 11 +- .../endorsed_transaction_response_handler.py | 2 +- .../endorse_transaction/v1_0/manager.py | 104 ++++++++++++---- .../v1_0/models/transaction_record.py | 10 +- .../endorse_transaction/v1_0/routes.py | 2 +- .../v1_0/tests/test_manager.py | 2 +- .../out_of_band/v1_0/models/oob_record.py | 112 ++++++++++++++++++ aries_cloudagent/wallet/routes.py | 4 +- demo/runners/agent_container.py | 51 ++++++-- demo/runners/support/agent.py | 50 +++++--- 11 files changed, 319 insertions(+), 67 deletions(-) diff --git a/aries_cloudagent/ledger/routes.py b/aries_cloudagent/ledger/routes.py index 504dd3d3cc..0c8505d49c 100644 --- a/aries_cloudagent/ledger/routes.py +++ b/aries_cloudagent/ledger/routes.py @@ -26,6 +26,7 @@ TransactionManagerError, ) from ..protocols.endorse_transaction.v1_0.models.transaction_record import ( + TransactionRecord, TransactionRecordSchema, ) from ..protocols.endorse_transaction.v1_0.util import ( @@ -295,18 +296,29 @@ async def register_ledger_nym(request: web.BaseRequest): ) endorser_did = endorser_info["endorser_did"] + meta_data = {"did": did, "verkey": verkey, "alias": alias, "role": role} success = False txn = None async with ledger: try: - (success, txn) = await ledger.register_nym( - did, - verkey, - alias, - role, - write_ledger=write_ledger, - endorser_did=endorser_did, - ) + # if we are an author check if we have a public DID or not + write_ledger_nym_transaction = True + # special case - if we are an author with no public DID + if create_transaction_for_endorser: + public_info = await ledger.get_wallet_public_did() + if not public_info: + write_ledger_nym_transaction = False + success = False + txn = {"signed_txn": json.dumps(meta_data)} + if write_ledger_nym_transaction: + (success, txn) = await ledger.register_nym( + did, + verkey, + alias, + role, + write_ledger=write_ledger, + endorser_did=endorser_did, + ) except LedgerTransactionError as err: raise web.HTTPForbidden(reason=err.roll_up) except LedgerError as err: @@ -321,7 +333,6 @@ async def register_ledger_nym(request: web.BaseRequest): ) ) - meta_data = {"did": did, "verkey": verkey, "alias": alias, "role": role} if not create_transaction_for_endorser: # Notify event await notify_register_did_event(context.profile, did, meta_data) @@ -340,12 +351,21 @@ async def register_ledger_nym(request: web.BaseRequest): # if auto-request, send the request to the endorser if context.settings.get_value("endorser.auto_request"): try: + endorser_write_txn = not write_ledger_nym_transaction transaction, transaction_request = await transaction_mgr.create_request( transaction=transaction, + author_goal_code=TransactionRecord.REGISTER_PUBLIC_DID + if endorser_write_txn + else None, + signer_goal_code=TransactionRecord.WRITE_DID_TRANSACTION + if endorser_write_txn + else None, + endorser_write_txn=endorser_write_txn, # TODO see if we need to parameterize these params # expires_time=expires_time, # endorser_write_txn=endorser_write_txn, ) + txn = transaction.serialize() except (StorageError, TransactionManagerError) as err: raise web.HTTPBadRequest(reason=err.roll_up) from err diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/controller.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/controller.py index a4ddfe7e6e..b9ea0d92cb 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/controller.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/controller.py @@ -5,6 +5,9 @@ ENDORSE_TRANSACTION = "aries.transaction.endorse" REFUSE_TRANSACTION = "aries.transaction.refuse" WRITE_TRANSACTION = "aries.transaction.ledger.write" +WRITE_TRANSACTION = "aries.transaction.ledger.write" +WRITE_DID_TRANSACTION = "aries.transaction.ledger.write_did" +REGISTER_PUBLIC_DID = "aries.transaction.register_public_did" class Controller: @@ -15,4 +18,10 @@ def __init__(self, protocol: str): def determine_goal_codes(self) -> Sequence[str]: """Return defined goal_codes.""" - return [ENDORSE_TRANSACTION, REFUSE_TRANSACTION, WRITE_TRANSACTION] + return [ + ENDORSE_TRANSACTION, + REFUSE_TRANSACTION, + WRITE_TRANSACTION, + WRITE_DID_TRANSACTION, + REGISTER_PUBLIC_DID, + ] diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/endorsed_transaction_response_handler.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/endorsed_transaction_response_handler.py index a8cff20d62..5cdb726d7f 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/endorsed_transaction_response_handler.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/endorsed_transaction_response_handler.py @@ -45,7 +45,7 @@ async def handle(self, context: RequestContext, responder: BaseResponder): ( transaction, transaction_acknowledgement_message, - ) = await mgr.complete_transaction(transaction=transaction) + ) = await mgr.complete_transaction(transaction, False) await responder.send_reply( transaction_acknowledgement_message, diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py index 3669a16eeb..c41508bd20 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py @@ -118,6 +118,8 @@ async def create_request( signed_request: dict = None, expires_time: str = None, endorser_write_txn: bool = None, + author_goal_code: str = None, + signer_goal_code: str = None, ): """ Create a new Transaction Request. @@ -142,8 +144,12 @@ async def create_request( "context": TransactionRecord.SIGNATURE_CONTEXT, "method": TransactionRecord.ADD_SIGNATURE, "signature_type": TransactionRecord.SIGNATURE_TYPE, - "signer_goal_code": TransactionRecord.ENDORSE_TRANSACTION, - "author_goal_code": TransactionRecord.WRITE_TRANSACTION, + "signer_goal_code": signer_goal_code + if signer_goal_code + else TransactionRecord.ENDORSE_TRANSACTION, + "author_goal_code": author_goal_code + if author_goal_code + else TransactionRecord.WRITE_TRANSACTION, } transaction.signature_request.clear() transaction.signature_request.append(signature_request) @@ -233,6 +239,7 @@ async def create_endorse_response( transaction._type = TransactionRecord.SIGNATURE_RESPONSE transaction_json = transaction.messages_attach[0]["data"]["json"] + ledger_response = {} async with self._profile.session() as session: wallet: BaseWallet = session.inject_or(BaseWallet) @@ -266,9 +273,40 @@ async def create_endorse_response( raise LedgerError(reason=reason) async with ledger: - endorsed_msg = await shield( - ledger.txn_endorse(transaction_json, endorse_did=endorser_did_info) + # check our goal code! + txn_goal_code = ( + transaction.signature_request[0]["signer_goal_code"] + if transaction.signature_request + and "signer_goal_code" in transaction.signature_request[0] + else TransactionRecord.ENDORSE_TRANSACTION ) + if txn_goal_code == TransactionRecord.ENDORSE_TRANSACTION: + endorsed_msg = await shield( + ledger.txn_endorse(transaction_json, endorse_did=endorser_did_info) + ) + elif txn_goal_code == TransactionRecord.WRITE_DID_TRANSACTION: + # get DID info from transaction.meta_data + meta_data = json.loads(transaction_json) + (success, txn) = await shield( + ledger.register_nym( + meta_data["did"], + meta_data["verkey"], + meta_data["alias"], + meta_data["role"], + ) + ) + # we don't have an endorsed transaction so just return did meta-data + ledger_response = { + "result": { + "txn": {"type": "1", "data": {"dest": meta_data["did"]}} + }, + "meta_data": meta_data, + } + endorsed_msg = json.dumps(ledger_response) + else: + raise TransactionManagerError( + f"Invalid goal code for transaction record:" f" {txn_goal_code}" + ) # need to return the endorsed msg or else the ledger will reject the # eventual transaction write @@ -278,7 +316,7 @@ async def create_endorse_response( "message_id": transaction.messages_attach[0]["@id"], "context": TransactionRecord.SIGNATURE_CONTEXT, "method": TransactionRecord.ADD_SIGNATURE, - "signer_goal_code": TransactionRecord.ENDORSE_TRANSACTION, + "signer_goal_code": txn_goal_code, "signature_type": TransactionRecord.SIGNATURE_TYPE, "signature": {endorser_did: endorsed_msg or endorser_verkey}, } @@ -291,8 +329,12 @@ async def create_endorse_response( async with self._profile.session() as session: await transaction.save(session, reason="Created an endorsed response") - if transaction.endorser_write_txn: - ledger_response = await self.complete_transaction(transaction) + if ( + transaction.endorser_write_txn + and txn_goal_code == TransactionRecord.ENDORSE_TRANSACTION + ): + # running as the endorser, we've been asked to write the transaction + ledger_response = await self.complete_transaction(transaction, True) endorsed_transaction_response = EndorsedTransactionResponse( transaction_id=transaction.thread_id, thread_id=transaction._id, @@ -310,6 +352,7 @@ async def create_endorse_response( signature_response=signature_response, state=state, endorser_did=endorser_did, + ledger_response=ledger_response, ) return transaction, endorsed_transaction_response @@ -357,7 +400,9 @@ async def receive_endorse_response(self, response: EndorsedTransactionResponse): return transaction - async def complete_transaction(self, transaction: TransactionRecord): + async def complete_transaction( + self, transaction: TransactionRecord, endorser: bool = False + ): """ Complete a transaction. @@ -371,24 +416,34 @@ async def complete_transaction(self, transaction: TransactionRecord): The updated transaction """ + ledger_transaction = transaction.messages_attach[0]["data"]["json"] - ledger = self._profile.inject(BaseLedger) - if not ledger: - reason = "No ledger available" - if not self._profile.context.settings.get_value("wallet.type"): - reason += ": missing wallet-type?" - raise TransactionManagerError(reason) + # check if we (author) have requested the endorser to write the transaction + if (endorser and transaction.endorser_write_txn) or ( + (not endorser) and (not transaction.endorser_write_txn) + ): + ledger = self._profile.inject(BaseLedger) + if not ledger: + reason = "No ledger available" + if not self._profile.context.settings.get_value("wallet.type"): + reason += ": missing wallet-type?" + raise TransactionManagerError(reason) - async with ledger: - try: - ledger_response_json = await shield( - ledger.txn_submit(ledger_transaction, sign=False, taa_accept=False) - ) - except (IndyIssuerError, LedgerError) as err: - raise TransactionManagerError(err.roll_up) from err + async with ledger: + try: + ledger_response_json = await shield( + ledger.txn_submit( + ledger_transaction, sign=False, taa_accept=False + ) + ) + except (IndyIssuerError, LedgerError) as err: + raise TransactionManagerError(err.roll_up) from err + + ledger_response = json.loads(ledger_response_json) - ledger_response = json.loads(ledger_response_json) + else: + ledger_response = ledger_transaction transaction.state = TransactionRecord.STATE_TRANSACTION_ACKED @@ -397,7 +452,7 @@ async def complete_transaction(self, transaction: TransactionRecord): # this scenario is where the endorser is writing the transaction # (called from self.create_endorse_response()) - if transaction.endorser_write_txn: + if endorser and transaction.endorser_write_txn: return ledger_response connection_id = transaction.connection_id @@ -733,6 +788,9 @@ async def endorsed_txn_post_processing( would be stored in wallet. """ + if isinstance(ledger_response, str): + ledger_response = json.loads(ledger_response) + ledger = self._profile.inject(BaseLedger) if not ledger: reason = "No ledger available" diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/models/transaction_record.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/models/transaction_record.py index 33e734e5ca..3b863c6837 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/models/transaction_record.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/models/transaction_record.py @@ -9,7 +9,13 @@ ) from .....messaging.valid import UUIDFour -from ..controller import ENDORSE_TRANSACTION, REFUSE_TRANSACTION, WRITE_TRANSACTION +from ..controller import ( + ENDORSE_TRANSACTION, + REFUSE_TRANSACTION, + WRITE_TRANSACTION, + WRITE_DID_TRANSACTION, + REGISTER_PUBLIC_DID, +) class TransactionRecord(BaseExchangeRecord): @@ -40,6 +46,8 @@ class Meta: ENDORSE_TRANSACTION = ENDORSE_TRANSACTION REFUSE_TRANSACTION = REFUSE_TRANSACTION WRITE_TRANSACTION = WRITE_TRANSACTION + WRITE_DID_TRANSACTION = WRITE_DID_TRANSACTION + REGISTER_PUBLIC_DID = REGISTER_PUBLIC_DID FORMAT_VERSION = "dif/endorse-transaction/request@v1.0" diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py index 7e3bc6820e..df8b08ae57 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py @@ -710,7 +710,7 @@ async def transaction_write(request: web.BaseRequest): ( tx_completed, transaction_acknowledgement_message, - ) = await transaction_mgr.complete_transaction(transaction=transaction) + ) = await transaction_mgr.complete_transaction(transaction, False) except StorageError as err: raise web.HTTPBadRequest(reason=err.roll_up) from err diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_manager.py index edad031889..30f5a6ad16 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_manager.py @@ -427,7 +427,7 @@ async def test_complete_transaction(self): ( transaction_record, transaction_acknowledgement_message, - ) = await self.manager.complete_transaction(transaction_record) + ) = await self.manager.complete_transaction(transaction_record, False) save_record.assert_called_once() assert transaction_record.state == TransactionRecord.STATE_TRANSACTION_ACKED diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/models/oob_record.py b/aries_cloudagent/protocols/out_of_band/v1_0/models/oob_record.py index f0b247ebb7..69f668335b 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/models/oob_record.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/models/oob_record.py @@ -1,9 +1,12 @@ """Record for out of band invitations.""" +import json from typing import Any, Mapping, Optional, Union from marshmallow import fields +from .....connections.models.conn_record import ConnRecord +from .....core.profile import ProfileSession from .....messaging.decorators.service_decorator import ( ServiceDecorator, ServiceDecoratorSchema, @@ -14,6 +17,10 @@ from ..messages.invitation import InvitationMessage, InvitationMessageSchema +from .....storage.base import BaseStorage +from .....storage.record import StorageRecord +from .....storage.error import StorageNotFoundError + class OobRecord(BaseExchangeRecord): """Represents an out of band record.""" @@ -24,6 +31,7 @@ class Meta: schema_class = "OobRecordSchema" RECORD_TYPE = "oob_record" + RECORD_TYPE_METADATA = ConnRecord.RECORD_TYPE_METADATA RECORD_ID_NAME = "oob_id" RECORD_TOPIC = "out_of_band" TAG_NAMES = { @@ -114,6 +122,110 @@ def record_value(self) -> dict: }, } + async def delete_record(self, session: ProfileSession): + """Perform connection record deletion actions. + + Args: + session (ProfileSession): session + + """ + await super().delete_record(session) + + # Delete metadata + if self.connection_id: + storage = session.inject(BaseStorage) + await storage.delete_all_records( + self.RECORD_TYPE_METADATA, + {"connection_id": self.connection_id}, + ) + + async def metadata_get( + self, session: ProfileSession, key: str, default: Any = None + ) -> Any: + """Retrieve arbitrary metadata associated with this connection. + + Args: + session (ProfileSession): session used for storage + key (str): key identifying metadata + default (Any): default value to get; type should be a JSON + compatible value. + + Returns: + Any: metadata stored by key + + """ + assert self.connection_id + storage: BaseStorage = session.inject(BaseStorage) + try: + record = await storage.find_record( + self.RECORD_TYPE_METADATA, + {"key": key, "connection_id": self.connection_id}, + ) + return json.loads(record.value) + except StorageNotFoundError: + return default + + async def metadata_set(self, session: ProfileSession, key: str, value: Any): + """Set arbitrary metadata associated with this connection. + + Args: + session (ProfileSession): session used for storage + key (str): key identifying metadata + value (Any): value to set + """ + assert self.connection_id + value = json.dumps(value) + storage: BaseStorage = session.inject(BaseStorage) + try: + record = await storage.find_record( + self.RECORD_TYPE_METADATA, + {"key": key, "connection_id": self.connection_id}, + ) + await storage.update_record(record, value, record.tags) + except StorageNotFoundError: + record = StorageRecord( + self.RECORD_TYPE_METADATA, + value, + {"key": key, "connection_id": self.connection_id}, + ) + await storage.add_record(record) + + async def metadata_delete(self, session: ProfileSession, key: str): + """Delete custom metadata associated with this connection. + + Args: + session (ProfileSession): session used for storage + key (str): key of metadata to delete + """ + assert self.connection_id + storage: BaseStorage = session.inject(BaseStorage) + try: + record = await storage.find_record( + self.RECORD_TYPE_METADATA, + {"key": key, "connection_id": self.connection_id}, + ) + await storage.delete_record(record) + except StorageNotFoundError as err: + raise KeyError(f"{key} not found in connection metadata") from err + + async def metadata_get_all(self, session: ProfileSession) -> dict: + """Return all custom metadata associated with this connection. + + Args: + session (ProfileSession): session used for storage + + Returns: + dict: dictionary representation of all metadata values + + """ + assert self.connection_id + storage: BaseStorage = session.inject(BaseStorage) + records = await storage.find_all_records( + self.RECORD_TYPE_METADATA, + {"connection_id": self.connection_id}, + ) + return {record.tags["key"]: json.loads(record.value) for record in records} + def __eq__(self, other: Any) -> bool: """Comparison between records.""" return super().__eq__(other) diff --git a/aries_cloudagent/wallet/routes.py b/aries_cloudagent/wallet/routes.py index eb18163fcb..1a1610955f 100644 --- a/aries_cloudagent/wallet/routes.py +++ b/aries_cloudagent/wallet/routes.py @@ -549,13 +549,13 @@ async def promote_wallet_public_did( raise LookupError(f"DID {did} is not posted to the ledger") # check if we need to endorse - if is_author_role(context.profile): + if is_author_role(profile): # authors cannot write to the ledger write_ledger = False # author has not provided a connection id, so determine which to use if not connection_id: - connection_id = await get_endorser_connection_id(context.profile) + connection_id = await get_endorser_connection_id(profile) if not connection_id: raise web.HTTPBadRequest(reason="No endorser connection found") if not write_ledger: diff --git a/demo/runners/agent_container.py b/demo/runners/agent_container.py index 7018ee046f..a7ee7745ab 100644 --- a/demo/runners/agent_container.py +++ b/demo/runners/agent_container.py @@ -20,6 +20,7 @@ start_mediator_agent, connect_wallet_to_mediator, start_endorser_agent, + connect_wallet_to_endorser, CRED_FORMAT_INDY, CRED_FORMAT_JSON_LD, DID_METHOD_KEY, @@ -656,6 +657,7 @@ def __init__( self.seed = seed self.aip = aip self.arg_file = arg_file + self.endorser_agent = None self.endorser_role = endorser_role if endorser_role: # endorsers and authors need public DIDs (assume cred_type is Indy) @@ -709,9 +711,11 @@ async def initialize( await self.agent.listen_webhooks(self.start_port + 2) - if self.public_did and self.cred_type != CRED_FORMAT_JSON_LD: - await self.agent.register_did(cred_type=CRED_FORMAT_INDY) - log_msg("Created public DID") + # create public DID ... UNLESS we are an author ... + if (not self.endorser_role) or (self.endorser_role == "endorser"): + if self.public_did and self.cred_type != CRED_FORMAT_JSON_LD: + await self.agent.register_did(cred_type=CRED_FORMAT_INDY) + log_msg("Created public DID") # if we are endorsing, create the endorser agent first, then we can use the # multi-use invitation to auto-connect the agent on startup @@ -753,19 +757,46 @@ async def initialize( rand_name = str(random.randint(100_000, 999_999)) await self.agent.register_or_switch_wallet( self.ident + ".initial." + rand_name, - public_did=self.public_did, + public_did=self.public_did + and ((not self.endorser_role) or (not self.endorser_role == "author")), webhook_port=None, mediator_agent=self.mediator_agent, endorser_agent=self.endorser_agent, taa_accept=self.taa_accept, ) - elif self.mediation: - # we need to pre-connect the agent to its mediator - if not await connect_wallet_to_mediator(self.agent, self.mediator_agent): - raise Exception("Mediation setup FAILED :-(") else: - if self.taa_accept: - await self.agent.taa_accept() + if self.mediation: + # we need to pre-connect the agent to its mediator + self.agent.log("Connect wallet to mediator ...") + if not await connect_wallet_to_mediator( + self.agent, self.mediator_agent + ): + raise Exception("Mediation setup FAILED :-(") + if self.endorser_agent: + self.agent.log("Connect wallet to endorser ...") + if not await connect_wallet_to_endorser( + self.agent, self.endorser_agent + ): + raise Exception("Endorser setup FAILED :-(") + if self.taa_accept: + await self.agent.taa_accept() + + # if we are an author, create our public DID here ... + if ( + self.endorser_role + and self.endorser_role == "author" + and self.endorser_agent + ): + if self.public_did and self.cred_type != CRED_FORMAT_JSON_LD: + new_did = await self.agent.admin_POST("/wallet/did/create") + self.agent.did = new_did["result"]["did"] + await self.agent.register_did( + did=new_did["result"]["did"], + verkey=new_did["result"]["verkey"], + ) + await self.agent.admin_POST("/wallet/did/public?did=" + self.agent.did) + await asyncio.sleep(3.0) + log_msg("Created public DID") if self.public_did and self.cred_type == CRED_FORMAT_JSON_LD: # create did of appropriate type diff --git a/demo/runners/support/agent.py b/demo/runners/support/agent.py index 89d38c61b3..8398df1279 100644 --- a/demo/runners/support/agent.py +++ b/demo/runners/support/agent.py @@ -186,7 +186,9 @@ def __init__( self.proc = None self.client_session: ClientSession = ClientSession() - if self.endorser_role and not seed: + if self.endorser_role and self.endorser_role == "author": + seed = None + elif self.endorser_role and not seed: seed = "random" rand_name = str(random.randint(100_000, 999_999)) self.seed = ( @@ -485,26 +487,33 @@ async def register_did( if self.endorser_role == "endorser": role = "ENDORSER" else: - role = "" - data["role"] = role + role = None + if role: + data["role"] = role if did and verkey: data["did"] = did data["verkey"] = verkey else: data["seed"] = self.seed - async with self.client_session.post( - ledger_url + "/register", json=data - ) as resp: + if role is None or role == "": + # if author using endorser register nym and wait for endorser ... + resp = await self.admin_POST("/ledger/register-nym", params=data) + await asyncio.sleep(3.0) + nym_info = data + else: + resp = await self.client_session.post( + ledger_url + "/register", json=data + ) if resp.status != 200: raise Exception( f"Error registering DID {data}, response code {resp.status}" ) nym_info = await resp.json() - self.did = nym_info["did"] - self.log(f"nym_info: {nym_info}") - if self.multitenant: - if not self.agency_wallet_did: - self.agency_wallet_did = self.did + self.did = nym_info["did"] + self.log(f"nym_info: {nym_info}") + if self.multitenant: + if not self.agency_wallet_did: + self.agency_wallet_did = self.did self.log(f"Registered DID: {self.did}") elif cred_type == CRED_FORMAT_JSON_LD: # TODO register a did:key with appropriate signature type @@ -582,9 +591,14 @@ async def register_or_switch_wallet( # if endorser, endorse the wallet ledger operations if endorser_agent: + self.log("Connect sub-wallet to endorser ...") if not await connect_wallet_to_endorser(self, endorser_agent): raise Exception("Endorser setup FAILED :-(") - + # if mediation, mediate the wallet connections + if mediator_agent: + if not await connect_wallet_to_mediator(self, mediator_agent): + log_msg("Mediation setup FAILED :-(") + raise Exception("Mediation setup FAILED :-(") if taa_accept: await self.taa_accept() @@ -615,12 +629,6 @@ async def register_or_switch_wallet( # todo ignore for now pass - # if mediation, mediate the wallet connections - if mediator_agent: - if not await connect_wallet_to_mediator(self, mediator_agent): - log_msg("Mediation setup FAILED :-(") - raise Exception("Mediation setup FAILED :-(") - self.log(f"Created NEW wallet {target_wallet_name}") return True @@ -824,6 +832,9 @@ async def handle_revocation_registry(self, message): reg_id = message.get("revoc_reg_id", "(undetermined)") self.log(f"Revocation registry: {reg_id} state: {message['state']}") + async def handle_mediation(self, message): + self.log(f"Received mediation message ...\n") + async def taa_accept(self): taa_info = await self.admin_GET("/ledger/taa") if taa_info["result"]["taa_required"]: @@ -1395,6 +1406,9 @@ async def handle_connections(self, message): async def handle_basicmessages(self, message): self.log("Received message:", message["content"]) + async def handle_out_of_band(self, message): + self.log("Received message:", message) + async def start_endorser_agent( start_port, From eb23ac04decf7772bdd7ba9e59704299e02fb699 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Tue, 20 Sep 2022 07:37:23 -0700 Subject: [PATCH 476/872] A couple of unit tests Signed-off-by: Ian Costanzo --- aries_cloudagent/ledger/tests/test_routes.py | 44 +++++++++ .../v1_0/tests/test_manager.py | 92 +++++++++++++++++++ 2 files changed, 136 insertions(+) diff --git a/aries_cloudagent/ledger/tests/test_routes.py b/aries_cloudagent/ledger/tests/test_routes.py index f4d0cd8ade..95bce0d497 100644 --- a/aries_cloudagent/ledger/tests/test_routes.py +++ b/aries_cloudagent/ledger/tests/test_routes.py @@ -359,6 +359,50 @@ async def test_register_nym_create_transaction_for_endorser(self): {"success": True, "txn": {"signed_txn": {"...": "..."}}} ) + async def test_register_nym_create_transaction_for_endorser_no_public_did(self): + self.request.query = { + "did": "a_test_did", + "verkey": "a_test_verkey", + "alias": "did_alias", + "role": "reset", + "create_transaction_for_endorser": "true", + "conn_id": "dummy", + } + self.profile.context.settings["endorser.author"] = True + + with async_mock.patch.object( + ConnRecord, "retrieve_by_id", async_mock.AsyncMock() + ) as mock_conn_rec_retrieve, async_mock.patch.object( + test_module, "TransactionManager", async_mock.MagicMock() + ) as mock_txn_mgr, async_mock.patch.object( + test_module.web, "json_response", async_mock.MagicMock() + ) as mock_response: + mock_txn_mgr.return_value = async_mock.MagicMock( + create_record=async_mock.AsyncMock( + return_value=async_mock.MagicMock( + serialize=async_mock.MagicMock(return_value={"...": "..."}) + ) + ) + ) + mock_conn_rec_retrieve.return_value = async_mock.MagicMock( + metadata_get=async_mock.AsyncMock( + return_value={ + "endorser_did": ("did"), + "endorser_name": ("name"), + } + ) + ) + self.ledger.register_nym.return_value: Tuple[bool, dict] = ( + True, + {"signed_txn": {"...": "..."}}, + ) + + result = await test_module.register_ledger_nym(self.request) + assert result == mock_response.return_value + mock_response.assert_called_once_with( + {"success": True, "txn": {"signed_txn": {"...": "..."}}} + ) + async def test_register_nym_create_transaction_for_endorser_storage_x(self): self.request.query = { "did": "a_test_did", diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_manager.py index 30f5a6ad16..f765843a03 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_manager.py @@ -115,6 +115,7 @@ async def setUp(self): self.ledger.txn_endorse = async_mock.CoroutineMock( return_value=self.test_endorsed_message ) + self.ledger.register_nym = async_mock.CoroutineMock(return_value=(True, {})) self.context = AdminRequestContext.test_context() self.profile = self.context.profile @@ -235,6 +236,48 @@ async def test_create_request(self): transaction_request.messages_attach == transaction_record.messages_attach[0] ) + async def test_create_request_author_did(self): + transaction_record = await self.manager.create_record( + messages_attach=self.test_messages_attach, + connection_id=self.test_connection_id, + ) + + with async_mock.patch.object( + TransactionRecord, "save", autospec=True + ) as save_record: + ( + transaction_record, + transaction_request, + ) = await self.manager.create_request( + transaction_record, + expires_time=self.test_expires_time, + author_goal_code=TransactionRecord.REGISTER_PUBLIC_DID, + signer_goal_code=TransactionRecord.WRITE_DID_TRANSACTION, + ) + save_record.assert_called_once() + + assert transaction_record._type == TransactionRecord.SIGNATURE_REQUEST + assert transaction_record.signature_request[0] == { + "context": TransactionRecord.SIGNATURE_CONTEXT, + "method": TransactionRecord.ADD_SIGNATURE, + "signature_type": TransactionRecord.SIGNATURE_TYPE, + "signer_goal_code": TransactionRecord.WRITE_DID_TRANSACTION, + "author_goal_code": TransactionRecord.REGISTER_PUBLIC_DID, + } + assert transaction_record.state == TransactionRecord.STATE_REQUEST_SENT + assert transaction_record.connection_id == self.test_connection_id + assert transaction_record.timing["expires_time"] == self.test_expires_time + + assert transaction_request.transaction_id == transaction_record._id + assert ( + transaction_request.signature_request + == transaction_record.signature_request[0] + ) + assert transaction_request.timing == transaction_record.timing + assert ( + transaction_request.messages_attach == transaction_record.messages_attach[0] + ) + async def test_recieve_request(self): mock_request = async_mock.MagicMock() mock_request.transaction_id = self.test_author_transaction_id @@ -340,6 +383,55 @@ async def test_create_endorse_response(self): ) assert endorsed_transaction_response.endorser_did == self.test_endorser_did + async def test_create_endorse_response_author_did(self): + transaction_record = await self.manager.create_record( + messages_attach=self.test_messages_attach, + connection_id=self.test_connection_id, + ) + + with async_mock.patch.object( + TransactionRecord, "save", autospec=True + ) as save_record: + ( + transaction_record, + transaction_request, + ) = await self.manager.create_request( + transaction_record, + expires_time=self.test_expires_time, + author_goal_code=TransactionRecord.REGISTER_PUBLIC_DID, + signer_goal_code=TransactionRecord.WRITE_DID_TRANSACTION, + ) + save_record.assert_called_once() + + transaction_record.state = TransactionRecord.STATE_REQUEST_RECEIVED + transaction_record.thread_id = self.test_author_transaction_id + transaction_record.messages_attach[0]["data"]["json"] = json.dumps( + { + "did": "test", + "verkey": "test", + "alias": "test", + "role": "", + } + ) + + with async_mock.patch.object( + TransactionRecord, "save", autospec=True + ) as save_record: + ( + transaction_record, + endorsed_transaction_response, + ) = await self.manager.create_endorse_response( + transaction_record, + state=TransactionRecord.STATE_TRANSACTION_ENDORSED, + ) + save_record.assert_called_once() + + assert transaction_record._type == TransactionRecord.SIGNATURE_RESPONSE + assert ( + transaction_record.messages_attach[0]["data"]["json"] + == '{"result": {"txn": {"type": "1", "data": {"dest": "test"}}}, "meta_data": {"did": "test", "verkey": "test", "alias": "test", "role": ""}}' + ) + async def test_receive_endorse_response(self): transaction_record = await self.manager.create_record( messages_attach=self.test_messages_attach, From 3efe5d889f22a36196c9eca6f0c4d378ef6b3fac Mon Sep 17 00:00:00 2001 From: Adam Burdett Date: Tue, 20 Sep 2022 14:02:10 -0600 Subject: [PATCH 477/872] did method registry Signed-off-by: Adam Burdett --- aries_cloudagent/wallet/did_method.py | 109 ++++++++++---------------- 1 file changed, 43 insertions(+), 66 deletions(-) diff --git a/aries_cloudagent/wallet/did_method.py b/aries_cloudagent/wallet/did_method.py index 797c26fd20..1d337e6ee3 100644 --- a/aries_cloudagent/wallet/did_method.py +++ b/aries_cloudagent/wallet/did_method.py @@ -1,91 +1,68 @@ """Did method enum.""" -from typing import List, Mapping, NamedTuple, Optional -from enum import Enum - -from .key_type import KeyType +from typing import Dict, List, Mapping, Optional from .error import BaseError +from .key_type import KeyType -DIDMethodSpec = NamedTuple( - "DIDMethodSpec", - [ - ("method_name", str), - ("supported_key_types", List[KeyType]), - ("supports_rotation", bool), - ], -) +class DIDMethod: + """Class to represent a did method.""" -class DIDMethod(Enum): - """DID Method class specifying DID methods with supported key types.""" - - SOV = DIDMethodSpec( - method_name="sov", supported_key_types=[KeyType.ED25519], supports_rotation=True - ) - KEY = DIDMethodSpec( - method_name="key", - supported_key_types=[KeyType.ED25519, KeyType.BLS12381G2], - supports_rotation=False, - ) + def __init__(self, name, key_types, rotation) -> None: + self._method_name: str = name + self._supported_key_types: List[KeyType] = key_types + self._supports_rotation: bool = rotation @property - def method_name(self) -> str: - """Getter for did method name. e.g. sov or key.""" - return self.value.method_name + def method_name(self): + """Get method name.""" + return self._method_name @property - def supported_key_types(self) -> List[KeyType]: - """Getter for supported key types of method.""" - return self.value.supported_key_types + def supports_rotation(self): + """Check rotation support.""" + return self._supports_rotation @property - def supports_rotation(self) -> bool: - """Check whether the current method supports key rotation.""" - return self.value.supports_rotation + def supported_key_types(self): + """Get supported key types.""" + return self._supported_key_types def supports_key_type(self, key_type: KeyType) -> bool: """Check whether the current method supports the key type.""" return key_type in self.supported_key_types - @classmethod - def from_metadata(cls, metadata: Mapping) -> "DIDMethod": - """Get DID method instance from metadata object. - - Returns SOV if no metadata was found for backwards compatability. - """ - method = metadata.get("method") - - # extract from metadata object - if method: - for did_method in DIDMethod: - if method == did_method.method_name: - return did_method - # return default SOV for backward compat - return DIDMethod.SOV +class DIDMethods: + """DID Method class specifying DID methods with supported key types.""" - @classmethod - def from_method(cls, method: str) -> Optional["DIDMethod"]: - """Get DID method instance from the method name.""" - for did_method in DIDMethod: - if method == did_method.method_name: - return did_method + def __init__(self) -> None: + self._registry: Dict[str, DIDMethod] = {} - return None + def registered(self, method: str) -> bool: + """Check for a supported method.""" + return method in list(self._registry.items()) - @classmethod - def from_did(cls, did: str) -> "DIDMethod": - """Get DID method instance from the method name.""" - if not did.startswith("did:"): - # sov has no prefix - return DIDMethod.SOV + def register(self, method: DIDMethod): + """Registers a new did method.""" + self._registry[method.method_name] = method - parts = did.split(":") - method_str = parts[1] + def from_method(self, method_name: str) -> Optional[DIDMethod]: + """Retrieve a did method from method name.""" + method: DIDMethod = self._registry[method_name] + if method: + return method + raise BaseError(f"Unsupported did method: {method_name}") - method = DIDMethod.from_method(method_str) + def from_metadata(self, metadata: Mapping) -> Optional[DIDMethod]: + """Get DID method instance from metadata object. - if not method: - raise BaseError(f"Unsupported did method: {method_str}") + Returns SOV if no metadata was found for backwards compatibility. + """ + method_name: str = metadata.get("method", "sov") + return self.from_method(method_name) - return method + def from_did(self, did: str) -> Optional[DIDMethod]: + """Get DID method instance from the did url.""" + method_name = did.split(":")[1] if did.startswith("did:") else "sov" + return self.from_method(method_name) From 3adcde672efc096a0904be0afd9a22f299104782 Mon Sep 17 00:00:00 2001 From: Adam Burdett Date: Tue, 20 Sep 2022 14:32:22 -0600 Subject: [PATCH 478/872] static SOV/KEY methods Signed-off-by: Adam Burdett --- aries_cloudagent/wallet/did_method.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/aries_cloudagent/wallet/did_method.py b/aries_cloudagent/wallet/did_method.py index 1d337e6ee3..87436a44d7 100644 --- a/aries_cloudagent/wallet/did_method.py +++ b/aries_cloudagent/wallet/did_method.py @@ -1,4 +1,4 @@ -"""Did method enum.""" +"""Did method classes.""" from typing import Dict, List, Mapping, Optional from .error import BaseError @@ -33,6 +33,14 @@ def supports_key_type(self, key_type: KeyType) -> bool: return key_type in self.supported_key_types +SOV = DIDMethod(name="sov", key_types=[KeyType.ED25519], rotation=True) +KEY = DIDMethod( + name="key", + key_types=[KeyType.ED25519, KeyType.BLS12381G2], + rotation=False, +) + + class DIDMethods: """DID Method class specifying DID methods with supported key types.""" @@ -49,10 +57,7 @@ def register(self, method: DIDMethod): def from_method(self, method_name: str) -> Optional[DIDMethod]: """Retrieve a did method from method name.""" - method: DIDMethod = self._registry[method_name] - if method: - return method - raise BaseError(f"Unsupported did method: {method_name}") + return self._registry[method_name] def from_metadata(self, metadata: Mapping) -> Optional[DIDMethod]: """Get DID method instance from metadata object. @@ -62,7 +67,10 @@ def from_metadata(self, metadata: Mapping) -> Optional[DIDMethod]: method_name: str = metadata.get("method", "sov") return self.from_method(method_name) - def from_did(self, did: str) -> Optional[DIDMethod]: + def from_did(self, did: str) -> DIDMethod: """Get DID method instance from the did url.""" - method_name = did.split(":")[1] if did.startswith("did:") else "sov" - return self.from_method(method_name) + method_name = did.split(":")[1] if did.startswith("did:") else SOV.method_name + method: DIDMethod | None = self.from_method(method_name) + if not method: + raise BaseError(f"Unsupported did method: {method_name}") + return method From e9cba3b6f533b9d37cf7f5b2ffe94aad77812532 Mon Sep 17 00:00:00 2001 From: Adam Burdett Date: Wed, 21 Sep 2022 07:38:40 -0600 Subject: [PATCH 479/872] static SOV & KEY method Signed-off-by: Adam Burdett --- aries_cloudagent/config/wallet.py | 10 +- aries_cloudagent/core/tests/test_conductor.py | 16 +-- aries_cloudagent/ledger/tests/test_indy.py | 14 +-- .../ledger/tests/test_indy_vdr.py | 34 +++--- .../decorators/tests/test_attach_decorator.py | 6 +- .../messaging/jsonld/tests/test_routes.py | 4 +- .../multitenant/tests/test_base.py | 4 +- .../protocols/connections/v1_0/manager.py | 16 +-- .../connections/v1_0/tests/test_manager.py | 104 +++++++++--------- .../coordinate_mediation/v1_0/manager.py | 4 +- .../v1_0/route_manager.py | 4 +- .../handlers/tests/test_request_handler.py | 4 +- .../handlers/tests/test_response_handler.py | 4 +- .../protocols/didexchange/v1_0/manager.py | 10 +- .../v1_0/messages/tests/test_request.py | 6 +- .../v1_0/messages/tests/test_response.py | 6 +- .../didexchange/v1_0/tests/test_manager.py | 46 ++++---- .../v1_0/tests/test_manager.py | 4 +- .../v1_0/tests/test_routes.py | 30 ++--- .../formats/ld_proof/tests/test_handler.py | 6 +- .../out_of_band/v1_0/tests/test_manager.py | 20 ++-- .../dif/tests/test_pres_exch_handler.py | 18 +-- .../transport/tests/test_pack_format.py | 8 +- .../utils/tests/test_outofband.py | 4 +- aries_cloudagent/wallet/askar.py | 12 +- aries_cloudagent/wallet/base.py | 4 +- aries_cloudagent/wallet/in_memory.py | 8 +- aries_cloudagent/wallet/indy.py | 32 +++--- aries_cloudagent/wallet/routes.py | 16 +-- .../wallet/tests/test_in_memory_wallet.py | 72 ++++++------ .../wallet/tests/test_indy_wallet.py | 24 ++-- .../wallet/tests/test_key_type.py | 18 +-- aries_cloudagent/wallet/tests/test_routes.py | 76 ++++++------- 33 files changed, 322 insertions(+), 322 deletions(-) diff --git a/aries_cloudagent/config/wallet.py b/aries_cloudagent/config/wallet.py index 61e34b2a73..577290ff7c 100644 --- a/aries_cloudagent/config/wallet.py +++ b/aries_cloudagent/config/wallet.py @@ -11,7 +11,7 @@ from ..wallet.base import BaseWallet from ..wallet.crypto import seed_to_did from ..wallet.did_info import DIDInfo -from ..wallet.did_method import DIDMethod +from ..wallet.did_method import SOV from ..wallet.key_type import KeyType from .base import ConfigError from .injection_context import InjectionContext @@ -79,7 +79,7 @@ async def wallet_config( if wallet_seed and seed_to_did(wallet_seed) != public_did: if context.settings.get("wallet.replace_public_did"): replace_did_info = await wallet.create_local_did( - method=DIDMethod.SOV, key_type=KeyType.ED25519, seed=wallet_seed + method=SOV, key_type=KeyType.ED25519, seed=wallet_seed ) public_did = replace_did_info.did await wallet.set_public_did(public_did) @@ -99,7 +99,7 @@ async def wallet_config( metadata = {"endpoint": endpoint} if endpoint else None local_did_info = await wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, seed=wallet_seed, metadata=metadata, @@ -110,7 +110,7 @@ async def wallet_config( print(f"Verkey: {local_did_info.verkey}") else: public_did_info = await wallet.create_public_did( - method=DIDMethod.SOV, key_type=KeyType.ED25519, seed=wallet_seed + method=SOV, key_type=KeyType.ED25519, seed=wallet_seed ) public_did = public_did_info.did if provision: @@ -128,7 +128,7 @@ async def wallet_config( test_seed = "testseed000000000000000000000001" if test_seed: await wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, seed=test_seed, metadata={"endpoint": "1.2.3.4:8021"}, diff --git a/aries_cloudagent/core/tests/test_conductor.py b/aries_cloudagent/core/tests/test_conductor.py index 203ca1b3b4..3a864cee7d 100644 --- a/aries_cloudagent/core/tests/test_conductor.py +++ b/aries_cloudagent/core/tests/test_conductor.py @@ -41,7 +41,7 @@ from ...version import __version__ from ...wallet.base import BaseWallet from ...wallet.key_type import KeyType -from ...wallet.did_method import DIDMethod +from ...wallet.did_method import SOV from .. import conductor as test_module @@ -131,7 +131,7 @@ async def test_startup(self): wallet = session.inject(BaseWallet) await wallet.create_public_did( - DIDMethod.SOV, + SOV, KeyType.ED25519, ) @@ -600,7 +600,7 @@ async def test_admin(self): session = await conductor.root_profile.session() wallet = session.inject(BaseWallet) await wallet.create_public_did( - DIDMethod.SOV, + SOV, KeyType.ED25519, ) @@ -644,7 +644,7 @@ async def test_admin_startx(self): session = await conductor.root_profile.session() wallet = session.inject(BaseWallet) await wallet.create_public_did( - DIDMethod.SOV, + SOV, KeyType.ED25519, ) @@ -716,7 +716,7 @@ async def test_start_static(self): session = await conductor.root_profile.session() wallet = session.inject(BaseWallet) await wallet.create_public_did( - DIDMethod.SOV, + SOV, KeyType.ED25519, ) @@ -886,7 +886,7 @@ async def test_print_invite_connection(self): session = await conductor.root_profile.session() wallet = session.inject(BaseWallet) await wallet.create_public_did( - DIDMethod.SOV, + SOV, KeyType.ED25519, ) @@ -1389,7 +1389,7 @@ async def test_startup_x_version_mismatch(self): wallet = session.inject(BaseWallet) await wallet.create_public_did( - DIDMethod.SOV, + SOV, KeyType.ED25519, ) @@ -1426,7 +1426,7 @@ async def test_startup_x_no_storage_version(self): wallet = session.inject(BaseWallet) await wallet.create_public_did( - DIDMethod.SOV, + SOV, KeyType.ED25519, ) diff --git a/aries_cloudagent/ledger/tests/test_indy.py b/aries_cloudagent/ledger/tests/test_indy.py index bda1890c2a..7696625b31 100644 --- a/aries_cloudagent/ledger/tests/test_indy.py +++ b/aries_cloudagent/ledger/tests/test_indy.py @@ -20,7 +20,7 @@ from ...wallet.error import WalletNotFoundError from ...wallet.indy import IndyOpenWallet, IndySdkWallet from ...wallet.key_type import KeyType -from ...wallet.did_method import DIDMethod +from ...wallet.did_method import SOV from ..endpoint_type import EndpointType from ..indy import ( @@ -70,7 +70,7 @@ async def setUp(self): did=self.test_did, verkey="3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx", metadata={"test": "test"}, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) self.test_verkey = "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx" @@ -1219,7 +1219,7 @@ async def test_send_credential_definition( did=self.test_did, verkey=self.test_verkey, metadata=None, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) mock_did = mock_wallet_get_public_did.return_value @@ -1299,7 +1299,7 @@ async def test_send_credential_definition_endorse_only( self.test_did, self.test_verkey, None, - DIDMethod.SOV, + SOV, KeyType.ED25519, ) async with ledger: @@ -1382,7 +1382,7 @@ async def test_send_credential_definition_exists_in_ledger_and_wallet( did=self.test_did, verkey=self.test_verkey, metadata=None, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) @@ -1794,7 +1794,7 @@ async def test_send_credential_definition_on_ledger_in_wallet( did=self.test_did, verkey=self.test_verkey, metadata=None, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) mock_did = mock_wallet_get_public_did.return_value @@ -1868,7 +1868,7 @@ async def test_send_credential_definition_create_cred_def_exception( did=self.test_did, verkey=self.test_verkey, metadata=None, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) async with ledger: diff --git a/aries_cloudagent/ledger/tests/test_indy_vdr.py b/aries_cloudagent/ledger/tests/test_indy_vdr.py index f20781e838..0d39e4daf6 100644 --- a/aries_cloudagent/ledger/tests/test_indy_vdr.py +++ b/aries_cloudagent/ledger/tests/test_indy_vdr.py @@ -10,7 +10,7 @@ from ...indy.issuer import IndyIssuer from ...wallet.base import BaseWallet from ...wallet.key_type import KeyType -from ...wallet.did_method import DIDMethod +from ...wallet.did_method import SOV from ...wallet.did_info import DIDInfo from ..endpoint_type import EndpointType @@ -67,7 +67,7 @@ async def test_submit_signed( ledger: IndyVdrLedger, ): wallet = (await ledger.profile.session()).wallet - test_did = await wallet.create_public_did(DIDMethod.SOV, KeyType.ED25519) + test_did = await wallet.create_public_did(SOV, KeyType.ED25519) test_msg = indy_vdr.ledger.build_get_txn_request(test_did.did, 1, 1) async with ledger: @@ -120,7 +120,7 @@ async def test_submit_signed_taa_accept( ledger: IndyVdrLedger, ): wallet = (await ledger.profile.session()).wallet - test_did = await wallet.create_public_did(DIDMethod.SOV, KeyType.ED25519) + test_did = await wallet.create_public_did(SOV, KeyType.ED25519) async with ledger: test_msg = indy_vdr.ledger.build_get_txn_request(test_did.did, 1, 1) @@ -188,7 +188,7 @@ async def test_txn_endorse( with pytest.raises(BadLedgerRequestError): await ledger.txn_endorse(request_json=test_msg.body) - test_did = await wallet.create_public_did(DIDMethod.SOV, KeyType.ED25519) + test_did = await wallet.create_public_did(SOV, KeyType.ED25519) test_msg.set_endorser(test_did.did) endorsed_json = await ledger.txn_endorse(request_json=test_msg.body) @@ -201,7 +201,7 @@ async def test_send_schema( ledger: IndyVdrLedger, ): wallet = (await ledger.profile.session()).wallet - test_did = await wallet.create_public_did(DIDMethod.SOV, KeyType.ED25519) + test_did = await wallet.create_public_did(SOV, KeyType.ED25519) issuer = async_mock.MagicMock(IndyIssuer) issuer.create_schema.return_value = ( "schema_issuer_did:schema_name:9.1", @@ -262,7 +262,7 @@ async def test_send_schema_already_exists( ledger: IndyVdrLedger, ): wallet = (await ledger.profile.session()).wallet - test_did = await wallet.create_public_did(DIDMethod.SOV, KeyType.ED25519) + test_did = await wallet.create_public_did(SOV, KeyType.ED25519) issuer = async_mock.MagicMock(IndyIssuer) issuer.create_schema.return_value = ( "schema_issuer_did:schema_name:9.1", @@ -292,7 +292,7 @@ async def test_send_schema_ledger_read_only( ledger: IndyVdrLedger, ): wallet = (await ledger.profile.session()).wallet - test_did = await wallet.create_public_did(DIDMethod.SOV, KeyType.ED25519) + test_did = await wallet.create_public_did(SOV, KeyType.ED25519) issuer = async_mock.MagicMock(IndyIssuer) issuer.create_schema.return_value = ( "schema_issuer_did:schema_name:9.1", @@ -321,7 +321,7 @@ async def test_send_schema_ledger_transaction_error( ledger: IndyVdrLedger, ): wallet = (await ledger.profile.session()).wallet - test_did = await wallet.create_public_did(DIDMethod.SOV, KeyType.ED25519) + test_did = await wallet.create_public_did(SOV, KeyType.ED25519) issuer = async_mock.MagicMock(IndyIssuer) issuer.create_schema.return_value = ( "schema_issuer_did:schema_name:9.1", @@ -383,7 +383,7 @@ async def test_send_credential_definition( ledger: IndyVdrLedger, ): wallet = (await ledger.profile.session()).wallet - test_did = await wallet.create_public_did(DIDMethod.SOV, KeyType.ED25519) + test_did = await wallet.create_public_did(SOV, KeyType.ED25519) schema_id = "55GkHamhTU1ZbTbV2ab9DE:2:schema_name:9.1" cred_def_id = "55GkHamhTU1ZbTbV2ab9DE:3:CL:99:tag" cred_def = { @@ -598,7 +598,7 @@ async def test_update_endpoint_for_did( ledger: IndyVdrLedger, ): wallet = (await ledger.profile.session()).wallet - test_did = await wallet.create_public_did(DIDMethod.SOV, KeyType.ED25519) + test_did = await wallet.create_public_did(SOV, KeyType.ED25519) async with ledger: ledger.pool_handle.submit_request.side_effect = ( {"data": None}, @@ -675,7 +675,7 @@ async def test_construct_attr_json( async def test_update_endpoint_for_did_calls_attr_json(self, ledger: IndyVdrLedger): routing_keys = ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"] wallet = (await ledger.profile.session()).wallet - test_did = await wallet.create_public_did(DIDMethod.SOV, KeyType.ED25519) + test_did = await wallet.create_public_did(SOV, KeyType.ED25519) async with ledger: with async_mock.patch.object( @@ -742,8 +742,8 @@ async def test_register_nym_local( ledger: IndyVdrLedger, ): wallet: BaseWallet = (await ledger.profile.session()).wallet - public_did = await wallet.create_public_did(DIDMethod.SOV, KeyType.ED25519) - post_did = await wallet.create_local_did(DIDMethod.SOV, KeyType.ED25519) + public_did = await wallet.create_public_did(SOV, KeyType.ED25519) + post_did = await wallet.create_local_did(SOV, KeyType.ED25519) async with ledger: await ledger.register_nym(post_did.did, post_did.verkey) did = await wallet.get_local_did(post_did.did) @@ -755,7 +755,7 @@ async def test_register_nym_non_local( ledger: IndyVdrLedger, ): wallet: BaseWallet = (await ledger.profile.session()).wallet - public_did = await wallet.create_public_did(DIDMethod.SOV, KeyType.ED25519) + public_did = await wallet.create_public_did(SOV, KeyType.ED25519) async with ledger: await ledger.register_nym("55GkHamhTU1ZbTbV2ab9DE", "verkey") @@ -898,7 +898,7 @@ async def test_send_revoc_reg_def( ledger: IndyVdrLedger, ): wallet: BaseWallet = (await ledger.profile.session()).wallet - public_did = await wallet.create_public_did(DIDMethod.SOV, KeyType.ED25519) + public_did = await wallet.create_public_did(SOV, KeyType.ED25519) async with ledger: reg_id = ( "55GkHamhTU1ZbTbV2ab9DE:4:55GkHamhTU1ZbTbV2ab9DE:3:CL:99:tag:CL_ACCUM:0" @@ -927,7 +927,7 @@ async def test_send_revoc_reg_entry( ledger: IndyVdrLedger, ): wallet: BaseWallet = (await ledger.profile.session()).wallet - public_did = await wallet.create_public_did(DIDMethod.SOV, KeyType.ED25519) + public_did = await wallet.create_public_did(SOV, KeyType.ED25519) async with ledger: reg_id = ( "55GkHamhTU1ZbTbV2ab9DE:4:55GkHamhTU1ZbTbV2ab9DE:3:CL:99:tag:CL_ACCUM:0" @@ -966,7 +966,7 @@ async def test_credential_definition_id2schema_id(self, ledger: IndyVdrLedger): @pytest.mark.asyncio async def test_rotate_did_keypair(self, ledger: IndyVdrLedger): wallet = (await ledger.profile.session()).wallet - public_did = await wallet.create_public_did(DIDMethod.SOV, KeyType.ED25519) + public_did = await wallet.create_public_did(SOV, KeyType.ED25519) async with ledger: with async_mock.patch.object( diff --git a/aries_cloudagent/messaging/decorators/tests/test_attach_decorator.py b/aries_cloudagent/messaging/decorators/tests/test_attach_decorator.py index 2687c7bf19..7a74698212 100644 --- a/aries_cloudagent/messaging/decorators/tests/test_attach_decorator.py +++ b/aries_cloudagent/messaging/decorators/tests/test_attach_decorator.py @@ -11,7 +11,7 @@ from ....wallet.indy import IndySdkWallet from ....wallet.util import b64_to_bytes, bytes_to_b64 from ....wallet.key_type import KeyType -from ....wallet.did_method import DIDMethod +from ....wallet.did_method import SOV from ..attach_decorator import ( AttachDecorator, @@ -434,7 +434,7 @@ class TestAttachDecoratorSignature: @pytest.mark.asyncio async def test_did_raw_key(self, wallet, seed): did_info = await wallet.create_local_did( - DIDMethod.SOV, KeyType.ED25519, seed[0] + SOV, KeyType.ED25519, seed[0] ) did_key0 = did_key(did_info.verkey) raw_key0 = raw_key(did_key0) @@ -457,7 +457,7 @@ async def test_indy_sign(self, wallet, seed): ) deco_indy_master = deepcopy(deco_indy) did_info = [ - await wallet.create_local_did(DIDMethod.SOV, KeyType.ED25519, seed[i]) + await wallet.create_local_did(SOV, KeyType.ED25519, seed[i]) for i in [0, 1] ] assert deco_indy.data.signatures == 0 diff --git a/aries_cloudagent/messaging/jsonld/tests/test_routes.py b/aries_cloudagent/messaging/jsonld/tests/test_routes.py index 129ac26515..7c20c07861 100644 --- a/aries_cloudagent/messaging/jsonld/tests/test_routes.py +++ b/aries_cloudagent/messaging/jsonld/tests/test_routes.py @@ -14,7 +14,7 @@ from ....resolver.did_resolver import DIDResolver from ....vc.ld_proofs.document_loader import DocumentLoader from ....wallet.base import BaseWallet -from ....wallet.did_method import DIDMethod +from ....wallet.did_method import SOV from ....wallet.error import WalletError from ....wallet.key_type import KeyType from ..error import ( @@ -275,7 +275,7 @@ async def setUp(self): DocumentLoader, custom_document_loader ) self.did_info = await (await self.context.session()).wallet.create_local_did( - DIDMethod.SOV, KeyType.ED25519 + SOV, KeyType.ED25519 ) self.request_dict = { "context": self.context, diff --git a/aries_cloudagent/multitenant/tests/test_base.py b/aries_cloudagent/multitenant/tests/test_base.py index 6f7c7b7e78..d8c50b0c88 100644 --- a/aries_cloudagent/multitenant/tests/test_base.py +++ b/aries_cloudagent/multitenant/tests/test_base.py @@ -18,7 +18,7 @@ from ...storage.error import StorageNotFoundError from ...storage.in_memory import InMemoryStorage from ...wallet.did_info import DIDInfo -from ...wallet.did_method import DIDMethod +from ...wallet.did_method import SOV from ...wallet.in_memory import InMemoryWallet from ...wallet.key_type import KeyType from ...wallet.models.wallet_record import WalletRecord @@ -244,7 +244,7 @@ async def test_create_wallet_adds_wallet_route(self): did="public-did", verkey="test_verkey", metadata={"meta": "data"}, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) diff --git a/aries_cloudagent/protocols/connections/v1_0/manager.py b/aries_cloudagent/protocols/connections/v1_0/manager.py index d937b7ecb2..9bf066140d 100644 --- a/aries_cloudagent/protocols/connections/v1_0/manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/manager.py @@ -19,7 +19,7 @@ from ....wallet.base import BaseWallet from ....wallet.crypto import create_keypair, seed_to_did from ....wallet.did_info import DIDInfo -from ....wallet.did_method import DIDMethod +from ....wallet.did_method import SOV from ....wallet.error import WalletNotFoundError from ....wallet.key_type import KeyType from ....wallet.util import bytes_to_b58 @@ -360,7 +360,7 @@ async def create_request( async with self.profile.session() as session: wallet = session.inject(BaseWallet) # Create new DID for connection - my_info = await wallet.create_local_did(DIDMethod.SOV, KeyType.ED25519) + my_info = await wallet.create_local_did(SOV, KeyType.ED25519) connection.my_did = my_info.did # Idempotent; if routing has already been set up, no action taken @@ -468,7 +468,7 @@ async def receive_request( async with self.profile.session() as session: wallet = session.inject(BaseWallet) my_info = await wallet.create_local_did( - DIDMethod.SOV, KeyType.ED25519 + SOV, KeyType.ED25519 ) new_connection = ConnRecord( @@ -522,7 +522,7 @@ async def receive_request( else: # request from public did async with self.profile.session() as session: wallet = session.inject(BaseWallet) - my_info = await wallet.create_local_did(DIDMethod.SOV, KeyType.ED25519) + my_info = await wallet.create_local_did(SOV, KeyType.ED25519) async with self.profile.session() as session: connection = await ConnRecord.retrieve_by_invitation_msg_id( @@ -613,7 +613,7 @@ async def create_response( else: async with self.profile.session() as session: wallet = session.inject(BaseWallet) - my_info = await wallet.create_local_did(DIDMethod.SOV, KeyType.ED25519) + my_info = await wallet.create_local_did(SOV, KeyType.ED25519) connection.my_did = my_info.did # Idempotent; if routing has already been set up, no action taken @@ -831,7 +831,7 @@ async def create_static_connection( wallet = session.inject(BaseWallet) # seed and DID optional my_info = await wallet.create_local_did( - DIDMethod.SOV, KeyType.ED25519, my_seed, my_did + SOV, KeyType.ED25519, my_seed, my_did ) # must provide their DID and verkey if the seed is not known @@ -845,7 +845,7 @@ async def create_static_connection( their_verkey_bin, _ = create_keypair(KeyType.ED25519, their_seed.encode()) their_verkey = bytes_to_b58(their_verkey_bin) their_info = DIDInfo( - their_did, their_verkey, {}, method=DIDMethod.SOV, key_type=KeyType.ED25519 + their_did, their_verkey, {}, method=SOV, key_type=KeyType.ED25519 ) # Create connection record @@ -1106,7 +1106,7 @@ async def establish_inbound( my_info = await wallet.get_local_did(connection.my_did) else: # Create new DID for connection - my_info = await wallet.create_local_did(DIDMethod.SOV, KeyType.ED25519) + my_info = await wallet.create_local_did(SOV, KeyType.ED25519) connection.my_did = my_info.did try: diff --git a/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py index 80efb456bf..4dfcd1f997 100644 --- a/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py @@ -23,7 +23,7 @@ from .....storage.error import StorageNotFoundError from .....transport.inbound.receipt import MessageReceipt from .....wallet.base import DIDInfo -from .....wallet.did_method import DIDMethod +from .....wallet.did_method import SOV from .....wallet.error import WalletNotFoundError from .....wallet.in_memory import InMemoryWallet from .....wallet.key_type import KeyType @@ -120,7 +120,7 @@ async def test_create_invitation_public_and_multi_use_fails(self): self.test_did, self.test_verkey, None, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) with self.assertRaises(ConnectionManagerError): @@ -166,7 +166,7 @@ async def test_create_invitation_public(self): self.test_did, self.test_verkey, None, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) connect_record, connect_invite = await self.manager.create_invitation( @@ -231,7 +231,7 @@ async def test_create_invitation_multi_use(self): async def test_create_invitation_recipient_routing_endpoint(self): async with self.profile.session() as session: await session.wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, seed=self.test_seed, did=self.test_did, @@ -274,7 +274,7 @@ async def test_create_invitation_public_and_metadata_fails(self): self.test_did, self.test_verkey, None, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) with self.assertRaises(ConnectionManagerError): @@ -435,7 +435,7 @@ async def test_create_request_my_endpoint(self): async def test_create_request_my_did(self): async with self.profile.session() as session: await session.wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, seed=None, did=self.test_did, @@ -480,7 +480,7 @@ async def test_create_request_multitenant(self): self.test_did, self.test_verkey, None, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) await self.manager.create_request( @@ -533,7 +533,7 @@ async def test_create_request_mediation_id(self): did=self.test_did, verkey=self.test_verkey, metadata={}, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) create_local_did.return_value = did_info @@ -542,7 +542,7 @@ async def test_create_request_mediation_id(self): mediation_id=mediation_record.mediation_id, my_endpoint=self.test_endpoint, ) - create_local_did.assert_called_once_with(DIDMethod.SOV, KeyType.ED25519) + create_local_did.assert_called_once_with(SOV, KeyType.ED25519) create_did_document.assert_called_once_with( self.manager, did_info, @@ -585,7 +585,7 @@ async def test_create_request_default_mediator(self): did=self.test_did, verkey=self.test_verkey, metadata={}, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) create_local_did.return_value = did_info @@ -593,7 +593,7 @@ async def test_create_request_default_mediator(self): record, my_endpoint=self.test_endpoint, ) - create_local_did.assert_called_once_with(DIDMethod.SOV, KeyType.ED25519) + create_local_did.assert_called_once_with(SOV, KeyType.ED25519) create_did_document.assert_called_once_with( self.manager, did_info, @@ -614,7 +614,7 @@ async def test_receive_request_public_did_oob_invite(self): recipient_did=self.test_did, recipient_did_public=True ) await session.wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, seed=None, did=self.test_did, @@ -654,7 +654,7 @@ async def test_receive_request_public_did_conn_invite(self): recipient_did=self.test_did, recipient_did_public=True ) await session.wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, seed=None, did=self.test_did, @@ -689,7 +689,7 @@ async def test_receive_request_public_did_no_did_doc(self): recipient_did=self.test_did, recipient_did_public=True ) await session.wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, seed=None, did=self.test_did, @@ -720,7 +720,7 @@ async def test_receive_request_public_did_wrong_did(self): recipient_did=self.test_did, recipient_did_public=True ) await session.wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, seed=None, did=self.test_did, @@ -749,7 +749,7 @@ async def test_receive_request_public_did_no_public_invites(self): receipt = MessageReceipt(recipient_did=self.test_did, recipient_did_public=True) async with self.profile.session() as session: await session.wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, seed=None, did=self.test_did, @@ -780,7 +780,7 @@ async def test_receive_request_public_did_no_auto_accept(self): recipient_did=self.test_did, recipient_did_public=True ) await session.wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, seed=None, did=self.test_did, @@ -867,7 +867,7 @@ async def test_create_response_multitenant(self): self.test_did, self.test_verkey, None, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) await self.manager.create_response( @@ -941,7 +941,7 @@ async def test_create_response_mediation(self): did=self.test_did, verkey=self.test_verkey, metadata={}, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) create_local_did.return_value = did_info @@ -950,7 +950,7 @@ async def test_create_response_mediation(self): mediation_id=mediation_record.mediation_id, my_endpoint=self.test_endpoint, ) - create_local_did.assert_called_once_with(DIDMethod.SOV, KeyType.ED25519) + create_local_did.assert_called_once_with(SOV, KeyType.ED25519) create_did_document.assert_called_once_with( self.manager, did_info, @@ -1266,7 +1266,7 @@ async def test_create_static_connection_multitenant(self): self.test_did, self.test_verkey, None, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) @@ -1299,7 +1299,7 @@ async def test_create_static_connection_multitenant_auto_disclose_features(self) self.test_did, self.test_verkey, None, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) await self.manager.create_static_connection( @@ -1331,7 +1331,7 @@ async def test_create_static_connection_multitenant_mediator(self): self.test_did, self.test_verkey, None, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) @@ -1359,7 +1359,7 @@ async def test_create_static_connection_multitenant_mediator(self): self.test_target_did, self.test_target_verkey, {}, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) create_did_document.assert_has_calls( @@ -1537,7 +1537,7 @@ async def test_resolve_inbound_connection(self): self.test_did, self.test_verkey, {"posted": True}, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) mock_mgr_find_conn.return_value = mock_conn @@ -1589,7 +1589,7 @@ async def test_create_did_document(self): self.test_did, self.test_verkey, None, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) @@ -1622,7 +1622,7 @@ async def test_create_did_document_not_active(self): self.test_did, self.test_verkey, None, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) @@ -1650,7 +1650,7 @@ async def test_create_did_document_no_services(self): self.test_did, self.test_verkey, None, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) @@ -1685,7 +1685,7 @@ async def test_create_did_document_no_service_endpoint(self): self.test_did, self.test_verkey, None, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) @@ -1723,7 +1723,7 @@ async def test_create_did_document_no_service_recip_keys(self): self.test_did, self.test_verkey, None, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) @@ -1769,7 +1769,7 @@ async def test_create_did_document_mediation(self): self.test_did, self.test_verkey, None, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) mediation_record = MediationRecord( @@ -1795,7 +1795,7 @@ async def test_create_did_document_multiple_mediators(self): self.test_did, self.test_verkey, None, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) mediation_record1 = MediationRecord( @@ -1828,7 +1828,7 @@ async def test_create_did_document_mediation_svc_endpoints_overwritten(self): self.test_did, self.test_verkey, None, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) mediation_record = MediationRecord( @@ -1863,7 +1863,7 @@ async def test_did_key_storage(self): async def test_get_connection_targets_conn_invitation_no_did(self): async with self.profile.session() as session: local_did = await session.wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, seed=self.test_seed, did=self.test_did, @@ -1922,7 +1922,7 @@ async def test_get_connection_targets_conn_invitation_no_did(self): async def test_get_connection_targets_retrieve_connection(self): async with self.profile.session() as session: local_did = await session.wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, seed=self.test_seed, did=self.test_did, @@ -1975,7 +1975,7 @@ async def test_get_conn_targets_conn_invitation_no_cache(self): async with self.profile.session() as session: self.context.injector.clear_binding(BaseCache) local_did = await session.wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, seed=self.test_seed, did=self.test_did, @@ -2025,7 +2025,7 @@ async def test_fetch_connection_targets_conn_invitation_did_no_resolver(self): async with self.profile.session() as session: self.context.injector.bind_instance(DIDResolver, DIDResolver([])) await session.wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, seed=self.test_seed, did=self.test_did, @@ -2071,7 +2071,7 @@ async def test_fetch_connection_targets_conn_invitation_did_resolver(self): self.context.injector.bind_instance(DIDResolver, self.resolver) local_did = await session.wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, seed=self.test_seed, did=self.test_did, @@ -2139,7 +2139,7 @@ async def test_fetch_connection_targets_conn_invitation_btcr_resolver(self): self.resolver.resolve = async_mock.CoroutineMock(return_value=did_doc) self.context.injector.bind_instance(DIDResolver, self.resolver) local_did = await session.wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, seed=self.test_seed, did=did_doc.id, @@ -2204,7 +2204,7 @@ async def test_fetch_connection_targets_conn_invitation_btcr_without_services(se self.context.injector.bind_instance(DIDResolver, self.resolver) local_did = await session.wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, seed=self.test_seed, did=did_doc.id, @@ -2244,7 +2244,7 @@ async def test_fetch_connection_targets_conn_invitation_no_didcomm_services(self self.resolver.resolve = async_mock.CoroutineMock(return_value=did_doc) self.context.injector.bind_instance(DIDResolver, self.resolver) await session.wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, seed=self.test_seed, did=did_doc.id, @@ -2290,7 +2290,7 @@ async def test_fetch_connection_targets_conn_invitation_unsupported_key_type(sel self.resolver.resolve = async_mock.CoroutineMock(return_value=did_doc) self.context.injector.bind_instance(DIDResolver, self.resolver) local_did = await session.wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, seed=self.test_seed, did=did_doc.id, @@ -2319,7 +2319,7 @@ async def test_fetch_connection_targets_oob_invitation_svc_did_no_resolver(self) async with self.profile.session() as session: self.context.injector.bind_instance(DIDResolver, DIDResolver([])) await session.wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, seed=self.test_seed, did=self.test_did, @@ -2360,7 +2360,7 @@ async def test_fetch_connection_targets_oob_invitation_svc_did_resolver(self): self.context.injector.bind_instance(DIDResolver, self.resolver) local_did = await session.wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, seed=self.test_seed, did=self.test_did, @@ -2405,7 +2405,7 @@ async def test_fetch_connection_targets_oob_invitation_svc_block_resolver(self): self.context.injector.bind_instance(DIDResolver, self.resolver) local_did = await session.wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, seed=self.test_seed, did=self.test_did, @@ -2451,7 +2451,7 @@ async def test_fetch_connection_targets_oob_invitation_svc_block_resolver(self): async def test_fetch_connection_targets_conn_initiator_completed_no_their_did(self): async with self.profile.session() as session: await session.wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, seed=self.test_seed, did=self.test_did, @@ -2468,7 +2468,7 @@ async def test_fetch_connection_targets_conn_initiator_completed_no_their_did(se async def test_fetch_connection_targets_conn_completed_their_did(self): async with self.profile.session() as session: local_did = await session.wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, seed=self.test_seed, did=self.test_did, @@ -2499,7 +2499,7 @@ async def test_fetch_connection_targets_conn_completed_their_did(self): async def test_fetch_connection_targets_conn_no_invi_with_their_did(self): async with self.profile.session() as session: local_did = await session.wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, seed=self.test_seed, did=self.test_did, @@ -2554,7 +2554,7 @@ async def test_diddoc_connection_targets_diddoc_underspecified(self): async def test_establish_inbound(self): async with self.profile.session() as session: await session.wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, seed=self.test_seed, did=self.test_did, @@ -2584,7 +2584,7 @@ async def test_establish_inbound(self): async def test_establish_inbound_conn_rec_no_my_did(self): async with self.profile.session() as session: await session.wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, seed=self.test_seed, did=self.test_did, @@ -2613,7 +2613,7 @@ async def test_establish_inbound_conn_rec_no_my_did(self): async def test_establish_inbound_no_conn_record(self): async with self.profile.session() as session: await session.wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, seed=self.test_seed, did=self.test_did, @@ -2642,7 +2642,7 @@ async def test_establish_inbound_no_conn_record(self): async def test_establish_inbound_router_not_ready(self): async with self.profile.session() as session: await session.wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, seed=self.test_seed, did=self.test_did, diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py index f525dd0088..561f57a6c8 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py @@ -11,7 +11,7 @@ from ....storage.record import StorageRecord from ....wallet.base import BaseWallet from ....wallet.did_info import DIDInfo -from ....wallet.did_method import DIDMethod +from ....wallet.did_method import SOV from ....wallet.key_type import KeyType from ...routing.v1_0.manager import RoutingManager from ...routing.v1_0.models.route_record import RouteRecord @@ -111,7 +111,7 @@ async def _create_routing_did(self, session: ProfileSession) -> DIDInfo: wallet = session.inject(BaseWallet) storage = session.inject(BaseStorage) info = await wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, metadata={"type": "routing_did"}, ) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py index 07da03fd51..473d7296bb 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py @@ -14,7 +14,7 @@ from ....storage.error import StorageNotFoundError from ....wallet.base import BaseWallet from ....wallet.did_info import DIDInfo -from ....wallet.did_method import DIDMethod +from ....wallet.did_method import SOV from ....wallet.key_type import KeyType from ...routing.v1_0.models.route_record import RouteRecord from .manager import MediationManager @@ -40,7 +40,7 @@ async def get_or_create_my_did( async with profile.session() as session: wallet = session.inject(BaseWallet) # Create new DID for connection - my_info = await wallet.create_local_did(DIDMethod.SOV, KeyType.ED25519) + my_info = await wallet.create_local_did(SOV, KeyType.ED25519) conn_record.my_did = my_info.did await conn_record.save(session, reason="Connection my did created") else: diff --git a/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_request_handler.py b/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_request_handler.py index ff76d6a9c6..9f6f4a204c 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_request_handler.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_request_handler.py @@ -10,7 +10,7 @@ ) from ......core.in_memory import InMemoryProfile from ......wallet.key_type import KeyType -from ......wallet.did_method import DIDMethod +from ......wallet.did_method import SOV from ......messaging.decorators.attach_decorator import AttachDecorator from ......messaging.request_context import RequestContext from ......messaging.responder import MockResponder @@ -88,7 +88,7 @@ async def setUp(self): wallet = self.session.wallet self.did_info = await wallet.create_local_did( - method=DIDMethod.SOV, key_type=KeyType.ED25519 + method=SOV, key_type=KeyType.ED25519 ) self.did_doc_attach = AttachDecorator.data_base64(self.did_doc().serialize()) diff --git a/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_response_handler.py b/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_response_handler.py index a161b5f325..d5cc3d23dc 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_response_handler.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_response_handler.py @@ -14,7 +14,7 @@ from ......messaging.responder import MockResponder from ......transport.inbound.receipt import MessageReceipt from ......wallet.key_type import KeyType -from ......wallet.did_method import DIDMethod +from ......wallet.did_method import SOV from .....problem_report.v1_0.message import ProblemReport from .....trustping.v1_0.messages.ping import Ping @@ -65,7 +65,7 @@ async def setUp(self): wallet = (await self.ctx.session()).wallet self.did_info = await wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) diff --git a/aries_cloudagent/protocols/didexchange/v1_0/manager.py b/aries_cloudagent/protocols/didexchange/v1_0/manager.py index 587e6b56b5..5edce76ac2 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/manager.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/manager.py @@ -23,7 +23,7 @@ from ....storage.error import StorageNotFoundError from ....transport.inbound.receipt import MessageReceipt from ....wallet.base import BaseWallet -from ....wallet.did_method import DIDMethod +from ....wallet.did_method import SOV from ....wallet.did_posture import DIDPosture from ....wallet.error import WalletError from ....wallet.key_type import KeyType @@ -284,7 +284,7 @@ async def create_request( async with self.profile.session() as session: wallet = session.inject(BaseWallet) my_info = await wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) conn_rec.my_did = my_info.did @@ -417,7 +417,7 @@ async def receive_request( async with self.profile.session() as session: wallet = session.inject(BaseWallet) my_info = await wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) @@ -486,7 +486,7 @@ async def receive_request( async with self.profile.session() as session: wallet = session.inject(BaseWallet) my_info = await wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) @@ -580,7 +580,7 @@ async def create_response( async with self.profile.session() as session: wallet = session.inject(BaseWallet) my_info = await wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) conn_rec.my_did = my_info.did diff --git a/aries_cloudagent/protocols/didexchange/v1_0/messages/tests/test_request.py b/aries_cloudagent/protocols/didexchange/v1_0/messages/tests/test_request.py index a7ad4f405f..392201abea 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/messages/tests/test_request.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/messages/tests/test_request.py @@ -8,7 +8,7 @@ PublicKeyType, Service, ) -from ......wallet.did_method import DIDMethod +from ......wallet.did_method import SOV from ......wallet.key_type import KeyType from ......core.in_memory import InMemoryProfile from ......messaging.decorators.attach_decorator import AttachDecorator @@ -59,7 +59,7 @@ class TestDIDXRequest(AsyncTestCase, TestConfig): async def setUp(self): self.wallet = InMemoryProfile.test_session().wallet self.did_info = await self.wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) @@ -116,7 +116,7 @@ class TestDIDXRequestSchema(AsyncTestCase, TestConfig): async def setUp(self): self.wallet = InMemoryProfile.test_session().wallet self.did_info = await self.wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) diff --git a/aries_cloudagent/protocols/didexchange/v1_0/messages/tests/test_response.py b/aries_cloudagent/protocols/didexchange/v1_0/messages/tests/test_response.py index 9cf32b8359..1a47bdb45f 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/messages/tests/test_response.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/messages/tests/test_response.py @@ -8,7 +8,7 @@ PublicKeyType, Service, ) -from ......wallet.did_method import DIDMethod +from ......wallet.did_method import SOV from ......wallet.key_type import KeyType from ......core.in_memory import InMemoryProfile from ......messaging.decorators.attach_decorator import AttachDecorator @@ -58,7 +58,7 @@ class TestDIDXResponse(AsyncTestCase, TestConfig): async def setUp(self): self.wallet = InMemoryProfile.test_session().wallet self.did_info = await self.wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) @@ -112,7 +112,7 @@ class TestDIDXResponseSchema(AsyncTestCase, TestConfig): async def setUp(self): self.wallet = InMemoryProfile.test_session().wallet self.did_info = await self.wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) diff --git a/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py index 131d87670e..e19711c2b8 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py @@ -28,7 +28,7 @@ from .....wallet.did_info import DIDInfo from .....wallet.error import WalletError from .....wallet.in_memory import InMemoryWallet -from .....wallet.did_method import DIDMethod +from .....wallet.did_method import SOV from .....wallet.key_type import KeyType from .....did.did_key import DIDKey @@ -114,7 +114,7 @@ async def setUp(self): self.context = self.profile.context async with self.profile.session() as session: self.did_info = await session.wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) @@ -202,7 +202,7 @@ async def test_receive_invitation_oob_public_did(self): self.profile.context.update_settings({"public_invites": True}) public_did_info = None await session.wallet.create_public_did( - DIDMethod.SOV, + SOV, KeyType.ED25519, ) public_did_info = await session.wallet.get_public_did() @@ -303,7 +303,7 @@ async def test_create_request_implicit(self): async def test_create_request_implicit_use_public_did(self): async with self.profile.session() as session: info_public = await session.wallet.create_public_did( - DIDMethod.SOV, + SOV, KeyType.ED25519, ) conn_rec = await self.manager.create_request_implicit( @@ -374,7 +374,7 @@ async def test_create_request_multitenant(self): TestConfig.test_did, TestConfig.test_verkey, None, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) mock_attach_deco.data_base64 = async_mock.MagicMock( @@ -502,7 +502,7 @@ async def test_receive_request_explicit_public_did(self): await mediation_record.save(session) await session.wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, seed=None, did=TestConfig.test_did, @@ -601,7 +601,7 @@ async def test_receive_request_invi_not_found(self): ) await session.wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, seed=None, did=TestConfig.test_did, @@ -633,7 +633,7 @@ async def test_receive_request_public_did_no_did_doc_attachment(self): ) await session.wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, seed=None, did=TestConfig.test_did, @@ -693,7 +693,7 @@ async def test_receive_request_public_did_x_not_public(self): ) await session.wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, seed=None, did=TestConfig.test_did, @@ -735,7 +735,7 @@ async def test_receive_request_public_did_x_wrong_did(self): ) await session.wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, seed=None, did=TestConfig.test_did, @@ -802,7 +802,7 @@ async def test_receive_request_public_did_x_did_doc_attach_bad_sig(self): ) await session.wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, seed=None, did=TestConfig.test_did, @@ -863,7 +863,7 @@ async def test_receive_request_public_did_no_public_invites(self): ) await session.wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, seed=None, did=TestConfig.test_did, @@ -910,7 +910,7 @@ async def test_receive_request_public_did_no_auto_accept(self): ) await session.wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, seed=None, did=TestConfig.test_did, @@ -1003,7 +1003,7 @@ async def test_receive_request_peer_did(self): mock_conn_rec_state_request = ConnRecord.State.REQUEST await session.wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, seed=None, did=TestConfig.test_did, @@ -1077,7 +1077,7 @@ async def test_receive_request_peer_did_not_found_x(self): ) await session.wallet.create_local_did( - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, seed=None, did=TestConfig.test_did, @@ -1253,7 +1253,7 @@ async def test_create_response_multitenant(self): TestConfig.test_did, TestConfig.test_verkey, None, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) mock_create_did_doc.return_value = async_mock.MagicMock( @@ -1670,7 +1670,7 @@ async def test_create_did_document(self): TestConfig.test_did, TestConfig.test_verkey, None, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) @@ -1704,7 +1704,7 @@ async def test_create_did_document_not_completed(self): TestConfig.test_did, TestConfig.test_verkey, None, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) @@ -1732,7 +1732,7 @@ async def test_create_did_document_no_services(self): TestConfig.test_did, TestConfig.test_verkey, None, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) @@ -1767,7 +1767,7 @@ async def test_create_did_document_no_service_endpoint(self): TestConfig.test_did, TestConfig.test_verkey, None, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) @@ -1805,7 +1805,7 @@ async def test_create_did_document_no_service_recip_keys(self): TestConfig.test_did, TestConfig.test_verkey, None, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) @@ -1851,7 +1851,7 @@ async def test_did_key_storage(self): TestConfig.test_did, TestConfig.test_verkey, None, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) @@ -1897,7 +1897,7 @@ async def test_resolve_did_document_error(self): public_did_info = None async with self.profile.session() as session: await session.wallet.create_public_did( - DIDMethod.SOV, + SOV, KeyType.ED25519, ) public_did_info = await session.wallet.get_public_did() diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_manager.py index edad031889..1264bb2271 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_manager.py @@ -16,7 +16,7 @@ from .....storage.error import StorageNotFoundError from .....wallet.base import BaseWallet from .....wallet.did_info import DIDInfo -from .....wallet.did_method import DIDMethod +from .....wallet.did_method import SOV from .....wallet.key_type import KeyType from ..manager import TransactionManager, TransactionManagerError @@ -124,7 +124,7 @@ async def setUp(self): async with self.profile.session() as session: self.wallet: BaseWallet = session.inject_or(BaseWallet) await self.wallet.create_local_did( - DIDMethod.SOV, + SOV, KeyType.ED25519, did="DJGEjaMunDtFtBVrn1qJMT", metadata={"meta": "data"}, diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_routes.py index d91b1ff8b8..9aeb3481e7 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_routes.py @@ -9,7 +9,7 @@ from .....ledger.base import BaseLedger from .....wallet.base import BaseWallet from .....wallet.did_info import DIDInfo -from .....wallet.did_method import DIDMethod +from .....wallet.did_method import SOV from .....wallet.key_type import KeyType from ..models.transaction_record import TransactionRecord @@ -436,7 +436,7 @@ async def test_endorse_transaction_response(self): "did", "verkey", {"meta": "data"}, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) ) @@ -515,7 +515,7 @@ async def test_endorse_transaction_response_not_found_x(self): "did", "verkey", {"meta": "data"}, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) ) @@ -544,7 +544,7 @@ async def test_endorse_transaction_response_base_model_x(self): "did", "verkey", {"meta": "data"}, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) ) @@ -579,7 +579,7 @@ async def test_endorse_transaction_response_no_jobs_x(self): "did", "verkey", {"meta": "data"}, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) ) @@ -616,7 +616,7 @@ async def skip_test_endorse_transaction_response_no_ledger_x(self): "did", "verkey", {"meta": "data"}, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) ) @@ -671,7 +671,7 @@ async def test_endorse_transaction_response_wrong_my_job_x(self): "did", "verkey", {"meta": "data"}, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) ) @@ -714,7 +714,7 @@ async def skip_test_endorse_transaction_response_ledger_x(self): "did", "verkey", {"meta": "data"}, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) ) @@ -772,7 +772,7 @@ async def test_endorse_transaction_response_txn_mgr_x(self): "did", "verkey", {"meta": "data"}, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) ) @@ -824,7 +824,7 @@ async def test_refuse_transaction_response(self): "did", "verkey", {"meta": "data"}, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) ) @@ -882,7 +882,7 @@ async def test_refuse_transaction_response_not_found_x(self): "did", "verkey", {"meta": "data"}, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) ) @@ -912,7 +912,7 @@ async def test_refuse_transaction_response_conn_base_model_x(self): "did", "verkey", {"meta": "data"}, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) ) @@ -947,7 +947,7 @@ async def test_refuse_transaction_response_no_jobs_x(self): "did", "verkey", {"meta": "data"}, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) ) @@ -984,7 +984,7 @@ async def test_refuse_transaction_response_wrong_my_job_x(self): "did", "verkey", {"meta": "data"}, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) ) @@ -1027,7 +1027,7 @@ async def test_refuse_transaction_response_txn_mgr_x(self): "did", "verkey", {"meta": "data"}, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) ) diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/tests/test_handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/tests/test_handler.py index c4666a679e..690ae592e9 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/tests/test_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/tests/test_handler.py @@ -28,7 +28,7 @@ from .......vc.tests.document_loader import custom_document_loader from .......wallet.key_type import KeyType from .......wallet.error import WalletNotFoundError -from .......wallet.did_method import DIDMethod +from .......wallet.did_method import SOV from .......wallet.base import BaseWallet from ....models.detail.ld_proof import V20CredExRecordLDProof @@ -217,7 +217,7 @@ async def test_assert_can_issue_with_id_and_proof_type(self): did=TEST_DID_SOV, verkey="verkey", metadata={}, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) mock_did_info.return_value = did_info @@ -229,7 +229,7 @@ async def test_assert_can_issue_with_id_and_proof_type(self): did=TEST_DID_SOV, verkey="verkey", metadata={}, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.BLS12381G2, ) mock_did_info.return_value = invalid_did_info diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py index 738e295a1f..9197d00447 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py @@ -65,7 +65,7 @@ from .....storage.error import StorageNotFoundError from .....transport.inbound.receipt import MessageReceipt from .....wallet.did_info import DIDInfo, KeyInfo -from .....wallet.did_method import DIDMethod +from .....wallet.did_method import SOV from .....wallet.in_memory import InMemoryWallet from .....wallet.key_type import KeyType from ....connections.v1_0.messages.connection_invitation import ConnectionInvitation @@ -378,7 +378,7 @@ async def test_create_invitation_handshake_succeeds(self): TestConfig.test_did, TestConfig.test_verkey, None, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) invi_rec = await self.manager.create_invitation( @@ -438,7 +438,7 @@ async def test_create_invitation_multitenant_public(self): self.test_did, self.test_verkey, None, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) await self.manager.create_invitation( @@ -513,7 +513,7 @@ async def test_create_invitation_attachment_v1_0_cred_offer(self): TestConfig.test_did, TestConfig.test_verkey, None, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) mock_retrieve_cxid.return_value = async_mock.MagicMock( @@ -547,7 +547,7 @@ async def test_create_invitation_attachment_v1_0_cred_offer_no_handshake(self): TestConfig.test_did, TestConfig.test_verkey, None, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) mock_retrieve_cxid.return_value = async_mock.MagicMock( @@ -585,7 +585,7 @@ async def test_create_invitation_attachment_v2_0_cred_offer(self): TestConfig.test_did, TestConfig.test_verkey, None, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) mock_retrieve_cxid_v1.side_effect = test_module.StorageNotFoundError() @@ -623,7 +623,7 @@ async def test_create_invitation_attachment_present_proof_v1_0(self): TestConfig.test_did, TestConfig.test_verkey, None, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) mock_retrieve_pxid.return_value = async_mock.MagicMock( @@ -662,7 +662,7 @@ async def test_create_invitation_attachment_present_proof_v2_0(self): TestConfig.test_did, TestConfig.test_verkey, None, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) mock_retrieve_pxid_1.side_effect = StorageNotFoundError() @@ -750,7 +750,7 @@ async def test_create_invitation_attachment_x(self): TestConfig.test_did, TestConfig.test_verkey, None, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) with self.assertRaises(OutOfBandManagerError) as context: @@ -834,7 +834,7 @@ async def test_create_invitation_x_public_metadata(self): TestConfig.test_did, TestConfig.test_verkey, None, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) with self.assertRaises(OutOfBandManagerError) as context: diff --git a/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py b/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py index 87587eb674..8443f1a048 100644 --- a/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py +++ b/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py @@ -12,7 +12,7 @@ from .....storage.vc_holder.vc_record import VCRecord from .....wallet.base import BaseWallet, DIDInfo from .....wallet.crypto import KeyType -from .....wallet.did_method import DIDMethod +from .....wallet.did_method import SOV, KEY from .....wallet.error import WalletNotFoundError from .....vc.ld_proofs import ( BbsBlsSignature2020, @@ -79,10 +79,10 @@ async def setup_tuple(profile): async with profile.session() as session: wallet = session.inject_or(BaseWallet) await wallet.create_local_did( - method=DIDMethod.SOV, key_type=KeyType.ED25519, did="WgWxqztrNooG92RXvxSTWv" + method=SOV, key_type=KeyType.ED25519, did="WgWxqztrNooG92RXvxSTWv" ) await wallet.create_local_did( - method=DIDMethod.KEY, + method=KEY, key_type=KeyType.BLS12381G2, ) creds, pds = get_test_data() @@ -2031,7 +2031,7 @@ async def test_get_sign_key_credential_subject_id(self, profile): did="did:sov:LjgpST2rjsoxYegQDRm7EL", verkey="verkey", metadata={}, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) mock_did_info.return_value = did_info @@ -2096,7 +2096,7 @@ async def test_get_sign_key_credential_subject_id_error(self, profile): did="did:sov:LjgpST2rjsoxYegQDRm7EL", verkey="verkey", metadata={}, - method=DIDMethod.SOV, + method=SOV, key_type=KeyType.ED25519, ) mock_did_info.return_value = did_info @@ -2164,7 +2164,7 @@ async def test_get_sign_key_credential_subject_id_bbsbls(self, profile): did="did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", verkey="verkey", metadata={}, - method=DIDMethod.KEY, + method=KEY, key_type=KeyType.BLS12381G2, ) mock_did_info.return_value = did_info @@ -2255,7 +2255,7 @@ async def test_create_vp_no_issuer(self, profile, setup_tuple): did="did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", verkey="verkey", metadata={}, - method=DIDMethod.KEY, + method=KEY, key_type=KeyType.BLS12381G2, ) mock_did_info.return_value = did_info @@ -2315,7 +2315,7 @@ async def test_create_vp_with_bbs_suite(self, profile, setup_tuple): did="did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", verkey="verkey", metadata={}, - method=DIDMethod.KEY, + method=KEY, key_type=KeyType.BLS12381G2, ) mock_did_info.return_value = did_info @@ -2369,7 +2369,7 @@ async def test_create_vp_no_issuer_with_bbs_suite(self, profile, setup_tuple): did="did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", verkey="verkey", metadata={}, - method=DIDMethod.KEY, + method=KEY, key_type=KeyType.BLS12381G2, ) mock_did_info.return_value = did_info diff --git a/aries_cloudagent/transport/tests/test_pack_format.py b/aries_cloudagent/transport/tests/test_pack_format.py index 1ca7b7f46f..c2c9bafcf1 100644 --- a/aries_cloudagent/transport/tests/test_pack_format.py +++ b/aries_cloudagent/transport/tests/test_pack_format.py @@ -10,7 +10,7 @@ from ...wallet.base import BaseWallet from ...wallet.error import WalletError from ...wallet.key_type import KeyType -from ...wallet.did_method import DIDMethod +from ...wallet.did_method import SOV from .. import pack_format as test_module from ..error import WireFormatEncodeError, WireFormatParseError, RecipientKeysError @@ -140,7 +140,7 @@ async def test_fallback(self): async def test_encode_decode(self): local_did = await self.wallet.create_local_did( - method=DIDMethod.SOV, key_type=KeyType.ED25519, seed=self.test_seed + method=SOV, key_type=KeyType.ED25519, seed=self.test_seed ) serializer = PackWireFormat() recipient_keys = (local_did.verkey,) @@ -174,10 +174,10 @@ async def test_encode_decode(self): async def test_forward(self): local_did = await self.wallet.create_local_did( - method=DIDMethod.SOV, key_type=KeyType.ED25519, seed=self.test_seed + method=SOV, key_type=KeyType.ED25519, seed=self.test_seed ) router_did = await self.wallet.create_local_did( - method=DIDMethod.SOV, key_type=KeyType.ED25519, seed=self.test_routing_seed + method=SOV, key_type=KeyType.ED25519, seed=self.test_routing_seed ) serializer = PackWireFormat() recipient_keys = (local_did.verkey,) diff --git a/aries_cloudagent/utils/tests/test_outofband.py b/aries_cloudagent/utils/tests/test_outofband.py index 2029553471..3f7ec57210 100644 --- a/aries_cloudagent/utils/tests/test_outofband.py +++ b/aries_cloudagent/utils/tests/test_outofband.py @@ -2,7 +2,7 @@ from ...protocols.out_of_band.v1_0.messages.invitation import InvitationMessage from ...wallet.key_type import KeyType -from ...wallet.did_method import DIDMethod +from ...wallet.did_method import SOV from ...wallet.did_info import DIDInfo from .. import outofband as test_module @@ -12,7 +12,7 @@ class TestOutOfBand(TestCase): test_did = "55GkHamhTU1ZbTbV2ab9DE" test_verkey = "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx" test_did_info = DIDInfo( - test_did, test_verkey, None, method=DIDMethod.SOV, key_type=KeyType.ED25519 + test_did, test_verkey, None, method=SOV, key_type=KeyType.ED25519 ) def test_serialize_oob(self): diff --git a/aries_cloudagent/wallet/askar.py b/aries_cloudagent/wallet/askar.py index f2a5c790c6..66ec452ff4 100644 --- a/aries_cloudagent/wallet/askar.py +++ b/aries_cloudagent/wallet/askar.py @@ -31,7 +31,7 @@ validate_seed, verify_signed_message, ) -from .did_method import DIDMethod +from .did_method import SOV, KEY, DIDMethod from .error import WalletError, WalletDuplicateError, WalletNotFoundError from .key_type import KeyType from .util import b58_to_bytes, bytes_to_b58 @@ -180,12 +180,12 @@ async def create_local_did( f" for DID method {method.method_name}" ) - if method == DIDMethod.KEY and did: + if method == KEY and did: raise WalletError("Not allowed to set DID for DID method 'key'") if not metadata: metadata = {} - if method not in [DIDMethod.SOV, DIDMethod.KEY]: + if method not in [SOV, KEY]: raise WalletError( f"Unsupported DID method for askar storage: {method.method_name}" ) @@ -206,7 +206,7 @@ async def create_local_did( else: raise WalletError("Error inserting key") from err - if method == DIDMethod.KEY: + if method == KEY: did = DIDKey.from_public_key(verkey_bytes, key_type).did elif not did: did = bytes_to_b58(verkey_bytes[:16]) @@ -408,7 +408,7 @@ async def set_public_did(self, did: Union[str, DIDInfo]) -> DIDInfo: info = did item = None - if info.method != DIDMethod.SOV: + if info.method != SOV: raise WalletError("Setting public DID is only allowed for did:sov DIDs") public = await self.get_public_did() @@ -462,7 +462,7 @@ async def set_did_endpoint( 'endpoint' affects local wallet """ did_info = await self.get_local_did(did) - if did_info.method != DIDMethod.SOV: + if did_info.method != SOV: raise WalletError("Setting DID endpoint is only allowed for did:sov DIDs") metadata = {**did_info.metadata} if not endpoint_type: diff --git a/aries_cloudagent/wallet/base.py b/aries_cloudagent/wallet/base.py index bbb1288061..46bbc3eab9 100644 --- a/aries_cloudagent/wallet/base.py +++ b/aries_cloudagent/wallet/base.py @@ -9,7 +9,7 @@ from .did_info import DIDInfo, KeyInfo from .key_type import KeyType -from .did_method import DIDMethod +from .did_method import SOV, DIDMethod class BaseWallet(ABC): @@ -241,7 +241,7 @@ async def set_did_endpoint( """ did_info = await self.get_local_did(did) - if did_info.method != DIDMethod.SOV: + if did_info.method != SOV: raise WalletError("Setting DID endpoint is only allowed for did:sov DIDs") metadata = {**did_info.metadata} if not endpoint_type: diff --git a/aries_cloudagent/wallet/in_memory.py b/aries_cloudagent/wallet/in_memory.py index f005ef1d21..adc3e7ba92 100644 --- a/aries_cloudagent/wallet/in_memory.py +++ b/aries_cloudagent/wallet/in_memory.py @@ -17,7 +17,7 @@ ) from .did_info import KeyInfo, DIDInfo from .did_posture import DIDPosture -from .did_method import DIDMethod +from .did_method import SOV, KEY, DIDMethod from .error import WalletError, WalletDuplicateError, WalletNotFoundError from .key_type import KeyType from .util import b58_to_bytes, bytes_to_b58, random_seed @@ -223,12 +223,12 @@ async def create_local_did( # We need some did method specific handling. If more did methods # are added it is probably better create a did method specific handler - if method == DIDMethod.KEY: + if method == KEY: if did: raise WalletError("Not allowed to set DID for DID method 'key'") did = DIDKey.from_public_key(verkey, key_type).did - elif method == DIDMethod.SOV: + elif method == SOV: if not did: did = bytes_to_b58(verkey[:16]) else: @@ -395,7 +395,7 @@ async def set_public_did(self, did: Union[str, DIDInfo]) -> DIDInfo: info = did did = info.did - if info.method != DIDMethod.SOV: + if info.method != SOV: raise WalletError("Setting public DID is only allowed for did:sov DIDs") public = await self.get_public_did() diff --git a/aries_cloudagent/wallet/indy.py b/aries_cloudagent/wallet/indy.py index 4e2406d04f..78ebfc0408 100644 --- a/aries_cloudagent/wallet/indy.py +++ b/aries_cloudagent/wallet/indy.py @@ -30,7 +30,7 @@ verify_signed_message, ) from .did_info import DIDInfo, KeyInfo -from .did_method import DIDMethod +from .did_method import SOV, KEY, DIDMethod from .error import WalletError, WalletDuplicateError, WalletNotFoundError from .key_pair import KeyPairStorageManager from .key_type import KeyType @@ -55,10 +55,10 @@ def __did_info_from_indy_info(self, info): did: str = info["did"] verkey = info["verkey"] - method = DIDMethod.KEY if did.startswith("did:key") else DIDMethod.SOV + method = KEY if did.startswith("did:key") else SOV key_type = KeyType.ED25519 - if method == DIDMethod.KEY: + if method == KEY: did = DIDKey.from_public_key_b58(info["verkey"], key_type).did return DIDInfo( @@ -73,7 +73,7 @@ def __did_info_from_key_pair_info(self, info: dict): method = DIDMethod.from_method(info["metadata"].get("method", "key")) key_type = KeyType.from_key_type(info["key_type"]) - if method == DIDMethod.KEY: + if method == KEY: did = DIDKey.from_public_key_b58(info["verkey"], key_type).did return DIDInfo( @@ -324,7 +324,7 @@ async def __create_indy_local_did( *, did: str = None, ) -> DIDInfo: - if method not in [DIDMethod.SOV, DIDMethod.KEY]: + if method not in [SOV, KEY]: raise WalletError( f"Unsupported DID method for indy storage: {method.method_name}" ) @@ -340,7 +340,7 @@ async def __create_indy_local_did( cfg["did"] = did # Create fully qualified did. This helps with determining the # did method when retrieving - if method != DIDMethod.SOV: + if method != SOV: cfg["method_name"] = method.method_name did_json = json.dumps(cfg) # crypto_type, cid - optional parameters skipped @@ -356,7 +356,7 @@ async def __create_indy_local_did( ) from x_indy # did key uses different format - if method == DIDMethod.KEY: + if method == KEY: did = DIDKey.from_public_key_b58(verkey, key_type).did await self.replace_local_did_metadata(did, metadata or {}) @@ -376,7 +376,7 @@ async def __create_keypair_local_did( metadata: dict = None, seed: str = None, ) -> DIDInfo: - if method != DIDMethod.KEY: + if method != KEY: raise WalletError( f"Unsupported DID method for keypair storage: {method.method_name}" ) @@ -444,7 +444,7 @@ async def create_local_did( f" for DID method {method.method_name}" ) - if method == DIDMethod.KEY and did: + if method == KEY and did: raise WalletError("Not allowed to set DID for DID method 'key'") # All ed25519 keys are handled by indy @@ -477,7 +477,7 @@ async def get_local_dids(self) -> Sequence[DIDInfo]: # this needs to change if more did methods are added key_pair_mgr = KeyPairStorageManager(IndySdkStorage(self.opened)) key_pairs = await key_pair_mgr.find_key_pairs( - tag_query={"method": DIDMethod.KEY.method_name} + tag_query={"method": KEY.method_name} ) for key_pair in key_pairs: ret.append(self.__did_info_from_key_pair_info(key_pair)) @@ -487,7 +487,7 @@ async def get_local_dids(self) -> Sequence[DIDInfo]: async def __get_indy_local_did( self, method: DIDMethod, key_type: KeyType, did: str ) -> DIDInfo: - if method not in [DIDMethod.SOV, DIDMethod.KEY]: + if method not in [SOV, KEY]: raise WalletError( f"Unsupported DID method for indy storage: {method.method_name}" ) @@ -497,7 +497,7 @@ async def __get_indy_local_did( ) # key type is always ed25519, method not always key - if method == DIDMethod.KEY and key_type == KeyType.ED25519: + if method == KEY and key_type == KeyType.ED25519: did_key = DIDKey.from_did(did) # Ed25519 did:keys are masked indy dids so transform to indy @@ -517,7 +517,7 @@ async def __get_indy_local_did( async def __get_keypair_local_did( self, method: DIDMethod, key_type: KeyType, did: str ): - if method != DIDMethod.KEY: + if method != KEY: raise WalletError( f"Unsupported DID method for keypair storage: {method.method_name}" ) @@ -552,7 +552,7 @@ async def get_local_did(self, did: str) -> DIDInfo: key_type = KeyType.ED25519 # If did key, the key type can differ - if method == DIDMethod.KEY: + if method == KEY: did_key = DIDKey.from_did(did) key_type = did_key.key_type @@ -679,7 +679,7 @@ async def set_public_did(self, did: Union[str, DIDInfo]) -> DIDInfo: else: info = did - if info.method != DIDMethod.SOV: + if info.method != SOV: raise WalletError("Setting public DID is only allowed for did:sov DIDs") public = await self.get_public_did() @@ -724,7 +724,7 @@ async def set_did_endpoint( 'endpoint' affects local wallet """ did_info = await self.get_local_did(did) - if did_info.method != DIDMethod.SOV: + if did_info.method != SOV: raise WalletError("Setting DID endpoint is only allowed for did:sov DIDs") metadata = {**did_info.metadata} diff --git a/aries_cloudagent/wallet/routes.py b/aries_cloudagent/wallet/routes.py index eb18163fcb..0783814167 100644 --- a/aries_cloudagent/wallet/routes.py +++ b/aries_cloudagent/wallet/routes.py @@ -38,7 +38,7 @@ from ..storage.error import StorageError, StorageNotFoundError from .base import BaseWallet from .did_info import DIDInfo -from .did_method import DIDMethod +from .did_method import SOV, KEY, DIDMethod from .did_posture import DIDPosture from .error import WalletError, WalletNotFoundError from .key_type import KeyType @@ -66,7 +66,7 @@ class DIDSchema(OpenAPISchema): ) method = fields.Str( description="Did method associated with the DID", - example=DIDMethod.SOV.method_name, + example=SOV.method_name, validate=validate.OneOf([method.method_name for method in DIDMethod]), ) key_type = fields.Str( @@ -136,8 +136,8 @@ class DIDListQueryStringSchema(OpenAPISchema): ) method = fields.Str( required=False, - example=DIDMethod.KEY.method_name, - validate=validate.OneOf([DIDMethod.KEY.method_name, DIDMethod.SOV.method_name]), + example=KEY.method_name, + validate=validate.OneOf([KEY.method_name, SOV.method_name]), description="DID method to query for. e.g. sov to only fetch indy/sov DIDs", ) key_type = fields.Str( @@ -173,9 +173,9 @@ class DIDCreateSchema(OpenAPISchema): method = fields.Str( required=False, - default=DIDMethod.SOV.method_name, - example=DIDMethod.SOV.method_name, - validate=validate.OneOf([DIDMethod.KEY.method_name, DIDMethod.SOV.method_name]), + default=SOV.method_name, + example=SOV.method_name, + validate=validate.OneOf([KEY.method_name, SOV.method_name]), ) options = fields.Nested( @@ -357,7 +357,7 @@ async def wallet_create_did(request: web.BaseRequest): KeyType.from_key_type(body.get("options", {}).get("key_type")) or KeyType.ED25519 ) - method = DIDMethod.from_method(body.get("method")) or DIDMethod.SOV + method = DIDMethod.from_method(body.get("method")) or SOV if not method.supports_key_type(key_type): raise web.HTTPForbidden( diff --git a/aries_cloudagent/wallet/tests/test_in_memory_wallet.py b/aries_cloudagent/wallet/tests/test_in_memory_wallet.py index 6554336908..09743e05d8 100644 --- a/aries_cloudagent/wallet/tests/test_in_memory_wallet.py +++ b/aries_cloudagent/wallet/tests/test_in_memory_wallet.py @@ -5,7 +5,7 @@ from ...messaging.decorators.signature_decorator import SignatureDecorator from ...wallet.in_memory import InMemoryWallet from ...wallet.key_type import KeyType -from ...wallet.did_method import DIDMethod +from ...wallet.did_method import SOV, KEY from ...wallet.error import ( WalletError, WalletDuplicateError, @@ -130,19 +130,19 @@ async def test_signing_key_metadata_bls(self, wallet: InMemoryWallet): @pytest.mark.asyncio async def test_create_local_sov_random(self, wallet: InMemoryWallet): - info = await wallet.create_local_did(DIDMethod.SOV, KeyType.ED25519, None, None) + info = await wallet.create_local_did(SOV, KeyType.ED25519, None, None) assert info and info.did and info.verkey @pytest.mark.asyncio async def test_create_local_key_random_ed25519(self, wallet: InMemoryWallet): - info = await wallet.create_local_did(DIDMethod.KEY, KeyType.ED25519, None, None) + info = await wallet.create_local_did(KEY, KeyType.ED25519, None, None) assert info and info.did and info.verkey @pytest.mark.asyncio @pytest.mark.ursa_bbs_signatures async def test_create_local_key_random_bls12381g2(self, wallet: InMemoryWallet): info = await wallet.create_local_did( - DIDMethod.KEY, KeyType.BLS12381G2, None, None + KEY, KeyType.BLS12381G2, None, None ) assert info and info.did and info.verkey @@ -151,61 +151,61 @@ async def test_create_local_incorrect_key_type_for_did_method( self, wallet: InMemoryWallet ): with pytest.raises(WalletError): - await wallet.create_local_did(DIDMethod.SOV, KeyType.BLS12381G2, None, None) + await wallet.create_local_did(SOV, KeyType.BLS12381G2, None, None) @pytest.mark.asyncio async def test_create_local_sov_seeded(self, wallet: InMemoryWallet): info = await wallet.create_local_did( - DIDMethod.SOV, KeyType.ED25519, self.test_seed, None + SOV, KeyType.ED25519, self.test_seed, None ) assert info.did == self.test_sov_did assert info.verkey == self.test_ed25519_verkey # should not raise WalletDuplicateError - same verkey await wallet.create_local_did( - DIDMethod.SOV, KeyType.ED25519, self.test_seed, None + SOV, KeyType.ED25519, self.test_seed, None ) with pytest.raises(WalletError): _ = await wallet.create_local_did( - DIDMethod.SOV, KeyType.ED25519, "invalid-seed", None + SOV, KeyType.ED25519, "invalid-seed", None ) @pytest.mark.asyncio @pytest.mark.ursa_bbs_signatures async def test_create_local_key_seeded_bls12381g2(self, wallet: InMemoryWallet): info = await wallet.create_local_did( - DIDMethod.KEY, KeyType.BLS12381G2, self.test_seed, None + KEY, KeyType.BLS12381G2, self.test_seed, None ) assert info.did == self.test_key_bls12381g2_did assert info.verkey == self.test_bls12381g2_verkey # should not raise WalletDuplicateError - same verkey await wallet.create_local_did( - DIDMethod.KEY, KeyType.BLS12381G2, self.test_seed, None + KEY, KeyType.BLS12381G2, self.test_seed, None ) with pytest.raises(WalletError): _ = await wallet.create_local_did( - DIDMethod.KEY, KeyType.BLS12381G2, "invalid-seed", None + KEY, KeyType.BLS12381G2, "invalid-seed", None ) @pytest.mark.asyncio async def test_create_local_key_seeded_ed25519(self, wallet: InMemoryWallet): info = await wallet.create_local_did( - DIDMethod.KEY, KeyType.ED25519, self.test_seed, None + KEY, KeyType.ED25519, self.test_seed, None ) assert info.did == self.test_key_ed25519_did assert info.verkey == self.test_ed25519_verkey # should not raise WalletDuplicateError - same verkey await wallet.create_local_did( - DIDMethod.KEY, KeyType.ED25519, self.test_seed, None + KEY, KeyType.ED25519, self.test_seed, None ) with pytest.raises(WalletError): _ = await wallet.create_local_did( - DIDMethod.KEY, KeyType.ED25519, "invalid-seed", None + KEY, KeyType.ED25519, "invalid-seed", None ) @pytest.mark.asyncio @@ -217,9 +217,9 @@ async def test_rotate_did_keypair(self, wallet: InMemoryWallet): await wallet.rotate_did_keypair_apply(self.test_sov_did) info = await wallet.create_local_did( - DIDMethod.SOV, KeyType.ED25519, self.test_seed, self.test_sov_did + SOV, KeyType.ED25519, self.test_seed, self.test_sov_did ) - key_info = await wallet.create_local_did(DIDMethod.KEY, KeyType.ED25519) + key_info = await wallet.create_local_did(KEY, KeyType.ED25519) with pytest.raises(WalletError): await wallet.rotate_did_keypair_apply(self.test_sov_did) @@ -239,25 +239,25 @@ async def test_rotate_did_keypair(self, wallet: InMemoryWallet): @pytest.mark.asyncio async def test_create_local_with_did(self, wallet: InMemoryWallet): info = await wallet.create_local_did( - DIDMethod.SOV, KeyType.ED25519, None, self.test_sov_did + SOV, KeyType.ED25519, None, self.test_sov_did ) assert info.did == self.test_sov_did with pytest.raises(WalletDuplicateError): await wallet.create_local_did( - DIDMethod.SOV, KeyType.ED25519, None, self.test_sov_did + SOV, KeyType.ED25519, None, self.test_sov_did ) with pytest.raises(WalletError) as context: await wallet.create_local_did( - DIDMethod.KEY, KeyType.ED25519, None, "did:sov:random" + KEY, KeyType.ED25519, None, "did:sov:random" ) assert "Not allowed to set DID for DID method 'key'" in str(context.value) @pytest.mark.asyncio async def test_local_verkey(self, wallet: InMemoryWallet): info = await wallet.create_local_did( - DIDMethod.SOV, KeyType.ED25519, self.test_seed, self.test_sov_did + SOV, KeyType.ED25519, self.test_seed, self.test_sov_did ) assert info.did == self.test_sov_did assert info.verkey == self.test_ed25519_verkey @@ -273,7 +273,7 @@ async def test_local_verkey(self, wallet: InMemoryWallet): @pytest.mark.asyncio @pytest.mark.ursa_bbs_signatures async def test_local_verkey_bls12381g2(self, wallet: InMemoryWallet): - await wallet.create_local_did(DIDMethod.KEY, KeyType.BLS12381G2, self.test_seed) + await wallet.create_local_did(KEY, KeyType.BLS12381G2, self.test_seed) bls_info_get = await wallet.get_local_did_for_verkey( self.test_bls12381g2_verkey ) @@ -288,7 +288,7 @@ async def test_local_verkey_bls12381g2(self, wallet: InMemoryWallet): @pytest.mark.asyncio async def test_local_metadata(self, wallet: InMemoryWallet): info = await wallet.create_local_did( - DIDMethod.SOV, + SOV, KeyType.ED25519, self.test_seed, self.test_sov_did, @@ -318,7 +318,7 @@ async def test_local_metadata(self, wallet: InMemoryWallet): @pytest.mark.ursa_bbs_signatures async def test_local_metadata_bbs(self, wallet: InMemoryWallet): info = await wallet.create_local_did( - DIDMethod.KEY, + KEY, KeyType.BLS12381G2, self.test_seed, None, @@ -338,7 +338,7 @@ async def test_local_metadata_bbs(self, wallet: InMemoryWallet): @pytest.mark.asyncio async def test_create_public_did(self, wallet: InMemoryWallet): info = await wallet.create_local_did( - DIDMethod.SOV, + SOV, KeyType.ED25519, self.test_seed, self.test_sov_did, @@ -350,7 +350,7 @@ async def test_create_public_did(self, wallet: InMemoryWallet): assert not posted info_public = await wallet.create_public_did( - DIDMethod.SOV, + SOV, KeyType.ED25519, ) assert info_public.metadata.get("posted") @@ -359,7 +359,7 @@ async def test_create_public_did(self, wallet: InMemoryWallet): # test replace info_replace = await wallet.create_public_did( - DIDMethod.SOV, + SOV, KeyType.ED25519, ) assert info_replace.metadata.get("posted") @@ -376,7 +376,7 @@ async def test_create_public_did(self, wallet: InMemoryWallet): async def test_create_public_did_x_not_sov(self, wallet: InMemoryWallet): with pytest.raises(WalletError) as context: await wallet.create_public_did( - DIDMethod.KEY, + KEY, KeyType.ED25519, ) assert "Setting public DID is only allowed for did:sov DIDs" in str( @@ -389,7 +389,7 @@ async def test_create_public_did_x_unsupported_key_type_method( ): with pytest.raises(WalletError) as context: await wallet.create_public_did( - DIDMethod.SOV, + SOV, KeyType.BLS12381G2, ) assert "Invalid key type" in str(context.value) @@ -397,7 +397,7 @@ async def test_create_public_did_x_unsupported_key_type_method( @pytest.mark.asyncio async def test_set_public_did(self, wallet: InMemoryWallet): info = await wallet.create_local_did( - DIDMethod.SOV, + SOV, KeyType.ED25519, self.test_seed, self.test_sov_did, @@ -413,7 +413,7 @@ async def test_set_public_did(self, wallet: InMemoryWallet): assert info_same.did == info.did assert info_same.metadata.get("posted") - info_new = await wallet.create_local_did(DIDMethod.SOV, KeyType.ED25519) + info_new = await wallet.create_local_did(SOV, KeyType.ED25519) assert info_new.did != info_same.did loc = await wallet.get_local_did(self.test_sov_did) @@ -429,7 +429,7 @@ async def test_set_public_did(self, wallet: InMemoryWallet): @pytest.mark.asyncio async def test_set_public_did_x_not_sov(self, wallet: InMemoryWallet): info = await wallet.create_local_did( - DIDMethod.KEY, + KEY, KeyType.ED25519, ) with pytest.raises(WalletError) as context: @@ -441,7 +441,7 @@ async def test_set_public_did_x_not_sov(self, wallet: InMemoryWallet): @pytest.mark.asyncio async def test_sign_verify(self, wallet: InMemoryWallet): info = await wallet.create_local_did( - DIDMethod.SOV, KeyType.ED25519, self.test_seed, self.test_sov_did + SOV, KeyType.ED25519, self.test_seed, self.test_sov_did ) message_bin = self.test_message.encode("ascii") signature = await wallet.sign_message(message_bin, info.verkey) @@ -493,7 +493,7 @@ async def test_sign_verify(self, wallet: InMemoryWallet): @pytest.mark.ursa_bbs_signatures async def test_sign_verify_bbs(self, wallet: InMemoryWallet): info = await wallet.create_local_did( - DIDMethod.KEY, KeyType.BLS12381G2, self.test_seed + KEY, KeyType.BLS12381G2, self.test_seed ) message_bin = self.test_message.encode("ascii") signature = await wallet.sign_message(message_bin, info.verkey) @@ -552,7 +552,7 @@ async def test_sign_verify_bbs(self, wallet: InMemoryWallet): @pytest.mark.asyncio async def test_pack_unpack(self, wallet: InMemoryWallet): await wallet.create_local_did( - DIDMethod.SOV, KeyType.ED25519, self.test_seed, self.test_sov_did + SOV, KeyType.ED25519, self.test_seed, self.test_sov_did ) packed_anon = await wallet.pack_message( @@ -568,7 +568,7 @@ async def test_pack_unpack(self, wallet: InMemoryWallet): assert "Message not provided" in str(excinfo.value) await wallet.create_local_did( - DIDMethod.SOV, KeyType.ED25519, self.test_target_seed, self.test_target_did + SOV, KeyType.ED25519, self.test_target_seed, self.test_target_did ) packed_auth = await wallet.pack_message( self.test_message, [self.test_target_verkey], self.test_ed25519_verkey @@ -600,7 +600,7 @@ async def test_signature_round_trip(self, wallet: InMemoryWallet): @pytest.mark.asyncio async def test_set_did_endpoint_x_not_sov(self, wallet: InMemoryWallet): info = await wallet.create_local_did( - DIDMethod.KEY, + KEY, KeyType.ED25519, ) with pytest.raises(WalletError) as context: diff --git a/aries_cloudagent/wallet/tests/test_indy_wallet.py b/aries_cloudagent/wallet/tests/test_indy_wallet.py index 6044a6a658..906ba2e44d 100644 --- a/aries_cloudagent/wallet/tests/test_indy_wallet.py +++ b/aries_cloudagent/wallet/tests/test_indy_wallet.py @@ -18,7 +18,7 @@ from ...indy.sdk.wallet_setup import IndyWalletConfig from ...ledger.endpoint_type import EndpointType from ...wallet.key_type import KeyType -from ...wallet.did_method import DIDMethod +from ...wallet.did_method import SOV from ...ledger.indy import IndySdkLedgerPool from .. import indy as test_module @@ -67,7 +67,7 @@ class TestIndySdkWallet(test_in_memory_wallet.TestInMemoryWallet): @pytest.mark.asyncio async def test_rotate_did_keypair_x(self, wallet: IndySdkWallet): info = await wallet.create_local_did( - DIDMethod.SOV, KeyType.ED25519, self.test_seed, self.test_sov_did + SOV, KeyType.ED25519, self.test_seed, self.test_sov_did ) with async_mock.patch.object( @@ -111,7 +111,7 @@ async def test_create_local_did_x(self, wallet: IndySdkWallet): test_module.ErrorCode.CommonIOError, {"message": "outlier"} ) with pytest.raises(test_module.WalletError) as excinfo: - await wallet.create_local_did(DIDMethod.SOV, KeyType.ED25519) + await wallet.create_local_did(SOV, KeyType.ED25519) assert "outlier" in str(excinfo.value) @pytest.mark.asyncio @@ -120,7 +120,7 @@ async def test_set_did_endpoint_ledger(self, wallet: IndySdkWallet): read_only=False, update_endpoint_for_did=async_mock.CoroutineMock() ) info_pub = await wallet.create_public_did( - DIDMethod.SOV, + SOV, KeyType.ED25519, ) await wallet.set_did_endpoint(info_pub.did, "https://example.com", mock_ledger) @@ -147,7 +147,7 @@ async def test_set_did_endpoint_ledger_with_routing_keys( mock_ledger = async_mock.MagicMock( read_only=False, update_endpoint_for_did=async_mock.CoroutineMock() ) - info_pub = await wallet.create_public_did(DIDMethod.SOV, KeyType.ED25519) + info_pub = await wallet.create_public_did(SOV, KeyType.ED25519) await wallet.set_did_endpoint( info_pub.did, "https://example.com", mock_ledger, routing_keys=routing_keys ) @@ -167,7 +167,7 @@ async def test_set_did_endpoint_readonly_ledger(self, wallet: IndySdkWallet): read_only=True, update_endpoint_for_did=async_mock.CoroutineMock() ) info_pub = await wallet.create_public_did( - DIDMethod.SOV, + SOV, KeyType.ED25519, ) await wallet.set_did_endpoint(info_pub.did, "https://example.com", mock_ledger) @@ -206,7 +206,7 @@ async def test_get_local_did_x(self, wallet: IndySdkWallet): @pytest.mark.asyncio async def test_replace_local_did_metadata_x(self, wallet: IndySdkWallet): info = await wallet.create_local_did( - DIDMethod.SOV, + SOV, KeyType.ED25519, self.test_seed, self.test_sov_did, @@ -283,13 +283,13 @@ async def test_compare_pack_unpack(self, in_memory_wallet, wallet: IndySdkWallet Ensure that python-based pack/unpack is compatible with indy-sdk implementation """ await in_memory_wallet.create_local_did( - DIDMethod.SOV, KeyType.ED25519, self.test_seed + SOV, KeyType.ED25519, self.test_seed ) py_packed = await in_memory_wallet.pack_message( self.test_message, [self.test_verkey], self.test_verkey ) - await wallet.create_local_did(DIDMethod.SOV, KeyType.ED25519, self.test_seed) + await wallet.create_local_did(SOV, KeyType.ED25519, self.test_seed) packed = await wallet.pack_message( self.test_message, [self.test_verkey], self.test_verkey ) @@ -820,7 +820,7 @@ async def test_postgres_wallet_works(self): opened = await postgres_wallet.create_wallet() wallet = IndySdkWallet(opened) - await wallet.create_local_did(DIDMethod.SOV, KeyType.ED25519, self.test_seed) + await wallet.create_local_did(SOV, KeyType.ED25519, self.test_seed) py_packed = await wallet.pack_message( self.test_message, [self.test_verkey], self.test_verkey ) @@ -862,7 +862,7 @@ async def test_postgres_wallet_scheme_works(self): assert "Wallet was not removed" in str(excinfo.value) wallet = IndySdkWallet(opened) - await wallet.create_local_did(DIDMethod.SOV, KeyType.ED25519, self.test_seed) + await wallet.create_local_did(SOV, KeyType.ED25519, self.test_seed) py_packed = await wallet.pack_message( self.test_message, [self.test_verkey], self.test_verkey ) @@ -899,7 +899,7 @@ async def test_postgres_wallet_scheme2_works(self): opened = await postgres_wallet.create_wallet() wallet = IndySdkWallet(opened) - await wallet.create_local_did(DIDMethod.SOV, KeyType.ED25519, self.test_seed) + await wallet.create_local_did(SOV, KeyType.ED25519, self.test_seed) py_packed = await wallet.pack_message( self.test_message, [self.test_verkey], self.test_verkey ) diff --git a/aries_cloudagent/wallet/tests/test_key_type.py b/aries_cloudagent/wallet/tests/test_key_type.py index 0dc025a099..0557e91fd4 100644 --- a/aries_cloudagent/wallet/tests/test_key_type.py +++ b/aries_cloudagent/wallet/tests/test_key_type.py @@ -1,7 +1,7 @@ from unittest import TestCase from ...core.error import BaseError -from ..did_method import DIDMethod +from ..did_method import SOV, KEY, DIDMethod from ..key_type import KeyType SOV_DID_METHOD_NAME = "sov" @@ -11,27 +11,27 @@ class TestDidMethod(TestCase): def test_from_metadata(self): - assert DIDMethod.from_metadata({"method": SOV_DID_METHOD_NAME}) == DIDMethod.SOV - assert DIDMethod.from_metadata({"method": KEY_DID_METHOD_NAME}) == DIDMethod.KEY + assert DIDMethod.from_metadata({"method": SOV_DID_METHOD_NAME}) == SOV + assert DIDMethod.from_metadata({"method": KEY_DID_METHOD_NAME}) == KEY # test backwards compat - assert DIDMethod.from_metadata({}) == DIDMethod.SOV + assert DIDMethod.from_metadata({}) == SOV def test_from_method(self): - assert DIDMethod.from_method(SOV_DID_METHOD_NAME) == DIDMethod.SOV - assert DIDMethod.from_method(KEY_DID_METHOD_NAME) == DIDMethod.KEY + assert DIDMethod.from_method(SOV_DID_METHOD_NAME) == SOV + assert DIDMethod.from_method(KEY_DID_METHOD_NAME) == KEY assert DIDMethod.from_method("random") == None def test_from_did(self): - assert DIDMethod.from_did(f"did:{SOV_DID_METHOD_NAME}:xxxx") == DIDMethod.SOV - assert DIDMethod.from_did(f"did:{KEY_DID_METHOD_NAME}:xxxx") == DIDMethod.KEY + assert DIDMethod.from_did(f"did:{SOV_DID_METHOD_NAME}:xxxx") == SOV + assert DIDMethod.from_did(f"did:{KEY_DID_METHOD_NAME}:xxxx") == KEY with self.assertRaises(BaseError) as context: DIDMethod.from_did("did:unknown:something") assert "Unsupported did method: unknown" in str(context.exception) def test_properties(self): - method = DIDMethod.SOV + method = SOV assert method.method_name == SOV_DID_METHOD_NAME assert method.supported_key_types == SOV_SUPPORTED_KEY_TYPES diff --git a/aries_cloudagent/wallet/tests/test_routes.py b/aries_cloudagent/wallet/tests/test_routes.py index c6adb4b5dc..e80a3a9b68 100644 --- a/aries_cloudagent/wallet/tests/test_routes.py +++ b/aries_cloudagent/wallet/tests/test_routes.py @@ -8,7 +8,7 @@ from ...core.in_memory import InMemoryProfile from ...ledger.base import BaseLedger from ...protocols.coordinate_mediation.v1_0.route_manager import RouteManager -from ...wallet.did_method import DIDMethod +from ...wallet.did_method import SOV from ...wallet.key_type import KeyType from ..base import BaseWallet from ..did_info import DIDInfo @@ -71,7 +71,7 @@ def test_format_did_info(self): self.test_did, self.test_verkey, DIDPosture.WALLET_ONLY.metadata, - DIDMethod.SOV, + SOV, KeyType.ED25519, ) result = test_module.format_did_info(did_info) @@ -85,7 +85,7 @@ def test_format_did_info(self): self.test_did, self.test_verkey, {"posted": True, "public": True}, - DIDMethod.SOV, + SOV, KeyType.ED25519, ) result = test_module.format_did_info(did_info) @@ -95,7 +95,7 @@ def test_format_did_info(self): self.test_did, self.test_verkey, {"posted": True, "public": False}, - DIDMethod.SOV, + SOV, KeyType.ED25519, ) result = test_module.format_did_info(did_info) @@ -109,7 +109,7 @@ async def test_create_did(self): self.test_did, self.test_verkey, DIDPosture.WALLET_ONLY.metadata, - DIDMethod.SOV, + SOV, KeyType.ED25519, ) result = await test_module.wallet_create_did(self.request) @@ -120,7 +120,7 @@ async def test_create_did(self): "verkey": self.test_verkey, "posture": DIDPosture.WALLET_ONLY.moniker, "key_type": KeyType.ED25519.key_type, - "method": DIDMethod.SOV.method_name, + "method": SOV.method_name, } } ) @@ -147,14 +147,14 @@ async def test_did_list(self): self.test_did, self.test_verkey, DIDPosture.WALLET_ONLY.metadata, - DIDMethod.SOV, + SOV, KeyType.ED25519, ), DIDInfo( self.test_posted_did, self.test_posted_verkey, DIDPosture.POSTED.metadata, - DIDMethod.SOV, + SOV, KeyType.ED25519, ), ] @@ -167,14 +167,14 @@ async def test_did_list(self): "verkey": self.test_posted_verkey, "posture": DIDPosture.POSTED.moniker, "key_type": KeyType.ED25519.key_type, - "method": DIDMethod.SOV.method_name, + "method": SOV.method_name, }, { "did": self.test_did, "verkey": self.test_verkey, "posture": DIDPosture.WALLET_ONLY.moniker, "key_type": KeyType.ED25519.key_type, - "method": DIDMethod.SOV.method_name, + "method": SOV.method_name, }, ] } @@ -191,7 +191,7 @@ async def test_did_list_filter_public(self): self.test_did, self.test_verkey, DIDPosture.PUBLIC.metadata, - DIDMethod.SOV, + SOV, KeyType.ED25519, ) self.wallet.get_posted_dids.return_value = [ @@ -199,7 +199,7 @@ async def test_did_list_filter_public(self): self.test_posted_did, self.test_posted_verkey, DIDPosture.POSTED.metadata, - DIDMethod.SOV, + SOV, KeyType.ED25519, ) ] @@ -212,7 +212,7 @@ async def test_did_list_filter_public(self): "verkey": self.test_verkey, "posture": DIDPosture.PUBLIC.moniker, "key_type": KeyType.ED25519.key_type, - "method": DIDMethod.SOV.method_name, + "method": SOV.method_name, } ] } @@ -229,7 +229,7 @@ async def test_did_list_filter_posted(self): self.test_did, self.test_verkey, DIDPosture.PUBLIC.metadata, - DIDMethod.SOV, + SOV, KeyType.ED25519, ) self.wallet.get_posted_dids.return_value = [ @@ -240,7 +240,7 @@ async def test_did_list_filter_posted(self): "posted": True, "public": False, }, - DIDMethod.SOV, + SOV, KeyType.ED25519, ) ] @@ -253,7 +253,7 @@ async def test_did_list_filter_posted(self): "verkey": self.test_posted_verkey, "posture": DIDPosture.POSTED.moniker, "key_type": KeyType.ED25519.key_type, - "method": DIDMethod.SOV.method_name, + "method": SOV.method_name, } ] } @@ -270,7 +270,7 @@ async def test_did_list_filter_did(self): self.test_did, self.test_verkey, DIDPosture.WALLET_ONLY.metadata, - DIDMethod.SOV, + SOV, KeyType.ED25519, ) result = await test_module.wallet_did_list(self.request) @@ -282,7 +282,7 @@ async def test_did_list_filter_did(self): "verkey": self.test_verkey, "posture": DIDPosture.WALLET_ONLY.moniker, "key_type": KeyType.ED25519.key_type, - "method": DIDMethod.SOV.method_name, + "method": SOV.method_name, } ] } @@ -310,7 +310,7 @@ async def test_did_list_filter_verkey(self): self.test_did, self.test_verkey, DIDPosture.WALLET_ONLY.metadata, - DIDMethod.SOV, + SOV, KeyType.ED25519, ) result = await test_module.wallet_did_list(self.request) @@ -322,7 +322,7 @@ async def test_did_list_filter_verkey(self): "verkey": self.test_verkey, "posture": DIDPosture.WALLET_ONLY.moniker, "key_type": KeyType.ED25519.key_type, - "method": DIDMethod.SOV.method_name, + "method": SOV.method_name, } ] } @@ -349,7 +349,7 @@ async def test_get_public_did(self): self.test_did, self.test_verkey, DIDPosture.PUBLIC.metadata, - DIDMethod.SOV, + SOV, KeyType.ED25519, ) result = await test_module.wallet_get_public_did(self.request) @@ -360,7 +360,7 @@ async def test_get_public_did(self): "verkey": self.test_verkey, "posture": DIDPosture.PUBLIC.moniker, "key_type": KeyType.ED25519.key_type, - "method": DIDMethod.SOV.method_name, + "method": SOV.method_name, } } ) @@ -396,7 +396,7 @@ async def test_set_public_did(self): self.test_did, self.test_verkey, DIDPosture.PUBLIC.metadata, - DIDMethod.SOV, + SOV, KeyType.ED25519, ) result = await test_module.wallet_set_public_did(self.request) @@ -408,7 +408,7 @@ async def test_set_public_did(self): "verkey": self.test_verkey, "posture": DIDPosture.PUBLIC.moniker, "key_type": KeyType.ED25519.key_type, - "method": DIDMethod.SOV.method_name, + "method": SOV.method_name, } } ) @@ -494,7 +494,7 @@ async def test_set_public_did_x(self): self.test_did, self.test_verkey, DIDPosture.PUBLIC.metadata, - DIDMethod.SOV, + SOV, KeyType.ED25519, ) self.wallet.set_public_did.side_effect = test_module.WalletError() @@ -525,7 +525,7 @@ async def test_set_public_did_no_wallet_did(self): self.test_did, self.test_verkey, DIDPosture.PUBLIC.metadata, - DIDMethod.SOV, + SOV, KeyType.ED25519, ) self.wallet.set_public_did.side_effect = test_module.WalletNotFoundError() @@ -557,7 +557,7 @@ async def test_set_public_did_update_endpoint(self): self.test_did, self.test_verkey, {**DIDPosture.PUBLIC.metadata, "endpoint": "https://endpoint.com"}, - DIDMethod.SOV, + SOV, KeyType.ED25519, ) result = await test_module.wallet_set_public_did(self.request) @@ -569,7 +569,7 @@ async def test_set_public_did_update_endpoint(self): "verkey": self.test_verkey, "posture": DIDPosture.PUBLIC.moniker, "key_type": KeyType.ED25519.key_type, - "method": DIDMethod.SOV.method_name, + "method": SOV.method_name, } } ) @@ -605,7 +605,7 @@ async def test_set_public_did_update_endpoint_use_default_update_in_wallet(self) self.test_did, self.test_verkey, DIDPosture.PUBLIC.metadata, - DIDMethod.SOV, + SOV, KeyType.ED25519, ) self.wallet.get_local_did.return_value = did_info @@ -627,7 +627,7 @@ async def test_set_public_did_update_endpoint_use_default_update_in_wallet(self) "verkey": self.test_verkey, "posture": DIDPosture.PUBLIC.moniker, "key_type": KeyType.ED25519.key_type, - "method": DIDMethod.SOV.method_name, + "method": SOV.method_name, } } ) @@ -651,14 +651,14 @@ async def test_set_did_endpoint(self): self.test_did, self.test_verkey, {"public": False, "endpoint": "http://old-endpoint.ca"}, - DIDMethod.SOV, + SOV, KeyType.ED25519, ) self.wallet.get_public_did.return_value = DIDInfo( self.test_did, self.test_verkey, DIDPosture.PUBLIC.metadata, - DIDMethod.SOV, + SOV, KeyType.ED25519, ) @@ -680,14 +680,14 @@ async def test_set_did_endpoint_public_did_no_ledger(self): self.test_did, self.test_verkey, {"public": False, "endpoint": "http://old-endpoint.ca"}, - DIDMethod.SOV, + SOV, KeyType.ED25519, ) self.wallet.get_public_did.return_value = DIDInfo( self.test_did, self.test_verkey, DIDPosture.PUBLIC.metadata, - DIDMethod.SOV, + SOV, KeyType.ED25519, ) self.wallet.set_did_endpoint.side_effect = test_module.LedgerConfigError() @@ -740,7 +740,7 @@ async def test_get_did_endpoint(self): self.test_did, self.test_verkey, {"public": False, "endpoint": "http://old-endpoint.ca"}, - DIDMethod.SOV, + SOV, KeyType.ED25519, ) @@ -788,7 +788,7 @@ async def test_rotate_did_keypair(self): "did", "verkey", {"public": False}, - DIDMethod.SOV, + SOV, KeyType.ED25519, ) ) @@ -823,7 +823,7 @@ async def test_rotate_did_keypair_did_not_local(self): "did", "verkey", {"posted": True, "public": True}, - DIDMethod.SOV, + SOV, KeyType.ED25519, ) ) @@ -838,7 +838,7 @@ async def test_rotate_did_keypair_x(self): "did", "verkey", {"public": False}, - DIDMethod.SOV, + SOV, KeyType.ED25519, ) ) From 709f0ab696648cbc05554d2f67766ff9088648d7 Mon Sep 17 00:00:00 2001 From: Adam Burdett Date: Wed, 21 Sep 2022 09:45:31 -0600 Subject: [PATCH 480/872] updated how did methods are consumed Signed-off-by: Adam Burdett --- aries_cloudagent/wallet/askar.py | 37 ++++++++++--------- aries_cloudagent/wallet/did_method.py | 5 ++- aries_cloudagent/wallet/in_memory.py | 6 +-- aries_cloudagent/wallet/indy.py | 14 ++++--- aries_cloudagent/wallet/routes.py | 22 ++++++----- .../wallet/tests/test_key_type.py | 33 ++++++++++------- 6 files changed, 68 insertions(+), 49 deletions(-) diff --git a/aries_cloudagent/wallet/askar.py b/aries_cloudagent/wallet/askar.py index 66ec452ff4..a3914d6376 100644 --- a/aries_cloudagent/wallet/askar.py +++ b/aries_cloudagent/wallet/askar.py @@ -31,7 +31,7 @@ validate_seed, verify_signed_message, ) -from .did_method import SOV, KEY, DIDMethod +from .did_method import SOV, KEY, DIDMethod, DIDMethods from .error import WalletError, WalletDuplicateError, WalletNotFoundError from .key_type import KeyType from .util import b58_to_bytes, bytes_to_b58 @@ -56,7 +56,7 @@ def __init__(self, session: AskarProfileSession): self._session = session @property - def session(self) -> Session: + def session(self) -> AskarProfileSession: """Accessor for Askar profile session instance.""" return self._session @@ -257,7 +257,7 @@ async def get_local_dids(self) -> Sequence[DIDInfo]: ret = [] for item in await self._session.handle.fetch_all(CATEGORY_DID): - ret.append(_load_did_entry(item)) + ret.append(self._load_did_entry(item)) return ret async def get_local_did(self, did: str) -> DIDInfo: @@ -284,7 +284,7 @@ async def get_local_did(self, did: str) -> DIDInfo: raise WalletError("Error when fetching local DID") from err if not did: raise WalletNotFoundError("Unknown DID: {}".format(did)) - return _load_did_entry(did) + return self._load_did_entry(did) async def get_local_did_for_verkey(self, verkey: str) -> DIDInfo: """ @@ -308,7 +308,7 @@ async def get_local_did_for_verkey(self, verkey: str) -> DIDInfo: except AskarError as err: raise WalletError("Error when fetching local DID for verkey") from err if dids: - return _load_did_entry(dids[0]) + return self._load_did_entry(dids[0]) raise WalletNotFoundError("No DID defined for verkey: {}".format(verkey)) async def replace_local_did_metadata(self, did: str, metadata: dict): @@ -403,7 +403,7 @@ async def set_public_did(self, did: Union[str, DIDInfo]) -> DIDInfo: raise WalletError("Error when fetching local DID") from err if not item: raise WalletNotFoundError("Unknown DID: {}".format(did)) - info = _load_did_entry(item) + info = self._load_did_entry(item) else: info = did item = None @@ -507,7 +507,8 @@ async def rotate_did_keypair_start(self, did: str, next_seed: str = None) -> str """ # Check if DID can rotate keys - did_method = DIDMethod.from_did(did) + did_methods = self._session.inject(DIDMethods) + did_method: DIDMethod = did_methods.from_did(did) if not did_method.supports_rotation: raise WalletError( f"DID method '{did_method.method_name}' does not support key rotation." @@ -727,6 +728,17 @@ async def unpack_message(self, enc_message: bytes) -> Tuple[str, str, str]: raise WalletError("Exception when unpacking message") from err return unpacked_json.decode("utf-8"), sender, recipient + def _load_did_entry(self, entry: Entry) -> DIDInfo: + """Convert a DID record into the expected DIDInfo format.""" + did_info = entry.value_json + did_methods: DIDMethods = self._session.inject(DIDMethods) + return DIDInfo( + did=did_info["did"], + verkey=did_info["verkey"], + metadata=did_info.get("metadata"), + method=did_methods.from_method(did_info.get("method", "sov")), + key_type=KeyType.from_key_type(did_info.get("verkey_type", "ed25519")), + ) def _create_keypair(key_type: KeyType, seed: Union[str, bytes] = None) -> Key: """Instantiate a new keypair with an optional seed value.""" @@ -757,13 +769,4 @@ def _create_keypair(key_type: KeyType, seed: Union[str, bytes] = None) -> Key: return Key.generate(alg) -def _load_did_entry(entry: Entry) -> DIDInfo: - """Convert a DID record into the expected DIDInfo format.""" - did_info = entry.value_json - return DIDInfo( - did=did_info["did"], - verkey=did_info["verkey"], - metadata=did_info.get("metadata"), - method=DIDMethod.from_method(did_info.get("method", "sov")), - key_type=KeyType.from_key_type(did_info.get("verkey_type", "ed25519")), - ) + diff --git a/aries_cloudagent/wallet/did_method.py b/aries_cloudagent/wallet/did_method.py index 87436a44d7..5b93666ea1 100644 --- a/aries_cloudagent/wallet/did_method.py +++ b/aries_cloudagent/wallet/did_method.py @@ -45,7 +45,10 @@ class DIDMethods: """DID Method class specifying DID methods with supported key types.""" def __init__(self) -> None: - self._registry: Dict[str, DIDMethod] = {} + self._registry: Dict[str, DIDMethod] = { + SOV.method_name: SOV, + KEY.method_name: KEY + } def registered(self, method: str) -> bool: """Check for a supported method.""" diff --git a/aries_cloudagent/wallet/in_memory.py b/aries_cloudagent/wallet/in_memory.py index adc3e7ba92..f9bb03a37f 100644 --- a/aries_cloudagent/wallet/in_memory.py +++ b/aries_cloudagent/wallet/in_memory.py @@ -17,7 +17,7 @@ ) from .did_info import KeyInfo, DIDInfo from .did_posture import DIDPosture -from .did_method import SOV, KEY, DIDMethod +from .did_method import SOV, KEY, DIDMethod, DIDMethods from .error import WalletError, WalletDuplicateError, WalletNotFoundError from .key_type import KeyType from .util import b58_to_bytes, bytes_to_b58, random_seed @@ -132,8 +132,8 @@ async def rotate_did_keypair_start(self, did: str, next_seed: str = None) -> str local_did = self.profile.local_dids.get(did) if not local_did: raise WalletNotFoundError("Wallet owns no such DID: {}".format(did)) - - did_method = DIDMethod.from_did(did) + did_methods: DIDMethods = self.profile.context.inject(DIDMethods) + did_method: DIDMethod = did_methods.from_did(did) if not did_method.supports_rotation: raise WalletError( f"DID method '{did_method.method_name}' does not support key rotation." diff --git a/aries_cloudagent/wallet/indy.py b/aries_cloudagent/wallet/indy.py index 78ebfc0408..9258199c76 100644 --- a/aries_cloudagent/wallet/indy.py +++ b/aries_cloudagent/wallet/indy.py @@ -48,7 +48,7 @@ class IndySdkWallet(BaseWallet): def __init__(self, opened: IndyOpenWallet): """Create a new IndySdkWallet instance.""" - self.opened = opened + self.opened: IndyOpenWallet = opened def __did_info_from_indy_info(self, info): metadata = json.loads(info["metadata"]) if info["metadata"] else {} @@ -69,8 +69,8 @@ def __did_info_from_key_pair_info(self, info: dict): metadata = info["metadata"] verkey = info["verkey"] - # this needs to change if other did methods are added - method = DIDMethod.from_method(info["metadata"].get("method", "key")) + # TODO: inject context to support did method registry + method = SOV if metadata.get("method", "key") == SOV.method_name else KEY key_type = KeyType.from_key_type(info["key_type"]) if method == KEY: @@ -270,7 +270,9 @@ async def rotate_did_keypair_start(self, did: str, next_seed: str = None) -> str """ # Check if DID can rotate keys - did_method = DIDMethod.from_did(did) + #TODO: inject context for did method registry support + method_name = did.split(":")[1] if did.startswith("did:") else SOV.method_name + did_method = SOV if method_name == SOV.method_name else KEY if not did_method.supports_rotation: raise WalletError( f"DID method '{did_method.method_name}' does not support key rotation." @@ -548,7 +550,9 @@ async def get_local_did(self, did: str) -> DIDInfo: WalletError: If there is a libindy error """ - method = DIDMethod.from_did(did) + #TODO: inject context for did method registry support + method_name = did.split(":")[1] if did.startswith("did:") else SOV.method_name + method = SOV if method_name == SOV.method_name else KEY key_type = KeyType.ED25519 # If did key, the key type can differ diff --git a/aries_cloudagent/wallet/routes.py b/aries_cloudagent/wallet/routes.py index 0783814167..04cad1bf76 100644 --- a/aries_cloudagent/wallet/routes.py +++ b/aries_cloudagent/wallet/routes.py @@ -38,7 +38,7 @@ from ..storage.error import StorageError, StorageNotFoundError from .base import BaseWallet from .did_info import DIDInfo -from .did_method import SOV, KEY, DIDMethod +from .did_method import SOV, KEY, DIDMethod, DIDMethods from .did_posture import DIDPosture from .error import WalletError, WalletNotFoundError from .key_type import KeyType @@ -244,11 +244,12 @@ async def wallet_did_list(request: web.BaseRequest): context: AdminRequestContext = request["context"] filter_did = request.query.get("did") filter_verkey = request.query.get("verkey") - filter_method = DIDMethod.from_method(request.query.get("method")) filter_posture = DIDPosture.get(request.query.get("posture")) filter_key_type = KeyType.from_key_type(request.query.get("key_type")) results = [] async with context.session() as session: + did_methods: DIDMethods = session.inject(DIDMethods) + filter_method: DIDMethod | None = did_methods.from_method(request.query.get("method")) wallet = session.inject_or(BaseWallet) if not wallet: raise web.HTTPForbidden(reason="No wallet available") @@ -357,20 +358,21 @@ async def wallet_create_did(request: web.BaseRequest): KeyType.from_key_type(body.get("options", {}).get("key_type")) or KeyType.ED25519 ) - method = DIDMethod.from_method(body.get("method")) or SOV - if not method.supports_key_type(key_type): - raise web.HTTPForbidden( - reason=( - f"method {method.method_name} does not" - f" support key type {key_type.key_type}" - ) - ) seed = body.get("seed") or None if seed and not context.settings.get("wallet.allow_insecure_seed"): raise web.HTTPBadRequest(reason="Seed support is not enabled") info = None async with context.session() as session: + did_methods = session.inject(DIDMethods) + method = did_methods.from_method(body.get("method", '')) or SOV + if not method.supports_key_type(key_type): + raise web.HTTPForbidden( + reason=( + f"method {method.method_name} does not" + f" support key type {key_type.key_type}" + ) + ) wallet = session.inject_or(BaseWallet) if not wallet: raise web.HTTPForbidden(reason="No wallet available") diff --git a/aries_cloudagent/wallet/tests/test_key_type.py b/aries_cloudagent/wallet/tests/test_key_type.py index 0557e91fd4..6778bae27f 100644 --- a/aries_cloudagent/wallet/tests/test_key_type.py +++ b/aries_cloudagent/wallet/tests/test_key_type.py @@ -1,7 +1,7 @@ from unittest import TestCase from ...core.error import BaseError -from ..did_method import SOV, KEY, DIDMethod +from ..did_method import SOV, KEY, DIDMethods from ..key_type import KeyType SOV_DID_METHOD_NAME = "sov" @@ -10,32 +10,39 @@ class TestDidMethod(TestCase): + """TestCases for did method""" + did_methods = DIDMethods() + def test_from_metadata(self): - assert DIDMethod.from_metadata({"method": SOV_DID_METHOD_NAME}) == SOV - assert DIDMethod.from_metadata({"method": KEY_DID_METHOD_NAME}) == KEY + """Testing 'from_metadata'""" + assert self.did_methods.from_metadata({"method": SOV_DID_METHOD_NAME}) == SOV + assert self.did_methods.from_metadata({"method": KEY_DID_METHOD_NAME}) == KEY # test backwards compat - assert DIDMethod.from_metadata({}) == SOV + assert self.did_methods.from_metadata({}) == SOV def test_from_method(self): - assert DIDMethod.from_method(SOV_DID_METHOD_NAME) == SOV - assert DIDMethod.from_method(KEY_DID_METHOD_NAME) == KEY - assert DIDMethod.from_method("random") == None + """Testing 'from_method'""" + assert self.did_methods.from_method(SOV_DID_METHOD_NAME) == SOV + assert self.did_methods.from_method(KEY_DID_METHOD_NAME) == KEY + assert self.did_methods.from_method("random") is None def test_from_did(self): - assert DIDMethod.from_did(f"did:{SOV_DID_METHOD_NAME}:xxxx") == SOV - assert DIDMethod.from_did(f"did:{KEY_DID_METHOD_NAME}:xxxx") == KEY + """Testing 'from_did'""" + assert self.did_methods.from_did(f"did:{SOV_DID_METHOD_NAME}:xxxx") == SOV + assert self.did_methods.from_did(f"did:{KEY_DID_METHOD_NAME}:xxxx") == KEY with self.assertRaises(BaseError) as context: - DIDMethod.from_did("did:unknown:something") + self.did_methods.from_did("did:unknown:something") assert "Unsupported did method: unknown" in str(context.exception) def test_properties(self): + """Testing 'properties'""" method = SOV assert method.method_name == SOV_DID_METHOD_NAME assert method.supported_key_types == SOV_SUPPORTED_KEY_TYPES - assert method.supports_rotation == True + assert method.supports_rotation is True - assert method.supports_key_type(KeyType.ED25519) == True - assert method.supports_key_type(KeyType.BLS12381G2) == False + assert method.supports_key_type(KeyType.ED25519) is True + assert method.supports_key_type(KeyType.BLS12381G2) is False From 52407cad9b560badb5bd7adb536a386c43f4ff13 Mon Sep 17 00:00:00 2001 From: Adam Burdett Date: Wed, 21 Sep 2022 09:46:13 -0600 Subject: [PATCH 481/872] formatting Signed-off-by: Adam Burdett --- .../decorators/tests/test_attach_decorator.py | 7 ++-- .../protocols/connections/v1_0/manager.py | 4 +-- aries_cloudagent/wallet/askar.py | 4 +-- aries_cloudagent/wallet/did_method.py | 2 +- aries_cloudagent/wallet/indy.py | 4 +-- aries_cloudagent/wallet/routes.py | 6 ++-- .../wallet/tests/test_in_memory_wallet.py | 36 +++++-------------- .../wallet/tests/test_indy_wallet.py | 4 +-- .../wallet/tests/test_key_type.py | 1 + 9 files changed, 22 insertions(+), 46 deletions(-) diff --git a/aries_cloudagent/messaging/decorators/tests/test_attach_decorator.py b/aries_cloudagent/messaging/decorators/tests/test_attach_decorator.py index 7a74698212..170579d9f5 100644 --- a/aries_cloudagent/messaging/decorators/tests/test_attach_decorator.py +++ b/aries_cloudagent/messaging/decorators/tests/test_attach_decorator.py @@ -433,9 +433,7 @@ def test_data_json_external_mutation(self): class TestAttachDecoratorSignature: @pytest.mark.asyncio async def test_did_raw_key(self, wallet, seed): - did_info = await wallet.create_local_did( - SOV, KeyType.ED25519, seed[0] - ) + did_info = await wallet.create_local_did(SOV, KeyType.ED25519, seed[0]) did_key0 = did_key(did_info.verkey) raw_key0 = raw_key(did_key0) assert raw_key0 != did_key0 @@ -457,8 +455,7 @@ async def test_indy_sign(self, wallet, seed): ) deco_indy_master = deepcopy(deco_indy) did_info = [ - await wallet.create_local_did(SOV, KeyType.ED25519, seed[i]) - for i in [0, 1] + await wallet.create_local_did(SOV, KeyType.ED25519, seed[i]) for i in [0, 1] ] assert deco_indy.data.signatures == 0 assert deco_indy.data.header_map() is None diff --git a/aries_cloudagent/protocols/connections/v1_0/manager.py b/aries_cloudagent/protocols/connections/v1_0/manager.py index 9bf066140d..d7418b79dc 100644 --- a/aries_cloudagent/protocols/connections/v1_0/manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/manager.py @@ -467,9 +467,7 @@ async def receive_request( if connection.is_multiuse_invitation: async with self.profile.session() as session: wallet = session.inject(BaseWallet) - my_info = await wallet.create_local_did( - SOV, KeyType.ED25519 - ) + my_info = await wallet.create_local_did(SOV, KeyType.ED25519) new_connection = ConnRecord( invitation_key=connection_key, diff --git a/aries_cloudagent/wallet/askar.py b/aries_cloudagent/wallet/askar.py index a3914d6376..93fc4ef7b4 100644 --- a/aries_cloudagent/wallet/askar.py +++ b/aries_cloudagent/wallet/askar.py @@ -740,6 +740,7 @@ def _load_did_entry(self, entry: Entry) -> DIDInfo: key_type=KeyType.from_key_type(did_info.get("verkey_type", "ed25519")), ) + def _create_keypair(key_type: KeyType, seed: Union[str, bytes] = None) -> Key: """Instantiate a new keypair with an optional seed value.""" if key_type == KeyType.ED25519: @@ -767,6 +768,3 @@ def _create_keypair(key_type: KeyType, seed: Union[str, bytes] = None) -> Key: raise WalletError("Invalid seed for key generation") from None else: return Key.generate(alg) - - - diff --git a/aries_cloudagent/wallet/did_method.py b/aries_cloudagent/wallet/did_method.py index 5b93666ea1..811be53d6b 100644 --- a/aries_cloudagent/wallet/did_method.py +++ b/aries_cloudagent/wallet/did_method.py @@ -47,7 +47,7 @@ class DIDMethods: def __init__(self) -> None: self._registry: Dict[str, DIDMethod] = { SOV.method_name: SOV, - KEY.method_name: KEY + KEY.method_name: KEY, } def registered(self, method: str) -> bool: diff --git a/aries_cloudagent/wallet/indy.py b/aries_cloudagent/wallet/indy.py index 9258199c76..46a5ad8ac6 100644 --- a/aries_cloudagent/wallet/indy.py +++ b/aries_cloudagent/wallet/indy.py @@ -270,7 +270,7 @@ async def rotate_did_keypair_start(self, did: str, next_seed: str = None) -> str """ # Check if DID can rotate keys - #TODO: inject context for did method registry support + # TODO: inject context for did method registry support method_name = did.split(":")[1] if did.startswith("did:") else SOV.method_name did_method = SOV if method_name == SOV.method_name else KEY if not did_method.supports_rotation: @@ -550,7 +550,7 @@ async def get_local_did(self, did: str) -> DIDInfo: WalletError: If there is a libindy error """ - #TODO: inject context for did method registry support + # TODO: inject context for did method registry support method_name = did.split(":")[1] if did.startswith("did:") else SOV.method_name method = SOV if method_name == SOV.method_name else KEY key_type = KeyType.ED25519 diff --git a/aries_cloudagent/wallet/routes.py b/aries_cloudagent/wallet/routes.py index 04cad1bf76..64274165d7 100644 --- a/aries_cloudagent/wallet/routes.py +++ b/aries_cloudagent/wallet/routes.py @@ -249,7 +249,9 @@ async def wallet_did_list(request: web.BaseRequest): results = [] async with context.session() as session: did_methods: DIDMethods = session.inject(DIDMethods) - filter_method: DIDMethod | None = did_methods.from_method(request.query.get("method")) + filter_method: DIDMethod | None = did_methods.from_method( + request.query.get("method") + ) wallet = session.inject_or(BaseWallet) if not wallet: raise web.HTTPForbidden(reason="No wallet available") @@ -365,7 +367,7 @@ async def wallet_create_did(request: web.BaseRequest): info = None async with context.session() as session: did_methods = session.inject(DIDMethods) - method = did_methods.from_method(body.get("method", '')) or SOV + method = did_methods.from_method(body.get("method", "")) or SOV if not method.supports_key_type(key_type): raise web.HTTPForbidden( reason=( diff --git a/aries_cloudagent/wallet/tests/test_in_memory_wallet.py b/aries_cloudagent/wallet/tests/test_in_memory_wallet.py index 09743e05d8..c843296d63 100644 --- a/aries_cloudagent/wallet/tests/test_in_memory_wallet.py +++ b/aries_cloudagent/wallet/tests/test_in_memory_wallet.py @@ -141,9 +141,7 @@ async def test_create_local_key_random_ed25519(self, wallet: InMemoryWallet): @pytest.mark.asyncio @pytest.mark.ursa_bbs_signatures async def test_create_local_key_random_bls12381g2(self, wallet: InMemoryWallet): - info = await wallet.create_local_did( - KEY, KeyType.BLS12381G2, None, None - ) + info = await wallet.create_local_did(KEY, KeyType.BLS12381G2, None, None) assert info and info.did and info.verkey @pytest.mark.asyncio @@ -155,16 +153,12 @@ async def test_create_local_incorrect_key_type_for_did_method( @pytest.mark.asyncio async def test_create_local_sov_seeded(self, wallet: InMemoryWallet): - info = await wallet.create_local_did( - SOV, KeyType.ED25519, self.test_seed, None - ) + info = await wallet.create_local_did(SOV, KeyType.ED25519, self.test_seed, None) assert info.did == self.test_sov_did assert info.verkey == self.test_ed25519_verkey # should not raise WalletDuplicateError - same verkey - await wallet.create_local_did( - SOV, KeyType.ED25519, self.test_seed, None - ) + await wallet.create_local_did(SOV, KeyType.ED25519, self.test_seed, None) with pytest.raises(WalletError): _ = await wallet.create_local_did( @@ -181,9 +175,7 @@ async def test_create_local_key_seeded_bls12381g2(self, wallet: InMemoryWallet): assert info.verkey == self.test_bls12381g2_verkey # should not raise WalletDuplicateError - same verkey - await wallet.create_local_did( - KEY, KeyType.BLS12381G2, self.test_seed, None - ) + await wallet.create_local_did(KEY, KeyType.BLS12381G2, self.test_seed, None) with pytest.raises(WalletError): _ = await wallet.create_local_did( @@ -192,16 +184,12 @@ async def test_create_local_key_seeded_bls12381g2(self, wallet: InMemoryWallet): @pytest.mark.asyncio async def test_create_local_key_seeded_ed25519(self, wallet: InMemoryWallet): - info = await wallet.create_local_did( - KEY, KeyType.ED25519, self.test_seed, None - ) + info = await wallet.create_local_did(KEY, KeyType.ED25519, self.test_seed, None) assert info.did == self.test_key_ed25519_did assert info.verkey == self.test_ed25519_verkey # should not raise WalletDuplicateError - same verkey - await wallet.create_local_did( - KEY, KeyType.ED25519, self.test_seed, None - ) + await wallet.create_local_did(KEY, KeyType.ED25519, self.test_seed, None) with pytest.raises(WalletError): _ = await wallet.create_local_did( @@ -244,14 +232,10 @@ async def test_create_local_with_did(self, wallet: InMemoryWallet): assert info.did == self.test_sov_did with pytest.raises(WalletDuplicateError): - await wallet.create_local_did( - SOV, KeyType.ED25519, None, self.test_sov_did - ) + await wallet.create_local_did(SOV, KeyType.ED25519, None, self.test_sov_did) with pytest.raises(WalletError) as context: - await wallet.create_local_did( - KEY, KeyType.ED25519, None, "did:sov:random" - ) + await wallet.create_local_did(KEY, KeyType.ED25519, None, "did:sov:random") assert "Not allowed to set DID for DID method 'key'" in str(context.value) @pytest.mark.asyncio @@ -492,9 +476,7 @@ async def test_sign_verify(self, wallet: InMemoryWallet): @pytest.mark.asyncio @pytest.mark.ursa_bbs_signatures async def test_sign_verify_bbs(self, wallet: InMemoryWallet): - info = await wallet.create_local_did( - KEY, KeyType.BLS12381G2, self.test_seed - ) + info = await wallet.create_local_did(KEY, KeyType.BLS12381G2, self.test_seed) message_bin = self.test_message.encode("ascii") signature = await wallet.sign_message(message_bin, info.verkey) assert signature diff --git a/aries_cloudagent/wallet/tests/test_indy_wallet.py b/aries_cloudagent/wallet/tests/test_indy_wallet.py index 906ba2e44d..b7de92ec88 100644 --- a/aries_cloudagent/wallet/tests/test_indy_wallet.py +++ b/aries_cloudagent/wallet/tests/test_indy_wallet.py @@ -282,9 +282,7 @@ async def test_compare_pack_unpack(self, in_memory_wallet, wallet: IndySdkWallet """ Ensure that python-based pack/unpack is compatible with indy-sdk implementation """ - await in_memory_wallet.create_local_did( - SOV, KeyType.ED25519, self.test_seed - ) + await in_memory_wallet.create_local_did(SOV, KeyType.ED25519, self.test_seed) py_packed = await in_memory_wallet.pack_message( self.test_message, [self.test_verkey], self.test_verkey ) diff --git a/aries_cloudagent/wallet/tests/test_key_type.py b/aries_cloudagent/wallet/tests/test_key_type.py index 6778bae27f..9b99d90a21 100644 --- a/aries_cloudagent/wallet/tests/test_key_type.py +++ b/aries_cloudagent/wallet/tests/test_key_type.py @@ -11,6 +11,7 @@ class TestDidMethod(TestCase): """TestCases for did method""" + did_methods = DIDMethods() def test_from_metadata(self): From 57777c3eb769da747652f843351a5073106fdfb8 Mon Sep 17 00:00:00 2001 From: Adam Burdett Date: Wed, 21 Sep 2022 14:08:48 -0600 Subject: [PATCH 482/872] fix tests Signed-off-by: Adam Burdett --- aries_cloudagent/config/default_context.py | 18 +++++++++--------- aries_cloudagent/ledger/tests/test_indy_vdr.py | 3 ++- aries_cloudagent/wallet/did_method.py | 6 ++++-- aries_cloudagent/wallet/routes.py | 4 +++- .../wallet/tests/test_in_memory_wallet.py | 3 ++- aries_cloudagent/wallet/tests/test_routes.py | 3 ++- 6 files changed, 22 insertions(+), 15 deletions(-) diff --git a/aries_cloudagent/config/default_context.py b/aries_cloudagent/config/default_context.py index 0c5f90cac2..a2d013c5da 100644 --- a/aries_cloudagent/config/default_context.py +++ b/aries_cloudagent/config/default_context.py @@ -1,27 +1,26 @@ """Classes for configuring the default injection context.""" -from .base_context import ContextBuilder -from .injection_context import InjectionContext -from .provider import CachedProvider, ClassProvider - from ..cache.base import BaseCache from ..cache.in_memory import InMemoryCache from ..core.event_bus import EventBus +from ..core.goal_code_registry import GoalCodeRegistry from ..core.plugin_registry import PluginRegistry from ..core.profile import ProfileManager, ProfileManagerProvider from ..core.protocol_registry import ProtocolRegistry -from ..core.goal_code_registry import GoalCodeRegistry -from ..resolver.did_resolver import DIDResolver -from ..tails.base import BaseTailsServer - from ..protocols.actionmenu.v1_0.base_service import BaseMenuService from ..protocols.actionmenu.v1_0.driver_service import DriverMenuService from ..protocols.didcomm_prefix import DIDCommPrefix from ..protocols.introduction.v0_1.base_service import BaseIntroductionService from ..protocols.introduction.v0_1.demo_service import DemoIntroductionService +from ..resolver.did_resolver import DIDResolver +from ..tails.base import BaseTailsServer from ..transport.wire_format import BaseWireFormat -from ..utils.stats import Collector from ..utils.dependencies import is_indy_sdk_module_installed +from ..utils.stats import Collector +from ..wallet.did_method import DIDMethods +from .base_context import ContextBuilder +from .injection_context import InjectionContext +from .provider import CachedProvider, ClassProvider class DefaultContextBuilder(ContextBuilder): @@ -51,6 +50,7 @@ async def build_context(self) -> InjectionContext: # Global did resolver context.injector.bind_instance(DIDResolver, DIDResolver([])) + context.injector.bind_instance(DIDMethods, DIDMethods()) await self.bind_providers(context) await self.load_plugins(context) diff --git a/aries_cloudagent/ledger/tests/test_indy_vdr.py b/aries_cloudagent/ledger/tests/test_indy_vdr.py index 0d39e4daf6..8605e36d74 100644 --- a/aries_cloudagent/ledger/tests/test_indy_vdr.py +++ b/aries_cloudagent/ledger/tests/test_indy_vdr.py @@ -10,7 +10,7 @@ from ...indy.issuer import IndyIssuer from ...wallet.base import BaseWallet from ...wallet.key_type import KeyType -from ...wallet.did_method import SOV +from ...wallet.did_method import SOV, DIDMethods from ...wallet.did_info import DIDInfo from ..endpoint_type import EndpointType @@ -980,4 +980,5 @@ async def test_rotate_did_keypair(self, ledger: IndyVdrLedger): ] ), ): + ledger.profile.context.injector.bind_instance(DIDMethods,DIDMethods()) await ledger.rotate_public_did_keypair() diff --git a/aries_cloudagent/wallet/did_method.py b/aries_cloudagent/wallet/did_method.py index 811be53d6b..685b66d871 100644 --- a/aries_cloudagent/wallet/did_method.py +++ b/aries_cloudagent/wallet/did_method.py @@ -1,4 +1,4 @@ -"""Did method classes.""" +"""Did method registry classes.""" from typing import Dict, List, Mapping, Optional from .error import BaseError @@ -9,6 +9,7 @@ class DIDMethod: """Class to represent a did method.""" def __init__(self, name, key_types, rotation) -> None: + """Constructor for did method class.""" self._method_name: str = name self._supported_key_types: List[KeyType] = key_types self._supports_rotation: bool = rotation @@ -45,6 +46,7 @@ class DIDMethods: """DID Method class specifying DID methods with supported key types.""" def __init__(self) -> None: + """Constructor for did method registry.""" self._registry: Dict[str, DIDMethod] = { SOV.method_name: SOV, KEY.method_name: KEY, @@ -60,7 +62,7 @@ def register(self, method: DIDMethod): def from_method(self, method_name: str) -> Optional[DIDMethod]: """Retrieve a did method from method name.""" - return self._registry[method_name] + return self._registry.get(method_name) def from_metadata(self, metadata: Mapping) -> Optional[DIDMethod]: """Get DID method instance from metadata object. diff --git a/aries_cloudagent/wallet/routes.py b/aries_cloudagent/wallet/routes.py index 64274165d7..2340833299 100644 --- a/aries_cloudagent/wallet/routes.py +++ b/aries_cloudagent/wallet/routes.py @@ -67,7 +67,9 @@ class DIDSchema(OpenAPISchema): method = fields.Str( description="Did method associated with the DID", example=SOV.method_name, - validate=validate.OneOf([method.method_name for method in DIDMethod]), + validate=validate.OneOf( + [method.method_name for method in [SOV, KEY]] + ), # TODO: support more methods ) key_type = fields.Str( description="Key type associated with the DID", diff --git a/aries_cloudagent/wallet/tests/test_in_memory_wallet.py b/aries_cloudagent/wallet/tests/test_in_memory_wallet.py index c843296d63..8181fdcff8 100644 --- a/aries_cloudagent/wallet/tests/test_in_memory_wallet.py +++ b/aries_cloudagent/wallet/tests/test_in_memory_wallet.py @@ -5,7 +5,7 @@ from ...messaging.decorators.signature_decorator import SignatureDecorator from ...wallet.in_memory import InMemoryWallet from ...wallet.key_type import KeyType -from ...wallet.did_method import SOV, KEY +from ...wallet.did_method import SOV, KEY, DIDMethods from ...wallet.error import ( WalletError, WalletDuplicateError, @@ -198,6 +198,7 @@ async def test_create_local_key_seeded_ed25519(self, wallet: InMemoryWallet): @pytest.mark.asyncio async def test_rotate_did_keypair(self, wallet: InMemoryWallet): + wallet.profile.context.injector.bind_instance(DIDMethods, DIDMethods()) with pytest.raises(WalletNotFoundError): await wallet.rotate_did_keypair_start(self.test_sov_did) diff --git a/aries_cloudagent/wallet/tests/test_routes.py b/aries_cloudagent/wallet/tests/test_routes.py index e80a3a9b68..e690e30761 100644 --- a/aries_cloudagent/wallet/tests/test_routes.py +++ b/aries_cloudagent/wallet/tests/test_routes.py @@ -8,7 +8,7 @@ from ...core.in_memory import InMemoryProfile from ...ledger.base import BaseLedger from ...protocols.coordinate_mediation.v1_0.route_manager import RouteManager -from ...wallet.did_method import SOV +from ...wallet.did_method import SOV, DIDMethods from ...wallet.key_type import KeyType from ..base import BaseWallet from ..did_info import DIDInfo @@ -38,6 +38,7 @@ def setUp(self): self.test_verkey = "verkey" self.test_posted_did = "posted-did" self.test_posted_verkey = "posted-verkey" + self.context.injector.bind_instance(DIDMethods, DIDMethods()) async def test_missing_wallet(self): self.session_inject[BaseWallet] = None From d12fb60085b4c93eee481dae93ebb6e96aae3bb0 Mon Sep 17 00:00:00 2001 From: Adam Burdett Date: Wed, 21 Sep 2022 14:53:48 -0600 Subject: [PATCH 483/872] imperative mood to pass flake8 Signed-off-by: Adam Burdett --- aries_cloudagent/ledger/tests/test_indy_vdr.py | 2 +- aries_cloudagent/wallet/askar.py | 1 - aries_cloudagent/wallet/did_method.py | 8 ++++---- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/aries_cloudagent/ledger/tests/test_indy_vdr.py b/aries_cloudagent/ledger/tests/test_indy_vdr.py index 8605e36d74..6df896c7b3 100644 --- a/aries_cloudagent/ledger/tests/test_indy_vdr.py +++ b/aries_cloudagent/ledger/tests/test_indy_vdr.py @@ -980,5 +980,5 @@ async def test_rotate_did_keypair(self, ledger: IndyVdrLedger): ] ), ): - ledger.profile.context.injector.bind_instance(DIDMethods,DIDMethods()) + ledger.profile.context.injector.bind_instance(DIDMethods, DIDMethods()) await ledger.rotate_public_did_keypair() diff --git a/aries_cloudagent/wallet/askar.py b/aries_cloudagent/wallet/askar.py index 93fc4ef7b4..ae5f7c76be 100644 --- a/aries_cloudagent/wallet/askar.py +++ b/aries_cloudagent/wallet/askar.py @@ -13,7 +13,6 @@ Key, KeyAlg, SeedMethod, - Session, ) from ..askar.didcomm.v1 import pack_message, unpack_message diff --git a/aries_cloudagent/wallet/did_method.py b/aries_cloudagent/wallet/did_method.py index 685b66d871..2abd883e2c 100644 --- a/aries_cloudagent/wallet/did_method.py +++ b/aries_cloudagent/wallet/did_method.py @@ -1,4 +1,4 @@ -"""Did method registry classes.""" +"""did method.py contains registry for did methods.""" from typing import Dict, List, Mapping, Optional from .error import BaseError @@ -9,7 +9,7 @@ class DIDMethod: """Class to represent a did method.""" def __init__(self, name, key_types, rotation) -> None: - """Constructor for did method class.""" + """Construct did method class.""" self._method_name: str = name self._supported_key_types: List[KeyType] = key_types self._supports_rotation: bool = rotation @@ -46,7 +46,7 @@ class DIDMethods: """DID Method class specifying DID methods with supported key types.""" def __init__(self) -> None: - """Constructor for did method registry.""" + """Construct did method registry.""" self._registry: Dict[str, DIDMethod] = { SOV.method_name: SOV, KEY.method_name: KEY, @@ -57,7 +57,7 @@ def registered(self, method: str) -> bool: return method in list(self._registry.items()) def register(self, method: DIDMethod): - """Registers a new did method.""" + """Register a new did method.""" self._registry[method.method_name] = method def from_method(self, method_name: str) -> Optional[DIDMethod]: From bd2d9385bf775dfbddf25e3173f612106bb93bd7 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Mon, 12 Sep 2022 11:54:56 -0700 Subject: [PATCH 484/872] updates to protocol_registry Signed-off-by: Shaanjot Gill --- aries_cloudagent/core/protocol_registry.py | 37 +++++++++++ .../core/tests/test_protocol_registry.py | 65 +++++++++++++++++++ .../protocols/out_of_band/definition.py | 4 +- 3 files changed, 104 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/core/protocol_registry.py b/aries_cloudagent/core/protocol_registry.py index 805c35efa7..95e2105b7b 100644 --- a/aries_cloudagent/core/protocol_registry.py +++ b/aries_cloudagent/core/protocol_registry.py @@ -1,6 +1,7 @@ """Handle registration and publication of supported protocols.""" import logging +import re from typing import Mapping, Sequence @@ -74,6 +75,36 @@ def parse_type_string(self, message_type): "minor_version": int(version_string_tokens[1]), } + def create_msg_types_for_minor_version(self, typesets, version_definition): + """ + Return a mapping of message type string [missing] with minor versions to module path. + + Args: + typesets: Mappings of message types to register + version_definition: Optional version definition dict + + Returns: + Typesets mapping + + """ + updated_typeset = None + curr_minor_version = version_definition["current_minor_version"] + min_minor_version = version_definition["minimum_minor_version"] + major_version = version_definition["major_version"] + if curr_minor_version >= min_minor_version and curr_minor_version >= 1: + for version_index in range(min_minor_version, curr_minor_version + 1): + to_check = f"/{str(major_version)}.{str(version_index)}/" + for typeset in typesets: + for msg_type_string, module_path in typeset.items(): + if to_check not in msg_type_string: + updated_msg_type_string = re.sub( + "(\/\d+\.)?(\*|\d+\/)", to_check, msg_type_string + ) + if not updated_typeset: + updated_typeset = {} + updated_typeset[updated_msg_type_string] = module_path + return updated_typeset + def register_message_types(self, *typesets, version_definition=None): """ Add new supported message types. @@ -90,6 +121,12 @@ def register_message_types(self, *typesets, version_definition=None): # Track versioned modules for version routing if version_definition: + # create updated typesets for minor versions and register them + updated_typeset = self.create_msg_types_for_minor_version( + typesets, version_definition + ) + if updated_typeset: + self._typemap.update(updated_typeset) for typeset in typesets: for message_type_string, module_path in typeset.items(): parsed_type_string = self.parse_type_string(message_type_string) diff --git a/aries_cloudagent/core/tests/test_protocol_registry.py b/aries_cloudagent/core/tests/test_protocol_registry.py index 5c43668d8b..3cd093f4c5 100644 --- a/aries_cloudagent/core/tests/test_protocol_registry.py +++ b/aries_cloudagent/core/tests/test_protocol_registry.py @@ -44,6 +44,71 @@ def test_message_type_query(self): matches = self.registry.protocols_matching_query(q) assert matches == () + def test_create_msg_types_for_minor_version(self): + test_typesets = { + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/0.1/forward-invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.forward_invitation.ForwardInvitation", + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/0.1/invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation.Invitation", + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/0.1/invitation-request": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation_request.InvitationRequest", + "https://didcom.org/introduction-service/0.1/forward-invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.forward_invitation.ForwardInvitation", + "https://didcom.org/introduction-service/0.1/invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation.Invitation", + "https://didcom.org/introduction-service/0.1/invitation-request": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation_request.InvitationRequest", + } + test_version_def = { + "current_minor_version": 1, + "major_version": 0, + "minimum_minor_version": 1, + "path": "v0_1", + } + updated_typeset = self.registry.create_msg_types_for_minor_version( + test_typesets, test_version_def + ) + assert not updated_typeset + test_typesets = { + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/1.0/fake-forward-invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.forward_invitation.ForwardInvitation", + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/1.0/fake-invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation.Invitation", + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/1.0/fake-invitation-request": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation_request.InvitationRequest", + "https://didcom.org/introduction-service/1.0/fake-forward-invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.forward_invitation.ForwardInvitation", + "https://didcom.org/introduction-service/1.0/fake-invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation.Invitation", + "https://didcom.org/introduction-service/1.0/fake-invitation-request": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation_request.InvitationRequest", + } + test_version_def = { + "current_minor_version": 1, + "major_version": 1, + "minimum_minor_version": 0, + "path": "v0_1", + } + updated_typeset = self.registry.create_msg_types_for_minor_version( + test_typesets, test_version_def + ) + assert ( + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/1.1/fake-forward-invitation" + in updated_typeset + ) + assert ( + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/1.1/fake-invitation" + in updated_typeset + ) + assert ( + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/1.1/fake-invitation-request" + in updated_typeset + ) + assert ( + "https://didcom.org/introduction-service/1.1/fake-forward-invitation" + in updated_typeset + ) + assert ( + "https://didcom.org/introduction-service/1.1/fake-invitation" + in updated_typeset + ) + assert ( + "https://didcom.org/introduction-service/1.1/fake-invitation-request" + in updated_typeset + ) + assert ( + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/1.0/fake-forward-invitation" + not in updated_typeset + ) + async def test_disclosed(self): self.registry.register_message_types( {self.test_message_type: self.test_message_handler} diff --git a/aries_cloudagent/protocols/out_of_band/definition.py b/aries_cloudagent/protocols/out_of_band/definition.py index 62bddef6f5..6167ac905f 100644 --- a/aries_cloudagent/protocols/out_of_band/definition.py +++ b/aries_cloudagent/protocols/out_of_band/definition.py @@ -3,8 +3,8 @@ versions = [ { "major_version": 1, - "minimum_minor_version": 0, - "current_minor_version": 0, + "minimum_minor_version": 1, + "current_minor_version": 1, "path": "v1_0", } ] From 00eea14c8e171d5293469c79c9d9bb405d772500 Mon Sep 17 00:00:00 2001 From: "DESKTOP-TAED8OJ\\shaangill025" Date: Mon, 12 Sep 2022 12:38:29 -0700 Subject: [PATCH 485/872] flake8 & unit test fix Signed-off-by: DESKTOP-TAED8OJ\shaangill025 --- aries_cloudagent/core/protocol_registry.py | 4 +-- .../core/tests/test_protocol_registry.py | 36 ++++++++++--------- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/aries_cloudagent/core/protocol_registry.py b/aries_cloudagent/core/protocol_registry.py index 95e2105b7b..95550ae872 100644 --- a/aries_cloudagent/core/protocol_registry.py +++ b/aries_cloudagent/core/protocol_registry.py @@ -77,7 +77,7 @@ def parse_type_string(self, message_type): def create_msg_types_for_minor_version(self, typesets, version_definition): """ - Return a mapping of message type string [missing] with minor versions to module path. + Return mapping of message type to module path for minor versions. Args: typesets: Mappings of message types to register @@ -98,7 +98,7 @@ def create_msg_types_for_minor_version(self, typesets, version_definition): for msg_type_string, module_path in typeset.items(): if to_check not in msg_type_string: updated_msg_type_string = re.sub( - "(\/\d+\.)?(\*|\d+\/)", to_check, msg_type_string + r"(\/\d+\.)?(\*|\d+\/)", to_check, msg_type_string ) if not updated_typeset: updated_typeset = {} diff --git a/aries_cloudagent/core/tests/test_protocol_registry.py b/aries_cloudagent/core/tests/test_protocol_registry.py index 3cd093f4c5..dad8b4c248 100644 --- a/aries_cloudagent/core/tests/test_protocol_registry.py +++ b/aries_cloudagent/core/tests/test_protocol_registry.py @@ -45,14 +45,16 @@ def test_message_type_query(self): assert matches == () def test_create_msg_types_for_minor_version(self): - test_typesets = { - "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/0.1/forward-invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.forward_invitation.ForwardInvitation", - "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/0.1/invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation.Invitation", - "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/0.1/invitation-request": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation_request.InvitationRequest", - "https://didcom.org/introduction-service/0.1/forward-invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.forward_invitation.ForwardInvitation", - "https://didcom.org/introduction-service/0.1/invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation.Invitation", - "https://didcom.org/introduction-service/0.1/invitation-request": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation_request.InvitationRequest", - } + test_typesets = ( + { + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/0.1/forward-invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.forward_invitation.ForwardInvitation", + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/0.1/invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation.Invitation", + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/0.1/invitation-request": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation_request.InvitationRequest", + "https://didcom.org/introduction-service/0.1/forward-invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.forward_invitation.ForwardInvitation", + "https://didcom.org/introduction-service/0.1/invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation.Invitation", + "https://didcom.org/introduction-service/0.1/invitation-request": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation_request.InvitationRequest", + }, + ) test_version_def = { "current_minor_version": 1, "major_version": 0, @@ -63,14 +65,16 @@ def test_create_msg_types_for_minor_version(self): test_typesets, test_version_def ) assert not updated_typeset - test_typesets = { - "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/1.0/fake-forward-invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.forward_invitation.ForwardInvitation", - "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/1.0/fake-invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation.Invitation", - "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/1.0/fake-invitation-request": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation_request.InvitationRequest", - "https://didcom.org/introduction-service/1.0/fake-forward-invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.forward_invitation.ForwardInvitation", - "https://didcom.org/introduction-service/1.0/fake-invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation.Invitation", - "https://didcom.org/introduction-service/1.0/fake-invitation-request": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation_request.InvitationRequest", - } + test_typesets = ( + { + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/1.0/fake-forward-invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.forward_invitation.ForwardInvitation", + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/1.0/fake-invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation.Invitation", + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/1.0/fake-invitation-request": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation_request.InvitationRequest", + "https://didcom.org/introduction-service/1.0/fake-forward-invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.forward_invitation.ForwardInvitation", + "https://didcom.org/introduction-service/1.0/fake-invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation.Invitation", + "https://didcom.org/introduction-service/1.0/fake-invitation-request": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation_request.InvitationRequest", + }, + ) test_version_def = { "current_minor_version": 1, "major_version": 1, From ca8308fde42964ec379af1bbecfde9220d82ebe8 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Thu, 22 Sep 2022 05:06:59 -0700 Subject: [PATCH 486/872] templated msg_type work Signed-off-by: Shaanjot Gill --- aries_cloudagent/core/dispatcher.py | 72 ++++++++- aries_cloudagent/core/protocol_registry.py | 44 +++-- .../core/tests/test_dispatcher.py | 153 +++++++++++++++--- .../core/tests/test_protocol_registry.py | 47 ++---- aries_cloudagent/core/tests/test_util.py | 73 +++++++++ aries_cloudagent/core/util.py | 135 ++++++++++++++++ .../protocols/out_of_band/definition.py | 2 +- .../protocols/out_of_band/v1_0/manager.py | 5 +- .../out_of_band/v1_0/message_types.py | 8 +- .../out_of_band/v1_0/messages/invitation.py | 11 ++ .../v1_0/messages/problem_report.py | 12 +- .../out_of_band/v1_0/messages/reuse.py | 11 ++ .../out_of_band/v1_0/messages/reuse_accept.py | 11 ++ .../v1_0/messages/tests/test_invitation.py | 9 +- .../protocols/out_of_band/v1_0/routes.py | 9 ++ .../out_of_band/v1_0/tests/test_manager.py | 13 +- 16 files changed, 532 insertions(+), 83 deletions(-) create mode 100644 aries_cloudagent/core/tests/test_util.py diff --git a/aries_cloudagent/core/dispatcher.py b/aries_cloudagent/core/dispatcher.py index e3f37b45ac..2193dd20b9 100644 --- a/aries_cloudagent/core/dispatcher.py +++ b/aries_cloudagent/core/dispatcher.py @@ -10,7 +10,7 @@ import os import warnings -from typing import Callable, Coroutine, Union +from typing import Callable, Coroutine, Optional, Union, Tuple import weakref from aiohttp.web import HTTPException @@ -36,6 +36,13 @@ from .error import ProtocolMinorVersionNotSupported from .protocol_registry import ProtocolRegistry +from .util import ( + get_version_from_message_type, + validate_get_response_version, + # WARNING_DEGRADED_FEATURES, + # WARNING_VERSION_MISMATCH, + # WARNING_VERSION_NOT_SUPPORTED, +) LOGGER = logging.getLogger(__name__) @@ -133,6 +140,9 @@ async def handle_message( inbound_message: The inbound message instance send_outbound: Async function to send outbound messages + # Raises: + # MessageParseError: If the message type version is not supported + Returns: The response from the handler @@ -140,9 +150,12 @@ async def handle_message( r_time = get_timer() error_result = None + version_warning = None message = None try: - message = await self.make_message(inbound_message.payload) + (message, warning) = await self.make_message( + profile, inbound_message.payload + ) except ProblemReportParseError: pass # avoid problem report recursion except MessageParseError as e: @@ -155,6 +168,47 @@ async def handle_message( ) if inbound_message.receipt.thread_id: error_result.assign_thread_id(inbound_message.receipt.thread_id) + # if warning: + # warning_message_type = inbound_message.payload.get("@type") + # if warning == WARNING_DEGRADED_FEATURES: + # LOGGER.error( + # f"Sending {WARNING_DEGRADED_FEATURES} problem report, " + # "message type received with a minor version at or higher" + # " than protocol minimum supported and current minor version " + # f"for message_type {warning_message_type}" + # ) + # version_warning = ProblemReport( + # description={ + # "en": ( + # "message type received with a minor version at or " + # "higher than protocol minimum supported and current" + # f" minor version for message_type {warning_message_type}" + # ), + # "code": WARNING_DEGRADED_FEATURES, + # } + # ) + # elif warning == WARNING_VERSION_MISMATCH: + # LOGGER.error( + # f"Sending {WARNING_VERSION_MISMATCH} problem report, message " + # "type received with a minor version higher than current minor " + # f"version for message_type {warning_message_type}" + # ) + # version_warning = ProblemReport( + # description={ + # "en": ( + # "message type received with a minor version higher" + # " than current minor version for message_type" + # f" {warning_message_type}" + # ), + # "code": WARNING_VERSION_MISMATCH, + # } + # ) + # elif warning == WARNING_VERSION_NOT_SUPPORTED: + # raise MessageParseError( + # f"Message type version not supported for {warning_message_type}" + # ) + # if version_warning and inbound_message.receipt.thread_id: + # version_warning.assign_thread_id(inbound_message.receipt.thread_id) trace_event( self.profile.settings, @@ -199,6 +253,8 @@ async def handle_message( if error_result: await responder.send_reply(error_result) + elif version_warning: + await responder.send_reply(version_warning) elif context.message: context.injector.bind_instance(BaseResponder, responder) @@ -215,7 +271,9 @@ async def handle_message( perf_counter=r_time, ) - async def make_message(self, parsed_msg: dict) -> BaseMessage: + async def make_message( + self, profile: Profile, parsed_msg: dict + ) -> Tuple[BaseMessage, Optional[str]]: """ Deserialize a message dict into the appropriate message instance. @@ -224,6 +282,7 @@ async def make_message(self, parsed_msg: dict) -> BaseMessage: Args: parsed_msg: The parsed message + profile: Profile Returns: An instance of the corresponding message class for this message @@ -237,6 +296,7 @@ async def make_message(self, parsed_msg: dict) -> BaseMessage: if not isinstance(parsed_msg, dict): raise MessageParseError("Expected a JSON object") message_type = parsed_msg.get("@type") + message_type_rec_version = get_version_from_message_type(message_type) if not message_type: raise MessageParseError("Message does not contain '@type' parameter") @@ -256,8 +316,10 @@ async def make_message(self, parsed_msg: dict) -> BaseMessage: if "/problem-report" in message_type: raise ProblemReportParseError("Error parsing problem report message") raise MessageParseError(f"Error deserializing message: {e}") from e - - return instance + _, warning = await validate_get_response_version( + profile, message_type_rec_version, message_cls + ) + return (instance, warning) async def complete(self, timeout: float = 0.1): """Wait for pending tasks to complete.""" diff --git a/aries_cloudagent/core/protocol_registry.py b/aries_cloudagent/core/protocol_registry.py index 95550ae872..42e2420411 100644 --- a/aries_cloudagent/core/protocol_registry.py +++ b/aries_cloudagent/core/protocol_registry.py @@ -1,8 +1,8 @@ """Handle registration and publication of supported protocols.""" import logging -import re +from string import Template from typing import Mapping, Sequence from ..config.injection_context import InjectionContext @@ -93,17 +93,22 @@ def create_msg_types_for_minor_version(self, typesets, version_definition): major_version = version_definition["major_version"] if curr_minor_version >= min_minor_version and curr_minor_version >= 1: for version_index in range(min_minor_version, curr_minor_version + 1): - to_check = f"/{str(major_version)}.{str(version_index)}/" + to_check = f"{str(major_version)}.{str(version_index)}" for typeset in typesets: for msg_type_string, module_path in typeset.items(): - if to_check not in msg_type_string: - updated_msg_type_string = re.sub( - r"(\/\d+\.)?(\*|\d+\/)", to_check, msg_type_string - ) - if not updated_typeset: - updated_typeset = {} - updated_typeset[updated_msg_type_string] = module_path - return updated_typeset + updated_msg_type_string = Template(msg_type_string).substitute( + version=to_check + ) + if not updated_typeset: + updated_typeset = {} + updated_typeset[updated_msg_type_string] = module_path + return (updated_typeset,) + + def _template_message_type_check(self, typeset) -> bool: + for msg_type_string, module_path in typeset.items(): + if "$version" in msg_type_string: + return True + return False def register_message_types(self, *typesets, version_definition=None): """ @@ -116,17 +121,24 @@ def register_message_types(self, *typesets, version_definition=None): """ # Maintain support for versionless protocol modules + template_msg_type_version = False for typeset in typesets: - self._typemap.update(typeset) + if not self._template_message_type_check(typeset): + self._typemap.update(typeset) + else: + template_msg_type_version = True # Track versioned modules for version routing if version_definition: # create updated typesets for minor versions and register them - updated_typeset = self.create_msg_types_for_minor_version( - typesets, version_definition - ) - if updated_typeset: - self._typemap.update(updated_typeset) + if template_msg_type_version: + updated_typesets = self.create_msg_types_for_minor_version( + typesets, version_definition + ) + if updated_typesets: + for typeset in updated_typesets: + self._typemap.update(typeset) + typesets = updated_typesets for typeset in typesets: for message_type_string, module_path in typeset.items(): parsed_type_string = self.parse_type_string(message_type_string) diff --git a/aries_cloudagent/core/tests/test_dispatcher.py b/aries_cloudagent/core/tests/test_dispatcher.py index 4722ad9530..023665c0db 100644 --- a/aries_cloudagent/core/tests/test_dispatcher.py +++ b/aries_cloudagent/core/tests/test_dispatcher.py @@ -1,7 +1,7 @@ import json from async_case import IsolatedAsyncioTestCase -import mock as async_mock +from asynctest import mock as async_mock import pytest from marshmallow import EXCLUDE @@ -111,9 +111,17 @@ async def test_dispatch(self): StubAgentMessageHandler, "handle", autospec=True ) as handler_mock, async_mock.patch.object( test_module, "ConnectionManager", autospec=True - ) as conn_mgr_mock: + ) as conn_mgr_mock, async_mock.patch.object( + test_module, + "get_version_from_message_type", + async_mock.CoroutineMock(return_value="1.1"), + ), async_mock.patch.object( + test_module, + "validate_get_response_version", + async_mock.CoroutineMock(return_value=("1.1", None)), + ): conn_mgr_mock.return_value = async_mock.MagicMock( - find_inbound_connection=async_mock.AsyncMock( + find_inbound_connection=async_mock.CoroutineMock( return_value=async_mock.MagicMock(connection_id="dummy") ) ) @@ -152,7 +160,15 @@ async def test_dispatch_versioned_message(self): with async_mock.patch.object( StubAgentMessageHandler, "handle", autospec=True - ) as handler_mock: + ) as handler_mock, async_mock.patch.object( + test_module, + "get_version_from_message_type", + async_mock.CoroutineMock(return_value="1.1"), + ), async_mock.patch.object( + test_module, + "validate_get_response_version", + async_mock.CoroutineMock(return_value=("1.1", None)), + ): await dispatcher.queue_message( dispatcher.profile, make_inbound(message), rcv.send ) @@ -265,7 +281,15 @@ async def test_dispatch_versioned_message_handle_greater_succeeds(self): with async_mock.patch.object( StubAgentMessageHandler, "handle", autospec=True - ) as handler_mock: + ) as handler_mock, async_mock.patch.object( + test_module, + "get_version_from_message_type", + async_mock.CoroutineMock(return_value="1.1"), + ), async_mock.patch.object( + test_module, + "validate_get_response_version", + async_mock.CoroutineMock(return_value=("1.1", None)), + ): await dispatcher.queue_message( dispatcher.profile, make_inbound(message), rcv.send ) @@ -317,17 +341,22 @@ async def test_bad_message_dispatch_parse_x(self): await dispatcher.setup() rcv = Receiver() bad_messages = ["not even a dict", {"bad": "message"}] - for bad in bad_messages: - await dispatcher.queue_message( - dispatcher.profile, make_inbound(bad), rcv.send - ) - await dispatcher.task_queue - assert rcv.messages and isinstance(rcv.messages[0][1], OutboundMessage) - payload = json.loads(rcv.messages[0][1].payload) - assert payload["@type"] == DIDCommPrefix.qualify_current( - ProblemReport.Meta.message_type - ) - rcv.messages.clear() + with async_mock.patch.object( + test_module, "get_version_from_message_type", async_mock.CoroutineMock() + ), async_mock.patch.object( + test_module, "validate_get_response_version", async_mock.CoroutineMock() + ): + for bad in bad_messages: + await dispatcher.queue_message( + dispatcher.profile, make_inbound(bad), rcv.send + ) + await dispatcher.task_queue + assert rcv.messages and isinstance(rcv.messages[0][1], OutboundMessage) + payload = json.loads(rcv.messages[0][1].payload) + assert payload["@type"] == DIDCommPrefix.qualify_current( + ProblemReport.Meta.message_type + ) + rcv.messages.clear() async def test_bad_message_dispatch_problem_report_x(self): profile = make_profile() @@ -387,7 +416,7 @@ async def test_create_send_outbound(self): message = StubAgentMessage() responder = test_module.DispatcherResponder(context, message, None) outbound_message = await responder.create_outbound(message) - with async_mock.patch.object(responder, "_send", async_mock.AsyncMock()): + with async_mock.patch.object(responder, "_send", async_mock.CoroutineMock()): await responder.send_outbound(outbound_message) async def test_create_send_webhook(self): @@ -404,7 +433,7 @@ async def test_create_enc_outbound(self): message = b"abc123xyz7890000" responder = test_module.DispatcherResponder(context, message, None) with async_mock.patch.object( - responder, "send_outbound", async_mock.AsyncMock() + responder, "send_outbound", async_mock.CoroutineMock() ) as mock_send_outbound: await responder.send(message) assert mock_send_outbound.called_once() @@ -425,3 +454,91 @@ def _smaller_scope(): with self.assertRaises(RuntimeError): await responder.send_webhook("test", {}) + + # async def test_dispatch_version_with_degraded_features(self): + # profile = make_profile() + # registry = profile.inject(ProtocolRegistry) + # registry.register_message_types( + # { + # pfx.qualify(StubAgentMessage.Meta.message_type): StubAgentMessage + # for pfx in DIDCommPrefix + # } + # ) + # dispatcher = test_module.Dispatcher(profile) + # await dispatcher.setup() + # rcv = Receiver() + # message = { + # "@type": DIDCommPrefix.qualify_current(StubAgentMessage.Meta.message_type) + # } + + # with async_mock.patch.object( + # test_module, + # "get_version_from_message_type", + # async_mock.CoroutineMock(return_value="1.1"), + # ), async_mock.patch.object( + # test_module, + # "validate_get_response_version", + # async_mock.CoroutineMock(return_value=("1.1", "fields-ignored-due-to-version-mismatch")), + # ): + # await dispatcher.queue_message( + # dispatcher.profile, make_inbound(message), rcv.send + # ) + + # async def test_dispatch_fields_ignored_due_to_version_mismatch(self): + # profile = make_profile() + # registry = profile.inject(ProtocolRegistry) + # registry.register_message_types( + # { + # pfx.qualify(StubAgentMessage.Meta.message_type): StubAgentMessage + # for pfx in DIDCommPrefix + # } + # ) + # dispatcher = test_module.Dispatcher(profile) + # await dispatcher.setup() + # rcv = Receiver() + # message = { + # "@type": DIDCommPrefix.qualify_current(StubAgentMessage.Meta.message_type) + # } + + # with async_mock.patch.object( + # test_module, + # "get_version_from_message_type", + # async_mock.CoroutineMock(return_value="1.1"), + # ), async_mock.patch.object( + # test_module, + # "validate_get_response_version", + # async_mock.CoroutineMock(return_value=("1.1", "version-with-degraded-features")), + # ): + # await dispatcher.queue_message( + # dispatcher.profile, make_inbound(message), rcv.send + # ) + + # async def test_dispatch_version_not_supported(self): + # profile = make_profile() + # registry = profile.inject(ProtocolRegistry) + # registry.register_message_types( + # { + # pfx.qualify(StubAgentMessage.Meta.message_type): StubAgentMessage + # for pfx in DIDCommPrefix + # } + # ) + # dispatcher = test_module.Dispatcher(profile) + # await dispatcher.setup() + # rcv = Receiver() + # message = { + # "@type": DIDCommPrefix.qualify_current(StubAgentMessage.Meta.message_type) + # } + + # with async_mock.patch.object( + # test_module, + # "get_version_from_message_type", + # async_mock.CoroutineMock(return_value="1.1"), + # ), async_mock.patch.object( + # test_module, + # "validate_get_response_version", + # async_mock.CoroutineMock(return_value=("1.1", "version-not-supported")), + # ): + # with self.assertRaises(test_module.MessageParseError): + # await dispatcher.queue_message( + # dispatcher.profile, make_inbound(message), rcv.send + # ) diff --git a/aries_cloudagent/core/tests/test_protocol_registry.py b/aries_cloudagent/core/tests/test_protocol_registry.py index dad8b4c248..15d99cbc36 100644 --- a/aries_cloudagent/core/tests/test_protocol_registry.py +++ b/aries_cloudagent/core/tests/test_protocol_registry.py @@ -47,32 +47,12 @@ def test_message_type_query(self): def test_create_msg_types_for_minor_version(self): test_typesets = ( { - "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/0.1/forward-invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.forward_invitation.ForwardInvitation", - "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/0.1/invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation.Invitation", - "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/0.1/invitation-request": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation_request.InvitationRequest", - "https://didcom.org/introduction-service/0.1/forward-invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.forward_invitation.ForwardInvitation", - "https://didcom.org/introduction-service/0.1/invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation.Invitation", - "https://didcom.org/introduction-service/0.1/invitation-request": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation_request.InvitationRequest", - }, - ) - test_version_def = { - "current_minor_version": 1, - "major_version": 0, - "minimum_minor_version": 1, - "path": "v0_1", - } - updated_typeset = self.registry.create_msg_types_for_minor_version( - test_typesets, test_version_def - ) - assert not updated_typeset - test_typesets = ( - { - "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/1.0/fake-forward-invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.forward_invitation.ForwardInvitation", - "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/1.0/fake-invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation.Invitation", - "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/1.0/fake-invitation-request": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation_request.InvitationRequest", - "https://didcom.org/introduction-service/1.0/fake-forward-invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.forward_invitation.ForwardInvitation", - "https://didcom.org/introduction-service/1.0/fake-invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation.Invitation", - "https://didcom.org/introduction-service/1.0/fake-invitation-request": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation_request.InvitationRequest", + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/$version/fake-forward-invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.forward_invitation.ForwardInvitation", + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/$version/fake-invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation.Invitation", + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/$version/fake-invitation-request": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation_request.InvitationRequest", + "https://didcom.org/introduction-service/$version/fake-forward-invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.forward_invitation.ForwardInvitation", + "https://didcom.org/introduction-service/$version/fake-invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation.Invitation", + "https://didcom.org/introduction-service/$version/fake-invitation-request": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation_request.InvitationRequest", }, ) test_version_def = { @@ -81,19 +61,20 @@ def test_create_msg_types_for_minor_version(self): "minimum_minor_version": 0, "path": "v0_1", } - updated_typeset = self.registry.create_msg_types_for_minor_version( + updated_typesets = self.registry.create_msg_types_for_minor_version( test_typesets, test_version_def ) + updated_typeset = updated_typesets[0] assert ( - "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/1.1/fake-forward-invitation" + "https://didcom.org/introduction-service/1.0/fake-forward-invitation" in updated_typeset ) assert ( - "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/1.1/fake-invitation" + "https://didcom.org/introduction-service/1.0/fake-invitation" in updated_typeset ) assert ( - "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/1.1/fake-invitation-request" + "https://didcom.org/introduction-service/1.0/fake-invitation-request" in updated_typeset ) assert ( @@ -110,7 +91,11 @@ def test_create_msg_types_for_minor_version(self): ) assert ( "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/1.0/fake-forward-invitation" - not in updated_typeset + in updated_typeset + ) + assert ( + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/1.1/fake-invitation-request" + in updated_typeset ) async def test_disclosed(self): diff --git a/aries_cloudagent/core/tests/test_util.py b/aries_cloudagent/core/tests/test_util.py new file mode 100644 index 0000000000..1b20487e71 --- /dev/null +++ b/aries_cloudagent/core/tests/test_util.py @@ -0,0 +1,73 @@ +from async_case import IsolatedAsyncioTestCase + +from ...cache.base import BaseCache +from ...cache.in_memory import InMemoryCache +from ...core.in_memory import InMemoryProfile +from ...core.profile import Profile +from ...protocols.didcomm_prefix import DIDCommPrefix +from ...protocols.introduction.v0_1.messages.invitation import Invitation +from ...protocols.out_of_band.v1_0.messages.reuse import HandshakeReuse + +from .. import util as test_module + + +def make_profile() -> Profile: + profile = InMemoryProfile.test_profile() + profile.context.injector.bind_instance(BaseCache, InMemoryCache()) + return profile + + +class TestUtils(IsolatedAsyncioTestCase): + async def test_validate_get_response_version(self): + profile = make_profile() + (resp_version, warning) = await test_module.validate_get_response_version( + profile, "1.1", HandshakeReuse + ) + assert resp_version == "1.1" + assert not warning + + # cached + (resp_version, warning) = await test_module.validate_get_response_version( + profile, "1.1", HandshakeReuse + ) + assert resp_version == "1.1" + assert not warning + + (resp_version, warning) = await test_module.validate_get_response_version( + profile, "1.0", HandshakeReuse + ) + assert resp_version == "1.0" + assert warning == test_module.WARNING_DEGRADED_FEATURES + + (resp_version, warning) = await test_module.validate_get_response_version( + profile, "1.2", HandshakeReuse + ) + assert resp_version == "1.1" + assert warning == test_module.WARNING_VERSION_MISMATCH + + with self.assertRaises(test_module.ProtocolMinorVersionNotSupported): + (resp_version, warning) = await test_module.validate_get_response_version( + profile, "0.0", Invitation + ) + + with self.assertRaises(Exception): + (resp_version, warning) = await test_module.validate_get_response_version( + profile, "1.0", Invitation + ) + + def test_get_version_from_message_type(self): + assert ( + test_module.get_version_from_message_type( + DIDCommPrefix.qualify_current("out-of-band/1.1/handshake-reuse") + ) + == "1.1" + ) + + def test_get_version_from_message(self): + assert test_module.get_version_from_message(HandshakeReuse()) == "1.0" + + async def test_get_proto_default_version(self): + profile = make_profile() + assert ( + await test_module.get_proto_default_version(profile, HandshakeReuse) + ) == "1.1" diff --git a/aries_cloudagent/core/util.py b/aries_cloudagent/core/util.py index 791f80c95d..7db3bd8b64 100644 --- a/aries_cloudagent/core/util.py +++ b/aries_cloudagent/core/util.py @@ -1,10 +1,145 @@ """Core utilities and constants.""" +import inspect +import os import re +from typing import Optional, Tuple + +from ..cache.base import BaseCache +from ..core.profile import Profile +from ..messaging.agent_message import AgentMessage +from ..utils.classloader import ClassLoader + +from .error import ProtocolMinorVersionNotSupported CORE_EVENT_PREFIX = "acapy::core::" STARTUP_EVENT_TOPIC = CORE_EVENT_PREFIX + "startup" STARTUP_EVENT_PATTERN = re.compile(f"^{STARTUP_EVENT_TOPIC}?$") SHUTDOWN_EVENT_TOPIC = CORE_EVENT_PREFIX + "shutdown" SHUTDOWN_EVENT_PATTERN = re.compile(f"^{SHUTDOWN_EVENT_TOPIC}?$") +WARNING_DEGRADED_FEATURES = "version-with-degraded-features" +WARNING_VERSION_MISMATCH = "fields-ignored-due-to-version-mismatch" +WARNING_VERSION_NOT_SUPPORTED = "version-not-supported" + + +async def validate_get_response_version( + profile: Profile, rec_version: str, msg_class: type +) -> Tuple[str, Optional[str]]: + """ + Return a tuple with version to respond with and warnings. + + Process received version and protocol version definition, + returns the tuple. + + Args: + profile: Profile + rec_version: received version from message + msg_class: type + + Returns: + Tuple with response version and any warnings + + """ + resp_version = rec_version + warning = None + version_string_tokens = rec_version.split(".") + rec_major_version = int(version_string_tokens[0]) + rec_minor_version = int(version_string_tokens[1]) + version_definition = await get_version_def_from_msg_class( + profile, msg_class, rec_major_version + ) + proto_major_version = int(version_definition["major_version"]) + proto_curr_minor_version = int(version_definition["current_minor_version"]) + proto_min_minor_version = int(version_definition["minimum_minor_version"]) + if rec_minor_version < proto_min_minor_version: + warning = WARNING_VERSION_NOT_SUPPORTED + elif ( + rec_minor_version >= proto_min_minor_version + and rec_minor_version < proto_curr_minor_version + ): + warning = WARNING_DEGRADED_FEATURES + elif rec_minor_version > proto_curr_minor_version: + warning = WARNING_VERSION_MISMATCH + if proto_major_version == rec_major_version: + if ( + proto_min_minor_version <= rec_minor_version + and proto_curr_minor_version >= rec_minor_version + ): + resp_version = f"{str(proto_major_version)}.{str(rec_minor_version)}" + elif rec_minor_version > proto_curr_minor_version: + resp_version = f"{str(proto_major_version)}.{str(proto_curr_minor_version)}" + elif rec_minor_version < proto_min_minor_version: + raise ProtocolMinorVersionNotSupported( + "Minimum supported minor version is " + + f"{proto_min_minor_version}." + + f" Received {rec_minor_version}." + ) + else: + raise Exception( + f"Supported major version {proto_major_version}" + " is not same as received major version" + f" {rec_major_version}." + ) + return (resp_version, warning) + + +def get_version_from_message_type(msg_type: str) -> str: + """Return version from provided message_type.""" + return (re.search(r"(\d+\.)?(\*|\d+)", msg_type)).group() + + +def get_version_from_message(msg: AgentMessage) -> str: + """Return version from provided AgentMessage.""" + msg_type = msg._type + return get_version_from_message_type(msg_type) + + +async def get_proto_default_version( + profile: Profile, msg_class: type, major_version: int = 1 +): + """Return default protocol version from version_definition.""" + version_definition = await get_version_def_from_msg_class( + profile, msg_class, major_version + ) + default_major_version = version_definition["major_version"] + default_minor_version = version_definition["current_minor_version"] + return f"{default_major_version}.{default_minor_version}" + + +def _get_path_from_msg_class(msg_class: type) -> str: + path = os.path.normpath(inspect.getfile(msg_class)) + split_str = os.getenv("ACAPY_HOME") or "aries_cloudagent" + path = split_str + path.rsplit(split_str, 1)[1] + version = (re.search(r"v(\d+\_)?(\*|\d+)", path)).group() + path = path.split(version, 1)[0] + return (path.replace("/", ".")) + "definition" + + +async def get_version_def_from_msg_class( + profile: Profile, msg_class: type, major_version: int = 1 +): + """Return version_definition of a protocol.""" + cache = profile.inject_or(BaseCache) + version_definition = None + if cache: + version_definition = await cache.get( + f"version_definition::{str(msg_class).lower()}" + ) + if version_definition: + return version_definition + definition_path = _get_path_from_msg_class(msg_class) + definition = ClassLoader.load_module(definition_path) + for protocol_version in definition.versions: + if major_version == protocol_version["major_version"]: + version_definition = protocol_version + break + if not version_definition: + raise Exception( + f"Unable to load protocol version_definition for {str(msg_class)}" + ) + if cache: + await cache.set( + f"version_definition::{str(msg_class).lower()}", version_definition + ) + return version_definition diff --git a/aries_cloudagent/protocols/out_of_band/definition.py b/aries_cloudagent/protocols/out_of_band/definition.py index 6167ac905f..13c1f8a8ef 100644 --- a/aries_cloudagent/protocols/out_of_band/definition.py +++ b/aries_cloudagent/protocols/out_of_band/definition.py @@ -3,7 +3,7 @@ versions = [ { "major_version": 1, - "minimum_minor_version": 1, + "minimum_minor_version": 0, "current_minor_version": 1, "path": "v1_0", } diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py index 4bdfcc39a6..1679069e1a 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py @@ -9,6 +9,7 @@ from ....messaging.decorators.service_decorator import ServiceDecorator from ....core.event_bus import EventBus +from ....core.util import get_version_from_message from ....connections.base_manager import BaseConnectionManager from ....connections.models.conn_record import ConnRecord from ....core.error import BaseError @@ -906,7 +907,9 @@ async def receive_reuse_message( invi_msg_id = reuse_msg._thread.pthid reuse_msg_id = reuse_msg._thread_id - reuse_accept_msg = HandshakeReuseAccept() + reuse_accept_msg = HandshakeReuseAccept( + version=get_version_from_message(reuse_msg) + ) reuse_accept_msg.assign_thread_id(thid=reuse_msg_id, pthid=invi_msg_id) connection_targets = await self.fetch_connection_targets(connection=conn_rec) diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/message_types.py b/aries_cloudagent/protocols/out_of_band/v1_0/message_types.py index d8fb709e09..c130b92e4f 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/message_types.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/message_types.py @@ -8,10 +8,10 @@ ) # Message types -INVITATION = "out-of-band/1.0/invitation" -MESSAGE_REUSE = "out-of-band/1.0/handshake-reuse" -MESSAGE_REUSE_ACCEPT = "out-of-band/1.0/handshake-reuse-accepted" -PROBLEM_REPORT = "out-of-band/1.0/problem_report" +INVITATION = "out-of-band/$version/invitation" +MESSAGE_REUSE = "out-of-band/$version/handshake-reuse" +MESSAGE_REUSE_ACCEPT = "out-of-band/$version/handshake-reuse-accepted" +PROBLEM_REPORT = "out-of-band/$version/problem_report" PROTOCOL_PACKAGE = "aries_cloudagent.protocols.out_of_band.v1_0" diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/messages/invitation.py b/aries_cloudagent/protocols/out_of_band/v1_0/messages/invitation.py index 04a2cfa3fa..14271ccd01 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/messages/invitation.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/messages/invitation.py @@ -3,6 +3,7 @@ from collections import namedtuple from enum import Enum from re import sub +from string import Template from typing import Sequence, Text, Union from urllib.parse import parse_qs, urljoin, urlparse @@ -30,6 +31,7 @@ from .service import Service +BASE_PROTO_VERSION = "1.0" HSProtoSpec = namedtuple("HSProtoSpec", "rfc name aka") @@ -123,6 +125,7 @@ def __init__( handshake_protocols: Sequence[Text] = None, requests_attach: Sequence[AttachDecorator] = None, services: Sequence[Union[Service, Text]] = None, + version: str = BASE_PROTO_VERSION, **kwargs, ): """ @@ -140,12 +143,20 @@ def __init__( ) self.requests_attach = list(requests_attach) if requests_attach else [] self.services = services + self.assign_version_to_message_type(version=version) @classmethod def wrap_message(cls, message: dict) -> AttachDecorator: """Convert an aries message to an attachment decorator.""" return AttachDecorator.data_json(mapping=message, ident="request-0") + @classmethod + def assign_version_to_message_type(cls, version: str): + """Assign version to Meta.message_type.""" + cls.Meta.message_type = Template(cls.Meta.message_type).substitute( + version=version + ) + def to_url(self, base_url: str = None) -> str: """ Convert an invitation message to URL format for sharing. diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/messages/problem_report.py b/aries_cloudagent/protocols/out_of_band/v1_0/messages/problem_report.py index f6ddb3bf86..679adacb5a 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/messages/problem_report.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/messages/problem_report.py @@ -10,6 +10,7 @@ validates_schema, ValidationError, ) +from string import Template from ....problem_report.v1_0.message import ProblemReport, ProblemReportSchema @@ -21,6 +22,7 @@ ) LOGGER = logging.getLogger(__name__) +BASE_PROTO_VERSION = "1.0" class ProblemReportReason(Enum): @@ -40,9 +42,17 @@ class Meta: message_type = PROBLEM_REPORT schema_class = "OOBProblemReportSchema" - def __init__(self, *args, **kwargs): + def __init__(self, version: str = BASE_PROTO_VERSION, *args, **kwargs): """Initialize a ProblemReport message instance.""" super().__init__(*args, **kwargs) + self.assign_version_to_message_type(version=version) + + @classmethod + def assign_version_to_message_type(cls, version: str): + """Assign version to Meta.message_type.""" + cls.Meta.message_type = Template(cls.Meta.message_type).substitute( + version=version + ) class OOBProblemReportSchema(ProblemReportSchema): diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/messages/reuse.py b/aries_cloudagent/protocols/out_of_band/v1_0/messages/reuse.py index df40511e80..f6b24d0e8b 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/messages/reuse.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/messages/reuse.py @@ -1,6 +1,7 @@ """Represents a Handshake Reuse message under RFC 0434.""" from marshmallow import EXCLUDE, pre_dump, ValidationError +from string import Template from .....messaging.agent_message import AgentMessage, AgentMessageSchema @@ -9,6 +10,7 @@ HANDLER_CLASS = ( f"{PROTOCOL_PACKAGE}.handlers.reuse_handler.HandshakeReuseMessageHandler" ) +BASE_PROTO_VERSION = "1.0" class HandshakeReuse(AgentMessage): @@ -23,10 +25,19 @@ class Meta: def __init__( self, + version: str = BASE_PROTO_VERSION, **kwargs, ): """Initialize Handshake Reuse message object.""" super().__init__(**kwargs) + self.assign_version_to_message_type(version=version) + + @classmethod + def assign_version_to_message_type(cls, version: str): + """Assign version to Meta.message_type.""" + cls.Meta.message_type = Template(cls.Meta.message_type).substitute( + version=version + ) class HandshakeReuseSchema(AgentMessageSchema): diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/messages/reuse_accept.py b/aries_cloudagent/protocols/out_of_band/v1_0/messages/reuse_accept.py index d519ab0a2b..e920506205 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/messages/reuse_accept.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/messages/reuse_accept.py @@ -1,6 +1,7 @@ """Represents a Handshake Reuse Accept message under RFC 0434.""" from marshmallow import EXCLUDE, pre_dump, ValidationError +from string import Template from .....messaging.agent_message import AgentMessage, AgentMessageSchema @@ -10,6 +11,7 @@ f"{PROTOCOL_PACKAGE}.handlers" ".reuse_accept_handler.HandshakeReuseAcceptMessageHandler" ) +BASE_PROTO_VERSION = "1.0" class HandshakeReuseAccept(AgentMessage): @@ -24,10 +26,19 @@ class Meta: def __init__( self, + version: str = BASE_PROTO_VERSION, **kwargs, ): """Initialize Handshake Reuse Accept object.""" super().__init__(**kwargs) + self.assign_version_to_message_type(version=version) + + @classmethod + def assign_version_to_message_type(cls, version: str): + """Assign version to Meta.message_type.""" + cls.Meta.message_type = Template(cls.Meta.message_type).substitute( + version=version + ) class HandshakeReuseAcceptSchema(AgentMessageSchema): diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/messages/tests/test_invitation.py b/aries_cloudagent/protocols/out_of_band/v1_0/messages/tests/test_invitation.py index 5340dd66dc..83b80773a6 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/messages/tests/test_invitation.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/messages/tests/test_invitation.py @@ -1,5 +1,6 @@ import pytest +from string import Template from unittest import TestCase from ......messaging.models.base import BaseModelError @@ -51,7 +52,9 @@ def test_init(self): services=[TEST_DID], ) assert invi.services == [TEST_DID] - assert invi._type == DIDCommPrefix.qualify_current(INVITATION) + assert invi._type == DIDCommPrefix.qualify_current( + Template(INVITATION).substitute(version="1.0") + ) service = Service(_id="#inline", _type=DID_COMM, did=TEST_DID) invi_msg = InvitationMessage( @@ -61,7 +64,9 @@ def test_init(self): services=[service], ) assert invi_msg.services == [service] - assert invi_msg._type == DIDCommPrefix.qualify_current(INVITATION) + assert invi_msg._type == DIDCommPrefix.qualify_current( + Template(INVITATION).substitute(version="1.0") + ) def test_wrap_serde(self): """Test conversion of aries message to attachment decorator.""" diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/routes.py b/aries_cloudagent/protocols/out_of_band/v1_0/routes.py index 3f7384c164..59454a69d2 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/routes.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/routes.py @@ -75,6 +75,15 @@ class AttachmentDefSchema(OpenAPISchema): ), required=False, ) + # accept = fields.List( + # fields.Str(), + # description=( + # "List of mime type in order of preference that should be" + # " use in responding to the message" + # ), + # example="['didcomm/aip1', 'didcomm/aip2;env=rfc19']", + # required=False, + # ) use_public_did = fields.Boolean( default=False, description="Whether to use public DID in invitation", diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py index 738e295a1f..df39e85088 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py @@ -3,6 +3,7 @@ import json from copy import deepcopy from datetime import datetime, timedelta, timezone +from string import Template from typing import List from unittest.mock import ANY @@ -388,7 +389,7 @@ async def test_create_invitation_handshake_succeeds(self): ) assert invi_rec.invitation._type == DIDCommPrefix.qualify_current( - INVITATION + Template(INVITATION).substitute(version="1.0") ) assert not invi_rec.invitation.requests_attach assert ( @@ -475,7 +476,7 @@ async def test_create_invitation_mediation_overwrites_routing_and_endpoint(self) ) assert isinstance(invite, InvitationRecord) assert invite.invitation._type == DIDCommPrefix.qualify_current( - INVITATION + Template(INVITATION).substitute(version="1.0") ) assert invite.invitation.label == "test123" assert ( @@ -793,7 +794,9 @@ async def test_create_invitation_peer_did(self): assert invi_rec._invitation.ser[ "@type" - ] == DIDCommPrefix.qualify_current(INVITATION) + ] == DIDCommPrefix.qualify_current( + Template(INVITATION).substitute(version="1.0") + ) assert not invi_rec._invitation.ser.get("requests~attach") assert invi_rec.invitation.label == "That guy" assert ( @@ -900,7 +903,9 @@ async def test_create_handshake_reuse_msg(self): assert oob_record.state == OobRecord.STATE_AWAIT_RESPONSE # Assert responder has been called with the reuse message - assert reuse_message._type == DIDCommPrefix.qualify_current(MESSAGE_REUSE) + assert reuse_message._type == DIDCommPrefix.qualify_current( + Template(MESSAGE_REUSE).substitute(version="1.0") + ) assert oob_record.reuse_msg_id == reuse_message._id async def test_create_handshake_reuse_msg_catch_exception(self): From 5f5e14c8574ff5bc6ac492ef517cdd25c296ea0c Mon Sep 17 00:00:00 2001 From: Adam Burdett Date: Thu, 22 Sep 2022 09:18:22 -0600 Subject: [PATCH 487/872] profile check Signed-off-by: Adam Burdett --- aries_cloudagent/wallet/tests/test_in_memory_wallet.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/wallet/tests/test_in_memory_wallet.py b/aries_cloudagent/wallet/tests/test_in_memory_wallet.py index 8181fdcff8..4d1faeeb15 100644 --- a/aries_cloudagent/wallet/tests/test_in_memory_wallet.py +++ b/aries_cloudagent/wallet/tests/test_in_memory_wallet.py @@ -198,7 +198,8 @@ async def test_create_local_key_seeded_ed25519(self, wallet: InMemoryWallet): @pytest.mark.asyncio async def test_rotate_did_keypair(self, wallet: InMemoryWallet): - wallet.profile.context.injector.bind_instance(DIDMethods, DIDMethods()) + if wallet.profile: + wallet.profile.context.injector.bind_instance(DIDMethods, DIDMethods()) with pytest.raises(WalletNotFoundError): await wallet.rotate_did_keypair_start(self.test_sov_did) From cb97190b29c9dfdc52169722429a4921bdc7360d Mon Sep 17 00:00:00 2001 From: Adam Burdett Date: Thu, 22 Sep 2022 09:28:12 -0600 Subject: [PATCH 488/872] comment to trigger checks Signed-off-by: Adam Burdett --- aries_cloudagent/wallet/tests/test_in_memory_wallet.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aries_cloudagent/wallet/tests/test_in_memory_wallet.py b/aries_cloudagent/wallet/tests/test_in_memory_wallet.py index 4d1faeeb15..997b9827f9 100644 --- a/aries_cloudagent/wallet/tests/test_in_memory_wallet.py +++ b/aries_cloudagent/wallet/tests/test_in_memory_wallet.py @@ -198,7 +198,7 @@ async def test_create_local_key_seeded_ed25519(self, wallet: InMemoryWallet): @pytest.mark.asyncio async def test_rotate_did_keypair(self, wallet: InMemoryWallet): - if wallet.profile: + if wallet.profile: # check incase indysdkwallet is being used wallet.profile.context.injector.bind_instance(DIDMethods, DIDMethods()) with pytest.raises(WalletNotFoundError): await wallet.rotate_did_keypair_start(self.test_sov_did) From 1201a430ae8fd80defd478202b5ea171a8960d13 Mon Sep 17 00:00:00 2001 From: Adam Burdett Date: Thu, 22 Sep 2022 10:15:23 -0600 Subject: [PATCH 489/872] attr check Signed-off-by: Adam Burdett --- aries_cloudagent/wallet/tests/test_in_memory_wallet.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/wallet/tests/test_in_memory_wallet.py b/aries_cloudagent/wallet/tests/test_in_memory_wallet.py index 997b9827f9..218b14bae8 100644 --- a/aries_cloudagent/wallet/tests/test_in_memory_wallet.py +++ b/aries_cloudagent/wallet/tests/test_in_memory_wallet.py @@ -1,6 +1,8 @@ import pytest import time +from aries_cloudagent.core.in_memory import profile + from ...core.in_memory import InMemoryProfile from ...messaging.decorators.signature_decorator import SignatureDecorator from ...wallet.in_memory import InMemoryWallet @@ -198,7 +200,7 @@ async def test_create_local_key_seeded_ed25519(self, wallet: InMemoryWallet): @pytest.mark.asyncio async def test_rotate_did_keypair(self, wallet: InMemoryWallet): - if wallet.profile: # check incase indysdkwallet is being used + if hasattr(wallet, "profile"): # check incase indysdkwallet is being used wallet.profile.context.injector.bind_instance(DIDMethods, DIDMethods()) with pytest.raises(WalletNotFoundError): await wallet.rotate_did_keypair_start(self.test_sov_did) From bb71a636882fa867dd8dd29355f281e38197810a Mon Sep 17 00:00:00 2001 From: Anastasiia Date: Thu, 22 Sep 2022 19:13:54 +0200 Subject: [PATCH 490/872] Enable VP creation if multiple VCs requested Signed-off-by: Anastasiia --- demo/runners/agent_container.py | 56 ++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 15 deletions(-) diff --git a/demo/runners/agent_container.py b/demo/runners/agent_container.py index 7018ee046f..c980d8c89f 100644 --- a/demo/runners/agent_container.py +++ b/demo/runners/agent_container.py @@ -395,6 +395,10 @@ async def handle_present_proof_v2_0(self, message): ) pres_request_indy = message["by_format"].get("pres_request", {}).get("indy") pres_request_dif = message["by_format"].get("pres_request", {}).get("dif") + request = {} + + if not pres_request_dif and not pres_request_indy: + raise Exception("Invalid presentation request received") if pres_request_indy: # include self-attested attributes (not included in credentials) @@ -409,6 +413,8 @@ async def handle_present_proof_v2_0(self, message): f"/present-proof-2.0/records/{pres_ex_id}/credentials" ) if creds: + # select only indy credentials + creds = [x for x in creds if "cred_info" in x] if "timestamp" in creds[0]["cred_info"]["attrs"]: sorted_creds = sorted( creds, @@ -444,46 +450,58 @@ async def handle_present_proof_v2_0(self, message): ] } - log_status("#25 Generate the proof") - request = { + log_status("#25 Generate the indy proof") + indy_request = { "indy": { "requested_predicates": predicates, "requested_attributes": revealed, "self_attested_attributes": self_attested, } } + request.update(indy_request) except ClientError: pass - elif pres_request_dif: + if pres_request_dif: try: # select credentials to provide for the proof creds = await self.admin_GET( f"/present-proof-2.0/records/{pres_ex_id}/credentials" ) if creds and 0 < len(creds): + # select only dif credentials + creds = [x for x in creds if "issuanceDate" in x] creds = sorted( creds, key=lambda c: c["issuanceDate"], reverse=True, ) - record_id = creds[0]["record_id"] + records = creds else: - record_id = None + records = [] - log_status("#25 Generate the proof") - request = { + log_status("#25 Generate the dif proof") + dif_request = { "dif": {}, } # specify the record id for each input_descriptor id: - request["dif"]["record_ids"] = {} + dif_request["dif"]["record_ids"] = {} for input_descriptor in pres_request_dif["presentation_definition"][ "input_descriptors" ]: - request["dif"]["record_ids"][input_descriptor["id"]] = [ - record_id, - ] - log_msg("presenting ld-presentation:", request) + input_descriptor_schema_uri = [] + for element in input_descriptor["schema"]: + input_descriptor_schema_uri.append(element["uri"]) + + for record in records: + if self.check_input_descriptor_record_id(input_descriptor_schema_uri, record): + record_id = record["record_id"] + dif_request["dif"]["record_ids"][input_descriptor["id"]] = [ + record_id, + ] + break + log_msg("presenting ld-presentation:", dif_request) + request.update(dif_request) # NOTE that the holder/prover can also/or specify constraints by including the whole proof request # and constraining the presented credentials by adding filters, for example: @@ -508,9 +526,6 @@ async def handle_present_proof_v2_0(self, message): except ClientError: pass - else: - raise Exception("Invalid presentation request received") - log_status("#26 Send the proof to X: " + json.dumps(request)) await self.admin_POST( f"/present-proof-2.0/records/{pres_ex_id}/send-presentation", @@ -611,6 +626,17 @@ async def create_schema_and_cred_def( ) return cred_def_id + def check_input_descriptor_record_id(self, input_descriptor_schema_uri, record) -> bool: + result = False + for uri in input_descriptor_schema_uri: + for record_type in record["type"]: + if record_type in uri: + result = True + break + result = False + + return result + class AgentContainer: def __init__( From b4f4308319c49be95aa0bc12f9c5c9ef72fd3ab8 Mon Sep 17 00:00:00 2001 From: Anastasiia Date: Thu, 22 Sep 2022 19:17:18 +0200 Subject: [PATCH 491/872] Enable handling VP requests with both indy and dif Signed-off-by: Anastasiia --- .../protocols/present_proof/v2_0/manager.py | 10 +- .../protocols/present_proof/v2_0/routes.py | 15 +- .../present_proof/v2_0/tests/test_manager.py | 193 ++++++++++++++++++ .../present_proof/v2_0/tests/test_routes.py | 64 +++++- 4 files changed, 270 insertions(+), 12 deletions(-) diff --git a/aries_cloudagent/protocols/present_proof/v2_0/manager.py b/aries_cloudagent/protocols/present_proof/v2_0/manager.py index 1804d11e51..1f41dd617a 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/manager.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/manager.py @@ -263,9 +263,15 @@ async def create_pres( pres_exch_format = V20PresFormat.Format.get(format.format) if pres_exch_format: + if not request_data: + request_data_pres_exch = {} + else: + request_data_pres_exch = { + pres_exch_format.api: request_data.get(pres_exch_format.api) + } pres_tuple = await pres_exch_format.handler(self._profile).create_pres( pres_ex_record, - request_data, + request_data_pres_exch, ) if pres_tuple: pres_formats.append(pres_tuple) @@ -387,6 +393,8 @@ async def verify_pres(self, pres_ex_record: V20PresExRecord): ).verify_pres( pres_ex_record, ) + if pres_ex_record.verified == 'false': + break pres_ex_record.state = V20PresExRecord.STATE_DONE diff --git a/aries_cloudagent/protocols/present_proof/v2_0/routes.py b/aries_cloudagent/protocols/present_proof/v2_0/routes.py index 0c10096422..02d5b7fff3 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/routes.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/routes.py @@ -273,9 +273,10 @@ def validate_fields(self, data, **kwargs): ValidationError: if data does not have exactly one format. """ - if len(data.keys() & {f.api for f in V20PresFormat.Format}) != 1: + if len(data.keys() & {f.api for f in V20PresFormat.Format}) < 1: raise ValidationError( - "V20PresSpecByFormatRequestSchema must specify one presentation format" + "V20PresSpecByFormatRequestSchema must specify " + "at least one presentation format" ) @@ -1057,11 +1058,8 @@ async def present_proof_send_presentation(request: web.BaseRequest): outbound_handler = request["outbound_message_router"] pres_ex_id = request.match_info["pres_ex_id"] body = await request.json() - if "dif" in body: - fmt = V20PresFormat.Format.get("dif").api - elif "indy" in body: - fmt = V20PresFormat.Format.get("indy").api - else: + supported_formats = ["dif", "indy"] + if not any(x in body for x in supported_formats): raise web.HTTPBadRequest( reason=( "No presentation format specification provided, " @@ -1106,10 +1104,9 @@ async def present_proof_send_presentation(request: web.BaseRequest): pres_manager = V20PresManager(profile) try: - request_data = {fmt: body.get(fmt)} pres_ex_record, pres_message = await pres_manager.create_pres( pres_ex_record, - request_data=request_data, + request_data=body, comment=comment, ) result = pres_ex_record.serialize() diff --git a/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py b/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py index 9081c61946..b31428f22c 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py @@ -31,6 +31,7 @@ from ..formats.dif.handler import DIFPresFormatHandler from ..formats.dif.tests.test_handler import ( DIF_PRES_REQUEST_B as DIF_PRES_REQ, + DIF_PRES_REQUEST_A as DIF_PRES_REQ_ALT, DIF_PRES, ) from ..formats.indy import handler as test_indy_handler @@ -48,6 +49,10 @@ from ..messages.pres_request import V20PresRequest from ..models.pres_exchange import V20PresExRecord +from .....vc.vc_ld.validation_result import PresentationVerificationResult +from .....vc.tests.document_loader import custom_document_loader +from .....vc.ld_proofs import DocumentLoader + CONN_ID = "connection_id" ISSUER_DID = "NcYxiDXkpYi6ov5FcYDi1e" S_ID = f"{ISSUER_DID}:2:vidya:1.0" @@ -806,6 +811,65 @@ async def test_create_pres_indy(self): save_ex.assert_called_once() assert px_rec_out.state == V20PresExRecord.STATE_PRESENTATION_SENT + async def test_create_pres_indy_and_dif(self): + pres_request = V20PresRequest( + formats=[ + V20PresFormat( + attach_id="indy", + format_=ATTACHMENT_FORMAT[PRES_20_REQUEST][ + V20PresFormat.Format.INDY.api + ], + ), + V20PresFormat( + attach_id="dif", + format_=ATTACHMENT_FORMAT[PRES_20_REQUEST][ + V20PresFormat.Format.DIF.api + ], + ) + ], + request_presentations_attach=[ + AttachDecorator.data_base64(INDY_PROOF_REQ_NAME, ident="indy"), + AttachDecorator.data_json(DIF_PRES_REQ, ident="dif") + ], + + ) + px_rec_in = V20PresExRecord(pres_request=pres_request.serialize()) + more_magic_rr = async_mock.MagicMock( + get_or_fetch_local_tails_path=async_mock.CoroutineMock( + return_value="/tmp/sample/tails/path" + ) + ) + with async_mock.patch.object( + V20PresExRecord, "save", autospec=True + ) as save_ex, async_mock.patch.object( + test_indy_handler, "AttachDecorator", autospec=True + ) as mock_attach_decorator_indy, async_mock.patch.object( + test_indy_util_module, "RevocationRegistry", autospec=True + ) as mock_rr, async_mock.patch.object( + DIFPresFormatHandler, "create_pres", autospec=True + ) as mock_create_pres: + mock_rr.from_definition = async_mock.MagicMock(return_value=more_magic_rr) + + mock_attach_decorator_indy.data_base64 = async_mock.MagicMock( + return_value=mock_attach_decorator_indy + ) + + mock_create_pres.return_value = (PRES_20, AttachDecorator.data_json(DIF_PRES, ident="dif")) + + req_creds = await indy_proof_req_preview2indy_requested_creds( + INDY_PROOF_REQ_NAME, preview=None, holder=self.holder + ) + request_data = {"indy": req_creds, "dif": DIF_PRES_REQ} + assert not req_creds["self_attested_attributes"] + assert len(req_creds["requested_attributes"]) == 2 + assert len(req_creds["requested_predicates"]) == 1 + + (px_rec_out, pres_msg) = await self.manager.create_pres( + px_rec_in, request_data + ) + save_ex.assert_called_once() + assert px_rec_out.state == V20PresExRecord.STATE_PRESENTATION_SENT + async def test_create_pres_proof_req_non_revoc_interval_none(self): indy_proof_req_vcx = deepcopy(INDY_PROOF_REQ_NAME) indy_proof_req_vcx["non_revoked"] = None # simulate interop with indy-vcx @@ -2066,6 +2130,135 @@ async def test_verify_pres(self): assert px_rec_out.state == (V20PresExRecord.STATE_DONE) + async def test_verify_pres_indy_and_dif(self): + pres_request = V20PresRequest( + formats=[ + V20PresFormat( + attach_id="indy", + format_=ATTACHMENT_FORMAT[PRES_20_REQUEST][ + V20PresFormat.Format.INDY.api + ], + ), + V20PresFormat( + attach_id="dif", + format_=ATTACHMENT_FORMAT[PRES_20_REQUEST][ + V20PresFormat.Format.DIF.api + ], + ) + ], + will_confirm=True, + request_presentations_attach=[ + AttachDecorator.data_base64(INDY_PROOF_REQ_NAME, ident="indy"), + AttachDecorator.data_json(DIF_PRES_REQ, ident="dif") + ], + ) + pres = V20Pres( + formats=[ + V20PresFormat( + attach_id="indy", + format_=ATTACHMENT_FORMAT[PRES_20][V20PresFormat.Format.INDY.api], + ), + V20PresFormat( + attach_id="dif", + format_=ATTACHMENT_FORMAT[PRES_20][V20PresFormat.Format.DIF.api], + ) + ], + presentations_attach=[ + AttachDecorator.data_base64(INDY_PROOF, ident="indy"), + AttachDecorator.data_json(DIF_PRES, ident="dif") + ], + ) + px_rec_in = V20PresExRecord( + pres_request=pres_request, + pres=pres, + ) + + self.profile.context.injector.bind_instance(DocumentLoader, custom_document_loader) + self.profile.context.injector.bind_instance( + BaseMultitenantManager, + async_mock.MagicMock(MultitenantManager, autospec=True), + ) + with async_mock.patch.object( + IndyLedgerRequestsExecutor, + "get_ledger_for_identifier", + async_mock.CoroutineMock(return_value=("test_ledger_id", self.ledger)), + ), async_mock.patch.object(V20PresExRecord, "save", autospec=True) as save_ex: + px_rec_out = await self.manager.verify_pres(px_rec_in) + save_ex.assert_called_once() + + assert px_rec_out.state == (V20PresExRecord.STATE_DONE) + + async def test_verify_pres_indy_and_dif_false(self): + pres_request = V20PresRequest( + formats=[ + V20PresFormat( + attach_id="indy", + format_=ATTACHMENT_FORMAT[PRES_20_REQUEST][ + V20PresFormat.Format.INDY.api + ], + ), + V20PresFormat( + attach_id="dif", + format_=ATTACHMENT_FORMAT[PRES_20_REQUEST][ + V20PresFormat.Format.DIF.api + ], + ) + ], + will_confirm=True, + request_presentations_attach=[ + AttachDecorator.data_base64(INDY_PROOF_REQ_NAME, ident="indy"), + AttachDecorator.data_json(DIF_PRES_REQ_ALT, ident="dif") + ], + ) + pres = V20Pres( + formats=[ + V20PresFormat( + attach_id="indy", + format_=ATTACHMENT_FORMAT[PRES_20][V20PresFormat.Format.INDY.api], + ), + V20PresFormat( + attach_id="dif", + format_=ATTACHMENT_FORMAT[PRES_20][V20PresFormat.Format.DIF.api], + ) + ], + presentations_attach=[ + AttachDecorator.data_base64(INDY_PROOF, ident="indy"), + AttachDecorator.data_json(DIF_PRES, ident="dif") + ], + ) + px_rec_in = V20PresExRecord( + pres_request=pres_request, + pres=pres, + ) + self.profile.context.injector.bind_instance(DocumentLoader, custom_document_loader) + self.profile.context.injector.bind_instance( + BaseMultitenantManager, + async_mock.MagicMock(MultitenantManager, autospec=True), + ) + with async_mock.patch.object( + IndyLedgerRequestsExecutor, + "get_ledger_for_identifier", + async_mock.CoroutineMock(return_value=("test_ledger_id", self.ledger)), + ), async_mock.patch( + "aries_cloudagent.vc.vc_ld.verify.verify_presentation", + async_mock.CoroutineMock( + return_value=PresentationVerificationResult(verified=False) + ), + ), async_mock.patch.object( + IndyVerifier, + "verify_presentation", + async_mock.CoroutineMock( + return_value=PresentationVerificationResult(verified=True) + ), + ), async_mock.patch.object( + V20PresExRecord, "save", autospec=True + ) as save_ex: + px_rec_out = await self.manager.verify_pres(px_rec_in) + save_ex.assert_called_once() + + assert px_rec_out.state == (V20PresExRecord.STATE_DONE) + assert px_rec_out.verified == 'false' + async def test_send_pres_ack(self): px_rec = V20PresExRecord() diff --git a/aries_cloudagent/protocols/present_proof/v2_0/tests/test_routes.py b/aries_cloudagent/protocols/present_proof/v2_0/tests/test_routes.py index 934404203f..704c3c2751 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/tests/test_routes.py @@ -212,8 +212,7 @@ async def test_validate(self): schema = test_module.V20PresSpecByFormatRequestSchema() schema.validate_fields({"indy": {"...": "..."}}) schema.validate_fields({"dif": {"...": "..."}}) - with self.assertRaises(test_module.ValidationError): - schema.validate_fields({"indy": {"...": "..."}, "dif": {"...": "..."}}) + schema.validate_fields({"indy": {"...": "..."}, "dif": {"...": "..."}}) with self.assertRaises(test_module.ValidationError): schema.validate_fields({}) with self.assertRaises(test_module.ValidationError): @@ -1843,6 +1842,67 @@ async def test_present_proof_send_presentation_dif(self): mock_px_rec_inst.serialize.return_value ) + async def test_present_proof_send_presentation_indy_and_dif(self): + proof_req = deepcopy(DIF_PROOF_REQ) + proof_req["issuer_id"] = "test123" + self.request.json = async_mock.CoroutineMock( + return_value={ + "dif": proof_req, + "indy": { + "comment": "dummy", + "self_attested_attributes": {}, + "requested_attributes": {}, + "requested_predicates": {}, + } + } + ) + self.request.match_info = { + "pres_ex_id": "dummy", + } + self.profile.context.injector.bind_instance( + IndyVerifier, + async_mock.MagicMock( + verify_presentation=async_mock.CoroutineMock(), + ), + ) + + with async_mock.patch.object( + test_module, "ConnRecord", autospec=True + ) as mock_conn_rec_cls, async_mock.patch.object( + test_module, "V20PresManager", autospec=True + ) as mock_pres_mgr_cls, async_mock.patch.object( + test_module, "V20PresExRecord", autospec=True + ) as mock_px_rec_cls, async_mock.patch.object( + test_module.web, "json_response" + ) as mock_response: + mock_px_rec_inst = async_mock.MagicMock( + connection_id="dummy", + state=test_module.V20PresExRecord.STATE_REQUEST_RECEIVED, + serialize=async_mock.MagicMock( + return_value={"thread_id": "sample-thread-id"} + ), + ) + mock_px_rec_cls.retrieve_by_id = async_mock.CoroutineMock( + return_value=mock_px_rec_inst + ) + + mock_conn_rec_inst = async_mock.MagicMock(is_ready=True) + mock_conn_rec_cls.retrieve_by_id = async_mock.CoroutineMock( + return_value=mock_conn_rec_inst + ) + + mock_pres_mgr_inst = async_mock.MagicMock( + create_pres=async_mock.CoroutineMock( + return_value=(mock_px_rec_inst, async_mock.MagicMock()) + ) + ) + mock_pres_mgr_cls.return_value = mock_pres_mgr_inst + + await test_module.present_proof_send_presentation(self.request) + mock_response.assert_called_once_with( + mock_px_rec_inst.serialize.return_value + ) + async def test_present_proof_send_presentation_dif_error(self): self.request.json = async_mock.CoroutineMock( return_value={"dif": DIF_PROOF_REQ} From 339694a085855c27a7ba3f241e66a7ec8b4c1ffa Mon Sep 17 00:00:00 2001 From: Adam Burdett Date: Thu, 22 Sep 2022 11:18:23 -0600 Subject: [PATCH 492/872] key type registry. Signed-off-by: Adam Burdett --- aries_cloudagent/wallet/key_type.py | 137 ++++++++++++++++------------ 1 file changed, 79 insertions(+), 58 deletions(-) diff --git a/aries_cloudagent/wallet/key_type.py b/aries_cloudagent/wallet/key_type.py index 57defdb521..901da168c5 100644 --- a/aries_cloudagent/wallet/key_type.py +++ b/aries_cloudagent/wallet/key_type.py @@ -1,78 +1,99 @@ -"""Key type enum.""" +"""Key type code.""" -from enum import Enum -from typing import NamedTuple, Optional +from typing import Optional -# Define keys -KeySpec = NamedTuple( - "KeySpec", - [("key_type", str), ("multicodec_name", str), ("multicodec_prefix", int)], -) +class KeyType: + """Key Type class.""" -class KeyTypeException(BaseException): - """Key type exception.""" - - -class KeyType(Enum): - """KeyType Enum specifying key types with multicodec name.""" - - # NOTE: the py_multicodec library is outdated. We use hardcoded prefixes here - # until this PR gets released: https://github.com/multiformats/py-multicodec/pull/14 - # multicodec is also not used now, but may be used again if py_multicodec is updated - ED25519 = KeySpec("ed25519", "ed25519-pub", b"\xed\x01") - X25519 = KeySpec("x25519", "x25519-pub", b"\xec\x01") - BLS12381G1 = KeySpec("bls12381g1", "bls12_381-g1-pub", b"\xea\x01") - BLS12381G2 = KeySpec("bls12381g2", "bls12_381-g2-pub", b"\xeb\x01") - BLS12381G1G2 = KeySpec("bls12381g1g2", "bls12_381-g1g2-pub", b"\xee\x01") + def __init__(self, key_type, multicodec_name, multicodec_prefix: bytes) -> None: + """Construct key type.""" + self._type: str = key_type + self._name: str = multicodec_name + self._prefix: bytes = multicodec_prefix @property def key_type(self) -> str: - """Getter for key type identifier.""" - return self.value.key_type + """Get Key type, type.""" + return self._type @property def multicodec_name(self) -> str: - """Getter for multicodec name.""" - return self.value.multicodec_name + """Get key type multicodec name.""" + return self._name @property def multicodec_prefix(self) -> bytes: - """Getter for multicodec prefix.""" - return self.value.multicodec_prefix - - @classmethod - def from_multicodec_name(cls, multicodec_name: str) -> Optional["KeyType"]: - """Get KeyType instance based on multicodec name. Returns None if not found.""" - for key_type in KeyType: - if key_type.multicodec_name == multicodec_name: - return key_type + """Get key type multicodec prefix.""" + return self._prefix - return None - @classmethod - def from_multicodec_prefix(cls, multicodec_prefix: bytes) -> Optional["KeyType"]: - """Get KeyType instance based on multicodec prefix. Returns None if not found.""" - for key_type in KeyType: - if key_type.multicodec_prefix == multicodec_prefix: - return key_type +class KeyTypeException(BaseException): + """Key type exception.""" - return None - @classmethod - def from_prefixed_bytes(cls, prefixed_bytes: bytes) -> Optional["KeyType"]: - """Get KeyType instance based on prefix in bytes. Returns None if not found.""" - for key_type in KeyType: - if prefixed_bytes.startswith(key_type.multicodec_prefix): - return key_type +# NOTE: the py_multicodec library is outdated. We use hardcoded prefixes here +# until this PR gets released: https://github.com/multiformats/py-multicodec/pull/14 +# multicodec is also not used now, but may be used again if py_multicodec is updated +ED25519: KeyType = KeyType("ed25519", "ed25519-pub", b"\xed\x01") +X25519: KeyType = KeyType("x25519", "x25519-pub", b"\xec\x01") +BLS12381G1: KeyType = KeyType("bls12381g1", "bls12_381-g1-pub", b"\xea\x01") +BLS12381G2: KeyType = KeyType("bls12381g2", "bls12_381-g2-pub", b"\xeb\x01") +BLS12381G1G2: KeyType = KeyType("bls12381g1g2", "bls12_381-g1g2-pub", b"\xee\x01") + + +class KeyTypes: + """KeyType class specifying key types with multicodec name.""" + + def __init__(self) -> None: + """Construct key type registry.""" + self._type_registry: dict[str, KeyType] = { + ED25519.key_type: ED25519, + X25519.key_type: X25519, + BLS12381G1.key_type: BLS12381G1, + BLS12381G2.key_type: BLS12381G2, + BLS12381G1G2.key_type: BLS12381G1G2, + } + self._name_registry: dict[str, KeyType] = { + ED25519.multicodec_name: ED25519, + X25519.multicodec_name: X25519, + BLS12381G1.multicodec_name: BLS12381G1, + BLS12381G2.multicodec_name: BLS12381G2, + BLS12381G1G2.multicodec_name: BLS12381G1G2, + } + self._prefix_registry: dict[bytes, KeyType] = { + ED25519.multicodec_prefix: ED25519, + X25519.multicodec_prefix: X25519, + BLS12381G1.multicodec_prefix: BLS12381G1, + BLS12381G2.multicodec_prefix: BLS12381G2, + BLS12381G1G2.multicodec_prefix: BLS12381G1G2, + } + + def register(self, key_type: KeyType): + """Register a new key type.""" + self._type_registry[key_type.key_type] = key_type + self._name_registry[key_type.multicodec_name] = key_type + self._prefix_registry[key_type.multicodec_prefix] = key_type + + def from_multicodec_name(self, multicodec_name: str) -> Optional["KeyType"]: + """Get KeyType instance based on multicodec name. Returns None if not found.""" + return self._name_registry.get(multicodec_name) - return None + def from_multicodec_prefix(self, multicodec_prefix: bytes) -> Optional["KeyType"]: + """Get KeyType instance based on multicodec prefix. Returns None if not found.""" + return self._prefix_registry.get(multicodec_prefix) - @classmethod - def from_key_type(cls, key_type: str) -> Optional["KeyType"]: + def from_prefixed_bytes(self, prefixed_bytes: bytes) -> Optional["KeyType"]: + """Get KeyType instance based on prefix in bytes. Returns None if not found.""" + return next( + ( + key_type + for key_type in self._name_registry.values() + if prefixed_bytes.startswith(key_type.multicodec_prefix) + ), + None, + ) + + def from_key_type(self, key_type: str) -> Optional["KeyType"]: """Get KeyType instance from the key type identifier.""" - for _key_type in KeyType: - if _key_type.key_type == key_type: - return _key_type - - return None + return self._type_registry.get(key_type) From b2b5cd94f2ed1f4b508018c67c2477f9d9129ff0 Mon Sep 17 00:00:00 2001 From: Anastasiia Date: Thu, 22 Sep 2022 19:18:42 +0200 Subject: [PATCH 493/872] Enable handling VP requests with multiple json-lds Signed-off-by: Anastasiia --- .../messaging/decorators/attach_decorator.py | 7 +- .../present_proof/dif/pres_exch_handler.py | 153 +++++---- .../dif/tests/test_pres_exch_handler.py | 74 +++-- .../present_proof/v2_0/formats/dif/handler.py | 23 +- .../v2_0/formats/dif/tests/test_handler.py | 306 ++++++++++++++++++ .../present_proof/v2_0/messages/pres.py | 6 +- .../v2_0/messages/tests/test_pres.py | 152 +++++++++ 7 files changed, 626 insertions(+), 95 deletions(-) diff --git a/aries_cloudagent/messaging/decorators/attach_decorator.py b/aries_cloudagent/messaging/decorators/attach_decorator.py index 309ebc6a68..4fd2375c2c 100644 --- a/aries_cloudagent/messaging/decorators/attach_decorator.py +++ b/aries_cloudagent/messaging/decorators/attach_decorator.py @@ -30,6 +30,7 @@ from ..valid import ( BASE64, BASE64URL_NO_PAD, + DictOrDictListField, INDY_ISO8601_DATETIME, JWS_HEADER_KID, SHA256, @@ -228,7 +229,7 @@ def __init__( sha256_: str = None, links_: Union[Sequence[str], str] = None, base64_: str = None, - json_: dict = None, + json_: Union[Sequence[dict], dict] = None, ): """ Initialize decorator data. @@ -492,7 +493,7 @@ def validate_data_spec(self, data: Mapping, **kwargs): required=False, data_key="jws", ) - json_ = fields.Dict( + json_ = DictOrDictListField( description="JSON-serialized data", required=False, example='{"sample": "content"}', @@ -619,7 +620,7 @@ def data_base64( @classmethod def data_json( cls, - mapping: dict, + mapping: Union[Sequence[dict], dict], *, ident: str = None, description: str = None, diff --git a/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py b/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py index 638ce0fa3e..a625e3db02 100644 --- a/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py +++ b/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py @@ -1230,7 +1230,7 @@ async def create_vp( challenge: str = None, domain: str = None, records_filter: dict = None, - ) -> dict: + ) -> Union[Sequence[dict], dict]: """ Create VerifiablePresentation. @@ -1244,78 +1244,95 @@ async def create_vp( req = await self.make_requirement( srs=pd.submission_requirements, descriptors=pd.input_descriptors ) - result = await self.apply_requirements( - req=req, credentials=credentials, records_filter=records_filter - ) - applicable_creds, descriptor_maps = await self.merge(result) - applicable_creds_list = [] - for credential in applicable_creds: - applicable_creds_list.append(credential.cred_value) - if ( - not self.profile.settings.get("debug.auto_respond_presentation_request") - and not records_filter - and len(applicable_creds_list) > 1 - ): - raise DIFPresExchError( - "Multiple credentials are applicable for presentation_definition " - f"{pd.id} and --auto-respond-presentation-request setting is not " - "enabled. Please specify which credentials should be applied to " - "which input_descriptors using record_ids filter." - ) - # submission_property - submission_property = PresentationSubmission( - id=str(uuid4()), definition_id=pd.id, descriptor_maps=descriptor_maps - ) - if self.is_holder: - ( - issuer_id, - filtered_creds_list, - ) = await self.get_sign_key_credential_subject_id( - applicable_creds=applicable_creds - ) - if not issuer_id and len(filtered_creds_list) == 0: - vp = await create_presentation(credentials=applicable_creds_list) - vp["presentation_submission"] = submission_property.serialize() - if self.proof_type is BbsBlsSignature2020.signature_type: - vp["@context"].append(SECURITY_CONTEXT_BBS_URL) - return vp - else: - vp = await create_presentation(credentials=filtered_creds_list) + result = [] + if req.nested_req: + for nested_req in req.nested_req: + res = await self.apply_requirements( + req=nested_req, credentials=credentials, records_filter=records_filter + ) + result.append(res) else: - if not self.pres_signing_did: + res = await self.apply_requirements( + req=req, credentials=credentials, records_filter=records_filter + ) + result.append(res) + + result_vp = [] + for res in result: + applicable_creds, descriptor_maps = await self.merge(res) + applicable_creds_list = [] + for credential in applicable_creds: + applicable_creds_list.append(credential.cred_value) + if ( + not self.profile.settings.get("debug.auto_respond_presentation_request") + and not records_filter + and len(applicable_creds_list) > 1 + ): + raise DIFPresExchError( + "Multiple credentials are applicable for presentation_definition " + f"{pd.id} and --auto-respond-presentation-request setting is not " + "enabled. Please specify which credentials should be applied to " + "which input_descriptors using record_ids filter." + ) + # submission_property + submission_property = PresentationSubmission( + id=str(uuid4()), definition_id=pd.id, descriptor_maps=descriptor_maps + ) + if self.is_holder: ( issuer_id, filtered_creds_list, ) = await self.get_sign_key_credential_subject_id( applicable_creds=applicable_creds ) - if not issuer_id: + if not issuer_id and len(filtered_creds_list) == 0: vp = await create_presentation(credentials=applicable_creds_list) vp["presentation_submission"] = submission_property.serialize() if self.proof_type is BbsBlsSignature2020.signature_type: vp["@context"].append(SECURITY_CONTEXT_BBS_URL) - return vp + result_vp.append(vp) + continue else: vp = await create_presentation(credentials=filtered_creds_list) else: - issuer_id = self.pres_signing_did - vp = await create_presentation(credentials=applicable_creds_list) - vp["presentation_submission"] = submission_property.serialize() - if self.proof_type is BbsBlsSignature2020.signature_type: - vp["@context"].append(SECURITY_CONTEXT_BBS_URL) - async with self.profile.session() as session: - wallet = session.inject(BaseWallet) - issue_suite = await self._get_issue_suite( - wallet=wallet, - issuer_id=issuer_id, - ) - signed_vp = await sign_presentation( - presentation=vp, - suite=issue_suite, - challenge=challenge, - document_loader=document_loader, - ) - return signed_vp + if not self.pres_signing_did: + ( + issuer_id, + filtered_creds_list, + ) = await self.get_sign_key_credential_subject_id( + applicable_creds=applicable_creds + ) + if not issuer_id: + vp = await create_presentation(credentials=applicable_creds_list) + vp["presentation_submission"] = submission_property.serialize() + if self.proof_type is BbsBlsSignature2020.signature_type: + vp["@context"].append(SECURITY_CONTEXT_BBS_URL) + result_vp.append(vp) + continue + else: + vp = await create_presentation(credentials=filtered_creds_list) + else: + issuer_id = self.pres_signing_did + vp = await create_presentation(credentials=applicable_creds_list) + vp["presentation_submission"] = submission_property.serialize() + if self.proof_type is BbsBlsSignature2020.signature_type: + vp["@context"].append(SECURITY_CONTEXT_BBS_URL) + async with self.profile.session() as session: + wallet = session.inject(BaseWallet) + issue_suite = await self._get_issue_suite( + wallet=wallet, + issuer_id=issuer_id, + ) + signed_vp = await sign_presentation( + presentation=vp, + suite=issue_suite, + challenge=challenge, + document_loader=document_loader, + ) + result_vp.append(signed_vp) + if len(result_vp) == 1: + return result_vp[0] + return result_vp def check_if_cred_id_derived(self, id: str) -> bool: """Check if credential or credentialSubjet id is derived.""" @@ -1367,7 +1384,7 @@ async def merge( async def verify_received_pres( self, pd: PresentationDefinition, - pres: dict, + pres: Union[Sequence[dict], dict], ): """ Verify credentials received in presentation. @@ -1376,8 +1393,22 @@ async def verify_received_pres( pres: received VerifiablePresentation pd: PresentationDefinition """ - descriptor_map_list = pres["presentation_submission"].get("descriptor_map") input_descriptors = pd.input_descriptors + if isinstance(pres, Sequence): + for pr in pres: + descriptor_map_list = pr["presentation_submission"].get("descriptor_map") + await self.__verify_desc_map_list( + descriptor_map_list, + pr, + input_descriptors) + else: + descriptor_map_list = pres["presentation_submission"].get("descriptor_map") + await self.__verify_desc_map_list( + descriptor_map_list, + pres, + input_descriptors) + + async def __verify_desc_map_list(self, descriptor_map_list, pres, input_descriptors): inp_desc_id_contraint_map = {} inp_desc_id_schema_one_of_filter = set() inp_desc_id_schemas_map = {} diff --git a/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py b/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py index 87587eb674..312926f016 100644 --- a/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py +++ b/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py @@ -1,6 +1,7 @@ import asyncio from copy import deepcopy from datetime import datetime +from typing import Sequence from uuid import uuid4 import mock as async_mock @@ -103,7 +104,15 @@ async def test_load_cred_json_a(self, setup_tuple, profile): pd=tmp_pd[0], challenge="1f44d55f-f161-4938-a659-f8026467f126", ) - assert len(tmp_vp.get("verifiableCredential")) == tmp_pd[1] + + if isinstance(tmp_vp, Sequence): + cred_count_list = [] + for tmp_vp_single in tmp_vp: + cred_count_list.append(len(tmp_vp_single.get("verifiableCredential"))) + + assert min(cred_count_list) == tmp_pd[1] + else: + assert len(tmp_vp.get("verifiableCredential")) == tmp_pd[1] @pytest.mark.asyncio @pytest.mark.ursa_bbs_signatures @@ -120,7 +129,15 @@ async def test_load_cred_json_b(self, setup_tuple, profile): pd=tmp_pd[0], challenge="1f44d55f-f161-4938-a659-f8026467f126", ) - assert len(tmp_vp.get("verifiableCredential")) == tmp_pd[1] + + if isinstance(tmp_vp, Sequence): + cred_count_list = [] + for tmp_vp_single in tmp_vp: + cred_count_list.append(len(tmp_vp_single.get("verifiableCredential"))) + + assert min(cred_count_list) == tmp_pd[1] + else: + assert len(tmp_vp.get("verifiableCredential")) == tmp_pd[1] @pytest.mark.asyncio async def test_to_requirement_catch_errors(self, profile): @@ -2264,11 +2281,12 @@ async def test_create_vp_no_issuer(self, profile, setup_tuple): pd=pd_list[0][0], challenge="3fa85f64-5717-4562-b3fc-2c963f66afa7", ) - assert vp["test"] == "1" - assert ( - vp["presentation_submission"]["definition_id"] - == "32f54163-7166-48f1-93d8-ff217bdb0653" - ) + for vp_single in vp: + assert vp_single["test"] == "1" + assert ( + vp_single["presentation_submission"]["definition_id"] + == "32f54163-7166-48f1-93d8-ff217bdb0653" + ) @pytest.mark.asyncio @pytest.mark.ursa_bbs_signatures @@ -2324,8 +2342,9 @@ async def test_create_vp_with_bbs_suite(self, profile, setup_tuple): pd=pd_list[0][0], challenge="3fa85f64-5717-4562-b3fc-2c963f66afa7", ) - assert vp["test"] == "1" - assert SECURITY_CONTEXT_BBS_URL in vp["@context"] + for vp_single in vp: + assert vp_single["test"] == "1" + assert SECURITY_CONTEXT_BBS_URL in vp_single["@context"] @pytest.mark.asyncio @pytest.mark.ursa_bbs_signatures @@ -2378,8 +2397,10 @@ async def test_create_vp_no_issuer_with_bbs_suite(self, profile, setup_tuple): pd=pd_list[0][0], challenge="3fa85f64-5717-4562-b3fc-2c963f66afa7", ) - assert vp["test"] == "1" - assert SECURITY_CONTEXT_BBS_URL in vp["@context"] + #2 sub_reqs, vp is a sequence + for vp_single in vp: + assert vp_single["test"] == "1" + assert SECURITY_CONTEXT_BBS_URL in vp_single["@context"] @pytest.mark.asyncio @pytest.mark.ursa_bbs_signatures @@ -3055,6 +3076,8 @@ async def test_multiple_applicable_creds_with_no_id(self, profile, setup_tuple): pd=tmp_pd[0], challenge="1f44d55f-f161-4938-a659-f8026467f126", ) + # only 1 sub_req + assert isinstance(tmp_vp, dict) assert len(tmp_vp["verifiableCredential"]) == 2 assert ( tmp_vp.get("verifiableCredential")[0] @@ -3075,19 +3098,22 @@ async def test_multiple_applicable_creds_with_no_id(self, profile, setup_tuple): pd=tmp_pd[0], challenge="1f44d55f-f161-4938-a659-f8026467f126", ) - assert len(tmp_vp["verifiableCredential"]) == 2 - assert ( - tmp_vp.get("verifiableCredential")[0] - .get("credentialSubject") - .get("givenName") - == "TEST" - ) - assert ( - tmp_vp.get("verifiableCredential")[1] - .get("credentialSubject") - .get("givenName") - == "TEST" - ) + assert isinstance(tmp_vp, Sequence) + # 1 for each submission requirement group + assert len(tmp_vp) == 3 + for tmp_vp_single in tmp_vp: + assert ( + tmp_vp_single.get("verifiableCredential")[0] + .get("credentialSubject") + .get("givenName") + == "TEST" + ) + assert ( + tmp_vp_single.get("verifiableCredential")[1] + .get("credentialSubject") + .get("givenName") + == "TEST" + ) @pytest.mark.asyncio @pytest.mark.ursa_bbs_signatures diff --git a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/handler.py b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/handler.py index 9efff76b16..2e9fff4b42 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/handler.py @@ -474,11 +474,22 @@ async def verify_pres(self, pres_ex_record: V20PresExRecord) -> V20PresExRecord: challenge = pres_request["options"].get("challenge", str(uuid4())) if not challenge: challenge = str(uuid4()) - pres_ver_result = await verify_presentation( - presentation=dif_proof, - suites=await self._get_all_suites(wallet=wallet), - document_loader=self._profile.inject(DocumentLoader), - challenge=challenge, - ) + if(isinstance(dif_proof, Sequence)): + for proof in dif_proof: + pres_ver_result = await verify_presentation( + presentation=proof, + suites=await self._get_all_suites(wallet=wallet), + document_loader=self._profile.inject(DocumentLoader), + challenge=challenge, + ) + if not pres_ver_result.verified: + break + else: + pres_ver_result = await verify_presentation( + presentation=dif_proof, + suites=await self._get_all_suites(wallet=wallet), + document_loader=self._profile.inject(DocumentLoader), + challenge=challenge, + ) pres_ex_record.verified = json.dumps(pres_ver_result.verified) return pres_ex_record diff --git a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py index 85d4a9b8a2..b2f0393bd6 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py @@ -134,6 +134,75 @@ }, } +DIF_PRES_REQUEST_SEQUENCE = { + "options": { + "challenge": "3fa85f64-5717-4562-b3fc-2c963f66afa7", + "domain": "4jt78h47fh47", + }, + "presentation_definition": { + "id": "32f54163-7166-48f1-93d8-ff217bdb0654", + "submission_requirements": [ + { + "name": "Citizenship Information", + "rule": "all", + "from": "A", + }, + { + "name": "Citizenship Information v2", + "rule": "pick", + "min": 1, + "from": "B", + } + ], + "input_descriptors": [ + { + "id": "citizenship_input_1", + "name": "EU Driver's License", + "group": ["A"], + "schema": [ + {"uri": "https://www.w3.org/2018/credentials#VerifiableCredential"}, + {"uri": "https://w3id.org/citizenship#PermanentResidentCard"}, + ], + "constraints": { + "limit_disclosure": "required", + "fields": [ + { + "path": ["$.credentialSubject.givenName"], + "purpose": "The claim must be from one of the specified issuers", + "filter": { + "type": "string", + "enum": ["JOHN", "CAI"], + }, + } + ], + }, + }, + { + "id": "citizenship_input_2", + "name": "EU Driver's License", + "group": ["B"], + "schema": [ + {"uri": "https://www.w3.org/2018/credentials#VerifiableCredential"}, + {"uri": "https://w3id.org/citizenship#PermanentResidentCard"}, + ], + "constraints": { + "limit_disclosure": "required", + "fields": [ + { + "path": ["$.credentialSubject.givenName"], + "purpose": "The claim must be from one of the specified issuers", + "filter": { + "type": "string", + "enum": ["JOHN", "CAI"], + }, + } + ], + }, + } + ], + }, +} + DIF_PRES_PROPOSAL = { "input_descriptors": [ { @@ -214,6 +283,106 @@ }, } +DIF_PRES_SEQUENCE = [{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "type": ["VerifiablePresentation"], + "verifiableCredential": [ + { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://w3id.org/citizenship/v1", + "https://w3id.org/security/bbs/v1", + ], + "id": "https://issuer.oidp.uscis.gov/credentials/83627465", + "type": ["PermanentResidentCard", "VerifiableCredential"], + "credentialSubject": { + "id": "did:example:b34ca6cd37bbf23", + "type": ["Person", "PermanentResident"], + "givenName": "JOHN", + }, + "issuanceDate": "2010-01-01T19:53:24Z", + "issuer": "did:key:zUC74bgefTdc43KS1psXgXf4jLaHyaj2qCQqQTXrtmSYGf1PxiJhrH6LGpaBMyj6tqAKmjGyMaS4RfNo2an77vT1HfzJUNPk4H7TCuJvSp4vet4Cu67kn2JSegoQNFSA1tbwU8v", + "proof": { + "type": "BbsBlsSignatureProof2020", + "nonce": "3AuruhJQrXtEgiagiJ+FwVf2S0SnzUDJvnO61YecQsJ7ImR1mPcoVjJJ0HOhfkFpoYI=", + "proofValue": "ABkBuAaPlP5A7JWY78Xf69oBnsMLcD1RXbIFYhcLoXPXW12CG9glnnqnPLsGri5xsA3LcP0kg74X+sAjKXGRGy3uvp412Dm0FuohYNboQcLne5KOAa5AxU4bjmwQsxdfduVqhriro1N+YTkuB4SMmO/5ooL0N3OHsYdExg7nSzWqmZoqgp+3CwIxF0a/oyKTcxJORuIqAAAAdInlL9teSIX49NJGEZfBO7IrdjT2iggH/G0AlPWoEvrWIbuCRQ69K83n5o7oJVjqhAAAAAIaVmlAD6+FEKA4eg0OaWOKPrd5Kq8rv0vIwjJ71egxll0Fqq4zDWQ/+yl3Pteh0Wyuyvpm19/sj6tiCWj4PkA+rpxtR2bXpnrCTKUffFFNBjVvVziXDS0KWkGUB7XU9mjUa4USC7Iub3bZZCnFjQA5AAAADzkGwGD837r33e7OTrGEti8eAkvFDcyCgA4ck/X+5HJjAJclHWbl4SNQR8CiNZyzJpvxW+jbNBcwmEvocYArddk3F78Ki0Qnp6aU9eDgfOOx1iW2BXLUjrhq5I2hP5/WQF3CEDYRjczGjzM9T8/coeC36YAp0zJunIXUKb8SPDSOISafibYRYFB4xhlWKXWloDelafyujOBST8KZNM8FmF4DSbXrO8vmZbjuR/8ntUcUK7X2rNbuZ3M5eWZDF8pL+SA9gQitKfPHEocoYAdhgEAM7ZNAJ+TgOcx9gtZIhDWKDNnFxIeoOAylbD1xZd9xbWtq3Bk3R79xqsKxFRJRNxk/9b6fJruP292+qM5lxcZ1jUz/dJUYFI93hH4Mso75CjGRN78MAY9SNifl6H8qcxTpBn4332LlFhRznLbtnc4YSWA/fvVqaN9h2zCH/6AdbLKXGffV34EF7DadwJsi9jsc+YlSMn6qaIUIDTdGLwh4KKpSH5bVbg/mVCcXPTJplFgYwRsOdiQbZY/740dJyo1lPjQ0Lvdio8W2M8c73ujeJU70CNLkgjJAMUPGrCFtGxBH2eeLBQ0P95qRZAIcJ7U0MibZLaRjoUOuTla5BIt2038PJ6XhcY6BEJaLyJOPEQ==", + "verificationMethod": "did:key:zUC74bgefTdc43KS1psXgXf4jLaHyaj2qCQqQTXrtmSYGf1PxiJhrH6LGpaBMyj6tqAKmjGyMaS4RfNo2an77vT1HfzJUNPk4H7TCuJvSp4vet4Cu67kn2JSegoQNFSA1tbwU8v#zUC74bgefTdc43KS1psXgXf4jLaHyaj2qCQqQTXrtmSYGf1PxiJhrH6LGpaBMyj6tqAKmjGyMaS4RfNo2an77vT1HfzJUNPk4H7TCuJvSp4vet4Cu67kn2JSegoQNFSA1tbwU8v", + "proofPurpose": "assertionMethod", + "created": "2021-05-05T15:22:30.523465", + }, + } + ], + "presentation_submission": { + "id": "a5fcfe44-2c30-497d-af02-98e539da9a0f", + "definition_id": "32f54163-7166-48f1-93d8-ff217bdb0653", + "descriptor_map": [ + { + "id": "citizenship_input_1", + "format": "ldp_vp", + "path": "$.verifiableCredential[0]", + } + ], + }, + "proof": { + "type": "Ed25519Signature2018", + "verificationMethod": "did:sov:4QxzWk3ajdnEA37NdNU5Kt#key-1", + "created": "2021-05-05T15:23:03.023971", + "proofPurpose": "authentication", + "challenge": "40429d49-5e8f-4ffc-baf8-e332412f1247", + "jws": "eyJhbGciOiAiRWREU0EiLCAiYjY0IjogZmFsc2UsICJjcml0IjogWyJiNjQiXX0..2uBYmg7muE9ZPVeAGo_ibVfLkCjf2hGshr2o5i8pAwFyNBM-kDHXofuq1MzJgb19wzb01VIu91hY_ajjt9KFAA", + }, +}, +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "type": ["VerifiablePresentation"], + "verifiableCredential": [ + { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://w3id.org/citizenship/v1", + "https://w3id.org/security/bbs/v1", + ], + "id": "https://issuer.oidp.uscis.gov/credentials/83627465", + "type": ["PermanentResidentCard", "VerifiableCredential"], + "credentialSubject": { + "id": "did:example:b34ca6cd37bbf23", + "type": ["Person", "PermanentResident"], + "givenName": "JOHN", + }, + "issuanceDate": "2010-01-01T19:53:24Z", + "issuer": "did:key:zUC74bgefTdc43KS1psXgXf4jLaHyaj2qCQqQTXrtmSYGf1PxiJhrH6LGpaBMyj6tqAKmjGyMaS4RfNo2an77vT1HfzJUNPk4H7TCuJvSp4vet4Cu67kn2JSegoQNFSA1tbwU8v", + "proof": { + "type": "BbsBlsSignatureProof2020", + "nonce": "3AuruhJQrXtEgiagiJ+FwVf2S0SnzUDJvnO61YecQsJ7ImR1mPcoVjJJ0HOhfkFpoYI=", + "proofValue": "ABkBuAaPlP5A7JWY78Xf69oBnsMLcD1RXbIFYhcLoXPXW12CG9glnnqnPLsGri5xsA3LcP0kg74X+sAjKXGRGy3uvp412Dm0FuohYNboQcLne5KOAa5AxU4bjmwQsxdfduVqhriro1N+YTkuB4SMmO/5ooL0N3OHsYdExg7nSzWqmZoqgp+3CwIxF0a/oyKTcxJORuIqAAAAdInlL9teSIX49NJGEZfBO7IrdjT2iggH/G0AlPWoEvrWIbuCRQ69K83n5o7oJVjqhAAAAAIaVmlAD6+FEKA4eg0OaWOKPrd5Kq8rv0vIwjJ71egxll0Fqq4zDWQ/+yl3Pteh0Wyuyvpm19/sj6tiCWj4PkA+rpxtR2bXpnrCTKUffFFNBjVvVziXDS0KWkGUB7XU9mjUa4USC7Iub3bZZCnFjQA5AAAADzkGwGD837r33e7OTrGEti8eAkvFDcyCgA4ck/X+5HJjAJclHWbl4SNQR8CiNZyzJpvxW+jbNBcwmEvocYArddk3F78Ki0Qnp6aU9eDgfOOx1iW2BXLUjrhq5I2hP5/WQF3CEDYRjczGjzM9T8/coeC36YAp0zJunIXUKb8SPDSOISafibYRYFB4xhlWKXWloDelafyujOBST8KZNM8FmF4DSbXrO8vmZbjuR/8ntUcUK7X2rNbuZ3M5eWZDF8pL+SA9gQitKfPHEocoYAdhgEAM7ZNAJ+TgOcx9gtZIhDWKDNnFxIeoOAylbD1xZd9xbWtq3Bk3R79xqsKxFRJRNxk/9b6fJruP292+qM5lxcZ1jUz/dJUYFI93hH4Mso75CjGRN78MAY9SNifl6H8qcxTpBn4332LlFhRznLbtnc4YSWA/fvVqaN9h2zCH/6AdbLKXGffV34EF7DadwJsi9jsc+YlSMn6qaIUIDTdGLwh4KKpSH5bVbg/mVCcXPTJplFgYwRsOdiQbZY/740dJyo1lPjQ0Lvdio8W2M8c73ujeJU70CNLkgjJAMUPGrCFtGxBH2eeLBQ0P95qRZAIcJ7U0MibZLaRjoUOuTla5BIt2038PJ6XhcY6BEJaLyJOPEQ==", + "verificationMethod": "did:key:zUC74bgefTdc43KS1psXgXf4jLaHyaj2qCQqQTXrtmSYGf1PxiJhrH6LGpaBMyj6tqAKmjGyMaS4RfNo2an77vT1HfzJUNPk4H7TCuJvSp4vet4Cu67kn2JSegoQNFSA1tbwU8v#zUC74bgefTdc43KS1psXgXf4jLaHyaj2qCQqQTXrtmSYGf1PxiJhrH6LGpaBMyj6tqAKmjGyMaS4RfNo2an77vT1HfzJUNPk4H7TCuJvSp4vet4Cu67kn2JSegoQNFSA1tbwU8v", + "proofPurpose": "assertionMethod", + "created": "2021-05-05T15:22:30.523465", + }, + } + ], + "presentation_submission": { + "id": "a5fcfe44-2c30-497d-af02-98e539da9a0f", + "definition_id": "32f54163-7166-48f1-93d8-ff217bdb0653", + "descriptor_map": [ + { + "id": "citizenship_input_2", + "format": "ldp_vp", + "path": "$.verifiableCredential[0]", + } + ], + }, + "proof": { + "type": "Ed25519Signature2018", + "verificationMethod": "did:sov:4QxzWk3ajdnEA37NdNU5Kt#key-1", + "created": "2021-05-05T15:23:03.023971", + "proofPurpose": "authentication", + "challenge": "40429d49-5e8f-4ffc-baf8-e332412f1247", + "jws": "eyJhbGciOiAiRWREU0EiLCAiYjY0IjogZmFsc2UsICJjcml0IjogWyJiNjQiXX0..2uBYmg7muE9ZPVeAGo_ibVfLkCjf2hGshr2o5i8pAwFyNBM-kDHXofuq1MzJgb19wzb01VIu91hY_ajjt9KFAA", + }, +}] + + TEST_CRED = { "@context": [ "https://www.w3.org/2018/credentials/v1", @@ -1001,6 +1170,100 @@ async def test_create_pres_pd_claim_format_bls12381g2(self): ) assert output[1].data.json_ == DIF_PRES + async def test_verify_pres_sequence(self): + dif_pres = V20Pres( + formats=[ + V20PresFormat( + attach_id="dif", + format_=ATTACHMENT_FORMAT[PRES_20][V20PresFormat.Format.DIF.api], + ) + ], + presentations_attach=[AttachDecorator.data_json(DIF_PRES_SEQUENCE, ident="dif")], + ) + dif_pres_request = V20PresRequest( + formats=[ + V20PresFormat( + attach_id="dif", + format_=ATTACHMENT_FORMAT[PRES_20_REQUEST][ + V20PresFormat.Format.DIF.api + ], + ) + ], + request_presentations_attach=[ + AttachDecorator.data_json(DIF_PRES_REQUEST_SEQUENCE, ident="dif") + ], + ) + record = V20PresExRecord( + pres_ex_id="pxid", + thread_id="thid", + connection_id="conn_id", + initiator="init", + role="role", + state="state", + pres_request=dif_pres_request, + pres=dif_pres, + verified="false", + auto_present=True, + error_msg="error", + ) + + with async_mock.patch.object( + test_module, + "verify_presentation", + async_mock.CoroutineMock( + return_value=PresentationVerificationResult(verified=True) + ), + ): + output = await self.handler.verify_pres(record) + assert output.verified + + async def test_verify_pres_sequence_one_pres_false(self): + dif_pres = V20Pres( + formats=[ + V20PresFormat( + attach_id="dif", + format_=ATTACHMENT_FORMAT[PRES_20][V20PresFormat.Format.DIF.api], + ) + ], + presentations_attach=[AttachDecorator.data_json([DIF_PRES, {}], ident="dif")], + ) + dif_pres_request = V20PresRequest( + formats=[ + V20PresFormat( + attach_id="dif", + format_=ATTACHMENT_FORMAT[PRES_20_REQUEST][ + V20PresFormat.Format.DIF.api + ], + ) + ], + request_presentations_attach=[ + AttachDecorator.data_json(DIF_PRES_REQUEST_SEQUENCE, ident="dif") + ], + ) + record = V20PresExRecord( + pres_ex_id="pxid", + thread_id="thid", + connection_id="conn_id", + initiator="init", + role="role", + state="state", + pres_request=dif_pres_request, + pres=dif_pres, + verified="false", + auto_present=True, + error_msg="error", + ) + + with async_mock.patch.object( + test_module, + "verify_presentation", + async_mock.CoroutineMock( + return_value=PresentationVerificationResult(verified=False) + ), + ): + output = await self.handler.verify_pres(record) + assert output.verified == "false" + async def test_verify_pres(self): dif_pres = V20Pres( formats=[ @@ -1514,6 +1777,49 @@ async def test_verify_received_pres_c(self): ) await self.handler.receive_pres(message=dif_pres, pres_ex_record=record) + async def test_verify_received_pres_sequence(self): + dif_pres = V20Pres( + formats=[ + V20PresFormat( + attach_id="dif", + format_=ATTACHMENT_FORMAT[PRES_20][V20PresFormat.Format.DIF.api], + ) + ], + presentations_attach=[ + AttachDecorator.data_json( + mapping=DIF_PRES_SEQUENCE, + ident="dif", + ) + ], + ) + dif_pres_request = V20PresRequest( + formats=[ + V20PresFormat( + attach_id="dif", + format_=ATTACHMENT_FORMAT[PRES_20_REQUEST][ + V20PresFormat.Format.DIF.api + ], + ) + ], + request_presentations_attach=[ + AttachDecorator.data_json(DIF_PRES_REQUEST_SEQUENCE, ident="dif") + ], + ) + record = V20PresExRecord( + pres_ex_id="pxid", + thread_id="thid", + connection_id="conn_id", + initiator="init", + role="role", + state="state", + pres_request=dif_pres_request, + pres=dif_pres, + verified="false", + auto_present=True, + error_msg="error", + ) + await self.handler.receive_pres(message=dif_pres, pres_ex_record=record) + async def test_verify_received_limit_disclosure_a(self): dif_proof = deepcopy(DIF_PRES) cred_dict = deepcopy(TEST_CRED_DICT) diff --git a/aries_cloudagent/protocols/present_proof/v2_0/messages/pres.py b/aries_cloudagent/protocols/present_proof/v2_0/messages/pres.py index d6b7861d9e..e3c1ba902b 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/messages/pres.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/messages/pres.py @@ -118,4 +118,8 @@ def get_attach_by_id(attach_id): atch = get_attach_by_id(fmt.attach_id) pres_format = V20PresFormat.Format.get(fmt.format) if pres_format: - pres_format.validate_fields(PRES_20, atch.content) + if(isinstance(atch.content, Sequence)): + for el in atch.content: + pres_format.validate_fields(PRES_20, el) + else: + pres_format.validate_fields(PRES_20, atch.content) diff --git a/aries_cloudagent/protocols/present_proof/v2_0/messages/tests/test_pres.py b/aries_cloudagent/protocols/present_proof/v2_0/messages/tests/test_pres.py index a9c5112337..dfc79e2181 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/messages/tests/test_pres.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/messages/tests/test_pres.py @@ -1662,6 +1662,129 @@ }""" ) +DIF_PROOF = json.loads( + """[ + { + "@context":[ + "https://www.w3.org/2018/credentials/v1" + ], + "type":[ + "VerifiablePresentation" + ], + "verifiableCredential":[ + { + "@context":[ + "https://www.w3.org/2018/credentials/v1", + "https://w3id.org/citizenship/v1", + "https://w3id.org/security/bbs/v1" + ], + "id":"https://issuer.oidp.uscis.gov/credentials/83627465", + "type":[ + "PermanentResidentCard", + "VerifiableCredential" + ], + "credentialSubject":{ + "id":"did:example:b34ca6cd37bbf23", + "type":[ + "Person", + "PermanentResident" + ], + "givenName":"JOHN" + }, + "issuanceDate":"2010-01-01T19:53:24Z", + "issuer":"did:key:zUC74bgefTdc43KS1psXgXf4jLaHyaj2qCQqQTXrtmSYGf1PxiJhrH6LGpaBMyj6tqAKmjGyMaS4RfNo2an77vT1HfzJUNPk4H7TCuJvSp4vet4Cu67kn2JSegoQNFSA1tbwU8v", + "proof":{ + "type":"BbsBlsSignatureProof2020", + "nonce":"3AuruhJQrXtEgiagiJ+FwVf2S0SnzUDJvnO61YecQsJ7ImR1mPcoVjJJ0HOhfkFpoYI=", + "proofValue":"ABkBuAaPlP5A7JWY78Xf69oBnsMLcD1RXbIFYhcLoXPXW12CG9glnnqnPLsGri5xsA3LcP0kg74X+sAjKXGRGy3uvp412Dm0FuohYNboQcLne5KOAa5AxU4bjmwQsxdfduVqhriro1N+YTkuB4SMmO/5ooL0N3OHsYdExg7nSzWqmZoqgp+3CwIxF0a/oyKTcxJORuIqAAAAdInlL9teSIX49NJGEZfBO7IrdjT2iggH/G0AlPWoEvrWIbuCRQ69K83n5o7oJVjqhAAAAAIaVmlAD6+FEKA4eg0OaWOKPrd5Kq8rv0vIwjJ71egxll0Fqq4zDWQ/+yl3Pteh0Wyuyvpm19/sj6tiCWj4PkA+rpxtR2bXpnrCTKUffFFNBjVvVziXDS0KWkGUB7XU9mjUa4USC7Iub3bZZCnFjQA5AAAADzkGwGD837r33e7OTrGEti8eAkvFDcyCgA4ck/X+5HJjAJclHWbl4SNQR8CiNZyzJpvxW+jbNBcwmEvocYArddk3F78Ki0Qnp6aU9eDgfOOx1iW2BXLUjrhq5I2hP5/WQF3CEDYRjczGjzM9T8/coeC36YAp0zJunIXUKb8SPDSOISafibYRYFB4xhlWKXWloDelafyujOBST8KZNM8FmF4DSbXrO8vmZbjuR/8ntUcUK7X2rNbuZ3M5eWZDF8pL+SA9gQitKfPHEocoYAdhgEAM7ZNAJ+TgOcx9gtZIhDWKDNnFxIeoOAylbD1xZd9xbWtq3Bk3R79xqsKxFRJRNxk/9b6fJruP292+qM5lxcZ1jUz/dJUYFI93hH4Mso75CjGRN78MAY9SNifl6H8qcxTpBn4332LlFhRznLbtnc4YSWA/fvVqaN9h2zCH/6AdbLKXGffV34EF7DadwJsi9jsc+YlSMn6qaIUIDTdGLwh4KKpSH5bVbg/mVCcXPTJplFgYwRsOdiQbZY/740dJyo1lPjQ0Lvdio8W2M8c73ujeJU70CNLkgjJAMUPGrCFtGxBH2eeLBQ0P95qRZAIcJ7U0MibZLaRjoUOuTla5BIt2038PJ6XhcY6BEJaLyJOPEQ==", + "verificationMethod":"did:key:zUC74bgefTdc43KS1psXgXf4jLaHyaj2qCQqQTXrtmSYGf1PxiJhrH6LGpaBMyj6tqAKmjGyMaS4RfNo2an77vT1HfzJUNPk4H7TCuJvSp4vet4Cu67kn2JSegoQNFSA1tbwU8v#zUC74bgefTdc43KS1psXgXf4jLaHyaj2qCQqQTXrtmSYGf1PxiJhrH6LGpaBMyj6tqAKmjGyMaS4RfNo2an77vT1HfzJUNPk4H7TCuJvSp4vet4Cu67kn2JSegoQNFSA1tbwU8v", + "proofPurpose":"assertionMethod", + "created":"2021-05-05T15:22:30.523465" + } + } + ], + "presentation_submission":{ + "id":"a5fcfe44-2c30-497d-af02-98e539da9a0f", + "definition_id":"32f54163-7166-48f1-93d8-ff217bdb0653", + "descriptor_map":[ + { + "id":"citizenship_input_1", + "format":"ldp_vp", + "path":"$.verifiableCredential[0]" + } + ] + }, + "proof":{ + "type":"Ed25519Signature2018", + "verificationMethod":"did:sov:4QxzWk3ajdnEA37NdNU5Kt#key-1", + "created":"2021-05-05T15:23:03.023971", + "proofPurpose":"authentication", + "challenge":"40429d49-5e8f-4ffc-baf8-e332412f1247", + "jws":"eyJhbGciOiAiRWREU0EiLCAiYjY0IjogZmFsc2UsICJjcml0IjogWyJiNjQiXX0..2uBYmg7muE9ZPVeAGo_ibVfLkCjf2hGshr2o5i8pAwFyNBM-kDHXofuq1MzJgb19wzb01VIu91hY_ajjt9KFAA" + } + }, + { + "@context":[ + "https://www.w3.org/2018/credentials/v1" + ], + "type":[ + "VerifiablePresentation" + ], + "verifiableCredential":[ + { + "@context":[ + "https://www.w3.org/2018/credentials/v1", + "https://w3id.org/citizenship/v1", + "https://w3id.org/security/bbs/v1" + ], + "id":"https://issuer.oidp.uscis.gov/credentials/83627465", + "type":[ + "PermanentResidentCard", + "VerifiableCredential" + ], + "credentialSubject":{ + "id":"did:example:b34ca6cd37bbf23", + "type":[ + "Person", + "PermanentResident" + ], + "givenName":"JOHN" + }, + "issuanceDate":"2010-01-01T19:53:24Z", + "issuer":"did:key:zUC74bgefTdc43KS1psXgXf4jLaHyaj2qCQqQTXrtmSYGf1PxiJhrH6LGpaBMyj6tqAKmjGyMaS4RfNo2an77vT1HfzJUNPk4H7TCuJvSp4vet4Cu67kn2JSegoQNFSA1tbwU8v", + "proof":{ + "type":"BbsBlsSignatureProof2020", + "nonce":"3AuruhJQrXtEgiagiJ+FwVf2S0SnzUDJvnO61YecQsJ7ImR1mPcoVjJJ0HOhfkFpoYI=", + "proofValue":"ABkBuAaPlP5A7JWY78Xf69oBnsMLcD1RXbIFYhcLoXPXW12CG9glnnqnPLsGri5xsA3LcP0kg74X+sAjKXGRGy3uvp412Dm0FuohYNboQcLne5KOAa5AxU4bjmwQsxdfduVqhriro1N+YTkuB4SMmO/5ooL0N3OHsYdExg7nSzWqmZoqgp+3CwIxF0a/oyKTcxJORuIqAAAAdInlL9teSIX49NJGEZfBO7IrdjT2iggH/G0AlPWoEvrWIbuCRQ69K83n5o7oJVjqhAAAAAIaVmlAD6+FEKA4eg0OaWOKPrd5Kq8rv0vIwjJ71egxll0Fqq4zDWQ/+yl3Pteh0Wyuyvpm19/sj6tiCWj4PkA+rpxtR2bXpnrCTKUffFFNBjVvVziXDS0KWkGUB7XU9mjUa4USC7Iub3bZZCnFjQA5AAAADzkGwGD837r33e7OTrGEti8eAkvFDcyCgA4ck/X+5HJjAJclHWbl4SNQR8CiNZyzJpvxW+jbNBcwmEvocYArddk3F78Ki0Qnp6aU9eDgfOOx1iW2BXLUjrhq5I2hP5/WQF3CEDYRjczGjzM9T8/coeC36YAp0zJunIXUKb8SPDSOISafibYRYFB4xhlWKXWloDelafyujOBST8KZNM8FmF4DSbXrO8vmZbjuR/8ntUcUK7X2rNbuZ3M5eWZDF8pL+SA9gQitKfPHEocoYAdhgEAM7ZNAJ+TgOcx9gtZIhDWKDNnFxIeoOAylbD1xZd9xbWtq3Bk3R79xqsKxFRJRNxk/9b6fJruP292+qM5lxcZ1jUz/dJUYFI93hH4Mso75CjGRN78MAY9SNifl6H8qcxTpBn4332LlFhRznLbtnc4YSWA/fvVqaN9h2zCH/6AdbLKXGffV34EF7DadwJsi9jsc+YlSMn6qaIUIDTdGLwh4KKpSH5bVbg/mVCcXPTJplFgYwRsOdiQbZY/740dJyo1lPjQ0Lvdio8W2M8c73ujeJU70CNLkgjJAMUPGrCFtGxBH2eeLBQ0P95qRZAIcJ7U0MibZLaRjoUOuTla5BIt2038PJ6XhcY6BEJaLyJOPEQ==", + "verificationMethod":"did:key:zUC74bgefTdc43KS1psXgXf4jLaHyaj2qCQqQTXrtmSYGf1PxiJhrH6LGpaBMyj6tqAKmjGyMaS4RfNo2an77vT1HfzJUNPk4H7TCuJvSp4vet4Cu67kn2JSegoQNFSA1tbwU8v#zUC74bgefTdc43KS1psXgXf4jLaHyaj2qCQqQTXrtmSYGf1PxiJhrH6LGpaBMyj6tqAKmjGyMaS4RfNo2an77vT1HfzJUNPk4H7TCuJvSp4vet4Cu67kn2JSegoQNFSA1tbwU8v", + "proofPurpose":"assertionMethod", + "created":"2021-05-05T15:22:30.523465" + } + } + ], + "presentation_submission":{ + "id":"a5fcfe44-2c30-497d-af02-98e539da9a0f", + "definition_id":"32f54163-7166-48f1-93d8-ff217bdb0653", + "descriptor_map":[ + { + "id":"citizenship_input_2", + "format":"ldp_vp", + "path":"$.verifiableCredential[0]" + } + ] + }, + "proof":{ + "type":"Ed25519Signature2018", + "verificationMethod":"did:sov:4QxzWk3ajdnEA37NdNU5Kt#key-1", + "created":"2021-05-05T15:23:03.023971", + "proofPurpose":"authentication", + "challenge":"40429d49-5e8f-4ffc-baf8-e332412f1247", + "jws":"eyJhbGciOiAiRWREU0EiLCAiYjY0IjogZmFsc2UsICJjcml0IjogWyJiNjQiXX0..2uBYmg7muE9ZPVeAGo_ibVfLkCjf2hGshr2o5i8pAwFyNBM-kDHXofuq1MzJgb19wzb01VIu91hY_ajjt9KFAA" + } + } + ]""" +) + PRES = V20Pres( comment="Test", formats=[ @@ -1678,6 +1801,23 @@ ], ) +PRES_DIF = V20Pres( + comment="Test", + formats=[ + V20PresFormat( + attach_id="dif", + format_=ATTACHMENT_FORMAT[PRES_20][V20PresFormat.Format.DIF.api], + ) + ], + presentations_attach=[ + AttachDecorator.data_json( + mapping=DIF_PROOF, + ident="dif", + ) + ], +) + + class TestV20Pres(TestCase): """Presentation tests.""" @@ -1688,6 +1828,11 @@ def test_init_type(self): assert len(PRES.formats) == len(PRES.presentations_attach) assert PRES.attachment(V20PresFormat.Format.INDY) == INDY_PROOF assert PRES._type == DIDCommPrefix.qualify_current(PRES_20) + + assert PRES_DIF.presentations_attach[0].content == DIF_PROOF + assert len(PRES_DIF.formats) == len(PRES.presentations_attach) + assert PRES_DIF.attachment(V20PresFormat.Format.DIF) == DIF_PROOF + assert PRES_DIF._type == DIDCommPrefix.qualify_current(PRES_20) def test_attachment_no_target_format(self): """Test attachment behaviour for only unknown formats.""" @@ -1740,3 +1885,10 @@ def test_serde(self): } ) V20Pres.deserialize(pres_dict) + + def test_serde_dif(self): + """Test deserialization dif.""" + pres_dict = PRES_DIF.serialize() + pres_obj = V20Pres.deserialize(pres_dict) + assert type(pres_obj) == V20Pres + From c860e9823eeceb2a57e8ad5cd3122ec2a824f10f Mon Sep 17 00:00:00 2001 From: Adam Burdett Date: Thu, 22 Sep 2022 12:11:42 -0600 Subject: [PATCH 494/872] static keytypes Signed-off-by: Adam Burdett --- aries_cloudagent/config/wallet.py | 10 +- aries_cloudagent/core/tests/test_conductor.py | 16 +-- aries_cloudagent/did/did_key.py | 18 +-- .../did/tests/test_did_key_bls12381g1.py | 12 +- .../did/tests/test_did_key_bls12381g1g2.py | 20 +-- .../did/tests/test_did_key_bls12381g2.py | 12 +- .../did/tests/test_did_key_ed25519.py | 12 +- .../did/tests/test_did_key_x25519.py | 12 +- aries_cloudagent/ledger/tests/test_indy.py | 14 +- .../ledger/tests/test_indy_vdr.py | 34 ++--- .../messaging/decorators/attach_decorator.py | 12 +- .../decorators/signature_decorator.py | 6 +- .../decorators/tests/test_attach_decorator.py | 8 +- .../tests/test_signature_decorator.py | 4 +- .../messaging/jsonld/credential.py | 4 +- .../messaging/jsonld/tests/test_credential.py | 4 +- .../messaging/jsonld/tests/test_routes.py | 2 +- .../messaging/tests/test_agent_message.py | 2 +- .../multitenant/tests/test_base.py | 2 +- .../protocols/connections/v1_0/manager.py | 22 ++- .../tests/test_connection_response.py | 4 +- .../connections/v1_0/tests/test_manager.py | 104 +++++++------- .../coordinate_mediation/v1_0/manager.py | 2 +- .../v1_0/normalization.py | 2 +- .../v1_0/route_manager.py | 2 +- .../handlers/tests/test_request_handler.py | 2 +- .../handlers/tests/test_response_handler.py | 2 +- .../protocols/didexchange/v1_0/manager.py | 8 +- .../v1_0/messages/tests/test_request.py | 4 +- .../v1_0/messages/tests/test_response.py | 4 +- .../didexchange/v1_0/tests/test_manager.py | 46 +++--- .../v1_0/tests/test_manager.py | 2 +- .../v1_0/tests/test_routes.py | 28 ++-- .../introduction/v0_1/tests/test_service.py | 6 +- .../v2_0/formats/ld_proof/handler.py | 4 +- .../formats/ld_proof/tests/test_handler.py | 10 +- .../protocols/out_of_band/v1_0/manager.py | 16 +-- .../v1_0/messages/tests/test_invitation.py | 8 +- .../out_of_band/v1_0/tests/test_manager.py | 34 ++--- .../present_proof/dif/pres_exch_handler.py | 10 +- .../dif/tests/test_pres_exch_handler.py | 16 +-- .../present_proof/v2_0/formats/dif/handler.py | 6 +- .../transport/tests/test_pack_format.py | 6 +- .../utils/tests/test_outofband.py | 2 +- .../crypto/tests/test_wallet_key_pair.py | 12 +- .../tests/test_bbs_bls_signature_2020.py | 8 +- .../test_bbs_bls_signature_proof_2020.py | 6 +- .../tests/test_ed25519_signature_2018.py | 8 +- .../vc/ld_proofs/tests/test_ld_proofs.py | 20 +-- .../vc/tests/test_bbs_mattr_interop.py | 8 +- aries_cloudagent/vc/vc_ld/tests/test_vc_ld.py | 22 +-- aries_cloudagent/wallet/askar.py | 20 +-- aries_cloudagent/wallet/crypto.py | 12 +- aries_cloudagent/wallet/did_method.py | 6 +- aries_cloudagent/wallet/indy.py | 34 ++--- aries_cloudagent/wallet/routes.py | 19 ++- aries_cloudagent/wallet/tests/test_crypto.py | 14 +- .../wallet/tests/test_did_method.py | 42 +++--- .../wallet/tests/test_in_memory_wallet.py | 136 +++++++++--------- .../wallet/tests/test_indy_wallet.py | 32 ++--- .../wallet/tests/test_key_pair.py | 14 +- .../wallet/tests/test_key_type.py | 8 +- aries_cloudagent/wallet/tests/test_routes.py | 74 +++++----- 63 files changed, 508 insertions(+), 541 deletions(-) diff --git a/aries_cloudagent/config/wallet.py b/aries_cloudagent/config/wallet.py index 61e34b2a73..d346f7740c 100644 --- a/aries_cloudagent/config/wallet.py +++ b/aries_cloudagent/config/wallet.py @@ -12,7 +12,7 @@ from ..wallet.crypto import seed_to_did from ..wallet.did_info import DIDInfo from ..wallet.did_method import DIDMethod -from ..wallet.key_type import KeyType +from ..wallet.key_type import ED25519 from .base import ConfigError from .injection_context import InjectionContext @@ -79,7 +79,7 @@ async def wallet_config( if wallet_seed and seed_to_did(wallet_seed) != public_did: if context.settings.get("wallet.replace_public_did"): replace_did_info = await wallet.create_local_did( - method=DIDMethod.SOV, key_type=KeyType.ED25519, seed=wallet_seed + method=DIDMethod.SOV, key_type=ED25519, seed=wallet_seed ) public_did = replace_did_info.did await wallet.set_public_did(public_did) @@ -100,7 +100,7 @@ async def wallet_config( local_did_info = await wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, seed=wallet_seed, metadata=metadata, ) @@ -110,7 +110,7 @@ async def wallet_config( print(f"Verkey: {local_did_info.verkey}") else: public_did_info = await wallet.create_public_did( - method=DIDMethod.SOV, key_type=KeyType.ED25519, seed=wallet_seed + method=DIDMethod.SOV, key_type=ED25519, seed=wallet_seed ) public_did = public_did_info.did if provision: @@ -129,7 +129,7 @@ async def wallet_config( if test_seed: await wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, seed=test_seed, metadata={"endpoint": "1.2.3.4:8021"}, ) diff --git a/aries_cloudagent/core/tests/test_conductor.py b/aries_cloudagent/core/tests/test_conductor.py index 203ca1b3b4..28763e2b8f 100644 --- a/aries_cloudagent/core/tests/test_conductor.py +++ b/aries_cloudagent/core/tests/test_conductor.py @@ -40,7 +40,7 @@ from ...utils.stats import Collector from ...version import __version__ from ...wallet.base import BaseWallet -from ...wallet.key_type import KeyType +from ...wallet.key_type import ED25519 from ...wallet.did_method import DIDMethod from .. import conductor as test_module @@ -132,7 +132,7 @@ async def test_startup(self): wallet = session.inject(BaseWallet) await wallet.create_public_did( DIDMethod.SOV, - KeyType.ED25519, + ED25519, ) mock_inbound_mgr.return_value.setup.assert_awaited_once() @@ -601,7 +601,7 @@ async def test_admin(self): wallet = session.inject(BaseWallet) await wallet.create_public_did( DIDMethod.SOV, - KeyType.ED25519, + ED25519, ) with async_mock.patch.object( @@ -645,7 +645,7 @@ async def test_admin_startx(self): wallet = session.inject(BaseWallet) await wallet.create_public_did( DIDMethod.SOV, - KeyType.ED25519, + ED25519, ) with async_mock.patch.object( @@ -717,7 +717,7 @@ async def test_start_static(self): wallet = session.inject(BaseWallet) await wallet.create_public_did( DIDMethod.SOV, - KeyType.ED25519, + ED25519, ) mock_mgr.return_value.create_static_connection = async_mock.AsyncMock() @@ -887,7 +887,7 @@ async def test_print_invite_connection(self): wallet = session.inject(BaseWallet) await wallet.create_public_did( DIDMethod.SOV, - KeyType.ED25519, + ED25519, ) await conductor.start() @@ -1390,7 +1390,7 @@ async def test_startup_x_version_mismatch(self): wallet = session.inject(BaseWallet) await wallet.create_public_did( DIDMethod.SOV, - KeyType.ED25519, + ED25519, ) mock_inbound_mgr.return_value.setup.assert_awaited_once() @@ -1427,7 +1427,7 @@ async def test_startup_x_no_storage_version(self): wallet = session.inject(BaseWallet) await wallet.create_public_did( DIDMethod.SOV, - KeyType.ED25519, + ED25519, ) mock_inbound_mgr.return_value.setup.assert_awaited_once() diff --git a/aries_cloudagent/did/did_key.py b/aries_cloudagent/did/did_key.py index 70101910fb..c85a66fc8b 100644 --- a/aries_cloudagent/did/did_key.py +++ b/aries_cloudagent/did/did_key.py @@ -1,7 +1,7 @@ """DID Key class and resolver methods.""" from ..wallet.crypto import ed25519_pk_to_curve25519 -from ..wallet.key_type import KeyType +from ..wallet.key_type import BLS12381G1G2, ED25519, KeyType, BLS12381G1, X25519, BLS12381G2 from ..wallet.util import b58_to_bytes, bytes_to_b58 from ..vc.ld_proofs.constants import DID_V1_CONTEXT_URL @@ -169,8 +169,8 @@ def construct_did_key_bls12381g1g2(did_key: "DIDKey") -> dict: g1_public_key = did_key.public_key[:48] g2_public_key = did_key.public_key[48:] - bls12381g1_key = DIDKey.from_public_key(g1_public_key, KeyType.BLS12381G1) - bls12381g2_key = DIDKey.from_public_key(g2_public_key, KeyType.BLS12381G2) + bls12381g1_key = DIDKey.from_public_key(g1_public_key, BLS12381G1) + bls12381g2_key = DIDKey.from_public_key(g2_public_key, BLS12381G2) bls12381g1_key_id = f"{did_key.did}#{bls12381g1_key.fingerprint}" bls12381g2_key_id = f"{did_key.did}#{bls12381g2_key.fingerprint}" @@ -241,7 +241,7 @@ def construct_did_key_ed25519(did_key: "DIDKey") -> dict: """ curve25519 = ed25519_pk_to_curve25519(did_key.public_key) - x25519 = DIDKey.from_public_key(curve25519, KeyType.X25519) + x25519 = DIDKey.from_public_key(curve25519, X25519) did_doc = construct_did_signature_key_base( id=did_key.did, @@ -289,9 +289,9 @@ def construct_did_signature_key_base( DID_KEY_RESOLVERS = { - KeyType.ED25519: construct_did_key_ed25519, - KeyType.X25519: construct_did_key_x25519, - KeyType.BLS12381G2: construct_did_key_bls12381g2, - KeyType.BLS12381G1: construct_did_key_bls12381g1, - KeyType.BLS12381G1G2: construct_did_key_bls12381g1g2, + ED25519: construct_did_key_ed25519, + X25519: construct_did_key_x25519, + BLS12381G2: construct_did_key_bls12381g2, + BLS12381G1: construct_did_key_bls12381g1, + BLS12381G1G2: construct_did_key_bls12381g1g2, } diff --git a/aries_cloudagent/did/tests/test_did_key_bls12381g1.py b/aries_cloudagent/did/tests/test_did_key_bls12381g1.py index 9b95465df4..b110bc2b2f 100644 --- a/aries_cloudagent/did/tests/test_did_key_bls12381g1.py +++ b/aries_cloudagent/did/tests/test_did_key_bls12381g1.py @@ -1,7 +1,7 @@ from unittest import TestCase -from ...wallet.key_type import KeyType +from ...wallet.key_type import BLS12381G1 from ...wallet.util import b58_to_bytes from ..did_key import DIDKey, DID_KEY_RESOLVERS from .test_dids import ( @@ -24,13 +24,13 @@ class TestDIDKey(TestCase): def test_bls12381g1_from_public_key(self): key_bytes = b58_to_bytes(TEST_BLS12381G1_BASE58_KEY) - did_key = DIDKey.from_public_key(key_bytes, KeyType.BLS12381G1) + did_key = DIDKey.from_public_key(key_bytes, BLS12381G1) assert did_key.did == TEST_BLS12381G1_DID def test_bls12381g1_from_public_key_b58(self): did_key = DIDKey.from_public_key_b58( - TEST_BLS12381G1_BASE58_KEY, KeyType.BLS12381G1 + TEST_BLS12381G1_BASE58_KEY, BLS12381G1 ) assert did_key.did == TEST_BLS12381G1_DID @@ -53,20 +53,20 @@ def test_bls12381g1_properties(self): assert did_key.did == TEST_BLS12381G1_DID assert did_key.public_key_b58 == TEST_BLS12381G1_BASE58_KEY assert did_key.public_key == b58_to_bytes(TEST_BLS12381G1_BASE58_KEY) - assert did_key.key_type == KeyType.BLS12381G1 + assert did_key.key_type == BLS12381G1 assert did_key.key_id == TEST_BLS12381G1_KEY_ID assert did_key.prefixed_public_key == TEST_BLS12381G1_PREFIX_BYTES def test_bls12381g1_diddoc(self): did_key = DIDKey.from_did(TEST_BLS12381G1_DID) - resolver = DID_KEY_RESOLVERS[KeyType.BLS12381G1] + resolver = DID_KEY_RESOLVERS[BLS12381G1] assert resolver(did_key) == did_key.did_doc def test_bls12381g1_resolver(self): did_key = DIDKey.from_did(TEST_BLS12381G1_DID) - resolver = DID_KEY_RESOLVERS[KeyType.BLS12381G1] + resolver = DID_KEY_RESOLVERS[BLS12381G1] did_doc = resolver(did_key) assert ( diff --git a/aries_cloudagent/did/tests/test_did_key_bls12381g1g2.py b/aries_cloudagent/did/tests/test_did_key_bls12381g1g2.py index 59e2a05907..3b5a21c8cd 100644 --- a/aries_cloudagent/did/tests/test_did_key_bls12381g1g2.py +++ b/aries_cloudagent/did/tests/test_did_key_bls12381g1g2.py @@ -1,6 +1,6 @@ from unittest import TestCase -from ...wallet.key_type import KeyType +from ...wallet.key_type import BLS12381G1, BLS12381G1G2, BLS12381G2 from ...wallet.util import b58_to_bytes from ..did_key import DIDKey, DID_KEY_RESOLVERS from .test_dids import ( @@ -31,13 +31,13 @@ class TestDIDKey(TestCase): def test_bls12381g1g2_from_public_key(self): key_bytes = b58_to_bytes(TEST_BLS12381G1G2_BASE58_KEY) - did_key = DIDKey.from_public_key(key_bytes, KeyType.BLS12381G1G2) + did_key = DIDKey.from_public_key(key_bytes, BLS12381G1G2) assert did_key.did == TEST_BLS12381G1G2_DID def test_bls12381g1g2_from_public_key_b58(self): did_key = DIDKey.from_public_key_b58( - TEST_BLS12381G1G2_BASE58_KEY, KeyType.BLS12381G1G2 + TEST_BLS12381G1G2_BASE58_KEY, BLS12381G1G2 ) assert did_key.did == TEST_BLS12381G1G2_DID @@ -60,13 +60,13 @@ def test_bls12381g1g2_properties(self): assert did_key.did == TEST_BLS12381G1G2_DID assert did_key.public_key_b58 == TEST_BLS12381G1G2_BASE58_KEY assert did_key.public_key == b58_to_bytes(TEST_BLS12381G1G2_BASE58_KEY) - assert did_key.key_type == KeyType.BLS12381G1G2 + assert did_key.key_type == BLS12381G1G2 assert did_key.prefixed_public_key == TEST_BLS12381G1G2_PREFIX_BYTES def test_bls12381g1g2_diddoc(self): did_key = DIDKey.from_did(TEST_BLS12381G1G2_DID) - resolver = DID_KEY_RESOLVERS[KeyType.BLS12381G1G2] + resolver = DID_KEY_RESOLVERS[BLS12381G1G2] assert resolver(did_key) == did_key.did_doc @@ -74,7 +74,7 @@ def test_bls12381g1g2_resolver(self): did_key = DIDKey.from_did( "did:key:z5TcESXuYUE9aZWYwSdrUEGK1HNQFHyTt4aVpaCTVZcDXQmUheFwfNZmRksaAbBneNm5KyE52SdJeRCN1g6PJmF31GsHWwFiqUDujvasK3wTiDr3vvkYwEJHt7H5RGEKYEp1ErtQtcEBgsgY2DA9JZkHj1J9HZ8MRDTguAhoFtR4aTBQhgnkP4SwVbxDYMEZoF2TMYn3s" ) - resolver = DID_KEY_RESOLVERS[KeyType.BLS12381G1G2] + resolver = DID_KEY_RESOLVERS[BLS12381G1G2] did_doc = resolver(did_key) assert ( @@ -88,13 +88,13 @@ def test_bls12381g1g1_to_g1(self): # TODO: add easier method to go form g1 <- g1g2 -> g2 # First 48 bytes is g1 key g1_public_key = g1g2_did.public_key[:48] - g1_did = DIDKey.from_public_key(g1_public_key, KeyType.BLS12381G1) + g1_did = DIDKey.from_public_key(g1_public_key, BLS12381G1) assert g1_did.fingerprint == TEST_BLS12381G1_FINGERPRINT assert g1_did.did == TEST_BLS12381G1_DID assert g1_did.public_key_b58 == TEST_BLS12381G1_BASE58_KEY assert g1_did.public_key == b58_to_bytes(TEST_BLS12381G1_BASE58_KEY) - assert g1_did.key_type == KeyType.BLS12381G1 + assert g1_did.key_type == BLS12381G1 def test_bls12381g1g1_to_g2(self): g1g2_did = DIDKey.from_did(TEST_BLS12381G1G2_DID) @@ -102,10 +102,10 @@ def test_bls12381g1g1_to_g2(self): # TODO: add easier method to go form g1 <- g1g2 -> g2 # From 48 bytes is g2 key g2_public_key = g1g2_did.public_key[48:] - g2_did = DIDKey.from_public_key(g2_public_key, KeyType.BLS12381G2) + g2_did = DIDKey.from_public_key(g2_public_key, BLS12381G2) assert g2_did.fingerprint == TEST_BLS12381G2_FINGERPRINT assert g2_did.did == TEST_BLS12381G2_DID assert g2_did.public_key_b58 == TEST_BLS12381G2_BASE58_KEY assert g2_did.public_key == b58_to_bytes(TEST_BLS12381G2_BASE58_KEY) - assert g2_did.key_type == KeyType.BLS12381G2 + assert g2_did.key_type == BLS12381G2 diff --git a/aries_cloudagent/did/tests/test_did_key_bls12381g2.py b/aries_cloudagent/did/tests/test_did_key_bls12381g2.py index 0f2cb67fc1..ddd1259b97 100644 --- a/aries_cloudagent/did/tests/test_did_key_bls12381g2.py +++ b/aries_cloudagent/did/tests/test_did_key_bls12381g2.py @@ -1,6 +1,6 @@ from unittest import TestCase -from ...wallet.key_type import KeyType +from ...wallet.key_type import BLS12381G2 from ...wallet.util import b58_to_bytes from ..did_key import DIDKey, DID_KEY_RESOLVERS from .test_dids import ( @@ -19,13 +19,13 @@ class TestDIDKey(TestCase): def test_bls12381g2_from_public_key(self): key_bytes = b58_to_bytes(TEST_BLS12381G2_BASE58_KEY) - did_key = DIDKey.from_public_key(key_bytes, KeyType.BLS12381G2) + did_key = DIDKey.from_public_key(key_bytes, BLS12381G2) assert did_key.did == TEST_BLS12381G2_DID def test_bls12381g2_from_public_key_b58(self): did_key = DIDKey.from_public_key_b58( - TEST_BLS12381G2_BASE58_KEY, KeyType.BLS12381G2 + TEST_BLS12381G2_BASE58_KEY, BLS12381G2 ) assert did_key.did == TEST_BLS12381G2_DID @@ -48,20 +48,20 @@ def test_bls12381g2_properties(self): assert did_key.did == TEST_BLS12381G2_DID assert did_key.public_key_b58 == TEST_BLS12381G2_BASE58_KEY assert did_key.public_key == b58_to_bytes(TEST_BLS12381G2_BASE58_KEY) - assert did_key.key_type == KeyType.BLS12381G2 + assert did_key.key_type == BLS12381G2 assert did_key.key_id == TEST_BLS12381G2_KEY_ID assert did_key.prefixed_public_key == TEST_BLS12381G2_PREFIX_BYTES def test_bls12381g2_diddoc(self): did_key = DIDKey.from_did(TEST_BLS12381G2_DID) - resolver = DID_KEY_RESOLVERS[KeyType.BLS12381G2] + resolver = DID_KEY_RESOLVERS[BLS12381G2] assert resolver(did_key) == did_key.did_doc def test_bls12381g2_resolver(self): did_key = DIDKey.from_did(TEST_BLS12381G2_DID) - resolver = DID_KEY_RESOLVERS[KeyType.BLS12381G2] + resolver = DID_KEY_RESOLVERS[BLS12381G2] did_doc = resolver(did_key) assert ( diff --git a/aries_cloudagent/did/tests/test_did_key_ed25519.py b/aries_cloudagent/did/tests/test_did_key_ed25519.py index 3911fc3e36..53c2eb8bf2 100644 --- a/aries_cloudagent/did/tests/test_did_key_ed25519.py +++ b/aries_cloudagent/did/tests/test_did_key_ed25519.py @@ -1,6 +1,6 @@ from unittest import TestCase -from ...wallet.key_type import KeyType +from ...wallet.key_type import ED25519 from ...wallet.util import b58_to_bytes from ..did_key import DIDKey, DID_KEY_RESOLVERS from .test_dids import DID_ED25519_z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th @@ -17,12 +17,12 @@ class TestDIDKey(TestCase): def test_ed25519_from_public_key(self): key_bytes = b58_to_bytes(TEST_ED25519_BASE58_KEY) - did_key = DIDKey.from_public_key(key_bytes, KeyType.ED25519) + did_key = DIDKey.from_public_key(key_bytes, ED25519) assert did_key.did == TEST_ED25519_DID def test_ed25519_from_public_key_b58(self): - did_key = DIDKey.from_public_key_b58(TEST_ED25519_BASE58_KEY, KeyType.ED25519) + did_key = DIDKey.from_public_key_b58(TEST_ED25519_BASE58_KEY, ED25519) assert did_key.did == TEST_ED25519_DID @@ -44,20 +44,20 @@ def test_ed25519_properties(self): assert did_key.did == TEST_ED25519_DID assert did_key.public_key_b58 == TEST_ED25519_BASE58_KEY assert did_key.public_key == b58_to_bytes(TEST_ED25519_BASE58_KEY) - assert did_key.key_type == KeyType.ED25519 + assert did_key.key_type == ED25519 assert did_key.key_id == TEST_ED25519_KEY_ID assert did_key.prefixed_public_key == TEST_ED25519_PREFIX_BYTES def test_ed25519_diddoc(self): did_key = DIDKey.from_did(TEST_ED25519_DID) - resolver = DID_KEY_RESOLVERS[KeyType.ED25519] + resolver = DID_KEY_RESOLVERS[ED25519] assert resolver(did_key) == did_key.did_doc def test_ed25519_resolver(self): did_key = DIDKey.from_did(TEST_ED25519_DID) - resolver = DID_KEY_RESOLVERS[KeyType.ED25519] + resolver = DID_KEY_RESOLVERS[ED25519] did_doc = resolver(did_key) # resolved using uniresolver, updated to did v1 diff --git a/aries_cloudagent/did/tests/test_did_key_x25519.py b/aries_cloudagent/did/tests/test_did_key_x25519.py index 924011f396..84513da814 100644 --- a/aries_cloudagent/did/tests/test_did_key_x25519.py +++ b/aries_cloudagent/did/tests/test_did_key_x25519.py @@ -1,6 +1,6 @@ from unittest import TestCase -from ...wallet.key_type import KeyType +from ...wallet.key_type import X25519 from ...wallet.util import b58_to_bytes from ..did_key import DIDKey, DID_KEY_RESOLVERS from .test_dids import DID_X25519_z6LShLeXRTzevtwcfehaGEzCMyL3bNsAeKCwcqwJxyCo63yE @@ -15,12 +15,12 @@ class TestDIDKey(TestCase): def test_x25519_from_public_key(self): key_bytes = b58_to_bytes(TEST_X25519_BASE58_KEY) - did_key = DIDKey.from_public_key(key_bytes, KeyType.X25519) + did_key = DIDKey.from_public_key(key_bytes, X25519) assert did_key.did == TEST_X25519_DID def test_x25519_from_public_key_b58(self): - did_key = DIDKey.from_public_key_b58(TEST_X25519_BASE58_KEY, KeyType.X25519) + did_key = DIDKey.from_public_key_b58(TEST_X25519_BASE58_KEY, X25519) assert did_key.did == TEST_X25519_DID @@ -42,20 +42,20 @@ def test_x25519_properties(self): assert did_key.did == TEST_X25519_DID assert did_key.public_key_b58 == TEST_X25519_BASE58_KEY assert did_key.public_key == b58_to_bytes(TEST_X25519_BASE58_KEY) - assert did_key.key_type == KeyType.X25519 + assert did_key.key_type == X25519 assert did_key.key_id == TEST_X25519_KEY_ID assert did_key.prefixed_public_key == TEST_X25519_PREFIX_BYTES def test_x25519_diddoc(self): did_key = DIDKey.from_did(TEST_X25519_DID) - resolver = DID_KEY_RESOLVERS[KeyType.X25519] + resolver = DID_KEY_RESOLVERS[X25519] assert resolver(did_key) == did_key.did_doc def test_x25519_resolver(self): did_key = DIDKey.from_did(TEST_X25519_DID) - resolver = DID_KEY_RESOLVERS[KeyType.X25519] + resolver = DID_KEY_RESOLVERS[X25519] did_doc = resolver(did_key) # resolved using uniresolver, updated to did v1 diff --git a/aries_cloudagent/ledger/tests/test_indy.py b/aries_cloudagent/ledger/tests/test_indy.py index bda1890c2a..52a0df6463 100644 --- a/aries_cloudagent/ledger/tests/test_indy.py +++ b/aries_cloudagent/ledger/tests/test_indy.py @@ -19,7 +19,7 @@ from ...wallet.did_posture import DIDPosture from ...wallet.error import WalletNotFoundError from ...wallet.indy import IndyOpenWallet, IndySdkWallet -from ...wallet.key_type import KeyType +from ...wallet.key_type import ED25519 from ...wallet.did_method import DIDMethod from ..endpoint_type import EndpointType @@ -71,7 +71,7 @@ async def setUp(self): verkey="3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx", metadata={"test": "test"}, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) self.test_verkey = "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx" context = InjectionContext() @@ -1220,7 +1220,7 @@ async def test_send_credential_definition( verkey=self.test_verkey, metadata=None, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) mock_did = mock_wallet_get_public_did.return_value ( @@ -1300,7 +1300,7 @@ async def test_send_credential_definition_endorse_only( self.test_verkey, None, DIDMethod.SOV, - KeyType.ED25519, + ED25519, ) async with ledger: ( @@ -1383,7 +1383,7 @@ async def test_send_credential_definition_exists_in_ledger_and_wallet( verkey=self.test_verkey, metadata=None, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) async with ledger: @@ -1795,7 +1795,7 @@ async def test_send_credential_definition_on_ledger_in_wallet( verkey=self.test_verkey, metadata=None, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) mock_did = mock_wallet_get_public_did.return_value @@ -1869,7 +1869,7 @@ async def test_send_credential_definition_create_cred_def_exception( verkey=self.test_verkey, metadata=None, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) async with ledger: with self.assertRaises(LedgerError): diff --git a/aries_cloudagent/ledger/tests/test_indy_vdr.py b/aries_cloudagent/ledger/tests/test_indy_vdr.py index f20781e838..bb765b0ca1 100644 --- a/aries_cloudagent/ledger/tests/test_indy_vdr.py +++ b/aries_cloudagent/ledger/tests/test_indy_vdr.py @@ -9,7 +9,7 @@ from ...core.in_memory import InMemoryProfile from ...indy.issuer import IndyIssuer from ...wallet.base import BaseWallet -from ...wallet.key_type import KeyType +from ...wallet.key_type import ED25519 from ...wallet.did_method import DIDMethod from ...wallet.did_info import DIDInfo @@ -67,7 +67,7 @@ async def test_submit_signed( ledger: IndyVdrLedger, ): wallet = (await ledger.profile.session()).wallet - test_did = await wallet.create_public_did(DIDMethod.SOV, KeyType.ED25519) + test_did = await wallet.create_public_did(DIDMethod.SOV, ED25519) test_msg = indy_vdr.ledger.build_get_txn_request(test_did.did, 1, 1) async with ledger: @@ -120,7 +120,7 @@ async def test_submit_signed_taa_accept( ledger: IndyVdrLedger, ): wallet = (await ledger.profile.session()).wallet - test_did = await wallet.create_public_did(DIDMethod.SOV, KeyType.ED25519) + test_did = await wallet.create_public_did(DIDMethod.SOV, ED25519) async with ledger: test_msg = indy_vdr.ledger.build_get_txn_request(test_did.did, 1, 1) @@ -188,7 +188,7 @@ async def test_txn_endorse( with pytest.raises(BadLedgerRequestError): await ledger.txn_endorse(request_json=test_msg.body) - test_did = await wallet.create_public_did(DIDMethod.SOV, KeyType.ED25519) + test_did = await wallet.create_public_did(DIDMethod.SOV, ED25519) test_msg.set_endorser(test_did.did) endorsed_json = await ledger.txn_endorse(request_json=test_msg.body) @@ -201,7 +201,7 @@ async def test_send_schema( ledger: IndyVdrLedger, ): wallet = (await ledger.profile.session()).wallet - test_did = await wallet.create_public_did(DIDMethod.SOV, KeyType.ED25519) + test_did = await wallet.create_public_did(DIDMethod.SOV, ED25519) issuer = async_mock.MagicMock(IndyIssuer) issuer.create_schema.return_value = ( "schema_issuer_did:schema_name:9.1", @@ -262,7 +262,7 @@ async def test_send_schema_already_exists( ledger: IndyVdrLedger, ): wallet = (await ledger.profile.session()).wallet - test_did = await wallet.create_public_did(DIDMethod.SOV, KeyType.ED25519) + test_did = await wallet.create_public_did(DIDMethod.SOV, ED25519) issuer = async_mock.MagicMock(IndyIssuer) issuer.create_schema.return_value = ( "schema_issuer_did:schema_name:9.1", @@ -292,7 +292,7 @@ async def test_send_schema_ledger_read_only( ledger: IndyVdrLedger, ): wallet = (await ledger.profile.session()).wallet - test_did = await wallet.create_public_did(DIDMethod.SOV, KeyType.ED25519) + test_did = await wallet.create_public_did(DIDMethod.SOV, ED25519) issuer = async_mock.MagicMock(IndyIssuer) issuer.create_schema.return_value = ( "schema_issuer_did:schema_name:9.1", @@ -321,7 +321,7 @@ async def test_send_schema_ledger_transaction_error( ledger: IndyVdrLedger, ): wallet = (await ledger.profile.session()).wallet - test_did = await wallet.create_public_did(DIDMethod.SOV, KeyType.ED25519) + test_did = await wallet.create_public_did(DIDMethod.SOV, ED25519) issuer = async_mock.MagicMock(IndyIssuer) issuer.create_schema.return_value = ( "schema_issuer_did:schema_name:9.1", @@ -383,7 +383,7 @@ async def test_send_credential_definition( ledger: IndyVdrLedger, ): wallet = (await ledger.profile.session()).wallet - test_did = await wallet.create_public_did(DIDMethod.SOV, KeyType.ED25519) + test_did = await wallet.create_public_did(DIDMethod.SOV, ED25519) schema_id = "55GkHamhTU1ZbTbV2ab9DE:2:schema_name:9.1" cred_def_id = "55GkHamhTU1ZbTbV2ab9DE:3:CL:99:tag" cred_def = { @@ -598,7 +598,7 @@ async def test_update_endpoint_for_did( ledger: IndyVdrLedger, ): wallet = (await ledger.profile.session()).wallet - test_did = await wallet.create_public_did(DIDMethod.SOV, KeyType.ED25519) + test_did = await wallet.create_public_did(DIDMethod.SOV, ED25519) async with ledger: ledger.pool_handle.submit_request.side_effect = ( {"data": None}, @@ -675,7 +675,7 @@ async def test_construct_attr_json( async def test_update_endpoint_for_did_calls_attr_json(self, ledger: IndyVdrLedger): routing_keys = ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"] wallet = (await ledger.profile.session()).wallet - test_did = await wallet.create_public_did(DIDMethod.SOV, KeyType.ED25519) + test_did = await wallet.create_public_did(DIDMethod.SOV, ED25519) async with ledger: with async_mock.patch.object( @@ -742,8 +742,8 @@ async def test_register_nym_local( ledger: IndyVdrLedger, ): wallet: BaseWallet = (await ledger.profile.session()).wallet - public_did = await wallet.create_public_did(DIDMethod.SOV, KeyType.ED25519) - post_did = await wallet.create_local_did(DIDMethod.SOV, KeyType.ED25519) + public_did = await wallet.create_public_did(DIDMethod.SOV, ED25519) + post_did = await wallet.create_local_did(DIDMethod.SOV, ED25519) async with ledger: await ledger.register_nym(post_did.did, post_did.verkey) did = await wallet.get_local_did(post_did.did) @@ -755,7 +755,7 @@ async def test_register_nym_non_local( ledger: IndyVdrLedger, ): wallet: BaseWallet = (await ledger.profile.session()).wallet - public_did = await wallet.create_public_did(DIDMethod.SOV, KeyType.ED25519) + public_did = await wallet.create_public_did(DIDMethod.SOV, ED25519) async with ledger: await ledger.register_nym("55GkHamhTU1ZbTbV2ab9DE", "verkey") @@ -898,7 +898,7 @@ async def test_send_revoc_reg_def( ledger: IndyVdrLedger, ): wallet: BaseWallet = (await ledger.profile.session()).wallet - public_did = await wallet.create_public_did(DIDMethod.SOV, KeyType.ED25519) + public_did = await wallet.create_public_did(DIDMethod.SOV, ED25519) async with ledger: reg_id = ( "55GkHamhTU1ZbTbV2ab9DE:4:55GkHamhTU1ZbTbV2ab9DE:3:CL:99:tag:CL_ACCUM:0" @@ -927,7 +927,7 @@ async def test_send_revoc_reg_entry( ledger: IndyVdrLedger, ): wallet: BaseWallet = (await ledger.profile.session()).wallet - public_did = await wallet.create_public_did(DIDMethod.SOV, KeyType.ED25519) + public_did = await wallet.create_public_did(DIDMethod.SOV, ED25519) async with ledger: reg_id = ( "55GkHamhTU1ZbTbV2ab9DE:4:55GkHamhTU1ZbTbV2ab9DE:3:CL:99:tag:CL_ACCUM:0" @@ -966,7 +966,7 @@ async def test_credential_definition_id2schema_id(self, ledger: IndyVdrLedger): @pytest.mark.asyncio async def test_rotate_did_keypair(self, ledger: IndyVdrLedger): wallet = (await ledger.profile.session()).wallet - public_did = await wallet.create_public_did(DIDMethod.SOV, KeyType.ED25519) + public_did = await wallet.create_public_did(DIDMethod.SOV, ED25519) async with ledger: with async_mock.patch.object( diff --git a/aries_cloudagent/messaging/decorators/attach_decorator.py b/aries_cloudagent/messaging/decorators/attach_decorator.py index 309ebc6a68..66284378df 100644 --- a/aries_cloudagent/messaging/decorators/attach_decorator.py +++ b/aries_cloudagent/messaging/decorators/attach_decorator.py @@ -24,7 +24,7 @@ str_to_b64, unpad, ) -from ...wallet.key_type import KeyType +from ...wallet.key_type import ED25519 from ...did.did_key import DIDKey from ..models.base import BaseModel, BaseModelError, BaseModelSchema from ..valid import ( @@ -201,7 +201,7 @@ def did_key(verkey: str) -> str: if verkey.startswith("did:key:"): return verkey - return DIDKey.from_public_key_b58(verkey, KeyType.ED25519).did + return DIDKey.from_public_key_b58(verkey, ED25519).did def raw_key(verkey: str) -> str: @@ -440,13 +440,9 @@ async def verify(self, wallet: BaseWallet, signer_verkey: str = None) -> bool: verkey = bytes_to_b58(b64_to_bytes(protected["jwk"]["x"], urlsafe=True)) encoded_pk = DIDKey.from_did(protected["jwk"]["kid"]).public_key_b58 verkey_to_check.append(encoded_pk) - if not await wallet.verify_message( - sign_input, b_sig, verkey, KeyType.ED25519 - ): + if not await wallet.verify_message(sign_input, b_sig, verkey, ED25519): return False - if not await wallet.verify_message( - sign_input, b_sig, encoded_pk, KeyType.ED25519 - ): + if not await wallet.verify_message(sign_input, b_sig, encoded_pk, ED25519): return False if signer_verkey and signer_verkey not in verkey_to_check: return False diff --git a/aries_cloudagent/messaging/decorators/signature_decorator.py b/aries_cloudagent/messaging/decorators/signature_decorator.py index 71bf1c42ea..a8b67cce29 100644 --- a/aries_cloudagent/messaging/decorators/signature_decorator.py +++ b/aries_cloudagent/messaging/decorators/signature_decorator.py @@ -9,7 +9,7 @@ from ...protocols.didcomm_prefix import DIDCommPrefix from ...wallet.base import BaseWallet from ...wallet.util import b64_to_bytes, bytes_to_b64 -from ...wallet.key_type import KeyType +from ...wallet.key_type import ED25519 from ..models.base import BaseModel, BaseModelSchema from ..valid import Base64URL, BASE64URL, INDY_RAW_PUBLIC_KEY @@ -111,9 +111,7 @@ async def verify(self, wallet: BaseWallet) -> bool: return False msg_bin = b64_to_bytes(self.sig_data, urlsafe=True) sig_bin = b64_to_bytes(self.signature, urlsafe=True) - return await wallet.verify_message( - msg_bin, sig_bin, self.signer, KeyType.ED25519 - ) + return await wallet.verify_message(msg_bin, sig_bin, self.signer, ED25519) def __str__(self): """Get a string representation of this class.""" diff --git a/aries_cloudagent/messaging/decorators/tests/test_attach_decorator.py b/aries_cloudagent/messaging/decorators/tests/test_attach_decorator.py index 2687c7bf19..13a91e61f2 100644 --- a/aries_cloudagent/messaging/decorators/tests/test_attach_decorator.py +++ b/aries_cloudagent/messaging/decorators/tests/test_attach_decorator.py @@ -10,7 +10,7 @@ from ....messaging.models.base import BaseModelError from ....wallet.indy import IndySdkWallet from ....wallet.util import b64_to_bytes, bytes_to_b64 -from ....wallet.key_type import KeyType +from ....wallet.key_type import ED25519 from ....wallet.did_method import DIDMethod from ..attach_decorator import ( @@ -433,9 +433,7 @@ def test_data_json_external_mutation(self): class TestAttachDecoratorSignature: @pytest.mark.asyncio async def test_did_raw_key(self, wallet, seed): - did_info = await wallet.create_local_did( - DIDMethod.SOV, KeyType.ED25519, seed[0] - ) + did_info = await wallet.create_local_did(DIDMethod.SOV, ED25519, seed[0]) did_key0 = did_key(did_info.verkey) raw_key0 = raw_key(did_key0) assert raw_key0 != did_key0 @@ -457,7 +455,7 @@ async def test_indy_sign(self, wallet, seed): ) deco_indy_master = deepcopy(deco_indy) did_info = [ - await wallet.create_local_did(DIDMethod.SOV, KeyType.ED25519, seed[i]) + await wallet.create_local_did(DIDMethod.SOV, ED25519, seed[i]) for i in [0, 1] ] assert deco_indy.data.signatures == 0 diff --git a/aries_cloudagent/messaging/decorators/tests/test_signature_decorator.py b/aries_cloudagent/messaging/decorators/tests/test_signature_decorator.py index c7d23a25e1..4eca03663f 100644 --- a/aries_cloudagent/messaging/decorators/tests/test_signature_decorator.py +++ b/aries_cloudagent/messaging/decorators/tests/test_signature_decorator.py @@ -1,6 +1,6 @@ from asynctest import TestCase as AsyncTestCase -from ....wallet.key_type import KeyType +from ....wallet.key_type import ED25519, KeyType from ....core.in_memory import InMemoryProfile from ....protocols.trustping.v1_0.messages.ping import Ping from ....wallet.in_memory import InMemoryWallet @@ -43,7 +43,7 @@ async def test_create_decode_verify(self): profile = InMemoryProfile.test_profile() wallet = InMemoryWallet(profile) - key_info = await wallet.create_signing_key(KeyType.ED25519) + key_info = await wallet.create_signing_key(ED25519) deco = await SignatureDecorator.create( Ping(), key_info.verkey, wallet, timestamp=None diff --git a/aries_cloudagent/messaging/jsonld/credential.py b/aries_cloudagent/messaging/jsonld/credential.py index 23813276b0..c584a83502 100644 --- a/aries_cloudagent/messaging/jsonld/credential.py +++ b/aries_cloudagent/messaging/jsonld/credential.py @@ -18,7 +18,7 @@ def did_key(verkey: str) -> str: if verkey.startswith("did:key:"): return verkey - return DIDKey.from_public_key_b58(verkey, KeyType.ED25519).did + return DIDKey.from_public_key_b58(verkey, ED25519).did def b64encode(str): @@ -76,7 +76,7 @@ async def jws_verify(session, verify_data, signature, public_key): wallet = session.inject(BaseWallet) verified = await wallet.verify_message( - jws_to_verify, decoded_signature, public_key, KeyType.ED25519 + jws_to_verify, decoded_signature, public_key, ED25519 ) return verified diff --git a/aries_cloudagent/messaging/jsonld/tests/test_credential.py b/aries_cloudagent/messaging/jsonld/tests/test_credential.py index 297d5a3b0e..d613ece328 100644 --- a/aries_cloudagent/messaging/jsonld/tests/test_credential.py +++ b/aries_cloudagent/messaging/jsonld/tests/test_credential.py @@ -13,7 +13,7 @@ from ....vc.ld_proofs import DocumentLoader from ....wallet.base import BaseWallet from ....wallet.in_memory import InMemoryWallet -from ....wallet.key_type import KeyType +from ....wallet.key_type import ED25519, KeyType from .. import credential as test_module from ..create_verify_data import DroppedAttributeError @@ -60,7 +60,7 @@ async def test_verify_jws_header(self): class TestOps(AsyncTestCase): async def setUp(self): self.wallet = InMemoryWallet(InMemoryProfile.test_profile()) - await self.wallet.create_signing_key(KeyType.ED25519, TEST_SEED) + await self.wallet.create_signing_key(ED25519, TEST_SEED) self.session = InMemoryProfile.test_session(bind={BaseWallet: self.wallet}) self.profile = self.session.profile diff --git a/aries_cloudagent/messaging/jsonld/tests/test_routes.py b/aries_cloudagent/messaging/jsonld/tests/test_routes.py index 129ac26515..84b66fc341 100644 --- a/aries_cloudagent/messaging/jsonld/tests/test_routes.py +++ b/aries_cloudagent/messaging/jsonld/tests/test_routes.py @@ -275,7 +275,7 @@ async def setUp(self): DocumentLoader, custom_document_loader ) self.did_info = await (await self.context.session()).wallet.create_local_did( - DIDMethod.SOV, KeyType.ED25519 + DIDMethod.SOV, ED25519 ) self.request_dict = { "context": self.context, diff --git a/aries_cloudagent/messaging/tests/test_agent_message.py b/aries_cloudagent/messaging/tests/test_agent_message.py index 4e1b87e28e..2c4f9f4021 100644 --- a/aries_cloudagent/messaging/tests/test_agent_message.py +++ b/aries_cloudagent/messaging/tests/test_agent_message.py @@ -72,7 +72,7 @@ class BadImplementationClass(AgentMessage): async def test_field_signature(self): session = InMemoryProfile.test_session() wallet = session.wallet - key_info = await wallet.create_signing_key(KeyType.ED25519) + key_info = await wallet.create_signing_key(ED25519) msg = SignedAgentMessage() msg.value = None diff --git a/aries_cloudagent/multitenant/tests/test_base.py b/aries_cloudagent/multitenant/tests/test_base.py index 6f7c7b7e78..fa29fc34e3 100644 --- a/aries_cloudagent/multitenant/tests/test_base.py +++ b/aries_cloudagent/multitenant/tests/test_base.py @@ -245,7 +245,7 @@ async def test_create_wallet_adds_wallet_route(self): verkey="test_verkey", metadata={"meta": "data"}, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) mock_route_manager = async_mock.MagicMock() diff --git a/aries_cloudagent/protocols/connections/v1_0/manager.py b/aries_cloudagent/protocols/connections/v1_0/manager.py index d937b7ecb2..9fa1f897a0 100644 --- a/aries_cloudagent/protocols/connections/v1_0/manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/manager.py @@ -21,7 +21,7 @@ from ....wallet.did_info import DIDInfo from ....wallet.did_method import DIDMethod from ....wallet.error import WalletNotFoundError -from ....wallet.key_type import KeyType +from ....wallet.key_type import ED25519, KeyType from ....wallet.util import bytes_to_b58 from ...coordinate_mediation.v1_0.manager import MediationManager from ...discovery.v2_0.manager import V20DiscoveryMgr @@ -177,7 +177,7 @@ async def create_invitation( async with self.profile.session() as session: wallet = session.inject(BaseWallet) invitation_signing_key = await wallet.create_signing_key( - key_type=KeyType.ED25519 + key_type=ED25519 ) invitation_key = invitation_signing_key.verkey recipient_keys = [invitation_key] @@ -360,7 +360,7 @@ async def create_request( async with self.profile.session() as session: wallet = session.inject(BaseWallet) # Create new DID for connection - my_info = await wallet.create_local_did(DIDMethod.SOV, KeyType.ED25519) + my_info = await wallet.create_local_did(DIDMethod.SOV, ED25519) connection.my_did = my_info.did # Idempotent; if routing has already been set up, no action taken @@ -467,9 +467,7 @@ async def receive_request( if connection.is_multiuse_invitation: async with self.profile.session() as session: wallet = session.inject(BaseWallet) - my_info = await wallet.create_local_did( - DIDMethod.SOV, KeyType.ED25519 - ) + my_info = await wallet.create_local_did(DIDMethod.SOV, ED25519) new_connection = ConnRecord( invitation_key=connection_key, @@ -522,7 +520,7 @@ async def receive_request( else: # request from public did async with self.profile.session() as session: wallet = session.inject(BaseWallet) - my_info = await wallet.create_local_did(DIDMethod.SOV, KeyType.ED25519) + my_info = await wallet.create_local_did(DIDMethod.SOV, ED25519) async with self.profile.session() as session: connection = await ConnRecord.retrieve_by_invitation_msg_id( @@ -613,7 +611,7 @@ async def create_response( else: async with self.profile.session() as session: wallet = session.inject(BaseWallet) - my_info = await wallet.create_local_did(DIDMethod.SOV, KeyType.ED25519) + my_info = await wallet.create_local_did(DIDMethod.SOV, ED25519) connection.my_did = my_info.did # Idempotent; if routing has already been set up, no action taken @@ -831,7 +829,7 @@ async def create_static_connection( wallet = session.inject(BaseWallet) # seed and DID optional my_info = await wallet.create_local_did( - DIDMethod.SOV, KeyType.ED25519, my_seed, my_did + DIDMethod.SOV, ED25519, my_seed, my_did ) # must provide their DID and verkey if the seed is not known @@ -842,10 +840,10 @@ async def create_static_connection( if not their_did: their_did = seed_to_did(their_seed) if not their_verkey: - their_verkey_bin, _ = create_keypair(KeyType.ED25519, their_seed.encode()) + their_verkey_bin, _ = create_keypair(ED25519, their_seed.encode()) their_verkey = bytes_to_b58(their_verkey_bin) their_info = DIDInfo( - their_did, their_verkey, {}, method=DIDMethod.SOV, key_type=KeyType.ED25519 + their_did, their_verkey, {}, method=DIDMethod.SOV, key_type=ED25519 ) # Create connection record @@ -1106,7 +1104,7 @@ async def establish_inbound( my_info = await wallet.get_local_did(connection.my_did) else: # Create new DID for connection - my_info = await wallet.create_local_did(DIDMethod.SOV, KeyType.ED25519) + my_info = await wallet.create_local_did(DIDMethod.SOV, ED25519) connection.my_did = my_info.did try: diff --git a/aries_cloudagent/protocols/connections/v1_0/messages/tests/test_connection_response.py b/aries_cloudagent/protocols/connections/v1_0/messages/tests/test_connection_response.py index 98beb344fd..2973ade36c 100644 --- a/aries_cloudagent/protocols/connections/v1_0/messages/tests/test_connection_response.py +++ b/aries_cloudagent/protocols/connections/v1_0/messages/tests/test_connection_response.py @@ -2,7 +2,7 @@ from asynctest import TestCase as AsyncTestCase -from ......wallet.key_type import KeyType +from ......wallet.key_type import ED25519, KeyType from ......connections.models.diddoc import ( DIDDoc, PublicKey, @@ -108,7 +108,7 @@ async def test_make_model(self): ) session = InMemoryProfile.test_session() wallet = session.wallet - key_info = await wallet.create_signing_key(KeyType.ED25519) + key_info = await wallet.create_signing_key(ED25519) await connection_response.sign_field("connection", key_info.verkey, wallet) data = connection_response.serialize() model_instance = ConnectionResponse.deserialize(data) diff --git a/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py index 80efb456bf..4437ab0d86 100644 --- a/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py @@ -121,7 +121,7 @@ async def test_create_invitation_public_and_multi_use_fails(self): self.test_verkey, None, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) with self.assertRaises(ConnectionManagerError): await self.manager.create_invitation(public=True, multi_use=True) @@ -167,7 +167,7 @@ async def test_create_invitation_public(self): self.test_verkey, None, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) connect_record, connect_invite = await self.manager.create_invitation( public=True, my_endpoint="testendpoint" @@ -232,7 +232,7 @@ async def test_create_invitation_recipient_routing_endpoint(self): async with self.profile.session() as session: await session.wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, seed=self.test_seed, did=self.test_did, metadata=None, @@ -275,7 +275,7 @@ async def test_create_invitation_public_and_metadata_fails(self): self.test_verkey, None, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) with self.assertRaises(ConnectionManagerError): await self.manager.create_invitation( @@ -436,7 +436,7 @@ async def test_create_request_my_did(self): async with self.profile.session() as session: await session.wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, seed=None, did=self.test_did, ) @@ -481,7 +481,7 @@ async def test_create_request_multitenant(self): self.test_verkey, None, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) await self.manager.create_request( ConnRecord( @@ -534,7 +534,7 @@ async def test_create_request_mediation_id(self): verkey=self.test_verkey, metadata={}, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) create_local_did.return_value = did_info await self.manager.create_request( @@ -542,7 +542,7 @@ async def test_create_request_mediation_id(self): mediation_id=mediation_record.mediation_id, my_endpoint=self.test_endpoint, ) - create_local_did.assert_called_once_with(DIDMethod.SOV, KeyType.ED25519) + create_local_did.assert_called_once_with(DIDMethod.SOV, ED25519) create_did_document.assert_called_once_with( self.manager, did_info, @@ -586,14 +586,14 @@ async def test_create_request_default_mediator(self): verkey=self.test_verkey, metadata={}, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) create_local_did.return_value = did_info await self.manager.create_request( record, my_endpoint=self.test_endpoint, ) - create_local_did.assert_called_once_with(DIDMethod.SOV, KeyType.ED25519) + create_local_did.assert_called_once_with(DIDMethod.SOV, ED25519) create_did_document.assert_called_once_with( self.manager, did_info, @@ -615,7 +615,7 @@ async def test_receive_request_public_did_oob_invite(self): ) await session.wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, seed=None, did=self.test_did, ) @@ -655,7 +655,7 @@ async def test_receive_request_public_did_conn_invite(self): ) await session.wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, seed=None, did=self.test_did, ) @@ -690,7 +690,7 @@ async def test_receive_request_public_did_no_did_doc(self): ) await session.wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, seed=None, did=self.test_did, ) @@ -721,7 +721,7 @@ async def test_receive_request_public_did_wrong_did(self): ) await session.wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, seed=None, did=self.test_did, ) @@ -750,7 +750,7 @@ async def test_receive_request_public_did_no_public_invites(self): async with self.profile.session() as session: await session.wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, seed=None, did=self.test_did, ) @@ -781,7 +781,7 @@ async def test_receive_request_public_did_no_auto_accept(self): ) await session.wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, seed=None, did=self.test_did, ) @@ -868,7 +868,7 @@ async def test_create_response_multitenant(self): self.test_verkey, None, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) await self.manager.create_response( ConnRecord( @@ -942,7 +942,7 @@ async def test_create_response_mediation(self): verkey=self.test_verkey, metadata={}, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) create_local_did.return_value = did_info await self.manager.create_response( @@ -950,7 +950,7 @@ async def test_create_response_mediation(self): mediation_id=mediation_record.mediation_id, my_endpoint=self.test_endpoint, ) - create_local_did.assert_called_once_with(DIDMethod.SOV, KeyType.ED25519) + create_local_did.assert_called_once_with(DIDMethod.SOV, ED25519) create_did_document.assert_called_once_with( self.manager, did_info, @@ -1267,7 +1267,7 @@ async def test_create_static_connection_multitenant(self): self.test_verkey, None, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) await self.manager.create_static_connection( @@ -1300,7 +1300,7 @@ async def test_create_static_connection_multitenant_auto_disclose_features(self) self.test_verkey, None, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) await self.manager.create_static_connection( my_did=self.test_did, @@ -1332,7 +1332,7 @@ async def test_create_static_connection_multitenant_mediator(self): self.test_verkey, None, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) # With default mediator @@ -1360,7 +1360,7 @@ async def test_create_static_connection_multitenant_mediator(self): self.test_target_verkey, {}, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) create_did_document.assert_has_calls( [ @@ -1538,7 +1538,7 @@ async def test_resolve_inbound_connection(self): self.test_verkey, {"posted": True}, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) mock_mgr_find_conn.return_value = mock_conn @@ -1590,7 +1590,7 @@ async def test_create_did_document(self): self.test_verkey, None, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) mock_conn = async_mock.MagicMock( @@ -1623,7 +1623,7 @@ async def test_create_did_document_not_active(self): self.test_verkey, None, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) mock_conn = async_mock.MagicMock( @@ -1651,7 +1651,7 @@ async def test_create_did_document_no_services(self): self.test_verkey, None, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) mock_conn = async_mock.MagicMock( @@ -1686,7 +1686,7 @@ async def test_create_did_document_no_service_endpoint(self): self.test_verkey, None, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) mock_conn = async_mock.MagicMock( @@ -1724,7 +1724,7 @@ async def test_create_did_document_no_service_recip_keys(self): self.test_verkey, None, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) mock_conn = async_mock.MagicMock( @@ -1770,7 +1770,7 @@ async def test_create_did_document_mediation(self): self.test_verkey, None, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) mediation_record = MediationRecord( role=MediationRecord.ROLE_CLIENT, @@ -1796,7 +1796,7 @@ async def test_create_did_document_multiple_mediators(self): self.test_verkey, None, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) mediation_record1 = MediationRecord( role=MediationRecord.ROLE_CLIENT, @@ -1829,7 +1829,7 @@ async def test_create_did_document_mediation_svc_endpoints_overwritten(self): self.test_verkey, None, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) mediation_record = MediationRecord( role=MediationRecord.ROLE_CLIENT, @@ -1864,7 +1864,7 @@ async def test_get_connection_targets_conn_invitation_no_did(self): async with self.profile.session() as session: local_did = await session.wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, seed=self.test_seed, did=self.test_did, metadata=None, @@ -1923,7 +1923,7 @@ async def test_get_connection_targets_retrieve_connection(self): async with self.profile.session() as session: local_did = await session.wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, seed=self.test_seed, did=self.test_did, metadata=None, @@ -1976,7 +1976,7 @@ async def test_get_conn_targets_conn_invitation_no_cache(self): self.context.injector.clear_binding(BaseCache) local_did = await session.wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, seed=self.test_seed, did=self.test_did, metadata=None, @@ -2026,7 +2026,7 @@ async def test_fetch_connection_targets_conn_invitation_did_no_resolver(self): self.context.injector.bind_instance(DIDResolver, DIDResolver([])) await session.wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, seed=self.test_seed, did=self.test_did, metadata=None, @@ -2072,7 +2072,7 @@ async def test_fetch_connection_targets_conn_invitation_did_resolver(self): local_did = await session.wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, seed=self.test_seed, did=self.test_did, metadata=None, @@ -2140,7 +2140,7 @@ async def test_fetch_connection_targets_conn_invitation_btcr_resolver(self): self.context.injector.bind_instance(DIDResolver, self.resolver) local_did = await session.wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, seed=self.test_seed, did=did_doc.id, metadata=None, @@ -2205,7 +2205,7 @@ async def test_fetch_connection_targets_conn_invitation_btcr_without_services(se local_did = await session.wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, seed=self.test_seed, did=did_doc.id, metadata=None, @@ -2245,7 +2245,7 @@ async def test_fetch_connection_targets_conn_invitation_no_didcomm_services(self self.context.injector.bind_instance(DIDResolver, self.resolver) await session.wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, seed=self.test_seed, did=did_doc.id, metadata=None, @@ -2291,7 +2291,7 @@ async def test_fetch_connection_targets_conn_invitation_unsupported_key_type(sel self.context.injector.bind_instance(DIDResolver, self.resolver) local_did = await session.wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, seed=self.test_seed, did=did_doc.id, metadata=None, @@ -2320,7 +2320,7 @@ async def test_fetch_connection_targets_oob_invitation_svc_did_no_resolver(self) self.context.injector.bind_instance(DIDResolver, DIDResolver([])) await session.wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, seed=self.test_seed, did=self.test_did, metadata=None, @@ -2361,7 +2361,7 @@ async def test_fetch_connection_targets_oob_invitation_svc_did_resolver(self): local_did = await session.wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, seed=self.test_seed, did=self.test_did, metadata=None, @@ -2406,7 +2406,7 @@ async def test_fetch_connection_targets_oob_invitation_svc_block_resolver(self): local_did = await session.wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, seed=self.test_seed, did=self.test_did, metadata=None, @@ -2420,7 +2420,7 @@ async def test_fetch_connection_targets_oob_invitation_svc_block_resolver(self): service_endpoint=self.test_endpoint, recipient_keys=[ DIDKey.from_public_key_b58( - self.test_target_verkey, KeyType.ED25519 + self.test_target_verkey, ED25519 ).did ], routing_keys=[], @@ -2452,7 +2452,7 @@ async def test_fetch_connection_targets_conn_initiator_completed_no_their_did(se async with self.profile.session() as session: await session.wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, seed=self.test_seed, did=self.test_did, metadata=None, @@ -2469,7 +2469,7 @@ async def test_fetch_connection_targets_conn_completed_their_did(self): async with self.profile.session() as session: local_did = await session.wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, seed=self.test_seed, did=self.test_did, metadata=None, @@ -2500,7 +2500,7 @@ async def test_fetch_connection_targets_conn_no_invi_with_their_did(self): async with self.profile.session() as session: local_did = await session.wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, seed=self.test_seed, did=self.test_did, metadata=None, @@ -2555,7 +2555,7 @@ async def test_establish_inbound(self): async with self.profile.session() as session: await session.wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, seed=self.test_seed, did=self.test_did, metadata=None, @@ -2585,7 +2585,7 @@ async def test_establish_inbound_conn_rec_no_my_did(self): async with self.profile.session() as session: await session.wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, seed=self.test_seed, did=self.test_did, metadata=None, @@ -2614,7 +2614,7 @@ async def test_establish_inbound_no_conn_record(self): async with self.profile.session() as session: await session.wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, seed=self.test_seed, did=self.test_did, metadata=None, @@ -2643,7 +2643,7 @@ async def test_establish_inbound_router_not_ready(self): async with self.profile.session() as session: await session.wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, seed=self.test_seed, did=self.test_did, metadata=None, diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py index f525dd0088..7a67b57369 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py @@ -112,7 +112,7 @@ async def _create_routing_did(self, session: ProfileSession) -> DIDInfo: storage = session.inject(BaseStorage) info = await wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, metadata={"type": "routing_did"}, ) record = StorageRecord( diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/normalization.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/normalization.py index 1d8610e60c..c105aa05d5 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/normalization.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/normalization.py @@ -16,4 +16,4 @@ def normalize_from_public_key(key: str): if key.startswith("did:key:"): return key - return DIDKey.from_public_key_b58(key, KeyType.ED25519).did + return DIDKey.from_public_key_b58(key, ED25519).did diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py index 07da03fd51..4e03d929a9 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py @@ -40,7 +40,7 @@ async def get_or_create_my_did( async with profile.session() as session: wallet = session.inject(BaseWallet) # Create new DID for connection - my_info = await wallet.create_local_did(DIDMethod.SOV, KeyType.ED25519) + my_info = await wallet.create_local_did(DIDMethod.SOV, ED25519) conn_record.my_did = my_info.did await conn_record.save(session, reason="Connection my did created") else: diff --git a/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_request_handler.py b/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_request_handler.py index ff76d6a9c6..34acf504ba 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_request_handler.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_request_handler.py @@ -88,7 +88,7 @@ async def setUp(self): wallet = self.session.wallet self.did_info = await wallet.create_local_did( - method=DIDMethod.SOV, key_type=KeyType.ED25519 + method=DIDMethod.SOV, key_type=ED25519 ) self.did_doc_attach = AttachDecorator.data_base64(self.did_doc().serialize()) diff --git a/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_response_handler.py b/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_response_handler.py index a161b5f325..79e0af0782 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_response_handler.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_response_handler.py @@ -66,7 +66,7 @@ async def setUp(self): wallet = (await self.ctx.session()).wallet self.did_info = await wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) self.did_doc_attach = AttachDecorator.data_base64(self.did_doc().serialize()) diff --git a/aries_cloudagent/protocols/didexchange/v1_0/manager.py b/aries_cloudagent/protocols/didexchange/v1_0/manager.py index 587e6b56b5..12e6726b87 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/manager.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/manager.py @@ -285,7 +285,7 @@ async def create_request( wallet = session.inject(BaseWallet) my_info = await wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) conn_rec.my_did = my_info.did @@ -418,7 +418,7 @@ async def receive_request( wallet = session.inject(BaseWallet) my_info = await wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) new_conn_rec = ConnRecord( @@ -487,7 +487,7 @@ async def receive_request( wallet = session.inject(BaseWallet) my_info = await wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) auto_accept = bool( @@ -581,7 +581,7 @@ async def create_response( wallet = session.inject(BaseWallet) my_info = await wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) conn_rec.my_did = my_info.did diff --git a/aries_cloudagent/protocols/didexchange/v1_0/messages/tests/test_request.py b/aries_cloudagent/protocols/didexchange/v1_0/messages/tests/test_request.py index a7ad4f405f..b3dae6e2a9 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/messages/tests/test_request.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/messages/tests/test_request.py @@ -60,7 +60,7 @@ async def setUp(self): self.wallet = InMemoryProfile.test_session().wallet self.did_info = await self.wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) did_doc_attach = AttachDecorator.data_base64(self.make_did_doc().serialize()) @@ -117,7 +117,7 @@ async def setUp(self): self.wallet = InMemoryProfile.test_session().wallet self.did_info = await self.wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) did_doc_attach = AttachDecorator.data_base64(self.make_did_doc().serialize()) diff --git a/aries_cloudagent/protocols/didexchange/v1_0/messages/tests/test_response.py b/aries_cloudagent/protocols/didexchange/v1_0/messages/tests/test_response.py index 9cf32b8359..78b8443255 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/messages/tests/test_response.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/messages/tests/test_response.py @@ -59,7 +59,7 @@ async def setUp(self): self.wallet = InMemoryProfile.test_session().wallet self.did_info = await self.wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) did_doc_attach = AttachDecorator.data_base64(self.make_did_doc().serialize()) @@ -113,7 +113,7 @@ async def setUp(self): self.wallet = InMemoryProfile.test_session().wallet self.did_info = await self.wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) did_doc_attach = AttachDecorator.data_base64(self.make_did_doc().serialize()) diff --git a/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py index 131d87670e..8cdbcbc316 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py @@ -115,7 +115,7 @@ async def setUp(self): async with self.profile.session() as session: self.did_info = await session.wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) self.ledger = async_mock.create_autospec(BaseLedger) @@ -142,7 +142,7 @@ async def setUp(self): self.oob_manager = OutOfBandManager(self.profile) self.test_mediator_routing_keys = [ DIDKey.from_public_key_b58( - "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRR", KeyType.ED25519 + "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRR", ED25519 ).did ] self.test_mediator_conn_id = "mediator-conn-id" @@ -203,7 +203,7 @@ async def test_receive_invitation_oob_public_did(self): public_did_info = None await session.wallet.create_public_did( DIDMethod.SOV, - KeyType.ED25519, + ED25519, ) public_did_info = await session.wallet.get_public_did() with async_mock.patch.object( @@ -304,7 +304,7 @@ async def test_create_request_implicit_use_public_did(self): async with self.profile.session() as session: info_public = await session.wallet.create_public_did( DIDMethod.SOV, - KeyType.ED25519, + ED25519, ) conn_rec = await self.manager.create_request_implicit( their_public_did=TestConfig.test_target_did, @@ -375,7 +375,7 @@ async def test_create_request_multitenant(self): TestConfig.test_verkey, None, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) mock_attach_deco.data_base64 = async_mock.MagicMock( return_value=async_mock.MagicMock( @@ -503,7 +503,7 @@ async def test_receive_request_explicit_public_did(self): await session.wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, seed=None, did=TestConfig.test_did, ) @@ -602,7 +602,7 @@ async def test_receive_request_invi_not_found(self): await session.wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, seed=None, did=TestConfig.test_did, ) @@ -634,7 +634,7 @@ async def test_receive_request_public_did_no_did_doc_attachment(self): await session.wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, seed=None, did=TestConfig.test_did, ) @@ -694,7 +694,7 @@ async def test_receive_request_public_did_x_not_public(self): await session.wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, seed=None, did=TestConfig.test_did, ) @@ -736,7 +736,7 @@ async def test_receive_request_public_did_x_wrong_did(self): await session.wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, seed=None, did=TestConfig.test_did, ) @@ -803,7 +803,7 @@ async def test_receive_request_public_did_x_did_doc_attach_bad_sig(self): await session.wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, seed=None, did=TestConfig.test_did, ) @@ -864,7 +864,7 @@ async def test_receive_request_public_did_no_public_invites(self): await session.wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, seed=None, did=TestConfig.test_did, ) @@ -911,7 +911,7 @@ async def test_receive_request_public_did_no_auto_accept(self): await session.wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, seed=None, did=TestConfig.test_did, ) @@ -1004,7 +1004,7 @@ async def test_receive_request_peer_did(self): await session.wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, seed=None, did=TestConfig.test_did, ) @@ -1078,7 +1078,7 @@ async def test_receive_request_peer_did_not_found_x(self): await session.wallet.create_local_did( method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, seed=None, did=TestConfig.test_did, ) @@ -1254,7 +1254,7 @@ async def test_create_response_multitenant(self): TestConfig.test_verkey, None, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) mock_create_did_doc.return_value = async_mock.MagicMock( serialize=async_mock.MagicMock() @@ -1671,7 +1671,7 @@ async def test_create_did_document(self): TestConfig.test_verkey, None, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) mock_conn = async_mock.MagicMock( @@ -1705,7 +1705,7 @@ async def test_create_did_document_not_completed(self): TestConfig.test_verkey, None, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) mock_conn = async_mock.MagicMock( @@ -1733,7 +1733,7 @@ async def test_create_did_document_no_services(self): TestConfig.test_verkey, None, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) mock_conn = async_mock.MagicMock( @@ -1768,7 +1768,7 @@ async def test_create_did_document_no_service_endpoint(self): TestConfig.test_verkey, None, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) mock_conn = async_mock.MagicMock( @@ -1806,7 +1806,7 @@ async def test_create_did_document_no_service_recip_keys(self): TestConfig.test_verkey, None, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) mock_conn = async_mock.MagicMock( @@ -1852,7 +1852,7 @@ async def test_did_key_storage(self): TestConfig.test_verkey, None, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) did_doc = self.make_did_doc( @@ -1898,7 +1898,7 @@ async def test_resolve_did_document_error(self): async with self.profile.session() as session: await session.wallet.create_public_did( DIDMethod.SOV, - KeyType.ED25519, + ED25519, ) public_did_info = await session.wallet.get_public_did() with async_mock.patch.object( diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_manager.py index edad031889..12f898b3be 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_manager.py @@ -125,7 +125,7 @@ async def setUp(self): self.wallet: BaseWallet = session.inject_or(BaseWallet) await self.wallet.create_local_did( DIDMethod.SOV, - KeyType.ED25519, + ED25519, did="DJGEjaMunDtFtBVrn1qJMT", metadata={"meta": "data"}, ) diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_routes.py index d91b1ff8b8..4d40885c0d 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_routes.py @@ -437,7 +437,7 @@ async def test_endorse_transaction_response(self): "verkey", {"meta": "data"}, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) ) ), @@ -516,7 +516,7 @@ async def test_endorse_transaction_response_not_found_x(self): "verkey", {"meta": "data"}, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) ) ), @@ -545,7 +545,7 @@ async def test_endorse_transaction_response_base_model_x(self): "verkey", {"meta": "data"}, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) ) ), @@ -580,7 +580,7 @@ async def test_endorse_transaction_response_no_jobs_x(self): "verkey", {"meta": "data"}, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) ) ), @@ -617,7 +617,7 @@ async def skip_test_endorse_transaction_response_no_ledger_x(self): "verkey", {"meta": "data"}, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) ) ), @@ -672,7 +672,7 @@ async def test_endorse_transaction_response_wrong_my_job_x(self): "verkey", {"meta": "data"}, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) ) ), @@ -715,7 +715,7 @@ async def skip_test_endorse_transaction_response_ledger_x(self): "verkey", {"meta": "data"}, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) ) ), @@ -773,7 +773,7 @@ async def test_endorse_transaction_response_txn_mgr_x(self): "verkey", {"meta": "data"}, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) ) ), @@ -825,7 +825,7 @@ async def test_refuse_transaction_response(self): "verkey", {"meta": "data"}, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) ) ), @@ -883,7 +883,7 @@ async def test_refuse_transaction_response_not_found_x(self): "verkey", {"meta": "data"}, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) ) ), @@ -913,7 +913,7 @@ async def test_refuse_transaction_response_conn_base_model_x(self): "verkey", {"meta": "data"}, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) ) ), @@ -948,7 +948,7 @@ async def test_refuse_transaction_response_no_jobs_x(self): "verkey", {"meta": "data"}, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) ) ), @@ -985,7 +985,7 @@ async def test_refuse_transaction_response_wrong_my_job_x(self): "verkey", {"meta": "data"}, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) ) ), @@ -1028,7 +1028,7 @@ async def test_refuse_transaction_response_txn_mgr_x(self): "verkey", {"meta": "data"}, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) ) ), diff --git a/aries_cloudagent/protocols/introduction/v0_1/tests/test_service.py b/aries_cloudagent/protocols/introduction/v0_1/tests/test_service.py index a2bff3f785..5947030e4b 100644 --- a/aries_cloudagent/protocols/introduction/v0_1/tests/test_service.py +++ b/aries_cloudagent/protocols/introduction/v0_1/tests/test_service.py @@ -36,12 +36,10 @@ def setUp(self): _type="did-communication", did=TEST_DID, recipient_keys=[ - DIDKey.from_public_key_b58(TEST_VERKEY, KeyType.ED25519).did + DIDKey.from_public_key_b58(TEST_VERKEY, ED25519).did ], routing_keys=[ - DIDKey.from_public_key_b58( - TEST_ROUTE_VERKEY, KeyType.ED25519 - ).did + DIDKey.from_public_key_b58(TEST_ROUTE_VERKEY, ED25519).did ], service_endpoint=TEST_ENDPOINT, ) diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py index 796e71e67f..66177376a2 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py @@ -64,13 +64,13 @@ AuthenticationProofPurpose.term, } SUPPORTED_ISSUANCE_SUITES = {Ed25519Signature2018} -SIGNATURE_SUITE_KEY_TYPE_MAPPING = {Ed25519Signature2018: KeyType.ED25519} +SIGNATURE_SUITE_KEY_TYPE_MAPPING = {Ed25519Signature2018: ED25519} # We only want to add bbs suites to supported if the module is installed if BbsBlsSignature2020.BBS_SUPPORTED: SUPPORTED_ISSUANCE_SUITES.add(BbsBlsSignature2020) - SIGNATURE_SUITE_KEY_TYPE_MAPPING[BbsBlsSignature2020] = KeyType.BLS12381G2 + SIGNATURE_SUITE_KEY_TYPE_MAPPING[BbsBlsSignature2020] = BLS12381G2 PROOF_TYPE_SIGNATURE_SUITE_MAPPING = { diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/tests/test_handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/tests/test_handler.py index c4666a679e..53ea35b095 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/tests/test_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/tests/test_handler.py @@ -218,7 +218,7 @@ async def test_assert_can_issue_with_id_and_proof_type(self): verkey="verkey", metadata={}, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) mock_did_info.return_value = did_info await self.handler._assert_can_issue_with_id_and_proof_type( @@ -230,7 +230,7 @@ async def test_assert_can_issue_with_id_and_proof_type(self): verkey="verkey", metadata={}, method=DIDMethod.SOV, - key_type=KeyType.BLS12381G2, + key_type=BLS12381G2, ) mock_did_info.return_value = invalid_did_info with self.assertRaises(V20CredFormatError) as context: @@ -281,7 +281,7 @@ async def test_get_suite_for_detail(self): assert type(suite) == Ed25519Signature2018 assert suite.verification_method == DIDKey.from_did(TEST_DID_KEY).key_id assert suite.proof == {"created": LD_PROOF_VC_DETAIL["options"]["created"]} - assert suite.key_pair.key_type == KeyType.ED25519 + assert suite.key_pair.key_type == ED25519 assert suite.key_pair.public_key_base58 == mock_did_info.return_value.verkey mock_can_issue.assert_called_once_with( @@ -303,7 +303,7 @@ async def test_get_suite(self): assert type(suite) == BbsBlsSignature2020 assert suite.verification_method == "verification_method" assert suite.proof == proof - assert suite.key_pair.key_type == KeyType.BLS12381G2 + assert suite.key_pair.key_type == BLS12381G2 assert suite.key_pair.public_key_base58 == did_info.verkey suite = await self.handler._get_suite( @@ -316,7 +316,7 @@ async def test_get_suite(self): assert type(suite) == Ed25519Signature2018 assert suite.verification_method == "verification_method" assert suite.proof == proof - assert suite.key_pair.key_type == KeyType.ED25519 + assert suite.key_pair.key_type == ED25519 assert suite.key_pair.public_key_base58 == did_info.verkey async def test_get_verification_method(self): diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py index 4bdfcc39a6..49fd1988e4 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py @@ -19,7 +19,7 @@ from ....storage.error import StorageNotFoundError from ....transport.inbound.receipt import MessageReceipt from ....wallet.base import BaseWallet -from ....wallet.key_type import KeyType +from ....wallet.key_type import ED25519 from ...connections.v1_0.manager import ConnectionManager from ...connections.v1_0.messages.connection_invitation import ConnectionInvitation from ...didcomm_prefix import DIDCommPrefix @@ -265,7 +265,7 @@ async def create_invitation( # Create and store new key for exchange async with self.profile.session() as session: wallet = session.inject(BaseWallet) - connection_key = await wallet.create_signing_key(KeyType.ED25519) + connection_key = await wallet.create_signing_key(ED25519) our_recipient_key = connection_key.verkey @@ -310,7 +310,7 @@ async def create_invitation( routing_keys = [ key if len(key.split(":")) == 3 - else DIDKey.from_public_key_b58(key, KeyType.ED25519).did + else DIDKey.from_public_key_b58(key, ED25519).did for key in routing_keys ] @@ -326,9 +326,7 @@ async def create_invitation( _id="#inline", _type="did-communication", recipient_keys=[ - DIDKey.from_public_key_b58( - connection_key.verkey, KeyType.ED25519 - ).did + DIDKey.from_public_key_b58(connection_key.verkey, ED25519).did ], service_endpoint=my_endpoint, routing_keys=routing_keys, @@ -514,7 +512,7 @@ async def receive_invitation( # Create and store new key for connectionless exchange async with self.profile.session() as session: wallet = session.inject(BaseWallet) - connection_key = await wallet.create_signing_key(KeyType.ED25519) + connection_key = await wallet.create_signing_key(ED25519) oob_record.our_recipient_key = connection_key.verkey oob_record.our_service = ServiceDecorator( recipient_keys=[connection_key.verkey], @@ -752,11 +750,11 @@ async def _perform_handshake( "id": "#inline", "type": "did-communication", "recipientKeys": [ - DIDKey.from_public_key_b58(key, KeyType.ED25519).did + DIDKey.from_public_key_b58(key, ED25519).did for key in recipient_keys ], "routingKeys": [ - DIDKey.from_public_key_b58(key, KeyType.ED25519).did + DIDKey.from_public_key_b58(key, ED25519).did for key in routing_keys ], "serviceEndpoint": endpoint, diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/messages/tests/test_invitation.py b/aries_cloudagent/protocols/out_of_band/v1_0/messages/tests/test_invitation.py index 5340dd66dc..f1e215744c 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/messages/tests/test_invitation.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/messages/tests/test_invitation.py @@ -80,9 +80,7 @@ def test_wrap_serde(self): service = Service( _id="#inline", _type=DID_COMM, - recipient_keys=[ - DIDKey.from_public_key_b58(TEST_VERKEY, KeyType.ED25519).did - ], + recipient_keys=[DIDKey.from_public_key_b58(TEST_VERKEY, ED25519).did], service_endpoint="http://1.2.3.4:8080/service", ) data_deser = { @@ -110,9 +108,7 @@ def test_url_round_trip(self): service = Service( _id="#inline", _type=DID_COMM, - recipient_keys=[ - DIDKey.from_public_key_b58(TEST_VERKEY, KeyType.ED25519).did - ], + recipient_keys=[DIDKey.from_public_key_b58(TEST_VERKEY, ED25519).did], service_endpoint="http://1.2.3.4:8080/service", ) invi_msg = InvitationMessage( diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py index 738e295a1f..1f082ba6df 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py @@ -67,7 +67,7 @@ from .....wallet.did_info import DIDInfo, KeyInfo from .....wallet.did_method import DIDMethod from .....wallet.in_memory import InMemoryWallet -from .....wallet.key_type import KeyType +from .....wallet.key_type import ED25519, KeyType from ....connections.v1_0.messages.connection_invitation import ConnectionInvitation from ....didcomm_prefix import DIDCommPrefix from ....issue_credential.v1_0.message_types import CREDENTIAL_OFFER @@ -379,7 +379,7 @@ async def test_create_invitation_handshake_succeeds(self): TestConfig.test_verkey, None, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) invi_rec = await self.manager.create_invitation( my_endpoint=TestConfig.test_endpoint, @@ -411,7 +411,7 @@ async def test_create_invitation_multitenant_local(self): self.multitenant_mgr, "get_default_mediator" ) as mock_get_default_mediator: mock_wallet_create_signing_key.return_value = KeyInfo( - TestConfig.test_verkey, None, KeyType.ED25519 + TestConfig.test_verkey, None, ED25519 ) mock_get_default_mediator.return_value = MediationRecord() await self.manager.create_invitation( @@ -439,7 +439,7 @@ async def test_create_invitation_multitenant_public(self): self.test_verkey, None, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) await self.manager.create_invitation( hs_protos=[HSProto.RFC23], @@ -514,7 +514,7 @@ async def test_create_invitation_attachment_v1_0_cred_offer(self): TestConfig.test_verkey, None, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) mock_retrieve_cxid.return_value = async_mock.MagicMock( credential_offer_dict=self.CRED_OFFER_V1 @@ -548,7 +548,7 @@ async def test_create_invitation_attachment_v1_0_cred_offer_no_handshake(self): TestConfig.test_verkey, None, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) mock_retrieve_cxid.return_value = async_mock.MagicMock( credential_offer_dict=self.CRED_OFFER_V1 @@ -586,7 +586,7 @@ async def test_create_invitation_attachment_v2_0_cred_offer(self): TestConfig.test_verkey, None, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) mock_retrieve_cxid_v1.side_effect = test_module.StorageNotFoundError() mock_retrieve_cxid_v2.return_value = async_mock.MagicMock( @@ -624,7 +624,7 @@ async def test_create_invitation_attachment_present_proof_v1_0(self): TestConfig.test_verkey, None, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) mock_retrieve_pxid.return_value = async_mock.MagicMock( presentation_request_dict=self.PRES_REQ_V1 @@ -663,7 +663,7 @@ async def test_create_invitation_attachment_present_proof_v2_0(self): TestConfig.test_verkey, None, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) mock_retrieve_pxid_1.side_effect = StorageNotFoundError() mock_retrieve_pxid_2.return_value = async_mock.MagicMock( @@ -751,7 +751,7 @@ async def test_create_invitation_attachment_x(self): TestConfig.test_verkey, None, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) with self.assertRaises(OutOfBandManagerError) as context: await self.manager.create_invitation( @@ -807,7 +807,7 @@ async def test_create_invitation_peer_did(self): assert ( service["routingKeys"][0] == DIDKey.from_public_key_b58( - self.test_mediator_routing_keys[0], KeyType.ED25519 + self.test_mediator_routing_keys[0], ED25519 ).did ) assert service["serviceEndpoint"] == self.test_mediator_endpoint @@ -835,7 +835,7 @@ async def test_create_invitation_x_public_metadata(self): TestConfig.test_verkey, None, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) with self.assertRaises(OutOfBandManagerError) as context: await self.manager.create_invitation( @@ -1294,7 +1294,7 @@ async def test_receive_invitation_connection_protocol(self): recipient_keys=[ DIDKey.from_public_key_b58( "9WCgWKUaAJj3VWxxtzvvMQN3AoFxoBtBDo9ntwJnVVCC", - KeyType.ED25519, + ED25519, ).did ], routing_keys=[], @@ -1564,7 +1564,7 @@ async def test_request_attach_oob_message_processor_connectionless(self): async_mock.CoroutineMock(), ) as mock_service_decorator_from_service: mock_create_signing_key.return_value = KeyInfo( - verkey="a-verkey", metadata={}, key_type=KeyType.ED25519 + verkey="a-verkey", metadata={}, key_type=ED25519 ) mock_service_decorator_from_service.return_value = mock_service_decorator oob_invitation = InvitationMessage( @@ -1581,7 +1581,7 @@ async def test_request_attach_oob_message_processor_connectionless(self): assert oob_record.our_service assert oob_record.state == OobRecord.STATE_PREPARE_RESPONSE - mock_create_signing_key.assert_called_once_with(KeyType.ED25519) + mock_create_signing_key.assert_called_once_with(ED25519) mock_oob_processor.handle_message.assert_called_once_with( self.profile, [attachment.content for attachment in requests_attach], @@ -1697,10 +1697,10 @@ async def test_service_decorator_from_service_object(self): oob_service = OobService( service_endpoint=TestConfig.test_endpoint, recipient_keys=[ - DIDKey.from_public_key_b58(TestConfig.test_verkey, KeyType.ED25519).did + DIDKey.from_public_key_b58(TestConfig.test_verkey, ED25519).did ], routing_keys=[ - DIDKey.from_public_key_b58(verkey, KeyType.ED25519).did + DIDKey.from_public_key_b58(verkey, ED25519).did for verkey in self.test_mediator_routing_keys ], ) diff --git a/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py b/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py index 638ce0fa3e..79a1aa8deb 100644 --- a/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py +++ b/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py @@ -73,14 +73,14 @@ class DIFPresExchHandler: """Base Presentation Exchange Handler.""" ISSUE_SIGNATURE_SUITE_KEY_TYPE_MAPPING = { - Ed25519Signature2018: KeyType.ED25519, + Ed25519Signature2018: ED25519, } if BbsBlsSignature2020.BBS_SUPPORTED: - ISSUE_SIGNATURE_SUITE_KEY_TYPE_MAPPING[BbsBlsSignature2020] = KeyType.BLS12381G2 + ISSUE_SIGNATURE_SUITE_KEY_TYPE_MAPPING[BbsBlsSignature2020] = BLS12381G2 DERIVE_SIGNATURE_SUITE_KEY_TYPE_MAPPING = { - BbsBlsSignatureProof2020: KeyType.BLS12381G2, + BbsBlsSignatureProof2020: BLS12381G2, } PROOF_TYPE_SIGNATURE_SUITE_MAPPING = { suite.signature_type: suite @@ -196,9 +196,9 @@ async def get_sign_key_credential_subject_id( issuer_id = None filtered_creds_list = [] if self.proof_type == BbsBlsSignature2020.signature_type: - reqd_key_type = KeyType.BLS12381G2 + reqd_key_type = BLS12381G2 else: - reqd_key_type = KeyType.ED25519 + reqd_key_type = ED25519 for cred in applicable_creds: if cred.subject_ids and len(cred.subject_ids) > 0: if not issuer_id: diff --git a/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py b/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py index 87587eb674..110252fd97 100644 --- a/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py +++ b/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py @@ -79,11 +79,11 @@ async def setup_tuple(profile): async with profile.session() as session: wallet = session.inject_or(BaseWallet) await wallet.create_local_did( - method=DIDMethod.SOV, key_type=KeyType.ED25519, did="WgWxqztrNooG92RXvxSTWv" + method=DIDMethod.SOV, key_type=ED25519, did="WgWxqztrNooG92RXvxSTWv" ) await wallet.create_local_did( method=DIDMethod.KEY, - key_type=KeyType.BLS12381G2, + key_type=BLS12381G2, ) creds, pds = get_test_data() return creds, pds @@ -2032,7 +2032,7 @@ async def test_get_sign_key_credential_subject_id(self, profile): verkey="verkey", metadata={}, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) mock_did_info.return_value = did_info ( @@ -2097,7 +2097,7 @@ async def test_get_sign_key_credential_subject_id_error(self, profile): verkey="verkey", metadata={}, method=DIDMethod.SOV, - key_type=KeyType.ED25519, + key_type=ED25519, ) mock_did_info.return_value = did_info with pytest.raises(DIFPresExchError): @@ -2165,7 +2165,7 @@ async def test_get_sign_key_credential_subject_id_bbsbls(self, profile): verkey="verkey", metadata={}, method=DIDMethod.KEY, - key_type=KeyType.BLS12381G2, + key_type=BLS12381G2, ) mock_did_info.return_value = did_info ( @@ -2256,7 +2256,7 @@ async def test_create_vp_no_issuer(self, profile, setup_tuple): verkey="verkey", metadata={}, method=DIDMethod.KEY, - key_type=KeyType.BLS12381G2, + key_type=BLS12381G2, ) mock_did_info.return_value = did_info vp = await dif_pres_exch_handler.create_vp( @@ -2316,7 +2316,7 @@ async def test_create_vp_with_bbs_suite(self, profile, setup_tuple): verkey="verkey", metadata={}, method=DIDMethod.KEY, - key_type=KeyType.BLS12381G2, + key_type=BLS12381G2, ) mock_did_info.return_value = did_info vp = await dif_pres_exch_handler.create_vp( @@ -2370,7 +2370,7 @@ async def test_create_vp_no_issuer_with_bbs_suite(self, profile, setup_tuple): verkey="verkey", metadata={}, method=DIDMethod.KEY, - key_type=KeyType.BLS12381G2, + key_type=BLS12381G2, ) mock_did_info.return_value = did_info vp = await dif_pres_exch_handler.create_vp( diff --git a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/handler.py b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/handler.py index 9efff76b16..c69b906085 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/handler.py @@ -56,14 +56,14 @@ class DIFPresFormatHandler(V20PresFormatHandler): format = V20PresFormat.Format.DIF ISSUE_SIGNATURE_SUITE_KEY_TYPE_MAPPING = { - Ed25519Signature2018: KeyType.ED25519, + Ed25519Signature2018: ED25519, } if BbsBlsSignature2020.BBS_SUPPORTED: - ISSUE_SIGNATURE_SUITE_KEY_TYPE_MAPPING[BbsBlsSignature2020] = KeyType.BLS12381G2 + ISSUE_SIGNATURE_SUITE_KEY_TYPE_MAPPING[BbsBlsSignature2020] = BLS12381G2 ISSUE_SIGNATURE_SUITE_KEY_TYPE_MAPPING[ BbsBlsSignatureProof2020 - ] = KeyType.BLS12381G2 + ] = BLS12381G2 async def _get_all_suites(self, wallet: BaseWallet): """Get all supported suites for verifying presentation.""" diff --git a/aries_cloudagent/transport/tests/test_pack_format.py b/aries_cloudagent/transport/tests/test_pack_format.py index 1ca7b7f46f..e9d9e5c0e2 100644 --- a/aries_cloudagent/transport/tests/test_pack_format.py +++ b/aries_cloudagent/transport/tests/test_pack_format.py @@ -140,7 +140,7 @@ async def test_fallback(self): async def test_encode_decode(self): local_did = await self.wallet.create_local_did( - method=DIDMethod.SOV, key_type=KeyType.ED25519, seed=self.test_seed + method=DIDMethod.SOV, key_type=ED25519, seed=self.test_seed ) serializer = PackWireFormat() recipient_keys = (local_did.verkey,) @@ -174,10 +174,10 @@ async def test_encode_decode(self): async def test_forward(self): local_did = await self.wallet.create_local_did( - method=DIDMethod.SOV, key_type=KeyType.ED25519, seed=self.test_seed + method=DIDMethod.SOV, key_type=ED25519, seed=self.test_seed ) router_did = await self.wallet.create_local_did( - method=DIDMethod.SOV, key_type=KeyType.ED25519, seed=self.test_routing_seed + method=DIDMethod.SOV, key_type=ED25519, seed=self.test_routing_seed ) serializer = PackWireFormat() recipient_keys = (local_did.verkey,) diff --git a/aries_cloudagent/utils/tests/test_outofband.py b/aries_cloudagent/utils/tests/test_outofband.py index 2029553471..e8f519479c 100644 --- a/aries_cloudagent/utils/tests/test_outofband.py +++ b/aries_cloudagent/utils/tests/test_outofband.py @@ -12,7 +12,7 @@ class TestOutOfBand(TestCase): test_did = "55GkHamhTU1ZbTbV2ab9DE" test_verkey = "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx" test_did_info = DIDInfo( - test_did, test_verkey, None, method=DIDMethod.SOV, key_type=KeyType.ED25519 + test_did, test_verkey, None, method=DIDMethod.SOV, key_type=ED25519 ) def test_serialize_oob(self): diff --git a/aries_cloudagent/vc/ld_proofs/crypto/tests/test_wallet_key_pair.py b/aries_cloudagent/vc/ld_proofs/crypto/tests/test_wallet_key_pair.py index 705ca244be..4ae0a83753 100644 --- a/aries_cloudagent/vc/ld_proofs/crypto/tests/test_wallet_key_pair.py +++ b/aries_cloudagent/vc/ld_proofs/crypto/tests/test_wallet_key_pair.py @@ -12,7 +12,7 @@ async def setUp(self): self.wallet = async_mock.MagicMock() async def test_sign_x_no_public_key(self): - key_pair = WalletKeyPair(wallet=self.wallet, key_type=KeyType.ED25519) + key_pair = WalletKeyPair(wallet=self.wallet, key_type=ED25519) with self.assertRaises(LinkedDataProofException) as context: await key_pair.sign(b"Message") @@ -22,7 +22,7 @@ async def test_sign(self): public_key_base58 = "verkey" key_pair = WalletKeyPair( wallet=self.wallet, - key_type=KeyType.ED25519, + key_type=ED25519, public_key_base58=public_key_base58, ) signed = async_mock.MagicMock() @@ -37,7 +37,7 @@ async def test_sign(self): ) async def test_verify_x_no_public_key(self): - key_pair = WalletKeyPair(wallet=self.wallet, key_type=KeyType.ED25519) + key_pair = WalletKeyPair(wallet=self.wallet, key_type=ED25519) with self.assertRaises(LinkedDataProofException) as context: await key_pair.verify(b"Message", b"signature") @@ -47,7 +47,7 @@ async def test_verify(self): public_key_base58 = "verkey" key_pair = WalletKeyPair( wallet=self.wallet, - key_type=KeyType.ED25519, + key_type=ED25519, public_key_base58=public_key_base58, ) self.wallet.verify_message = async_mock.CoroutineMock(return_value=True) @@ -59,11 +59,11 @@ async def test_verify(self): message=b"Message", signature=b"signature", from_verkey=public_key_base58, - key_type=KeyType.ED25519, + key_type=ED25519, ) async def test_from_verification_method_x_no_public_key_base58(self): - key_pair = WalletKeyPair(wallet=self.wallet, key_type=KeyType.ED25519) + key_pair = WalletKeyPair(wallet=self.wallet, key_type=ED25519) with self.assertRaises(LinkedDataProofException) as context: key_pair.from_verification_method({}) diff --git a/aries_cloudagent/vc/ld_proofs/suites/tests/test_bbs_bls_signature_2020.py b/aries_cloudagent/vc/ld_proofs/suites/tests/test_bbs_bls_signature_2020.py index f8bfdf6533..7fac62baad 100644 --- a/aries_cloudagent/vc/ld_proofs/suites/tests/test_bbs_bls_signature_2020.py +++ b/aries_cloudagent/vc/ld_proofs/suites/tests/test_bbs_bls_signature_2020.py @@ -30,19 +30,19 @@ async def setUp(self): self.profile = InMemoryProfile.test_profile() self.wallet = InMemoryWallet(self.profile) self.key = await self.wallet.create_signing_key( - key_type=KeyType.BLS12381G2, seed=self.test_seed + key_type=BLS12381G2, seed=self.test_seed ) self.verification_method = DIDKey.from_public_key_b58( - self.key.verkey, KeyType.BLS12381G2 + self.key.verkey, BLS12381G2 ).key_id self.sign_key_pair = WalletKeyPair( wallet=self.wallet, - key_type=KeyType.BLS12381G2, + key_type=BLS12381G2, public_key_base58=self.key.verkey, ) self.verify_key_pair = WalletKeyPair( - wallet=self.wallet, key_type=KeyType.BLS12381G2 + wallet=self.wallet, key_type=BLS12381G2 ) async def test_sign_ld_proofs(self): diff --git a/aries_cloudagent/vc/ld_proofs/suites/tests/test_bbs_bls_signature_proof_2020.py b/aries_cloudagent/vc/ld_proofs/suites/tests/test_bbs_bls_signature_proof_2020.py index e8a79e2297..e60f923983 100644 --- a/aries_cloudagent/vc/ld_proofs/suites/tests/test_bbs_bls_signature_proof_2020.py +++ b/aries_cloudagent/vc/ld_proofs/suites/tests/test_bbs_bls_signature_proof_2020.py @@ -39,13 +39,13 @@ async def setUp(self): self.profile = InMemoryProfile.test_profile() self.wallet = InMemoryWallet(self.profile) self.key = await self.wallet.create_signing_key( - key_type=KeyType.BLS12381G2, seed=self.test_seed + key_type=BLS12381G2, seed=self.test_seed ) self.verification_method = DIDKey.from_public_key_b58( - self.key.verkey, KeyType.BLS12381G2 + self.key.verkey, BLS12381G2 ).key_id - self.key_pair = WalletKeyPair(wallet=self.wallet, key_type=KeyType.BLS12381G2) + self.key_pair = WalletKeyPair(wallet=self.wallet, key_type=BLS12381G2) async def test_derive_ld_proofs(self): derived = await derive( diff --git a/aries_cloudagent/vc/ld_proofs/suites/tests/test_ed25519_signature_2018.py b/aries_cloudagent/vc/ld_proofs/suites/tests/test_ed25519_signature_2018.py index 613aec46ab..88dca016d3 100644 --- a/aries_cloudagent/vc/ld_proofs/suites/tests/test_ed25519_signature_2018.py +++ b/aries_cloudagent/vc/ld_proofs/suites/tests/test_ed25519_signature_2018.py @@ -29,19 +29,19 @@ async def setUp(self): self.profile = InMemoryProfile.test_profile() self.wallet = InMemoryWallet(self.profile) self.key = await self.wallet.create_signing_key( - key_type=KeyType.ED25519, seed=self.test_seed + key_type=ED25519, seed=self.test_seed ) self.verification_method = DIDKey.from_public_key_b58( - self.key.verkey, KeyType.ED25519 + self.key.verkey, ED25519 ).key_id self.sign_key_pair = WalletKeyPair( wallet=self.wallet, - key_type=KeyType.ED25519, + key_type=ED25519, public_key_base58=self.key.verkey, ) self.verify_key_pair = WalletKeyPair( - wallet=self.wallet, key_type=KeyType.ED25519 + wallet=self.wallet, key_type=ED25519 ) async def test_sign_ld_proofs(self): diff --git a/aries_cloudagent/vc/ld_proofs/tests/test_ld_proofs.py b/aries_cloudagent/vc/ld_proofs/tests/test_ld_proofs.py index 15d9ea5e90..b1369f9602 100644 --- a/aries_cloudagent/vc/ld_proofs/tests/test_ld_proofs.py +++ b/aries_cloudagent/vc/ld_proofs/tests/test_ld_proofs.py @@ -40,18 +40,18 @@ async def setUp(self): self.wallet = InMemoryWallet(self.profile) self.ed25519_key_info = await self.wallet.create_signing_key( - key_type=KeyType.ED25519, seed=self.test_seed + key_type=ED25519, seed=self.test_seed ) self.ed25519_verification_method = DIDKey.from_public_key_b58( - self.ed25519_key_info.verkey, KeyType.ED25519 + self.ed25519_key_info.verkey, ED25519 ).key_id self.bls12381g2_key_info = await self.wallet.create_signing_key( - key_type=KeyType.BLS12381G2, seed=self.test_seed + key_type=BLS12381G2, seed=self.test_seed ) self.bls12381g2_verification_method = DIDKey.from_public_key_b58( - self.bls12381g2_key_info.verkey, KeyType.BLS12381G2 + self.bls12381g2_key_info.verkey, BLS12381G2 ).key_id async def test_sign_Ed25519Signature2018(self): @@ -62,7 +62,7 @@ async def test_sign_Ed25519Signature2018(self): verification_method=self.ed25519_verification_method, key_pair=WalletKeyPair( wallet=self.wallet, - key_type=KeyType.ED25519, + key_type=ED25519, public_key_base58=self.ed25519_key_info.verkey, ), date=datetime(2019, 12, 11, 3, 50, 55, 0, timezone.utc), @@ -79,7 +79,7 @@ async def test_sign_Ed25519Signature2018(self): async def test_verify_Ed25519Signature2018(self): # Verification requires lot less input parameters suite = Ed25519Signature2018( - key_pair=WalletKeyPair(wallet=self.wallet, key_type=KeyType.ED25519), + key_pair=WalletKeyPair(wallet=self.wallet, key_type=ED25519), ) result = await verify( @@ -100,7 +100,7 @@ async def test_sign_BbsBlsSignature2020(self): verification_method=self.bls12381g2_verification_method, key_pair=WalletKeyPair( wallet=self.wallet, - key_type=KeyType.BLS12381G2, + key_type=BLS12381G2, public_key_base58=self.bls12381g2_key_info.verkey, ), date=datetime(2019, 12, 11, 3, 50, 55, 0), @@ -128,7 +128,7 @@ async def test_sign_BbsBlsSignature2020(self): async def test_verify_BbsBlsSignature2020(self): # Verification requires lot less input parameters suite = BbsBlsSignature2020( - key_pair=WalletKeyPair(wallet=self.wallet, key_type=KeyType.BLS12381G2), + key_pair=WalletKeyPair(wallet=self.wallet, key_type=BLS12381G2), ) result = await verify( @@ -144,7 +144,7 @@ async def test_verify_BbsBlsSignature2020(self): async def test_derive_BbsBlsSignatureProof2020(self): # Verification requires lot less input parameters suite = BbsBlsSignatureProof2020( - key_pair=WalletKeyPair(wallet=self.wallet, key_type=KeyType.BLS12381G2), + key_pair=WalletKeyPair(wallet=self.wallet, key_type=BLS12381G2), ) result = await derive( @@ -160,7 +160,7 @@ async def test_derive_BbsBlsSignatureProof2020(self): async def test_verify_BbsBlsSignatureProof2020(self): # Verification requires lot less input parameters suite = BbsBlsSignatureProof2020( - key_pair=WalletKeyPair(wallet=self.wallet, key_type=KeyType.BLS12381G2), + key_pair=WalletKeyPair(wallet=self.wallet, key_type=BLS12381G2), ) result = await verify( diff --git a/aries_cloudagent/vc/tests/test_bbs_mattr_interop.py b/aries_cloudagent/vc/tests/test_bbs_mattr_interop.py index 707dec1e6b..cbc8aa795a 100644 --- a/aries_cloudagent/vc/tests/test_bbs_mattr_interop.py +++ b/aries_cloudagent/vc/tests/test_bbs_mattr_interop.py @@ -45,23 +45,23 @@ async def setUp(self): "secret": b58_to_bytes(private_key_base58), "verkey": public_key_base58, "metadata": {}, - "key_type": KeyType.BLS12381G2, + "key_type": BLS12381G2, } self.signature_issuer_suite = BbsBlsSignature2020( verification_method="did:example:489398593#test", key_pair=WalletKeyPair( wallet=self.wallet, - key_type=KeyType.BLS12381G2, + key_type=BLS12381G2, public_key_base58=public_key_base58, ), ) self.signature_suite = BbsBlsSignature2020( - key_pair=WalletKeyPair(wallet=self.wallet, key_type=KeyType.BLS12381G2), + key_pair=WalletKeyPair(wallet=self.wallet, key_type=BLS12381G2), ) self.proof_suite = BbsBlsSignatureProof2020( - key_pair=WalletKeyPair(wallet=self.wallet, key_type=KeyType.BLS12381G2) + key_pair=WalletKeyPair(wallet=self.wallet, key_type=BLS12381G2) ) async def test_sign_bbs_vc_mattr(self): diff --git a/aries_cloudagent/vc/vc_ld/tests/test_vc_ld.py b/aries_cloudagent/vc/vc_ld/tests/test_vc_ld.py index 8fe29c799f..121c76d50d 100644 --- a/aries_cloudagent/vc/vc_ld/tests/test_vc_ld.py +++ b/aries_cloudagent/vc/vc_ld/tests/test_vc_ld.py @@ -37,18 +37,18 @@ async def setUp(self): self.wallet = InMemoryWallet(self.profile) self.ed25519_key_info = await self.wallet.create_signing_key( - key_type=KeyType.ED25519, seed=self.test_seed + key_type=ED25519, seed=self.test_seed ) self.ed25519_verification_method = DIDKey.from_public_key_b58( - self.ed25519_key_info.verkey, KeyType.ED25519 + self.ed25519_key_info.verkey, ED25519 ).key_id self.bls12381g2_key_info = await self.wallet.create_signing_key( - key_type=KeyType.BLS12381G2, seed=self.test_seed + key_type=BLS12381G2, seed=self.test_seed ) self.bls12381g2_verification_method = DIDKey.from_public_key_b58( - self.bls12381g2_key_info.verkey, KeyType.BLS12381G2 + self.bls12381g2_key_info.verkey, BLS12381G2 ).key_id self.presentation_challenge = "2b1bbff6-e608-4368-bf84-67471b27e41c" @@ -61,7 +61,7 @@ async def test_issue_Ed25519Signature2018(self): verification_method=self.ed25519_verification_method, key_pair=WalletKeyPair( wallet=self.wallet, - key_type=KeyType.ED25519, + key_type=ED25519, public_key_base58=self.ed25519_key_info.verkey, ), date=datetime.strptime("2019-12-11T03:50:55Z", "%Y-%m-%dT%H:%M:%SZ"), @@ -103,7 +103,7 @@ async def test_derive_x_invalid_credential_structure(self): async def test_verify_Ed25519Signature2018(self): # Verification requires lot less input parameters suite = Ed25519Signature2018( - key_pair=WalletKeyPair(wallet=self.wallet, key_type=KeyType.ED25519), + key_pair=WalletKeyPair(wallet=self.wallet, key_type=ED25519), ) verified = await verify_credential( credential=CREDENTIAL_ISSUED, @@ -135,7 +135,7 @@ async def test_issue_BbsBlsSignature2020(self): verification_method=self.bls12381g2_verification_method, key_pair=WalletKeyPair( wallet=self.wallet, - key_type=KeyType.BLS12381G2, + key_type=BLS12381G2, public_key_base58=self.bls12381g2_key_info.verkey, ), date=datetime.strptime("2019-12-11T03:50:55Z", "%Y-%m-%dT%H:%M:%SZ"), @@ -158,7 +158,7 @@ async def test_issue_BbsBlsSignature2020(self): async def test_verify_BbsBlsSignature2020(self): # Verification requires lot less input parameters suite = BbsBlsSignature2020( - key_pair=WalletKeyPair(wallet=self.wallet, key_type=KeyType.BLS12381G2), + key_pair=WalletKeyPair(wallet=self.wallet, key_type=BLS12381G2), ) result = await verify_credential( credential=CREDENTIAL_ISSUED_BBS, @@ -185,7 +185,7 @@ async def test_create_presentation_x_invalid_credential_structures(self): verification_method=self.ed25519_verification_method, key_pair=WalletKeyPair( wallet=self.wallet, - key_type=KeyType.ED25519, + key_type=ED25519, public_key_base58=self.ed25519_key_info.verkey, ), date=datetime.strptime("2020-12-11T03:50:55Z", "%Y-%m-%dT%H:%M:%SZ"), @@ -218,7 +218,7 @@ async def test_sign_presentation_bbsbls(self): verification_method=self.bls12381g2_verification_method, key_pair=WalletKeyPair( wallet=self.wallet, - key_type=KeyType.BLS12381G2, + key_type=BLS12381G2, public_key_base58=self.bls12381g2_key_info.verkey, ), date=datetime.strptime("2020-12-11T03:50:55Z", "%Y-%m-%dT%H:%M:%SZ"), @@ -235,7 +235,7 @@ async def test_sign_presentation_bbsbls(self): async def test_verify_presentation(self): suite = Ed25519Signature2018( - key_pair=WalletKeyPair(wallet=self.wallet, key_type=KeyType.ED25519), + key_pair=WalletKeyPair(wallet=self.wallet, key_type=ED25519), ) verification_result = await verify_presentation( presentation=PRESENTATION_SIGNED, diff --git a/aries_cloudagent/wallet/askar.py b/aries_cloudagent/wallet/askar.py index f2a5c790c6..b881dbba13 100644 --- a/aries_cloudagent/wallet/askar.py +++ b/aries_cloudagent/wallet/askar.py @@ -33,7 +33,7 @@ ) from .did_method import DIDMethod from .error import WalletError, WalletDuplicateError, WalletNotFoundError -from .key_type import KeyType +from .key_type import ED25519, KeyType from .util import b58_to_bytes, bytes_to_b58 CATEGORY_DID = "did" @@ -119,7 +119,7 @@ async def get_signing_key(self, verkey: str) -> KeyInfo: raise WalletNotFoundError("Unknown key: {}".format(verkey)) metadata = json.loads(key.metadata or "{}") # FIXME implement key types - return KeyInfo(verkey=verkey, metadata=metadata, key_type=KeyType.ED25519) + return KeyInfo(verkey=verkey, metadata=metadata, key_type=ED25519) async def replace_signing_key_metadata(self, verkey: str, metadata: dict): """ @@ -514,7 +514,7 @@ async def rotate_did_keypair_start(self, did: str, next_seed: str = None) -> str ) # create a new key to be rotated to (only did:sov/ED25519 supported for now) - keypair = _create_keypair(KeyType.ED25519, next_seed) + keypair = _create_keypair(ED25519, next_seed) verkey = bytes_to_b58(keypair.get_public_bytes()) try: await self._session.handle.insert_key( @@ -607,7 +607,7 @@ async def sign_message( return sign_message( message=message, secret=key.get_secret_bytes(), - key_type=KeyType.BLS12381G2, + key_type=BLS12381G2, ) else: @@ -650,7 +650,7 @@ async def verify_message( verkey = b58_to_bytes(from_verkey) - if key_type == KeyType.ED25519: + if key_type == ED25519: try: pk = Key.from_public_bytes(KeyAlg.ED25519, verkey) return pk.verify_signature(message, signature) @@ -730,21 +730,21 @@ async def unpack_message(self, enc_message: bytes) -> Tuple[str, str, str]: def _create_keypair(key_type: KeyType, seed: Union[str, bytes] = None) -> Key: """Instantiate a new keypair with an optional seed value.""" - if key_type == KeyType.ED25519: + if key_type == ED25519: alg = KeyAlg.ED25519 method = None - # elif key_type == KeyType.BLS12381G1: + # elif key_type == BLS12381G1: # alg = KeyAlg.BLS12_381_G1 - elif key_type == KeyType.BLS12381G2: + elif key_type == BLS12381G2: alg = KeyAlg.BLS12_381_G2 method = SeedMethod.BlsKeyGen - # elif key_type == KeyType.BLS12381G1G2: + # elif key_type == BLS12381G1G2: # alg = KeyAlg.BLS12_381_G1G2 else: raise WalletError(f"Unsupported key algorithm: {key_type}") if seed: try: - if key_type == KeyType.ED25519: + if key_type == ED25519: # not a seed - it is the secret key seed = validate_seed(seed) return Key.from_secret_bytes(alg, seed) diff --git a/aries_cloudagent/wallet/crypto.py b/aries_cloudagent/wallet/crypto.py index 5e79c3c15f..34615d908f 100644 --- a/aries_cloudagent/wallet/crypto.py +++ b/aries_cloudagent/wallet/crypto.py @@ -38,9 +38,9 @@ def create_keypair(key_type: KeyType, seed: bytes = None) -> Tuple[bytes, bytes] A tuple of (public key, secret key) """ - if key_type == KeyType.ED25519: + if key_type == ED25519: return create_ed25519_keypair(seed) - elif key_type == KeyType.BLS12381G2: + elif key_type == BLS12381G2: # This ensures python won't crash if bbs is not installed and not used return create_bls12381g2_keypair(seed) @@ -149,7 +149,7 @@ def sign_message( # Make messages list if not already for easier checking going forward messages = message if isinstance(message, list) else [message] - if key_type == KeyType.ED25519: + if key_type == ED25519: if len(messages) > 1: raise WalletError("ed25519 can only sign a single message") @@ -157,7 +157,7 @@ def sign_message( message=messages[0], secret=secret, ) - elif key_type == KeyType.BLS12381G2: + elif key_type == BLS12381G2: return sign_messages_bls12381g2(messages=messages, secret=secret) else: raise WalletError(f"Unsupported key type: {key_type.key_type}") @@ -201,14 +201,14 @@ def verify_signed_message( # Make messages list if not already for easier checking going forward messages = message if isinstance(message, list) else [message] - if key_type == KeyType.ED25519: + if key_type == ED25519: if len(messages) > 1: raise WalletError("ed25519 can only verify a single message") return verify_signed_message_ed25519( message=messages[0], signature=signature, verkey=verkey ) - elif key_type == KeyType.BLS12381G2: + elif key_type == BLS12381G2: try: return verify_signed_messages_bls12381g2( messages=messages, signature=signature, public_key=verkey diff --git a/aries_cloudagent/wallet/did_method.py b/aries_cloudagent/wallet/did_method.py index 797c26fd20..840d2836c1 100644 --- a/aries_cloudagent/wallet/did_method.py +++ b/aries_cloudagent/wallet/did_method.py @@ -3,7 +3,7 @@ from typing import List, Mapping, NamedTuple, Optional from enum import Enum -from .key_type import KeyType +from .key_type import BLS12381G2, ED25519, KeyType from .error import BaseError DIDMethodSpec = NamedTuple( @@ -20,11 +20,11 @@ class DIDMethod(Enum): """DID Method class specifying DID methods with supported key types.""" SOV = DIDMethodSpec( - method_name="sov", supported_key_types=[KeyType.ED25519], supports_rotation=True + method_name="sov", supported_key_types=[ED25519], supports_rotation=True ) KEY = DIDMethodSpec( method_name="key", - supported_key_types=[KeyType.ED25519, KeyType.BLS12381G2], + supported_key_types=[ED25519, BLS12381G2], supports_rotation=False, ) diff --git a/aries_cloudagent/wallet/indy.py b/aries_cloudagent/wallet/indy.py index 4e2406d04f..b34f041c23 100644 --- a/aries_cloudagent/wallet/indy.py +++ b/aries_cloudagent/wallet/indy.py @@ -56,7 +56,7 @@ def __did_info_from_indy_info(self, info): verkey = info["verkey"] method = DIDMethod.KEY if did.startswith("did:key") else DIDMethod.SOV - key_type = KeyType.ED25519 + key_type = ED25519 if method == DIDMethod.KEY: did = DIDKey.from_public_key_b58(info["verkey"], key_type).did @@ -83,7 +83,7 @@ def __did_info_from_key_pair_info(self, info: dict): async def __create_indy_signing_key( self, key_type: KeyType, metadata: dict, seed: str = None ) -> str: - if key_type != KeyType.ED25519: + if key_type != ED25519: raise WalletError(f"Unsupported key type: {key_type.key_type}") args = {} @@ -107,7 +107,7 @@ async def __create_indy_signing_key( async def __create_keypair_signing_key( self, key_type: KeyType, metadata: dict, seed: str = None ) -> str: - if key_type != KeyType.BLS12381G2: + if key_type != BLS12381G2: raise WalletError(f"Unsupported key type: {key_type.key_type}") public_key, secret_key = create_keypair(key_type, validate_seed(seed)) @@ -158,7 +158,7 @@ async def create_signing_key( metadata = {} # All ed25519 keys are handled by indy - if key_type == KeyType.ED25519: + if key_type == ED25519: verkey = await self.__create_indy_signing_key(key_type, metadata, seed) # All other (only bls12381g2 atm) are handled outside of indy else: @@ -173,7 +173,7 @@ async def __get_indy_signing_key(self, verkey: str) -> KeyInfo: return KeyInfo( verkey=verkey, metadata=json.loads(metadata) if metadata else {}, - key_type=KeyType.ED25519, + key_type=ED25519, ) except IndyError as x_indy: if x_indy.error_code == ErrorCode.WalletItemNotFound: @@ -246,7 +246,7 @@ async def replace_signing_key_metadata(self, verkey: str, metadata: dict): key_info = await self.get_signing_key(verkey) # All ed25519 keys are handled by indy - if key_info.key_type == KeyType.ED25519: + if key_info.key_type == ED25519: await indy.crypto.set_key_metadata( self.opened.handle, verkey, json.dumps(metadata) ) @@ -328,7 +328,7 @@ async def __create_indy_local_did( raise WalletError( f"Unsupported DID method for indy storage: {method.method_name}" ) - if key_type != KeyType.ED25519: + if key_type != ED25519: raise WalletError( f"Unsupported key type for indy storage: {key_type.key_type}" ) @@ -380,7 +380,7 @@ async def __create_keypair_local_did( raise WalletError( f"Unsupported DID method for keypair storage: {method.method_name}" ) - if key_type != KeyType.BLS12381G2: + if key_type != BLS12381G2: raise WalletError( f"Unsupported key type for keypair storage: {key_type.key_type}" ) @@ -448,7 +448,7 @@ async def create_local_did( raise WalletError("Not allowed to set DID for DID method 'key'") # All ed25519 keys are handled by indy - if key_type == KeyType.ED25519: + if key_type == ED25519: return await self.__create_indy_local_did( method, key_type, metadata, seed, did=did ) @@ -491,13 +491,13 @@ async def __get_indy_local_did( raise WalletError( f"Unsupported DID method for indy storage: {method.method_name}" ) - if key_type != KeyType.ED25519: + if key_type != ED25519: raise WalletError( f"Unsupported DID type for indy storage: {key_type.key_type}" ) # key type is always ed25519, method not always key - if method == DIDMethod.KEY and key_type == KeyType.ED25519: + if method == DIDMethod.KEY and key_type == ED25519: did_key = DIDKey.from_did(did) # Ed25519 did:keys are masked indy dids so transform to indy @@ -521,7 +521,7 @@ async def __get_keypair_local_did( raise WalletError( f"Unsupported DID method for keypair storage: {method.method_name}" ) - if key_type != KeyType.BLS12381G2: + if key_type != BLS12381G2: raise WalletError( f"Unsupported DID type for keypair storage: {key_type.key_type}" ) @@ -549,14 +549,14 @@ async def get_local_did(self, did: str) -> DIDInfo: """ method = DIDMethod.from_did(did) - key_type = KeyType.ED25519 + key_type = ED25519 # If did key, the key type can differ if method == DIDMethod.KEY: did_key = DIDKey.from_did(did) key_type = did_key.key_type - if key_type == KeyType.ED25519: + if key_type == ED25519: return await self.__get_indy_local_did(method, key_type, did) else: return await self.__get_keypair_local_did(method, key_type, did) @@ -596,7 +596,7 @@ async def replace_local_did_metadata(self, did: str, metadata: dict): did_info = await self.get_local_did(did) # throw exception if undefined # ed25519 keys are handled by indy - if did_info.key_type == KeyType.ED25519: + if did_info.key_type == ED25519: try: await indy.did.set_did_metadata( self.opened.handle, did, json.dumps(metadata) @@ -785,7 +785,7 @@ async def sign_message(self, message: bytes, from_verkey: str) -> bytes: key_info = await self.get_local_did_for_verkey(from_verkey) # ed25519 keys are handled by indy - if key_info.key_type == KeyType.ED25519: + if key_info.key_type == ED25519: try: result = await indy.crypto.crypto_sign( self.opened.handle, from_verkey, message @@ -837,7 +837,7 @@ async def verify_message( raise WalletError("Message not provided") # ed25519 keys are handled by indy - if key_type == KeyType.ED25519: + if key_type == ED25519: try: result = await indy.crypto.crypto_verify( from_verkey, message, signature diff --git a/aries_cloudagent/wallet/routes.py b/aries_cloudagent/wallet/routes.py index eb18163fcb..bb6fa58236 100644 --- a/aries_cloudagent/wallet/routes.py +++ b/aries_cloudagent/wallet/routes.py @@ -41,7 +41,7 @@ from .did_method import DIDMethod from .did_posture import DIDPosture from .error import WalletError, WalletNotFoundError -from .key_type import KeyType +from .key_type import BLS12381G2, ED25519, KeyType from .util import EVENT_LISTENER_PATTERN LOGGER = logging.getLogger(__name__) @@ -71,9 +71,9 @@ class DIDSchema(OpenAPISchema): ) key_type = fields.Str( description="Key type associated with the DID", - example=KeyType.ED25519.key_type, + example=ED25519.key_type, validate=validate.OneOf( - [KeyType.ED25519.key_type, KeyType.BLS12381G2.key_type] + [ED25519.key_type, BLS12381G2.key_type] ), ) @@ -142,9 +142,9 @@ class DIDListQueryStringSchema(OpenAPISchema): ) key_type = fields.Str( required=False, - example=KeyType.ED25519.key_type, + example=ED25519.key_type, validate=validate.OneOf( - [KeyType.ED25519.key_type, KeyType.BLS12381G2.key_type] + [ED25519.key_type, BLS12381G2.key_type] ), description="Key type to query for.", ) @@ -161,9 +161,9 @@ class DIDCreateOptionsSchema(OpenAPISchema): key_type = fields.Str( required=True, - example=KeyType.ED25519.key_type, + example=ED25519.key_type, validate=validate.OneOf( - [KeyType.ED25519.key_type, KeyType.BLS12381G2.key_type] + [ED25519.key_type, BLS12381G2.key_type] ), ) @@ -353,10 +353,7 @@ async def wallet_create_did(request: web.BaseRequest): body = {} # set default method and key type for backwards compat - key_type = ( - KeyType.from_key_type(body.get("options", {}).get("key_type")) - or KeyType.ED25519 - ) + key_type = KeyType.from_key_type(body.get("options", {}).get("key_type")) or ED25519 method = DIDMethod.from_method(body.get("method")) or DIDMethod.SOV if not method.supports_key_type(key_type): diff --git a/aries_cloudagent/wallet/tests/test_crypto.py b/aries_cloudagent/wallet/tests/test_crypto.py index 66f6fc770a..2e72c3b70f 100644 --- a/aries_cloudagent/wallet/tests/test_crypto.py +++ b/aries_cloudagent/wallet/tests/test_crypto.py @@ -3,7 +3,7 @@ from unittest import mock, TestCase -from ..key_type import KeyType +from ..key_type import ED25519 from ..error import WalletError from ...utils.jwe import JweRecipient from ..util import str_to_b64 @@ -33,7 +33,7 @@ def test_seeds_keys(self): assert len(test_module.seed_to_did(SEED)) in (22, 23) (public_key, secret_key) = test_module.create_keypair( - test_module.KeyType.ED25519 + test_module.ED25519 ) assert public_key assert secret_key @@ -156,28 +156,26 @@ def test_extract_pack_recipients_x(self): def test_sign_ed25519_x_multiple_messages(self): with self.assertRaises(WalletError) as context: - test_module.sign_message( - [b"message1", b"message2"], b"secret", KeyType.ED25519 - ) + test_module.sign_message([b"message1", b"message2"], b"secret", ED25519) assert "ed25519 can only sign a single message" in str(context.exception) def test_sign_x_unsupported_key_type(self): with self.assertRaises(WalletError) as context: test_module.sign_message( - [b"message1", b"message2"], b"secret", KeyType.BLS12381G1 + [b"message1", b"message2"], b"secret", BLS12381G1 ) assert "Unsupported key type: bls12381g1" in str(context.exception) def test_verify_ed25519_x_multiple_messages(self): with self.assertRaises(WalletError) as context: test_module.verify_signed_message( - [b"message1", b"message2"], b"signature", b"verkey", KeyType.ED25519 + [b"message1", b"message2"], b"signature", b"verkey", ED25519 ) assert "ed25519 can only verify a single message" in str(context.exception) def test_verify_x_unsupported_key_type(self): with self.assertRaises(WalletError) as context: test_module.verify_signed_message( - [b"message1", b"message2"], b"signature", b"verkey", KeyType.BLS12381G1 + [b"message1", b"message2"], b"signature", b"verkey", BLS12381G1 ) assert "Unsupported key type: bls12381g1" in str(context.exception) diff --git a/aries_cloudagent/wallet/tests/test_did_method.py b/aries_cloudagent/wallet/tests/test_did_method.py index 2134d01555..834ac63fb9 100644 --- a/aries_cloudagent/wallet/tests/test_did_method.py +++ b/aries_cloudagent/wallet/tests/test_did_method.py @@ -25,46 +25,46 @@ class TestKeyType(TestCase): def test_from_multicodec_name(self): - assert KeyType.from_multicodec_name(ED25519_MULTICODEC_NAME) == KeyType.ED25519 - assert KeyType.from_multicodec_name(X25519_MULTICODEC_NAME) == KeyType.X25519 + assert KeyType.from_multicodec_name(ED25519_MULTICODEC_NAME) == ED25519 + assert KeyType.from_multicodec_name(X25519_MULTICODEC_NAME) == X25519 assert ( KeyType.from_multicodec_name(BLS12381G1_MULTICODEC_NAME) - == KeyType.BLS12381G1 + == BLS12381G1 ) assert ( KeyType.from_multicodec_name(BLS12381G2_MULTICODEC_NAME) - == KeyType.BLS12381G2 + == BLS12381G2 ) assert ( KeyType.from_multicodec_name(BLS12381G1G2_MULTICODEC_NAME) - == KeyType.BLS12381G1G2 + == BLS12381G1G2 ) assert KeyType.from_multicodec_name("non-existing") == None def test_from_key_type(self): - assert KeyType.from_key_type(ED25519_KEY_NAME) == KeyType.ED25519 - assert KeyType.from_key_type(X25519_KEY_NAME) == KeyType.X25519 - assert KeyType.from_key_type(BLS12381G1_KEY_NAME) == KeyType.BLS12381G1 - assert KeyType.from_key_type(BLS12381G2_KEY_NAME) == KeyType.BLS12381G2 - assert KeyType.from_key_type(BLS12381G1G2_KEY_NAME) == KeyType.BLS12381G1G2 + assert KeyType.from_key_type(ED25519_KEY_NAME) == ED25519 + assert KeyType.from_key_type(X25519_KEY_NAME) == X25519 + assert KeyType.from_key_type(BLS12381G1_KEY_NAME) == BLS12381G1 + assert KeyType.from_key_type(BLS12381G2_KEY_NAME) == BLS12381G2 + assert KeyType.from_key_type(BLS12381G1G2_KEY_NAME) == BLS12381G1G2 assert KeyType.from_key_type("non-existing") == None def test_from_multicodec_prefix(self): - assert KeyType.from_multicodec_prefix(ED25519_PREFIX_BYTES) == KeyType.ED25519 - assert KeyType.from_multicodec_prefix(X25519_PREFIX_BYTES) == KeyType.X25519 + assert KeyType.from_multicodec_prefix(ED25519_PREFIX_BYTES) == ED25519 + assert KeyType.from_multicodec_prefix(X25519_PREFIX_BYTES) == X25519 assert ( KeyType.from_multicodec_prefix(BLS12381G1_PREFIX_BYTES) - == KeyType.BLS12381G1 + == BLS12381G1 ) assert ( KeyType.from_multicodec_prefix(BLS12381G2_PREFIX_BYTES) - == KeyType.BLS12381G2 + == BLS12381G2 ) assert ( KeyType.from_multicodec_prefix(BLS12381G1G2_PREFIX_BYTES) - == KeyType.BLS12381G1G2 + == BLS12381G1G2 ) assert KeyType.from_multicodec_prefix(b"\xef\x01") == None @@ -74,31 +74,31 @@ def test_from_prefixed_bytes(self): KeyType.from_prefixed_bytes( b"".join([ED25519_PREFIX_BYTES, b"random-bytes"]) ) - == KeyType.ED25519 + == ED25519 ) assert ( KeyType.from_prefixed_bytes( b"".join([X25519_PREFIX_BYTES, b"random-bytes"]) ) - == KeyType.X25519 + == X25519 ) assert ( KeyType.from_prefixed_bytes( b"".join([BLS12381G1_PREFIX_BYTES, b"random-bytes"]) ) - == KeyType.BLS12381G1 + == BLS12381G1 ) assert ( KeyType.from_prefixed_bytes( b"".join([BLS12381G2_PREFIX_BYTES, b"random-bytes"]) ) - == KeyType.BLS12381G2 + == BLS12381G2 ) assert ( KeyType.from_prefixed_bytes( b"".join([BLS12381G1G2_PREFIX_BYTES, b"random-bytes"]) ) - == KeyType.BLS12381G1G2 + == BLS12381G1G2 ) assert ( KeyType.from_prefixed_bytes(b"".join([b"\xef\x01", b"other-random-bytes"])) @@ -106,7 +106,7 @@ def test_from_prefixed_bytes(self): ) def test_properties(self): - key_type = KeyType.ED25519 + key_type = ED25519 assert key_type.key_type == ED25519_KEY_NAME assert key_type.multicodec_name == ED25519_MULTICODEC_NAME diff --git a/aries_cloudagent/wallet/tests/test_in_memory_wallet.py b/aries_cloudagent/wallet/tests/test_in_memory_wallet.py index 6554336908..97cdcea8cb 100644 --- a/aries_cloudagent/wallet/tests/test_in_memory_wallet.py +++ b/aries_cloudagent/wallet/tests/test_in_memory_wallet.py @@ -4,7 +4,7 @@ from ...core.in_memory import InMemoryProfile from ...messaging.decorators.signature_decorator import SignatureDecorator from ...wallet.in_memory import InMemoryWallet -from ...wallet.key_type import KeyType +from ...wallet.key_type import BLS12381G2, ED25519, KeyType from ...wallet.did_method import DIDMethod from ...wallet.error import ( WalletError, @@ -45,56 +45,56 @@ class TestInMemoryWallet: @pytest.mark.asyncio async def test_create_signing_key_ed25519_random(self, wallet: InMemoryWallet): assert str(wallet) - info = await wallet.create_signing_key(KeyType.ED25519) + info = await wallet.create_signing_key(ED25519) assert info and info.verkey @pytest.mark.asyncio @pytest.mark.ursa_bbs_signatures async def test_create_signing_key_bls12381g2_random(self, wallet: InMemoryWallet): assert str(wallet) - info = await wallet.create_signing_key(KeyType.BLS12381G2) + info = await wallet.create_signing_key(BLS12381G2) assert info and info.verkey @pytest.mark.asyncio async def test_create_signing_key_ed25519_seeded(self, wallet: InMemoryWallet): - info = await wallet.create_signing_key(KeyType.ED25519, self.test_seed) + info = await wallet.create_signing_key(ED25519, self.test_seed) assert info.verkey == self.test_ed25519_verkey with pytest.raises(WalletDuplicateError): - await wallet.create_signing_key(KeyType.ED25519, self.test_seed) + await wallet.create_signing_key(ED25519, self.test_seed) with pytest.raises(WalletError): - await wallet.create_signing_key(KeyType.ED25519, "invalid-seed", None) + await wallet.create_signing_key(ED25519, "invalid-seed", None) @pytest.mark.asyncio @pytest.mark.ursa_bbs_signatures async def test_create_signing_key_bls12381g2_seeded(self, wallet: InMemoryWallet): - info = await wallet.create_signing_key(KeyType.BLS12381G2, self.test_seed) + info = await wallet.create_signing_key(BLS12381G2, self.test_seed) assert info.verkey == self.test_bls12381g2_verkey with pytest.raises(WalletDuplicateError): - await wallet.create_signing_key(KeyType.BLS12381G2, self.test_seed) + await wallet.create_signing_key(BLS12381G2, self.test_seed) with pytest.raises(WalletError): - await wallet.create_signing_key(KeyType.BLS12381G2, "invalid-seed", None) + await wallet.create_signing_key(BLS12381G2, "invalid-seed", None) @pytest.mark.asyncio async def test_create_signing_key_unsupported_key_type( self, wallet: InMemoryWallet ): with pytest.raises(WalletError): - await wallet.create_signing_key(KeyType.X25519) + await wallet.create_signing_key(X25519) with pytest.raises(WalletError): - await wallet.create_signing_key(KeyType.BLS12381G1) + await wallet.create_signing_key(BLS12381G1) with pytest.raises(WalletError): - await wallet.create_signing_key(KeyType.BLS12381G1G2) + await wallet.create_signing_key(BLS12381G1G2) @pytest.mark.asyncio async def test_signing_key_metadata(self, wallet: InMemoryWallet): info = await wallet.create_signing_key( - KeyType.ED25519, self.test_seed, self.test_metadata + ED25519, self.test_seed, self.test_metadata ) assert info.metadata == self.test_metadata info2 = await wallet.get_signing_key(self.test_ed25519_verkey) @@ -117,7 +117,7 @@ async def test_signing_key_metadata(self, wallet: InMemoryWallet): @pytest.mark.ursa_bbs_signatures async def test_signing_key_metadata_bls(self, wallet: InMemoryWallet): info = await wallet.create_signing_key( - KeyType.BLS12381G2, self.test_seed, self.test_metadata + BLS12381G2, self.test_seed, self.test_metadata ) assert info.metadata == self.test_metadata info2 = await wallet.get_signing_key(self.test_bls12381g2_verkey) @@ -130,19 +130,19 @@ async def test_signing_key_metadata_bls(self, wallet: InMemoryWallet): @pytest.mark.asyncio async def test_create_local_sov_random(self, wallet: InMemoryWallet): - info = await wallet.create_local_did(DIDMethod.SOV, KeyType.ED25519, None, None) + info = await wallet.create_local_did(DIDMethod.SOV, ED25519, None, None) assert info and info.did and info.verkey @pytest.mark.asyncio async def test_create_local_key_random_ed25519(self, wallet: InMemoryWallet): - info = await wallet.create_local_did(DIDMethod.KEY, KeyType.ED25519, None, None) + info = await wallet.create_local_did(DIDMethod.KEY, ED25519, None, None) assert info and info.did and info.verkey @pytest.mark.asyncio @pytest.mark.ursa_bbs_signatures async def test_create_local_key_random_bls12381g2(self, wallet: InMemoryWallet): info = await wallet.create_local_did( - DIDMethod.KEY, KeyType.BLS12381G2, None, None + DIDMethod.KEY, BLS12381G2, None, None ) assert info and info.did and info.verkey @@ -151,61 +151,57 @@ async def test_create_local_incorrect_key_type_for_did_method( self, wallet: InMemoryWallet ): with pytest.raises(WalletError): - await wallet.create_local_did(DIDMethod.SOV, KeyType.BLS12381G2, None, None) + await wallet.create_local_did(DIDMethod.SOV, BLS12381G2, None, None) @pytest.mark.asyncio async def test_create_local_sov_seeded(self, wallet: InMemoryWallet): info = await wallet.create_local_did( - DIDMethod.SOV, KeyType.ED25519, self.test_seed, None + DIDMethod.SOV, ED25519, self.test_seed, None ) assert info.did == self.test_sov_did assert info.verkey == self.test_ed25519_verkey # should not raise WalletDuplicateError - same verkey - await wallet.create_local_did( - DIDMethod.SOV, KeyType.ED25519, self.test_seed, None - ) + await wallet.create_local_did(DIDMethod.SOV, ED25519, self.test_seed, None) with pytest.raises(WalletError): _ = await wallet.create_local_did( - DIDMethod.SOV, KeyType.ED25519, "invalid-seed", None + DIDMethod.SOV, ED25519, "invalid-seed", None ) @pytest.mark.asyncio @pytest.mark.ursa_bbs_signatures async def test_create_local_key_seeded_bls12381g2(self, wallet: InMemoryWallet): info = await wallet.create_local_did( - DIDMethod.KEY, KeyType.BLS12381G2, self.test_seed, None + DIDMethod.KEY, BLS12381G2, self.test_seed, None ) assert info.did == self.test_key_bls12381g2_did assert info.verkey == self.test_bls12381g2_verkey # should not raise WalletDuplicateError - same verkey await wallet.create_local_did( - DIDMethod.KEY, KeyType.BLS12381G2, self.test_seed, None + DIDMethod.KEY, BLS12381G2, self.test_seed, None ) with pytest.raises(WalletError): _ = await wallet.create_local_did( - DIDMethod.KEY, KeyType.BLS12381G2, "invalid-seed", None + DIDMethod.KEY, BLS12381G2, "invalid-seed", None ) @pytest.mark.asyncio async def test_create_local_key_seeded_ed25519(self, wallet: InMemoryWallet): info = await wallet.create_local_did( - DIDMethod.KEY, KeyType.ED25519, self.test_seed, None + DIDMethod.KEY, ED25519, self.test_seed, None ) assert info.did == self.test_key_ed25519_did assert info.verkey == self.test_ed25519_verkey # should not raise WalletDuplicateError - same verkey - await wallet.create_local_did( - DIDMethod.KEY, KeyType.ED25519, self.test_seed, None - ) + await wallet.create_local_did(DIDMethod.KEY, ED25519, self.test_seed, None) with pytest.raises(WalletError): _ = await wallet.create_local_did( - DIDMethod.KEY, KeyType.ED25519, "invalid-seed", None + DIDMethod.KEY, ED25519, "invalid-seed", None ) @pytest.mark.asyncio @@ -217,9 +213,9 @@ async def test_rotate_did_keypair(self, wallet: InMemoryWallet): await wallet.rotate_did_keypair_apply(self.test_sov_did) info = await wallet.create_local_did( - DIDMethod.SOV, KeyType.ED25519, self.test_seed, self.test_sov_did + DIDMethod.SOV, ED25519, self.test_seed, self.test_sov_did ) - key_info = await wallet.create_local_did(DIDMethod.KEY, KeyType.ED25519) + key_info = await wallet.create_local_did(DIDMethod.KEY, ED25519) with pytest.raises(WalletError): await wallet.rotate_did_keypair_apply(self.test_sov_did) @@ -239,25 +235,25 @@ async def test_rotate_did_keypair(self, wallet: InMemoryWallet): @pytest.mark.asyncio async def test_create_local_with_did(self, wallet: InMemoryWallet): info = await wallet.create_local_did( - DIDMethod.SOV, KeyType.ED25519, None, self.test_sov_did + DIDMethod.SOV, ED25519, None, self.test_sov_did ) assert info.did == self.test_sov_did with pytest.raises(WalletDuplicateError): await wallet.create_local_did( - DIDMethod.SOV, KeyType.ED25519, None, self.test_sov_did + DIDMethod.SOV, ED25519, None, self.test_sov_did ) with pytest.raises(WalletError) as context: await wallet.create_local_did( - DIDMethod.KEY, KeyType.ED25519, None, "did:sov:random" + DIDMethod.KEY, ED25519, None, "did:sov:random" ) assert "Not allowed to set DID for DID method 'key'" in str(context.value) @pytest.mark.asyncio async def test_local_verkey(self, wallet: InMemoryWallet): info = await wallet.create_local_did( - DIDMethod.SOV, KeyType.ED25519, self.test_seed, self.test_sov_did + DIDMethod.SOV, ED25519, self.test_seed, self.test_sov_did ) assert info.did == self.test_sov_did assert info.verkey == self.test_ed25519_verkey @@ -273,7 +269,7 @@ async def test_local_verkey(self, wallet: InMemoryWallet): @pytest.mark.asyncio @pytest.mark.ursa_bbs_signatures async def test_local_verkey_bls12381g2(self, wallet: InMemoryWallet): - await wallet.create_local_did(DIDMethod.KEY, KeyType.BLS12381G2, self.test_seed) + await wallet.create_local_did(DIDMethod.KEY, BLS12381G2, self.test_seed) bls_info_get = await wallet.get_local_did_for_verkey( self.test_bls12381g2_verkey ) @@ -289,7 +285,7 @@ async def test_local_verkey_bls12381g2(self, wallet: InMemoryWallet): async def test_local_metadata(self, wallet: InMemoryWallet): info = await wallet.create_local_did( DIDMethod.SOV, - KeyType.ED25519, + ED25519, self.test_seed, self.test_sov_did, self.test_metadata, @@ -319,7 +315,7 @@ async def test_local_metadata(self, wallet: InMemoryWallet): async def test_local_metadata_bbs(self, wallet: InMemoryWallet): info = await wallet.create_local_did( DIDMethod.KEY, - KeyType.BLS12381G2, + BLS12381G2, self.test_seed, None, self.test_metadata, @@ -339,7 +335,7 @@ async def test_local_metadata_bbs(self, wallet: InMemoryWallet): async def test_create_public_did(self, wallet: InMemoryWallet): info = await wallet.create_local_did( DIDMethod.SOV, - KeyType.ED25519, + ED25519, self.test_seed, self.test_sov_did, self.test_metadata, @@ -351,7 +347,7 @@ async def test_create_public_did(self, wallet: InMemoryWallet): info_public = await wallet.create_public_did( DIDMethod.SOV, - KeyType.ED25519, + ED25519, ) assert info_public.metadata.get("posted") posted = await wallet.get_posted_dids() @@ -360,7 +356,7 @@ async def test_create_public_did(self, wallet: InMemoryWallet): # test replace info_replace = await wallet.create_public_did( DIDMethod.SOV, - KeyType.ED25519, + ED25519, ) assert info_replace.metadata.get("posted") info_check = await wallet.get_local_did(info_public.did) @@ -377,7 +373,7 @@ async def test_create_public_did_x_not_sov(self, wallet: InMemoryWallet): with pytest.raises(WalletError) as context: await wallet.create_public_did( DIDMethod.KEY, - KeyType.ED25519, + ED25519, ) assert "Setting public DID is only allowed for did:sov DIDs" in str( context.value @@ -390,7 +386,7 @@ async def test_create_public_did_x_unsupported_key_type_method( with pytest.raises(WalletError) as context: await wallet.create_public_did( DIDMethod.SOV, - KeyType.BLS12381G2, + BLS12381G2, ) assert "Invalid key type" in str(context.value) @@ -398,7 +394,7 @@ async def test_create_public_did_x_unsupported_key_type_method( async def test_set_public_did(self, wallet: InMemoryWallet): info = await wallet.create_local_did( DIDMethod.SOV, - KeyType.ED25519, + ED25519, self.test_seed, self.test_sov_did, self.test_metadata, @@ -413,7 +409,7 @@ async def test_set_public_did(self, wallet: InMemoryWallet): assert info_same.did == info.did assert info_same.metadata.get("posted") - info_new = await wallet.create_local_did(DIDMethod.SOV, KeyType.ED25519) + info_new = await wallet.create_local_did(DIDMethod.SOV, ED25519) assert info_new.did != info_same.did loc = await wallet.get_local_did(self.test_sov_did) @@ -430,7 +426,7 @@ async def test_set_public_did(self, wallet: InMemoryWallet): async def test_set_public_did_x_not_sov(self, wallet: InMemoryWallet): info = await wallet.create_local_did( DIDMethod.KEY, - KeyType.ED25519, + ED25519, ) with pytest.raises(WalletError) as context: await wallet.set_public_did(info.did) @@ -441,28 +437,24 @@ async def test_set_public_did_x_not_sov(self, wallet: InMemoryWallet): @pytest.mark.asyncio async def test_sign_verify(self, wallet: InMemoryWallet): info = await wallet.create_local_did( - DIDMethod.SOV, KeyType.ED25519, self.test_seed, self.test_sov_did + DIDMethod.SOV, ED25519, self.test_seed, self.test_sov_did ) message_bin = self.test_message.encode("ascii") signature = await wallet.sign_message(message_bin, info.verkey) assert signature == self.test_signature verify = await wallet.verify_message( - message_bin, signature, info.verkey, KeyType.ED25519 + message_bin, signature, info.verkey, ED25519 ) assert verify bad_sig = b"x" + signature[1:] - verify = await wallet.verify_message( - message_bin, bad_sig, info.verkey, KeyType.ED25519 - ) + verify = await wallet.verify_message(message_bin, bad_sig, info.verkey, ED25519) assert not verify bad_msg = b"x" + message_bin[1:] - verify = await wallet.verify_message( - bad_msg, signature, info.verkey, KeyType.ED25519 - ) + verify = await wallet.verify_message(bad_msg, signature, info.verkey, ED25519) assert not verify verify = await wallet.verify_message( - message_bin, signature, self.test_target_verkey, KeyType.ED25519 + message_bin, signature, self.test_target_verkey, ED25519 ) assert not verify @@ -478,46 +470,46 @@ async def test_sign_verify(self, wallet: InMemoryWallet): assert "Verkey not provided" in str(excinfo.value) with pytest.raises(WalletError) as excinfo: - await wallet.verify_message(message_bin, signature, None, KeyType.ED25519) + await wallet.verify_message(message_bin, signature, None, ED25519) assert "Verkey not provided" in str(excinfo.value) with pytest.raises(WalletError) as excinfo: - await wallet.verify_message(message_bin, None, info.verkey, KeyType.ED25519) + await wallet.verify_message(message_bin, None, info.verkey, ED25519) assert "Signature not provided" in str(excinfo.value) with pytest.raises(WalletError) as excinfo: - await wallet.verify_message(None, message_bin, info.verkey, KeyType.ED25519) + await wallet.verify_message(None, message_bin, info.verkey, ED25519) assert "Message not provided" in str(excinfo.value) @pytest.mark.asyncio @pytest.mark.ursa_bbs_signatures async def test_sign_verify_bbs(self, wallet: InMemoryWallet): info = await wallet.create_local_did( - DIDMethod.KEY, KeyType.BLS12381G2, self.test_seed + DIDMethod.KEY, BLS12381G2, self.test_seed ) message_bin = self.test_message.encode("ascii") signature = await wallet.sign_message(message_bin, info.verkey) assert signature verify = await wallet.verify_message( - message_bin, signature, info.verkey, KeyType.BLS12381G2 + message_bin, signature, info.verkey, BLS12381G2 ) assert verify bad_msg = b"x" + message_bin[1:] verify = await wallet.verify_message( - bad_msg, signature, info.verkey, KeyType.BLS12381G2 + bad_msg, signature, info.verkey, BLS12381G2 ) assert not verify with pytest.raises(WalletError): bad_sig = b"x" + signature[1:] verify = await wallet.verify_message( - message_bin, bad_sig, info.verkey, KeyType.BLS12381G2 + message_bin, bad_sig, info.verkey, BLS12381G2 ) with pytest.raises(WalletError): await wallet.verify_message( - message_bin, signature, self.test_target_verkey, KeyType.BLS12381G2 + message_bin, signature, self.test_target_verkey, BLS12381G2 ) with pytest.raises(WalletError): @@ -533,26 +525,26 @@ async def test_sign_verify_bbs(self, wallet: InMemoryWallet): with pytest.raises(WalletError) as excinfo: await wallet.verify_message( - message_bin, signature, None, KeyType.BLS12381G2 + message_bin, signature, None, BLS12381G2 ) assert "Verkey not provided" in str(excinfo.value) with pytest.raises(WalletError) as excinfo: await wallet.verify_message( - message_bin, None, info.verkey, KeyType.BLS12381G2 + message_bin, None, info.verkey, BLS12381G2 ) assert "Signature not provided" in str(excinfo.value) with pytest.raises(WalletError) as excinfo: await wallet.verify_message( - None, message_bin, info.verkey, KeyType.BLS12381G2 + None, message_bin, info.verkey, BLS12381G2 ) assert "Message not provided" in str(excinfo.value) @pytest.mark.asyncio async def test_pack_unpack(self, wallet: InMemoryWallet): await wallet.create_local_did( - DIDMethod.SOV, KeyType.ED25519, self.test_seed, self.test_sov_did + DIDMethod.SOV, ED25519, self.test_seed, self.test_sov_did ) packed_anon = await wallet.pack_message( @@ -568,7 +560,7 @@ async def test_pack_unpack(self, wallet: InMemoryWallet): assert "Message not provided" in str(excinfo.value) await wallet.create_local_did( - DIDMethod.SOV, KeyType.ED25519, self.test_target_seed, self.test_target_did + DIDMethod.SOV, ED25519, self.test_target_seed, self.test_target_did ) packed_auth = await wallet.pack_message( self.test_message, [self.test_target_verkey], self.test_ed25519_verkey @@ -587,7 +579,7 @@ async def test_pack_unpack(self, wallet: InMemoryWallet): @pytest.mark.asyncio async def test_signature_round_trip(self, wallet: InMemoryWallet): - key_info = await wallet.create_signing_key(KeyType.ED25519) + key_info = await wallet.create_signing_key(ED25519) msg = {"test": "signed field"} timestamp = int(time.time()) sig = await SignatureDecorator.create(msg, key_info.verkey, wallet, timestamp) @@ -601,7 +593,7 @@ async def test_signature_round_trip(self, wallet: InMemoryWallet): async def test_set_did_endpoint_x_not_sov(self, wallet: InMemoryWallet): info = await wallet.create_local_did( DIDMethod.KEY, - KeyType.ED25519, + ED25519, ) with pytest.raises(WalletError) as context: await wallet.set_did_endpoint( diff --git a/aries_cloudagent/wallet/tests/test_indy_wallet.py b/aries_cloudagent/wallet/tests/test_indy_wallet.py index 6044a6a658..7d05d78233 100644 --- a/aries_cloudagent/wallet/tests/test_indy_wallet.py +++ b/aries_cloudagent/wallet/tests/test_indy_wallet.py @@ -17,7 +17,7 @@ from ...indy.sdk.profile import IndySdkProfile, IndySdkProfileManager from ...indy.sdk.wallet_setup import IndyWalletConfig from ...ledger.endpoint_type import EndpointType -from ...wallet.key_type import KeyType +from ...wallet.key_type import ED25519, KeyType from ...wallet.did_method import DIDMethod from ...ledger.indy import IndySdkLedgerPool @@ -67,7 +67,7 @@ class TestIndySdkWallet(test_in_memory_wallet.TestInMemoryWallet): @pytest.mark.asyncio async def test_rotate_did_keypair_x(self, wallet: IndySdkWallet): info = await wallet.create_local_did( - DIDMethod.SOV, KeyType.ED25519, self.test_seed, self.test_sov_did + DIDMethod.SOV, ED25519, self.test_seed, self.test_sov_did ) with async_mock.patch.object( @@ -99,7 +99,7 @@ async def test_create_signing_key_x(self, wallet: IndySdkWallet): test_module.ErrorCode.CommonIOError, {"message": "outlier"} ) with pytest.raises(test_module.WalletError) as excinfo: - await wallet.create_signing_key(KeyType.ED25519) + await wallet.create_signing_key(ED25519) assert "outlier" in str(excinfo.value) @pytest.mark.asyncio @@ -111,7 +111,7 @@ async def test_create_local_did_x(self, wallet: IndySdkWallet): test_module.ErrorCode.CommonIOError, {"message": "outlier"} ) with pytest.raises(test_module.WalletError) as excinfo: - await wallet.create_local_did(DIDMethod.SOV, KeyType.ED25519) + await wallet.create_local_did(DIDMethod.SOV, ED25519) assert "outlier" in str(excinfo.value) @pytest.mark.asyncio @@ -121,7 +121,7 @@ async def test_set_did_endpoint_ledger(self, wallet: IndySdkWallet): ) info_pub = await wallet.create_public_did( DIDMethod.SOV, - KeyType.ED25519, + ED25519, ) await wallet.set_did_endpoint(info_pub.did, "https://example.com", mock_ledger) mock_ledger.update_endpoint_for_did.assert_called_once_with( @@ -147,7 +147,7 @@ async def test_set_did_endpoint_ledger_with_routing_keys( mock_ledger = async_mock.MagicMock( read_only=False, update_endpoint_for_did=async_mock.CoroutineMock() ) - info_pub = await wallet.create_public_did(DIDMethod.SOV, KeyType.ED25519) + info_pub = await wallet.create_public_did(DIDMethod.SOV, ED25519) await wallet.set_did_endpoint( info_pub.did, "https://example.com", mock_ledger, routing_keys=routing_keys ) @@ -168,7 +168,7 @@ async def test_set_did_endpoint_readonly_ledger(self, wallet: IndySdkWallet): ) info_pub = await wallet.create_public_did( DIDMethod.SOV, - KeyType.ED25519, + ED25519, ) await wallet.set_did_endpoint(info_pub.did, "https://example.com", mock_ledger) mock_ledger.update_endpoint_for_did.assert_not_called() @@ -207,7 +207,7 @@ async def test_get_local_did_x(self, wallet: IndySdkWallet): async def test_replace_local_did_metadata_x(self, wallet: IndySdkWallet): info = await wallet.create_local_did( DIDMethod.SOV, - KeyType.ED25519, + ED25519, self.test_seed, self.test_sov_did, self.test_metadata, @@ -239,7 +239,7 @@ async def test_verify_message_x(self, wallet: IndySdkWallet): b"hello world", b"signature", self.test_ed25519_verkey, - KeyType.ED25519, + ED25519, ) assert "outlier" in str(excinfo.value) @@ -247,7 +247,7 @@ async def test_verify_message_x(self, wallet: IndySdkWallet): test_module.ErrorCode.CommonInvalidStructure ) assert not await wallet.verify_message( - b"hello world", b"signature", self.test_ed25519_verkey, KeyType.ED25519 + b"hello world", b"signature", self.test_ed25519_verkey, ED25519 ) @pytest.mark.asyncio @@ -282,14 +282,12 @@ async def test_compare_pack_unpack(self, in_memory_wallet, wallet: IndySdkWallet """ Ensure that python-based pack/unpack is compatible with indy-sdk implementation """ - await in_memory_wallet.create_local_did( - DIDMethod.SOV, KeyType.ED25519, self.test_seed - ) + await in_memory_wallet.create_local_did(DIDMethod.SOV, ED25519, self.test_seed) py_packed = await in_memory_wallet.pack_message( self.test_message, [self.test_verkey], self.test_verkey ) - await wallet.create_local_did(DIDMethod.SOV, KeyType.ED25519, self.test_seed) + await wallet.create_local_did(DIDMethod.SOV, ED25519, self.test_seed) packed = await wallet.pack_message( self.test_message, [self.test_verkey], self.test_verkey ) @@ -820,7 +818,7 @@ async def test_postgres_wallet_works(self): opened = await postgres_wallet.create_wallet() wallet = IndySdkWallet(opened) - await wallet.create_local_did(DIDMethod.SOV, KeyType.ED25519, self.test_seed) + await wallet.create_local_did(DIDMethod.SOV, ED25519, self.test_seed) py_packed = await wallet.pack_message( self.test_message, [self.test_verkey], self.test_verkey ) @@ -862,7 +860,7 @@ async def test_postgres_wallet_scheme_works(self): assert "Wallet was not removed" in str(excinfo.value) wallet = IndySdkWallet(opened) - await wallet.create_local_did(DIDMethod.SOV, KeyType.ED25519, self.test_seed) + await wallet.create_local_did(DIDMethod.SOV, ED25519, self.test_seed) py_packed = await wallet.pack_message( self.test_message, [self.test_verkey], self.test_verkey ) @@ -899,7 +897,7 @@ async def test_postgres_wallet_scheme2_works(self): opened = await postgres_wallet.create_wallet() wallet = IndySdkWallet(opened) - await wallet.create_local_did(DIDMethod.SOV, KeyType.ED25519, self.test_seed) + await wallet.create_local_did(DIDMethod.SOV, ED25519, self.test_seed) py_packed = await wallet.pack_message( self.test_message, [self.test_verkey], self.test_verkey ) diff --git a/aries_cloudagent/wallet/tests/test_key_pair.py b/aries_cloudagent/wallet/tests/test_key_pair.py index b81062d856..843cae21af 100644 --- a/aries_cloudagent/wallet/tests/test_key_pair.py +++ b/aries_cloudagent/wallet/tests/test_key_pair.py @@ -23,7 +23,7 @@ async def test_create_key_pair(self): await self.key_pair_mgr.store_key_pair( public_key=self.test_public_key, secret_key=self.test_secret, - key_type=KeyType.ED25519, + key_type=ED25519, ) verkey = bytes_to_b58(self.test_public_key) @@ -34,17 +34,17 @@ async def test_create_key_pair(self): value = json.loads(record.value) - assert record.tags == {"verkey": verkey, "key_type": KeyType.ED25519.key_type} + assert record.tags == {"verkey": verkey, "key_type": ED25519.key_type} assert value["verkey"] == verkey assert value["secret_key"] == bytes_to_b58(self.test_secret) assert value["metadata"] == {} - assert value["key_type"] == KeyType.ED25519.key_type + assert value["key_type"] == ED25519.key_type async def test_get_key_pair(self): await self.key_pair_mgr.store_key_pair( public_key=self.test_public_key, secret_key=self.test_secret, - key_type=KeyType.ED25519, + key_type=ED25519, ) verkey = bytes_to_b58(self.test_public_key) @@ -54,7 +54,7 @@ async def test_get_key_pair(self): assert key_pair["verkey"] == verkey assert key_pair["secret_key"] == bytes_to_b58(self.test_secret) assert key_pair["metadata"] == {} - assert key_pair["key_type"] == KeyType.ED25519.key_type + assert key_pair["key_type"] == ED25519.key_type async def test_get_key_pair_x_not_found(self): with self.assertRaises(StorageNotFoundError): @@ -64,7 +64,7 @@ async def test_delete_key_pair(self): await self.key_pair_mgr.store_key_pair( public_key=self.test_public_key, secret_key=self.test_secret, - key_type=KeyType.ED25519, + key_type=ED25519, ) verkey = bytes_to_b58(self.test_public_key) @@ -86,7 +86,7 @@ async def test_update_key_pair_metadata(self): await self.key_pair_mgr.store_key_pair( public_key=self.test_public_key, secret_key=self.test_secret, - key_type=KeyType.ED25519, + key_type=ED25519, metadata={"some": "data"}, ) diff --git a/aries_cloudagent/wallet/tests/test_key_type.py b/aries_cloudagent/wallet/tests/test_key_type.py index 0dc025a099..984ebcbd22 100644 --- a/aries_cloudagent/wallet/tests/test_key_type.py +++ b/aries_cloudagent/wallet/tests/test_key_type.py @@ -2,10 +2,10 @@ from ...core.error import BaseError from ..did_method import DIDMethod -from ..key_type import KeyType +from ..key_type import BLS12381G2, ED25519, KeyType SOV_DID_METHOD_NAME = "sov" -SOV_SUPPORTED_KEY_TYPES = [KeyType.ED25519] +SOV_SUPPORTED_KEY_TYPES = [ED25519] KEY_DID_METHOD_NAME = "key" @@ -37,5 +37,5 @@ def test_properties(self): assert method.supported_key_types == SOV_SUPPORTED_KEY_TYPES assert method.supports_rotation == True - assert method.supports_key_type(KeyType.ED25519) == True - assert method.supports_key_type(KeyType.BLS12381G2) == False + assert method.supports_key_type(ED25519) == True + assert method.supports_key_type(BLS12381G2) == False diff --git a/aries_cloudagent/wallet/tests/test_routes.py b/aries_cloudagent/wallet/tests/test_routes.py index c6adb4b5dc..c6a017b0ab 100644 --- a/aries_cloudagent/wallet/tests/test_routes.py +++ b/aries_cloudagent/wallet/tests/test_routes.py @@ -72,7 +72,7 @@ def test_format_did_info(self): self.test_verkey, DIDPosture.WALLET_ONLY.metadata, DIDMethod.SOV, - KeyType.ED25519, + ED25519, ) result = test_module.format_did_info(did_info) assert ( @@ -86,7 +86,7 @@ def test_format_did_info(self): self.test_verkey, {"posted": True, "public": True}, DIDMethod.SOV, - KeyType.ED25519, + ED25519, ) result = test_module.format_did_info(did_info) assert result["posture"] == DIDPosture.PUBLIC.moniker @@ -96,7 +96,7 @@ def test_format_did_info(self): self.test_verkey, {"posted": True, "public": False}, DIDMethod.SOV, - KeyType.ED25519, + ED25519, ) result = test_module.format_did_info(did_info) assert result["posture"] == DIDPosture.POSTED.moniker @@ -110,7 +110,7 @@ async def test_create_did(self): self.test_verkey, DIDPosture.WALLET_ONLY.metadata, DIDMethod.SOV, - KeyType.ED25519, + ED25519, ) result = await test_module.wallet_create_did(self.request) json_response.assert_called_once_with( @@ -119,7 +119,7 @@ async def test_create_did(self): "did": self.test_did, "verkey": self.test_verkey, "posture": DIDPosture.WALLET_ONLY.moniker, - "key_type": KeyType.ED25519.key_type, + "key_type": ED25519.key_type, "method": DIDMethod.SOV.method_name, } } @@ -148,14 +148,14 @@ async def test_did_list(self): self.test_verkey, DIDPosture.WALLET_ONLY.metadata, DIDMethod.SOV, - KeyType.ED25519, + ED25519, ), DIDInfo( self.test_posted_did, self.test_posted_verkey, DIDPosture.POSTED.metadata, DIDMethod.SOV, - KeyType.ED25519, + ED25519, ), ] result = await test_module.wallet_did_list(self.request) @@ -166,14 +166,14 @@ async def test_did_list(self): "did": self.test_posted_did, "verkey": self.test_posted_verkey, "posture": DIDPosture.POSTED.moniker, - "key_type": KeyType.ED25519.key_type, + "key_type": ED25519.key_type, "method": DIDMethod.SOV.method_name, }, { "did": self.test_did, "verkey": self.test_verkey, "posture": DIDPosture.WALLET_ONLY.moniker, - "key_type": KeyType.ED25519.key_type, + "key_type": ED25519.key_type, "method": DIDMethod.SOV.method_name, }, ] @@ -192,7 +192,7 @@ async def test_did_list_filter_public(self): self.test_verkey, DIDPosture.PUBLIC.metadata, DIDMethod.SOV, - KeyType.ED25519, + ED25519, ) self.wallet.get_posted_dids.return_value = [ DIDInfo( @@ -200,7 +200,7 @@ async def test_did_list_filter_public(self): self.test_posted_verkey, DIDPosture.POSTED.metadata, DIDMethod.SOV, - KeyType.ED25519, + ED25519, ) ] result = await test_module.wallet_did_list(self.request) @@ -211,7 +211,7 @@ async def test_did_list_filter_public(self): "did": self.test_did, "verkey": self.test_verkey, "posture": DIDPosture.PUBLIC.moniker, - "key_type": KeyType.ED25519.key_type, + "key_type": ED25519.key_type, "method": DIDMethod.SOV.method_name, } ] @@ -230,7 +230,7 @@ async def test_did_list_filter_posted(self): self.test_verkey, DIDPosture.PUBLIC.metadata, DIDMethod.SOV, - KeyType.ED25519, + ED25519, ) self.wallet.get_posted_dids.return_value = [ DIDInfo( @@ -241,7 +241,7 @@ async def test_did_list_filter_posted(self): "public": False, }, DIDMethod.SOV, - KeyType.ED25519, + ED25519, ) ] result = await test_module.wallet_did_list(self.request) @@ -252,7 +252,7 @@ async def test_did_list_filter_posted(self): "did": self.test_posted_did, "verkey": self.test_posted_verkey, "posture": DIDPosture.POSTED.moniker, - "key_type": KeyType.ED25519.key_type, + "key_type": ED25519.key_type, "method": DIDMethod.SOV.method_name, } ] @@ -271,7 +271,7 @@ async def test_did_list_filter_did(self): self.test_verkey, DIDPosture.WALLET_ONLY.metadata, DIDMethod.SOV, - KeyType.ED25519, + ED25519, ) result = await test_module.wallet_did_list(self.request) json_response.assert_called_once_with( @@ -281,7 +281,7 @@ async def test_did_list_filter_did(self): "did": self.test_did, "verkey": self.test_verkey, "posture": DIDPosture.WALLET_ONLY.moniker, - "key_type": KeyType.ED25519.key_type, + "key_type": ED25519.key_type, "method": DIDMethod.SOV.method_name, } ] @@ -311,7 +311,7 @@ async def test_did_list_filter_verkey(self): self.test_verkey, DIDPosture.WALLET_ONLY.metadata, DIDMethod.SOV, - KeyType.ED25519, + ED25519, ) result = await test_module.wallet_did_list(self.request) json_response.assert_called_once_with( @@ -321,7 +321,7 @@ async def test_did_list_filter_verkey(self): "did": self.test_did, "verkey": self.test_verkey, "posture": DIDPosture.WALLET_ONLY.moniker, - "key_type": KeyType.ED25519.key_type, + "key_type": ED25519.key_type, "method": DIDMethod.SOV.method_name, } ] @@ -350,7 +350,7 @@ async def test_get_public_did(self): self.test_verkey, DIDPosture.PUBLIC.metadata, DIDMethod.SOV, - KeyType.ED25519, + ED25519, ) result = await test_module.wallet_get_public_did(self.request) json_response.assert_called_once_with( @@ -359,7 +359,7 @@ async def test_get_public_did(self): "did": self.test_did, "verkey": self.test_verkey, "posture": DIDPosture.PUBLIC.moniker, - "key_type": KeyType.ED25519.key_type, + "key_type": ED25519.key_type, "method": DIDMethod.SOV.method_name, } } @@ -397,7 +397,7 @@ async def test_set_public_did(self): self.test_verkey, DIDPosture.PUBLIC.metadata, DIDMethod.SOV, - KeyType.ED25519, + ED25519, ) result = await test_module.wallet_set_public_did(self.request) self.wallet.set_public_did.assert_awaited_once() @@ -407,7 +407,7 @@ async def test_set_public_did(self): "did": self.test_did, "verkey": self.test_verkey, "posture": DIDPosture.PUBLIC.moniker, - "key_type": KeyType.ED25519.key_type, + "key_type": ED25519.key_type, "method": DIDMethod.SOV.method_name, } } @@ -495,7 +495,7 @@ async def test_set_public_did_x(self): self.test_verkey, DIDPosture.PUBLIC.metadata, DIDMethod.SOV, - KeyType.ED25519, + ED25519, ) self.wallet.set_public_did.side_effect = test_module.WalletError() with self.assertRaises(test_module.web.HTTPBadRequest): @@ -526,7 +526,7 @@ async def test_set_public_did_no_wallet_did(self): self.test_verkey, DIDPosture.PUBLIC.metadata, DIDMethod.SOV, - KeyType.ED25519, + ED25519, ) self.wallet.set_public_did.side_effect = test_module.WalletNotFoundError() with self.assertRaises(test_module.web.HTTPNotFound): @@ -558,7 +558,7 @@ async def test_set_public_did_update_endpoint(self): self.test_verkey, {**DIDPosture.PUBLIC.metadata, "endpoint": "https://endpoint.com"}, DIDMethod.SOV, - KeyType.ED25519, + ED25519, ) result = await test_module.wallet_set_public_did(self.request) self.wallet.set_public_did.assert_awaited_once() @@ -568,7 +568,7 @@ async def test_set_public_did_update_endpoint(self): "did": self.test_did, "verkey": self.test_verkey, "posture": DIDPosture.PUBLIC.moniker, - "key_type": KeyType.ED25519.key_type, + "key_type": ED25519.key_type, "method": DIDMethod.SOV.method_name, } } @@ -606,7 +606,7 @@ async def test_set_public_did_update_endpoint_use_default_update_in_wallet(self) self.test_verkey, DIDPosture.PUBLIC.metadata, DIDMethod.SOV, - KeyType.ED25519, + ED25519, ) self.wallet.get_local_did.return_value = did_info self.wallet.set_public_did.return_value = did_info @@ -626,7 +626,7 @@ async def test_set_public_did_update_endpoint_use_default_update_in_wallet(self) "did": self.test_did, "verkey": self.test_verkey, "posture": DIDPosture.PUBLIC.moniker, - "key_type": KeyType.ED25519.key_type, + "key_type": ED25519.key_type, "method": DIDMethod.SOV.method_name, } } @@ -652,14 +652,14 @@ async def test_set_did_endpoint(self): self.test_verkey, {"public": False, "endpoint": "http://old-endpoint.ca"}, DIDMethod.SOV, - KeyType.ED25519, + ED25519, ) self.wallet.get_public_did.return_value = DIDInfo( self.test_did, self.test_verkey, DIDPosture.PUBLIC.metadata, DIDMethod.SOV, - KeyType.ED25519, + ED25519, ) with async_mock.patch.object( @@ -681,14 +681,14 @@ async def test_set_did_endpoint_public_did_no_ledger(self): self.test_verkey, {"public": False, "endpoint": "http://old-endpoint.ca"}, DIDMethod.SOV, - KeyType.ED25519, + ED25519, ) self.wallet.get_public_did.return_value = DIDInfo( self.test_did, self.test_verkey, DIDPosture.PUBLIC.metadata, DIDMethod.SOV, - KeyType.ED25519, + ED25519, ) self.wallet.set_did_endpoint.side_effect = test_module.LedgerConfigError() @@ -741,7 +741,7 @@ async def test_get_did_endpoint(self): self.test_verkey, {"public": False, "endpoint": "http://old-endpoint.ca"}, DIDMethod.SOV, - KeyType.ED25519, + ED25519, ) with async_mock.patch.object( @@ -789,7 +789,7 @@ async def test_rotate_did_keypair(self): "verkey", {"public": False}, DIDMethod.SOV, - KeyType.ED25519, + ED25519, ) ) self.wallet.rotate_did_keypair_start = async_mock.AsyncMock() @@ -824,7 +824,7 @@ async def test_rotate_did_keypair_did_not_local(self): "verkey", {"posted": True, "public": True}, DIDMethod.SOV, - KeyType.ED25519, + ED25519, ) ) with self.assertRaises(test_module.web.HTTPBadRequest): @@ -839,7 +839,7 @@ async def test_rotate_did_keypair_x(self): "verkey", {"public": False}, DIDMethod.SOV, - KeyType.ED25519, + ED25519, ) ) self.wallet.rotate_did_keypair_start = async_mock.AsyncMock( From 430202a97a1744ee7633948ff847032e07821101 Mon Sep 17 00:00:00 2001 From: Adam Burdett Date: Thu, 22 Sep 2022 13:02:42 -0600 Subject: [PATCH 495/872] static imports Signed-off-by: Adam Burdett --- .../messaging/decorators/tests/test_signature_decorator.py | 2 +- aries_cloudagent/messaging/jsonld/credential.py | 2 +- aries_cloudagent/messaging/jsonld/tests/test_credential.py | 2 +- aries_cloudagent/messaging/jsonld/tests/test_routes.py | 2 +- aries_cloudagent/messaging/tests/test_agent_message.py | 2 +- aries_cloudagent/multitenant/tests/test_base.py | 2 +- aries_cloudagent/protocols/connections/v1_0/manager.py | 2 +- .../v1_0/messages/tests/test_connection_response.py | 2 +- .../protocols/connections/v1_0/tests/test_manager.py | 2 +- .../protocols/coordinate_mediation/v1_0/manager.py | 2 +- .../protocols/coordinate_mediation/v1_0/normalization.py | 2 +- .../protocols/coordinate_mediation/v1_0/route_manager.py | 2 +- .../didexchange/v1_0/handlers/tests/test_request_handler.py | 2 +- .../didexchange/v1_0/handlers/tests/test_response_handler.py | 2 +- aries_cloudagent/protocols/didexchange/v1_0/manager.py | 2 +- .../protocols/didexchange/v1_0/messages/tests/test_request.py | 2 +- .../didexchange/v1_0/messages/tests/test_response.py | 2 +- .../protocols/didexchange/v1_0/tests/test_manager.py | 2 +- .../protocols/endorse_transaction/v1_0/tests/test_manager.py | 2 +- .../protocols/endorse_transaction/v1_0/tests/test_routes.py | 2 +- .../protocols/introduction/v0_1/tests/test_service.py | 2 +- .../issue_credential/v2_0/formats/ld_proof/handler.py | 2 +- .../v2_0/formats/ld_proof/tests/test_handler.py | 4 ++-- .../protocols/issue_credential/v2_0/tests/test_routes.py | 4 ---- .../out_of_band/v1_0/messages/tests/test_invitation.py | 2 +- .../protocols/out_of_band/v1_0/tests/test_manager.py | 2 +- .../protocols/present_proof/dif/pres_exch_handler.py | 2 +- .../protocols/present_proof/v2_0/formats/dif/handler.py | 2 +- aries_cloudagent/transport/tests/test_pack_format.py | 2 +- aries_cloudagent/utils/tests/test_outofband.py | 2 +- aries_cloudagent/vc/ld_proofs/tests/test_ld_proofs.py | 2 +- aries_cloudagent/vc/tests/test_bbs_mattr_interop.py | 2 +- aries_cloudagent/vc/vc_ld/tests/test_vc_ld.py | 2 +- aries_cloudagent/wallet/askar.py | 2 +- aries_cloudagent/wallet/crypto.py | 2 +- aries_cloudagent/wallet/indy.py | 2 +- aries_cloudagent/wallet/tests/test_crypto.py | 2 +- aries_cloudagent/wallet/tests/test_did_method.py | 2 +- aries_cloudagent/wallet/tests/test_in_memory_wallet.py | 2 +- aries_cloudagent/wallet/tests/test_indy_wallet.py | 2 +- aries_cloudagent/wallet/tests/test_key_pair.py | 2 +- aries_cloudagent/wallet/tests/test_key_type.py | 2 +- aries_cloudagent/wallet/tests/test_routes.py | 2 +- 43 files changed, 43 insertions(+), 47 deletions(-) diff --git a/aries_cloudagent/messaging/decorators/tests/test_signature_decorator.py b/aries_cloudagent/messaging/decorators/tests/test_signature_decorator.py index 4eca03663f..de863ca4b9 100644 --- a/aries_cloudagent/messaging/decorators/tests/test_signature_decorator.py +++ b/aries_cloudagent/messaging/decorators/tests/test_signature_decorator.py @@ -1,6 +1,6 @@ from asynctest import TestCase as AsyncTestCase -from ....wallet.key_type import ED25519, KeyType +from ....wallet.key_type import ED25519 from ....core.in_memory import InMemoryProfile from ....protocols.trustping.v1_0.messages.ping import Ping from ....wallet.in_memory import InMemoryWallet diff --git a/aries_cloudagent/messaging/jsonld/credential.py b/aries_cloudagent/messaging/jsonld/credential.py index c584a83502..5c693a0b63 100644 --- a/aries_cloudagent/messaging/jsonld/credential.py +++ b/aries_cloudagent/messaging/jsonld/credential.py @@ -5,7 +5,7 @@ from ...did.did_key import DIDKey from ...vc.ld_proofs import DocumentLoader from ...wallet.base import BaseWallet -from ...wallet.key_type import KeyType +from ...wallet.key_type import ED25519 from ...wallet.util import b64_to_bytes, b64_to_str, bytes_to_b64, str_to_b64 from .create_verify_data import create_verify_data diff --git a/aries_cloudagent/messaging/jsonld/tests/test_credential.py b/aries_cloudagent/messaging/jsonld/tests/test_credential.py index d613ece328..3046388533 100644 --- a/aries_cloudagent/messaging/jsonld/tests/test_credential.py +++ b/aries_cloudagent/messaging/jsonld/tests/test_credential.py @@ -13,7 +13,7 @@ from ....vc.ld_proofs import DocumentLoader from ....wallet.base import BaseWallet from ....wallet.in_memory import InMemoryWallet -from ....wallet.key_type import ED25519, KeyType +from ....wallet.key_type import ED25519 from .. import credential as test_module from ..create_verify_data import DroppedAttributeError diff --git a/aries_cloudagent/messaging/jsonld/tests/test_routes.py b/aries_cloudagent/messaging/jsonld/tests/test_routes.py index 84b66fc341..ff8ce0b8a1 100644 --- a/aries_cloudagent/messaging/jsonld/tests/test_routes.py +++ b/aries_cloudagent/messaging/jsonld/tests/test_routes.py @@ -16,7 +16,7 @@ from ....wallet.base import BaseWallet from ....wallet.did_method import DIDMethod from ....wallet.error import WalletError -from ....wallet.key_type import KeyType +from ....wallet.key_type import ED25519 from ..error import ( BadJWSHeaderError, DroppedAttributeError, diff --git a/aries_cloudagent/messaging/tests/test_agent_message.py b/aries_cloudagent/messaging/tests/test_agent_message.py index 2c4f9f4021..947328da63 100644 --- a/aries_cloudagent/messaging/tests/test_agent_message.py +++ b/aries_cloudagent/messaging/tests/test_agent_message.py @@ -4,7 +4,7 @@ from ...core.in_memory import InMemoryProfile from ...protocols.didcomm_prefix import DIDCommPrefix -from ...wallet.key_type import KeyType +from ...wallet.key_type import ED25519 from ..agent_message import AgentMessage, AgentMessageSchema from ..decorators.signature_decorator import SignatureDecorator diff --git a/aries_cloudagent/multitenant/tests/test_base.py b/aries_cloudagent/multitenant/tests/test_base.py index fa29fc34e3..1c3022104e 100644 --- a/aries_cloudagent/multitenant/tests/test_base.py +++ b/aries_cloudagent/multitenant/tests/test_base.py @@ -20,7 +20,7 @@ from ...wallet.did_info import DIDInfo from ...wallet.did_method import DIDMethod from ...wallet.in_memory import InMemoryWallet -from ...wallet.key_type import KeyType +from ...wallet.key_type import ED25519 from ...wallet.models.wallet_record import WalletRecord from ..base import BaseMultitenantManager, MultitenantManagerError from ..error import WalletKeyMissingError diff --git a/aries_cloudagent/protocols/connections/v1_0/manager.py b/aries_cloudagent/protocols/connections/v1_0/manager.py index 9fa1f897a0..fe65f09dd9 100644 --- a/aries_cloudagent/protocols/connections/v1_0/manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/manager.py @@ -21,7 +21,7 @@ from ....wallet.did_info import DIDInfo from ....wallet.did_method import DIDMethod from ....wallet.error import WalletNotFoundError -from ....wallet.key_type import ED25519, KeyType +from ....wallet.key_type import ED25519 from ....wallet.util import bytes_to_b58 from ...coordinate_mediation.v1_0.manager import MediationManager from ...discovery.v2_0.manager import V20DiscoveryMgr diff --git a/aries_cloudagent/protocols/connections/v1_0/messages/tests/test_connection_response.py b/aries_cloudagent/protocols/connections/v1_0/messages/tests/test_connection_response.py index 2973ade36c..fd47e9c896 100644 --- a/aries_cloudagent/protocols/connections/v1_0/messages/tests/test_connection_response.py +++ b/aries_cloudagent/protocols/connections/v1_0/messages/tests/test_connection_response.py @@ -2,7 +2,7 @@ from asynctest import TestCase as AsyncTestCase -from ......wallet.key_type import ED25519, KeyType +from ......wallet.key_type import ED25519 from ......connections.models.diddoc import ( DIDDoc, PublicKey, diff --git a/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py index 4437ab0d86..2dc670224f 100644 --- a/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py @@ -26,7 +26,7 @@ from .....wallet.did_method import DIDMethod from .....wallet.error import WalletNotFoundError from .....wallet.in_memory import InMemoryWallet -from .....wallet.key_type import KeyType +from .....wallet.key_type import ED25519 from ....coordinate_mediation.v1_0.manager import MediationManager from ....coordinate_mediation.v1_0.route_manager import RouteManager from ....coordinate_mediation.v1_0.messages.mediate_request import MediationRequest diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py index 7a67b57369..913643c1f0 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py @@ -12,7 +12,7 @@ from ....wallet.base import BaseWallet from ....wallet.did_info import DIDInfo from ....wallet.did_method import DIDMethod -from ....wallet.key_type import KeyType +from ....wallet.key_type import ED25519 from ...routing.v1_0.manager import RoutingManager from ...routing.v1_0.models.route_record import RouteRecord from ...routing.v1_0.models.route_update import RouteUpdate diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/normalization.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/normalization.py index c105aa05d5..d699565367 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/normalization.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/normalization.py @@ -1,6 +1,6 @@ """Normalization methods used while transitioning to DID:Key method.""" from ....did.did_key import DIDKey -from ....wallet.key_type import KeyType +from ....wallet.key_type import ED25519 def normalize_from_did_key(key: str): diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py index 4e03d929a9..7d935a6995 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py @@ -15,7 +15,7 @@ from ....wallet.base import BaseWallet from ....wallet.did_info import DIDInfo from ....wallet.did_method import DIDMethod -from ....wallet.key_type import KeyType +from ....wallet.key_type import ED25519 from ...routing.v1_0.models.route_record import RouteRecord from .manager import MediationManager from .messages.keylist_update import KeylistUpdate diff --git a/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_request_handler.py b/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_request_handler.py index 34acf504ba..d9b7d583b0 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_request_handler.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_request_handler.py @@ -9,7 +9,7 @@ Service, ) from ......core.in_memory import InMemoryProfile -from ......wallet.key_type import KeyType +from ......wallet.key_type import ED25519 from ......wallet.did_method import DIDMethod from ......messaging.decorators.attach_decorator import AttachDecorator from ......messaging.request_context import RequestContext diff --git a/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_response_handler.py b/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_response_handler.py index 79e0af0782..2ab4f79919 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_response_handler.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_response_handler.py @@ -13,7 +13,7 @@ from ......messaging.request_context import RequestContext from ......messaging.responder import MockResponder from ......transport.inbound.receipt import MessageReceipt -from ......wallet.key_type import KeyType +from ......wallet.key_type import ED25519 from ......wallet.did_method import DIDMethod from .....problem_report.v1_0.message import ProblemReport diff --git a/aries_cloudagent/protocols/didexchange/v1_0/manager.py b/aries_cloudagent/protocols/didexchange/v1_0/manager.py index 12e6726b87..890a1cc8f8 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/manager.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/manager.py @@ -26,7 +26,7 @@ from ....wallet.did_method import DIDMethod from ....wallet.did_posture import DIDPosture from ....wallet.error import WalletError -from ....wallet.key_type import KeyType +from ....wallet.key_type import ED25519 from ...coordinate_mediation.v1_0.manager import MediationManager from ...discovery.v2_0.manager import V20DiscoveryMgr from ...out_of_band.v1_0.messages.invitation import ( diff --git a/aries_cloudagent/protocols/didexchange/v1_0/messages/tests/test_request.py b/aries_cloudagent/protocols/didexchange/v1_0/messages/tests/test_request.py index b3dae6e2a9..5f55a1cc22 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/messages/tests/test_request.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/messages/tests/test_request.py @@ -9,7 +9,7 @@ Service, ) from ......wallet.did_method import DIDMethod -from ......wallet.key_type import KeyType +from ......wallet.key_type import ED25519 from ......core.in_memory import InMemoryProfile from ......messaging.decorators.attach_decorator import AttachDecorator diff --git a/aries_cloudagent/protocols/didexchange/v1_0/messages/tests/test_response.py b/aries_cloudagent/protocols/didexchange/v1_0/messages/tests/test_response.py index 78b8443255..e8043de420 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/messages/tests/test_response.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/messages/tests/test_response.py @@ -9,7 +9,7 @@ Service, ) from ......wallet.did_method import DIDMethod -from ......wallet.key_type import KeyType +from ......wallet.key_type import ED25519 from ......core.in_memory import InMemoryProfile from ......messaging.decorators.attach_decorator import AttachDecorator diff --git a/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py index 8cdbcbc316..de1ea64a4d 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py @@ -29,7 +29,7 @@ from .....wallet.error import WalletError from .....wallet.in_memory import InMemoryWallet from .....wallet.did_method import DIDMethod -from .....wallet.key_type import KeyType +from .....wallet.key_type import ED25519 from .....did.did_key import DIDKey from .....connections.base_manager import BaseConnectionManagerError diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_manager.py index 12f898b3be..c0a6cc3b11 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_manager.py @@ -17,7 +17,7 @@ from .....wallet.base import BaseWallet from .....wallet.did_info import DIDInfo from .....wallet.did_method import DIDMethod -from .....wallet.key_type import KeyType +from .....wallet.key_type import ED25519 from ..manager import TransactionManager, TransactionManagerError from ..messages.messages_attach import MessagesAttach diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_routes.py index 4d40885c0d..6bbc0fc8c2 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_routes.py @@ -10,7 +10,7 @@ from .....wallet.base import BaseWallet from .....wallet.did_info import DIDInfo from .....wallet.did_method import DIDMethod -from .....wallet.key_type import KeyType +from .....wallet.key_type import ED25519 from ..models.transaction_record import TransactionRecord from .. import routes as test_module diff --git a/aries_cloudagent/protocols/introduction/v0_1/tests/test_service.py b/aries_cloudagent/protocols/introduction/v0_1/tests/test_service.py index 5947030e4b..ea758821a8 100644 --- a/aries_cloudagent/protocols/introduction/v0_1/tests/test_service.py +++ b/aries_cloudagent/protocols/introduction/v0_1/tests/test_service.py @@ -5,7 +5,7 @@ from .....messaging.request_context import RequestContext from .....messaging.responder import MockResponder from .....did.did_key import DIDKey -from .....wallet.key_type import KeyType +from .....wallet.key_type import ED25519 from ....didcomm_prefix import DIDCommPrefix from ....out_of_band.v1_0.message_types import INVITATION as OOB_INVITATION diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py index 66177376a2..9863f018d0 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py @@ -35,7 +35,7 @@ from ......vc.ld_proofs.constants import SECURITY_CONTEXT_BBS_URL from ......wallet.base import BaseWallet, DIDInfo from ......wallet.error import WalletNotFoundError -from ......wallet.key_type import KeyType +from ......wallet.key_type import BLS12381G2, ED25519 from ...message_types import ( ATTACHMENT_FORMAT, diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/tests/test_handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/tests/test_handler.py index 53ea35b095..8283379059 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/tests/test_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/tests/test_handler.py @@ -26,7 +26,7 @@ ) from .......vc.ld_proofs.constants import SECURITY_CONTEXT_BBS_URL from .......vc.tests.document_loader import custom_document_loader -from .......wallet.key_type import KeyType +from .......wallet.key_type import BLS12381G1G2, ED25519 from .......wallet.error import WalletNotFoundError from .......wallet.did_method import DIDMethod from .......wallet.base import BaseWallet @@ -230,7 +230,7 @@ async def test_assert_can_issue_with_id_and_proof_type(self): verkey="verkey", metadata={}, method=DIDMethod.SOV, - key_type=BLS12381G2, + key_type=BLS12381G1G2, ) mock_did_info.return_value = invalid_did_info with self.assertRaises(V20CredFormatError) as context: diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/tests/test_routes.py b/aries_cloudagent/protocols/issue_credential/v2_0/tests/test_routes.py index 3c72c33e75..7a689b66b4 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/tests/test_routes.py @@ -2,10 +2,6 @@ from asynctest import mock as async_mock, TestCase as AsyncTestCase from .....admin.request_context import AdminRequestContext -from .....wallet.key_type import KeyType -from .....wallet.did_method import DIDMethod -from .....wallet.base import BaseWallet -from .....wallet.did_info import DIDInfo from .. import routes as test_module from ..formats.indy.handler import IndyCredFormatHandler diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/messages/tests/test_invitation.py b/aries_cloudagent/protocols/out_of_band/v1_0/messages/tests/test_invitation.py index f1e215744c..60b3298c4f 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/messages/tests/test_invitation.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/messages/tests/test_invitation.py @@ -4,7 +4,7 @@ from ......messaging.models.base import BaseModelError from ......did.did_key import DIDKey -from ......wallet.key_type import KeyType +from ......wallet.key_type import ED25519 from .....connections.v1_0.message_types import ARIES_PROTOCOL as CONN_PROTO from .....didcomm_prefix import DIDCommPrefix diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py index 1f082ba6df..7bbce062ef 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py @@ -67,7 +67,7 @@ from .....wallet.did_info import DIDInfo, KeyInfo from .....wallet.did_method import DIDMethod from .....wallet.in_memory import InMemoryWallet -from .....wallet.key_type import ED25519, KeyType +from .....wallet.key_type import ED25519 from ....connections.v1_0.messages.connection_invitation import ConnectionInvitation from ....didcomm_prefix import DIDCommPrefix from ....issue_credential.v1_0.message_types import CREDENTIAL_OFFER diff --git a/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py b/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py index 79a1aa8deb..813dbe287d 100644 --- a/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py +++ b/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py @@ -40,7 +40,7 @@ from ....vc.vc_ld.prove import sign_presentation, create_presentation, derive_credential from ....wallet.base import BaseWallet, DIDInfo from ....wallet.error import WalletError, WalletNotFoundError -from ....wallet.key_type import KeyType +from ....wallet.key_type import BLS12381G2, ED25519 from .pres_exch import ( PresentationDefinition, diff --git a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/handler.py b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/handler.py index c69b906085..a55912c1e3 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/handler.py @@ -21,7 +21,7 @@ ) from ......vc.vc_ld.verify import verify_presentation from ......wallet.base import BaseWallet -from ......wallet.key_type import KeyType +from ......wallet.key_type import ED25519, BLS12381G2 from .....problem_report.v1_0.message import ProblemReport diff --git a/aries_cloudagent/transport/tests/test_pack_format.py b/aries_cloudagent/transport/tests/test_pack_format.py index e9d9e5c0e2..b0ea56408f 100644 --- a/aries_cloudagent/transport/tests/test_pack_format.py +++ b/aries_cloudagent/transport/tests/test_pack_format.py @@ -9,7 +9,7 @@ from ...protocols.didcomm_prefix import DIDCommPrefix from ...wallet.base import BaseWallet from ...wallet.error import WalletError -from ...wallet.key_type import KeyType +from ...wallet.key_type import ED25519 from ...wallet.did_method import DIDMethod from .. import pack_format as test_module diff --git a/aries_cloudagent/utils/tests/test_outofband.py b/aries_cloudagent/utils/tests/test_outofband.py index e8f519479c..4305394c30 100644 --- a/aries_cloudagent/utils/tests/test_outofband.py +++ b/aries_cloudagent/utils/tests/test_outofband.py @@ -1,7 +1,7 @@ from asynctest import TestCase from ...protocols.out_of_band.v1_0.messages.invitation import InvitationMessage -from ...wallet.key_type import KeyType +from ...wallet.key_type import ED25519 from ...wallet.did_method import DIDMethod from ...wallet.did_info import DIDInfo diff --git a/aries_cloudagent/vc/ld_proofs/tests/test_ld_proofs.py b/aries_cloudagent/vc/ld_proofs/tests/test_ld_proofs.py index b1369f9602..0143cfe17f 100644 --- a/aries_cloudagent/vc/ld_proofs/tests/test_ld_proofs.py +++ b/aries_cloudagent/vc/ld_proofs/tests/test_ld_proofs.py @@ -4,7 +4,7 @@ from asynctest import TestCase -from ....wallet.key_type import KeyType +from ....wallet.key_type import BLS12381G2, ED25519 from ....did.did_key import DIDKey from ....wallet.in_memory import InMemoryWallet from ....core.in_memory import InMemoryProfile diff --git a/aries_cloudagent/vc/tests/test_bbs_mattr_interop.py b/aries_cloudagent/vc/tests/test_bbs_mattr_interop.py index cbc8aa795a..d9f4dde009 100644 --- a/aries_cloudagent/vc/tests/test_bbs_mattr_interop.py +++ b/aries_cloudagent/vc/tests/test_bbs_mattr_interop.py @@ -1,7 +1,7 @@ from asynctest import TestCase import pytest -from ...wallet.key_type import KeyType +from ...wallet.key_type import BLS12381G2 from ...wallet.util import b58_to_bytes from ...wallet.in_memory import InMemoryWallet from ...core.in_memory import InMemoryProfile diff --git a/aries_cloudagent/vc/vc_ld/tests/test_vc_ld.py b/aries_cloudagent/vc/vc_ld/tests/test_vc_ld.py index 121c76d50d..3c3643ec1a 100644 --- a/aries_cloudagent/vc/vc_ld/tests/test_vc_ld.py +++ b/aries_cloudagent/vc/vc_ld/tests/test_vc_ld.py @@ -3,7 +3,7 @@ import pytest -from ....wallet.key_type import KeyType +from ....wallet.key_type import BLS12381G2, ED25519 from ....did.did_key import DIDKey from ....wallet.in_memory import InMemoryWallet from ....core.in_memory import InMemoryProfile diff --git a/aries_cloudagent/wallet/askar.py b/aries_cloudagent/wallet/askar.py index b881dbba13..b8b26bee93 100644 --- a/aries_cloudagent/wallet/askar.py +++ b/aries_cloudagent/wallet/askar.py @@ -33,7 +33,7 @@ ) from .did_method import DIDMethod from .error import WalletError, WalletDuplicateError, WalletNotFoundError -from .key_type import ED25519, KeyType +from .key_type import BLS12381G2, ED25519, KeyType from .util import b58_to_bytes, bytes_to_b58 CATEGORY_DID = "did" diff --git a/aries_cloudagent/wallet/crypto.py b/aries_cloudagent/wallet/crypto.py index 34615d908f..f44e9cff77 100644 --- a/aries_cloudagent/wallet/crypto.py +++ b/aries_cloudagent/wallet/crypto.py @@ -14,7 +14,7 @@ from ..utils.jwe import JweRecipient, b64url, JweEnvelope, from_b64url from .error import WalletError from .util import bytes_to_b58, b64_to_bytes, b58_to_bytes, random_seed -from .key_type import KeyType +from .key_type import ED25519, BLS12381G2, KeyType from .bbs import ( create_bls12381g2_keypair, verify_signed_messages_bls12381g2, diff --git a/aries_cloudagent/wallet/indy.py b/aries_cloudagent/wallet/indy.py index b34f041c23..9e28e9fbca 100644 --- a/aries_cloudagent/wallet/indy.py +++ b/aries_cloudagent/wallet/indy.py @@ -33,7 +33,7 @@ from .did_method import DIDMethod from .error import WalletError, WalletDuplicateError, WalletNotFoundError from .key_pair import KeyPairStorageManager -from .key_type import KeyType +from .key_type import BLS12381G2, ED25519, KeyType from .util import b58_to_bytes, bytes_to_b58, bytes_to_b64 diff --git a/aries_cloudagent/wallet/tests/test_crypto.py b/aries_cloudagent/wallet/tests/test_crypto.py index 2e72c3b70f..f682e40673 100644 --- a/aries_cloudagent/wallet/tests/test_crypto.py +++ b/aries_cloudagent/wallet/tests/test_crypto.py @@ -3,7 +3,7 @@ from unittest import mock, TestCase -from ..key_type import ED25519 +from ..key_type import BLS12381G1, ED25519 from ..error import WalletError from ...utils.jwe import JweRecipient from ..util import str_to_b64 diff --git a/aries_cloudagent/wallet/tests/test_did_method.py b/aries_cloudagent/wallet/tests/test_did_method.py index 834ac63fb9..6bd147777f 100644 --- a/aries_cloudagent/wallet/tests/test_did_method.py +++ b/aries_cloudagent/wallet/tests/test_did_method.py @@ -1,7 +1,7 @@ from unittest import TestCase -from ..key_type import KeyType +from ..key_type import BLS12381G1, BLS12381G1G2, BLS12381G2, ED25519, X25519, KeyType ED25519_PREFIX_BYTES = b"\xed\x01" BLS12381G1_PREFIX_BYTES = b"\xea\x01" diff --git a/aries_cloudagent/wallet/tests/test_in_memory_wallet.py b/aries_cloudagent/wallet/tests/test_in_memory_wallet.py index 97cdcea8cb..fd550cbf1f 100644 --- a/aries_cloudagent/wallet/tests/test_in_memory_wallet.py +++ b/aries_cloudagent/wallet/tests/test_in_memory_wallet.py @@ -4,7 +4,7 @@ from ...core.in_memory import InMemoryProfile from ...messaging.decorators.signature_decorator import SignatureDecorator from ...wallet.in_memory import InMemoryWallet -from ...wallet.key_type import BLS12381G2, ED25519, KeyType +from ...wallet.key_type import BLS12381G2, ED25519 from ...wallet.did_method import DIDMethod from ...wallet.error import ( WalletError, diff --git a/aries_cloudagent/wallet/tests/test_indy_wallet.py b/aries_cloudagent/wallet/tests/test_indy_wallet.py index 7d05d78233..7a1c22903b 100644 --- a/aries_cloudagent/wallet/tests/test_indy_wallet.py +++ b/aries_cloudagent/wallet/tests/test_indy_wallet.py @@ -17,7 +17,7 @@ from ...indy.sdk.profile import IndySdkProfile, IndySdkProfileManager from ...indy.sdk.wallet_setup import IndyWalletConfig from ...ledger.endpoint_type import EndpointType -from ...wallet.key_type import ED25519, KeyType +from ...wallet.key_type import ED25519 from ...wallet.did_method import DIDMethod from ...ledger.indy import IndySdkLedgerPool diff --git a/aries_cloudagent/wallet/tests/test_key_pair.py b/aries_cloudagent/wallet/tests/test_key_pair.py index 843cae21af..6d0ddccb67 100644 --- a/aries_cloudagent/wallet/tests/test_key_pair.py +++ b/aries_cloudagent/wallet/tests/test_key_pair.py @@ -4,7 +4,7 @@ from ...storage.error import StorageNotFoundError from ..util import bytes_to_b58 -from ..key_type import KeyType +from ..key_type import ED25519 from ...core.in_memory import InMemoryProfile from ...storage.in_memory import InMemoryStorage from ..key_pair import KeyPairStorageManager, KEY_PAIR_STORAGE_TYPE diff --git a/aries_cloudagent/wallet/tests/test_key_type.py b/aries_cloudagent/wallet/tests/test_key_type.py index 984ebcbd22..69c77a80fa 100644 --- a/aries_cloudagent/wallet/tests/test_key_type.py +++ b/aries_cloudagent/wallet/tests/test_key_type.py @@ -2,7 +2,7 @@ from ...core.error import BaseError from ..did_method import DIDMethod -from ..key_type import BLS12381G2, ED25519, KeyType +from ..key_type import BLS12381G2, ED25519 SOV_DID_METHOD_NAME = "sov" SOV_SUPPORTED_KEY_TYPES = [ED25519] diff --git a/aries_cloudagent/wallet/tests/test_routes.py b/aries_cloudagent/wallet/tests/test_routes.py index c6a017b0ab..c657a99780 100644 --- a/aries_cloudagent/wallet/tests/test_routes.py +++ b/aries_cloudagent/wallet/tests/test_routes.py @@ -9,7 +9,7 @@ from ...ledger.base import BaseLedger from ...protocols.coordinate_mediation.v1_0.route_manager import RouteManager from ...wallet.did_method import DIDMethod -from ...wallet.key_type import KeyType +from ...wallet.key_type import ED25519 from ..base import BaseWallet from ..did_info import DIDInfo from ..did_posture import DIDPosture From 9a80789730da57a506e33677cbdcf7a7950a649e Mon Sep 17 00:00:00 2001 From: Adam Burdett Date: Thu, 22 Sep 2022 13:31:08 -0600 Subject: [PATCH 496/872] use keytype registry Signed-off-by: Adam Burdett --- aries_cloudagent/config/default_context.py | 2 + aries_cloudagent/did/did_key.py | 16 +++- .../did/tests/test_did_key_bls12381g1.py | 4 +- .../did/tests/test_did_key_bls12381g1g2.py | 4 +- .../did/tests/test_did_key_bls12381g2.py | 4 +- .../present_proof/v2_0/formats/dif/handler.py | 4 +- .../tests/test_bbs_bls_signature_2020.py | 4 +- .../tests/test_ed25519_signature_2018.py | 4 +- aries_cloudagent/wallet/indy.py | 6 +- aries_cloudagent/wallet/key_type.py | 4 - aries_cloudagent/wallet/routes.py | 38 +++++----- aries_cloudagent/wallet/tests/test_crypto.py | 8 +- .../wallet/tests/test_did_method.py | 74 ++++++++----------- .../wallet/tests/test_in_memory_wallet.py | 24 ++---- 14 files changed, 83 insertions(+), 113 deletions(-) diff --git a/aries_cloudagent/config/default_context.py b/aries_cloudagent/config/default_context.py index 0c5f90cac2..4c8483f5ee 100644 --- a/aries_cloudagent/config/default_context.py +++ b/aries_cloudagent/config/default_context.py @@ -22,6 +22,7 @@ from ..transport.wire_format import BaseWireFormat from ..utils.stats import Collector from ..utils.dependencies import is_indy_sdk_module_installed +from ..wallet.key_type import KeyTypes class DefaultContextBuilder(ContextBuilder): @@ -51,6 +52,7 @@ async def build_context(self) -> InjectionContext: # Global did resolver context.injector.bind_instance(DIDResolver, DIDResolver([])) + context.injector.bind_instance(KeyTypes, KeyTypes()) await self.bind_providers(context) await self.load_plugins(context) diff --git a/aries_cloudagent/did/did_key.py b/aries_cloudagent/did/did_key.py index c85a66fc8b..3a74971612 100644 --- a/aries_cloudagent/did/did_key.py +++ b/aries_cloudagent/did/did_key.py @@ -1,7 +1,15 @@ """DID Key class and resolver methods.""" from ..wallet.crypto import ed25519_pk_to_curve25519 -from ..wallet.key_type import BLS12381G1G2, ED25519, KeyType, BLS12381G1, X25519, BLS12381G2 +from ..wallet.key_type import ( + BLS12381G1G2, + ED25519, + KeyType, + BLS12381G1, + X25519, + BLS12381G2, + KeyTypes, +) from ..wallet.util import b58_to_bytes, bytes_to_b58 from ..vc.ld_proofs.constants import DID_V1_CONTEXT_URL @@ -31,7 +39,7 @@ def from_public_key_b58(cls, public_key: str, key_type: KeyType) -> "DIDKey": return cls.from_public_key(public_key_bytes, key_type) @classmethod - def from_fingerprint(cls, fingerprint: str) -> "DIDKey": + def from_fingerprint(cls, fingerprint: str, key_types=None) -> "DIDKey": """Initialize new DIDKey instance from multibase encoded fingerprint. The fingerprint contains both the public key and key type. @@ -43,7 +51,9 @@ def from_fingerprint(cls, fingerprint: str) -> "DIDKey": key_bytes_with_prefix = b58_to_bytes(fingerprint[1:]) # Get associated key type with prefixed bytes - key_type = KeyType.from_prefixed_bytes(key_bytes_with_prefix) + if not key_types: + key_types = KeyTypes() + key_type = key_types.from_prefixed_bytes(key_bytes_with_prefix) if not key_type: raise Exception( diff --git a/aries_cloudagent/did/tests/test_did_key_bls12381g1.py b/aries_cloudagent/did/tests/test_did_key_bls12381g1.py index b110bc2b2f..f5b84b3bd0 100644 --- a/aries_cloudagent/did/tests/test_did_key_bls12381g1.py +++ b/aries_cloudagent/did/tests/test_did_key_bls12381g1.py @@ -29,9 +29,7 @@ def test_bls12381g1_from_public_key(self): assert did_key.did == TEST_BLS12381G1_DID def test_bls12381g1_from_public_key_b58(self): - did_key = DIDKey.from_public_key_b58( - TEST_BLS12381G1_BASE58_KEY, BLS12381G1 - ) + did_key = DIDKey.from_public_key_b58(TEST_BLS12381G1_BASE58_KEY, BLS12381G1) assert did_key.did == TEST_BLS12381G1_DID diff --git a/aries_cloudagent/did/tests/test_did_key_bls12381g1g2.py b/aries_cloudagent/did/tests/test_did_key_bls12381g1g2.py index 3b5a21c8cd..f5ed77e64d 100644 --- a/aries_cloudagent/did/tests/test_did_key_bls12381g1g2.py +++ b/aries_cloudagent/did/tests/test_did_key_bls12381g1g2.py @@ -36,9 +36,7 @@ def test_bls12381g1g2_from_public_key(self): assert did_key.did == TEST_BLS12381G1G2_DID def test_bls12381g1g2_from_public_key_b58(self): - did_key = DIDKey.from_public_key_b58( - TEST_BLS12381G1G2_BASE58_KEY, BLS12381G1G2 - ) + did_key = DIDKey.from_public_key_b58(TEST_BLS12381G1G2_BASE58_KEY, BLS12381G1G2) assert did_key.did == TEST_BLS12381G1G2_DID diff --git a/aries_cloudagent/did/tests/test_did_key_bls12381g2.py b/aries_cloudagent/did/tests/test_did_key_bls12381g2.py index ddd1259b97..0eb4b4c8f4 100644 --- a/aries_cloudagent/did/tests/test_did_key_bls12381g2.py +++ b/aries_cloudagent/did/tests/test_did_key_bls12381g2.py @@ -24,9 +24,7 @@ def test_bls12381g2_from_public_key(self): assert did_key.did == TEST_BLS12381G2_DID def test_bls12381g2_from_public_key_b58(self): - did_key = DIDKey.from_public_key_b58( - TEST_BLS12381G2_BASE58_KEY, BLS12381G2 - ) + did_key = DIDKey.from_public_key_b58(TEST_BLS12381G2_BASE58_KEY, BLS12381G2) assert did_key.did == TEST_BLS12381G2_DID diff --git a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/handler.py b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/handler.py index a55912c1e3..5459213d3f 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/handler.py @@ -61,9 +61,7 @@ class DIFPresFormatHandler(V20PresFormatHandler): if BbsBlsSignature2020.BBS_SUPPORTED: ISSUE_SIGNATURE_SUITE_KEY_TYPE_MAPPING[BbsBlsSignature2020] = BLS12381G2 - ISSUE_SIGNATURE_SUITE_KEY_TYPE_MAPPING[ - BbsBlsSignatureProof2020 - ] = BLS12381G2 + ISSUE_SIGNATURE_SUITE_KEY_TYPE_MAPPING[BbsBlsSignatureProof2020] = BLS12381G2 async def _get_all_suites(self, wallet: BaseWallet): """Get all supported suites for verifying presentation.""" diff --git a/aries_cloudagent/vc/ld_proofs/suites/tests/test_bbs_bls_signature_2020.py b/aries_cloudagent/vc/ld_proofs/suites/tests/test_bbs_bls_signature_2020.py index 7fac62baad..de3efe4475 100644 --- a/aries_cloudagent/vc/ld_proofs/suites/tests/test_bbs_bls_signature_2020.py +++ b/aries_cloudagent/vc/ld_proofs/suites/tests/test_bbs_bls_signature_2020.py @@ -41,9 +41,7 @@ async def setUp(self): key_type=BLS12381G2, public_key_base58=self.key.verkey, ) - self.verify_key_pair = WalletKeyPair( - wallet=self.wallet, key_type=BLS12381G2 - ) + self.verify_key_pair = WalletKeyPair(wallet=self.wallet, key_type=BLS12381G2) async def test_sign_ld_proofs(self): signed = await sign( diff --git a/aries_cloudagent/vc/ld_proofs/suites/tests/test_ed25519_signature_2018.py b/aries_cloudagent/vc/ld_proofs/suites/tests/test_ed25519_signature_2018.py index 88dca016d3..46e9b02558 100644 --- a/aries_cloudagent/vc/ld_proofs/suites/tests/test_ed25519_signature_2018.py +++ b/aries_cloudagent/vc/ld_proofs/suites/tests/test_ed25519_signature_2018.py @@ -40,9 +40,7 @@ async def setUp(self): key_type=ED25519, public_key_base58=self.key.verkey, ) - self.verify_key_pair = WalletKeyPair( - wallet=self.wallet, key_type=ED25519 - ) + self.verify_key_pair = WalletKeyPair(wallet=self.wallet, key_type=ED25519) async def test_sign_ld_proofs(self): signed = await sign( diff --git a/aries_cloudagent/wallet/indy.py b/aries_cloudagent/wallet/indy.py index 9e28e9fbca..7e02feb6e9 100644 --- a/aries_cloudagent/wallet/indy.py +++ b/aries_cloudagent/wallet/indy.py @@ -33,7 +33,7 @@ from .did_method import DIDMethod from .error import WalletError, WalletDuplicateError, WalletNotFoundError from .key_pair import KeyPairStorageManager -from .key_type import BLS12381G2, ED25519, KeyType +from .key_type import BLS12381G2, ED25519, KeyType, KeyTypes from .util import b58_to_bytes, bytes_to_b58, bytes_to_b64 @@ -71,7 +71,9 @@ def __did_info_from_key_pair_info(self, info: dict): # this needs to change if other did methods are added method = DIDMethod.from_method(info["metadata"].get("method", "key")) - key_type = KeyType.from_key_type(info["key_type"]) + # TODO: inject context to support keytype registry + key_types = KeyTypes() + key_type = key_types.from_key_type(info["key_type"]) if method == DIDMethod.KEY: did = DIDKey.from_public_key_b58(info["verkey"], key_type).did diff --git a/aries_cloudagent/wallet/key_type.py b/aries_cloudagent/wallet/key_type.py index 901da168c5..bb8f2c6718 100644 --- a/aries_cloudagent/wallet/key_type.py +++ b/aries_cloudagent/wallet/key_type.py @@ -28,10 +28,6 @@ def multicodec_prefix(self) -> bytes: return self._prefix -class KeyTypeException(BaseException): - """Key type exception.""" - - # NOTE: the py_multicodec library is outdated. We use hardcoded prefixes here # until this PR gets released: https://github.com/multiformats/py-multicodec/pull/14 # multicodec is also not used now, but may be used again if py_multicodec is updated diff --git a/aries_cloudagent/wallet/routes.py b/aries_cloudagent/wallet/routes.py index bb6fa58236..b3c9ff80df 100644 --- a/aries_cloudagent/wallet/routes.py +++ b/aries_cloudagent/wallet/routes.py @@ -41,7 +41,7 @@ from .did_method import DIDMethod from .did_posture import DIDPosture from .error import WalletError, WalletNotFoundError -from .key_type import BLS12381G2, ED25519, KeyType +from .key_type import BLS12381G2, ED25519, KeyType, KeyTypes from .util import EVENT_LISTENER_PATTERN LOGGER = logging.getLogger(__name__) @@ -72,9 +72,7 @@ class DIDSchema(OpenAPISchema): key_type = fields.Str( description="Key type associated with the DID", example=ED25519.key_type, - validate=validate.OneOf( - [ED25519.key_type, BLS12381G2.key_type] - ), + validate=validate.OneOf([ED25519.key_type, BLS12381G2.key_type]), ) @@ -143,9 +141,7 @@ class DIDListQueryStringSchema(OpenAPISchema): key_type = fields.Str( required=False, example=ED25519.key_type, - validate=validate.OneOf( - [ED25519.key_type, BLS12381G2.key_type] - ), + validate=validate.OneOf([ED25519.key_type, BLS12381G2.key_type]), description="Key type to query for.", ) @@ -162,9 +158,7 @@ class DIDCreateOptionsSchema(OpenAPISchema): key_type = fields.Str( required=True, example=ED25519.key_type, - validate=validate.OneOf( - [ED25519.key_type, BLS12381G2.key_type] - ), + validate=validate.OneOf([ED25519.key_type, BLS12381G2.key_type]), ) @@ -246,9 +240,10 @@ async def wallet_did_list(request: web.BaseRequest): filter_verkey = request.query.get("verkey") filter_method = DIDMethod.from_method(request.query.get("method")) filter_posture = DIDPosture.get(request.query.get("posture")) - filter_key_type = KeyType.from_key_type(request.query.get("key_type")) results = [] async with context.session() as session: + key_types = session.inject(KeyTypes) + filter_key_type = key_types.from_key_type(request.query.get("key_type", "")) wallet = session.inject_or(BaseWallet) if not wallet: raise web.HTTPForbidden(reason="No wallet available") @@ -353,21 +348,26 @@ async def wallet_create_did(request: web.BaseRequest): body = {} # set default method and key type for backwards compat - key_type = KeyType.from_key_type(body.get("options", {}).get("key_type")) or ED25519 method = DIDMethod.from_method(body.get("method")) or DIDMethod.SOV - if not method.supports_key_type(key_type): - raise web.HTTPForbidden( - reason=( - f"method {method.method_name} does not" - f" support key type {key_type.key_type}" - ) - ) seed = body.get("seed") or None if seed and not context.settings.get("wallet.allow_insecure_seed"): raise web.HTTPBadRequest(reason="Seed support is not enabled") info = None async with context.session() as session: + key_types = session.inject(KeyTypes) + # set default method and key type for backwards compat + key_type = ( + key_types.from_key_type(body.get("options", {}).get("key_type", "")) + or ED25519 + ) + if not method.supports_key_type(key_type): + raise web.HTTPForbidden( + reason=( + f"method {method.method_name} does not" + f" support key type {key_type.key_type}" + ) + ) wallet = session.inject_or(BaseWallet) if not wallet: raise web.HTTPForbidden(reason="No wallet available") diff --git a/aries_cloudagent/wallet/tests/test_crypto.py b/aries_cloudagent/wallet/tests/test_crypto.py index f682e40673..43b0d03452 100644 --- a/aries_cloudagent/wallet/tests/test_crypto.py +++ b/aries_cloudagent/wallet/tests/test_crypto.py @@ -32,9 +32,7 @@ def test_validate_seed(self): def test_seeds_keys(self): assert len(test_module.seed_to_did(SEED)) in (22, 23) - (public_key, secret_key) = test_module.create_keypair( - test_module.ED25519 - ) + (public_key, secret_key) = test_module.create_keypair(test_module.ED25519) assert public_key assert secret_key @@ -161,9 +159,7 @@ def test_sign_ed25519_x_multiple_messages(self): def test_sign_x_unsupported_key_type(self): with self.assertRaises(WalletError) as context: - test_module.sign_message( - [b"message1", b"message2"], b"secret", BLS12381G1 - ) + test_module.sign_message([b"message1", b"message2"], b"secret", BLS12381G1) assert "Unsupported key type: bls12381g1" in str(context.exception) def test_verify_ed25519_x_multiple_messages(self): diff --git a/aries_cloudagent/wallet/tests/test_did_method.py b/aries_cloudagent/wallet/tests/test_did_method.py index 6bd147777f..a952844383 100644 --- a/aries_cloudagent/wallet/tests/test_did_method.py +++ b/aries_cloudagent/wallet/tests/test_did_method.py @@ -1,7 +1,7 @@ from unittest import TestCase -from ..key_type import BLS12381G1, BLS12381G1G2, BLS12381G2, ED25519, X25519, KeyType +from ..key_type import BLS12381G1, BLS12381G1G2, BLS12381G2, ED25519, X25519, KeyTypes ED25519_PREFIX_BYTES = b"\xed\x01" BLS12381G1_PREFIX_BYTES = b"\xea\x01" @@ -24,84 +24,72 @@ class TestKeyType(TestCase): def test_from_multicodec_name(self): - - assert KeyType.from_multicodec_name(ED25519_MULTICODEC_NAME) == ED25519 - assert KeyType.from_multicodec_name(X25519_MULTICODEC_NAME) == X25519 - assert ( - KeyType.from_multicodec_name(BLS12381G1_MULTICODEC_NAME) - == BLS12381G1 - ) - assert ( - KeyType.from_multicodec_name(BLS12381G2_MULTICODEC_NAME) - == BLS12381G2 - ) + key_types = KeyTypes() + assert key_types.from_multicodec_name(ED25519_MULTICODEC_NAME) == ED25519 + assert key_types.from_multicodec_name(X25519_MULTICODEC_NAME) == X25519 + assert key_types.from_multicodec_name(BLS12381G1_MULTICODEC_NAME) == BLS12381G1 + assert key_types.from_multicodec_name(BLS12381G2_MULTICODEC_NAME) == BLS12381G2 assert ( - KeyType.from_multicodec_name(BLS12381G1G2_MULTICODEC_NAME) - == BLS12381G1G2 + key_types.from_multicodec_name(BLS12381G1G2_MULTICODEC_NAME) == BLS12381G1G2 ) - assert KeyType.from_multicodec_name("non-existing") == None + assert key_types.from_multicodec_name("non-existing") == None def test_from_key_type(self): - - assert KeyType.from_key_type(ED25519_KEY_NAME) == ED25519 - assert KeyType.from_key_type(X25519_KEY_NAME) == X25519 - assert KeyType.from_key_type(BLS12381G1_KEY_NAME) == BLS12381G1 - assert KeyType.from_key_type(BLS12381G2_KEY_NAME) == BLS12381G2 - assert KeyType.from_key_type(BLS12381G1G2_KEY_NAME) == BLS12381G1G2 - assert KeyType.from_key_type("non-existing") == None + key_types = KeyTypes() + assert key_types.from_key_type(ED25519_KEY_NAME) == ED25519 + assert key_types.from_key_type(X25519_KEY_NAME) == X25519 + assert key_types.from_key_type(BLS12381G1_KEY_NAME) == BLS12381G1 + assert key_types.from_key_type(BLS12381G2_KEY_NAME) == BLS12381G2 + assert key_types.from_key_type(BLS12381G1G2_KEY_NAME) == BLS12381G1G2 + assert key_types.from_key_type("non-existing") == None def test_from_multicodec_prefix(self): - - assert KeyType.from_multicodec_prefix(ED25519_PREFIX_BYTES) == ED25519 - assert KeyType.from_multicodec_prefix(X25519_PREFIX_BYTES) == X25519 + key_types = KeyTypes() + assert key_types.from_multicodec_prefix(ED25519_PREFIX_BYTES) == ED25519 + assert key_types.from_multicodec_prefix(X25519_PREFIX_BYTES) == X25519 + assert key_types.from_multicodec_prefix(BLS12381G1_PREFIX_BYTES) == BLS12381G1 + assert key_types.from_multicodec_prefix(BLS12381G2_PREFIX_BYTES) == BLS12381G2 assert ( - KeyType.from_multicodec_prefix(BLS12381G1_PREFIX_BYTES) - == BLS12381G1 + key_types.from_multicodec_prefix(BLS12381G1G2_PREFIX_BYTES) == BLS12381G1G2 ) - assert ( - KeyType.from_multicodec_prefix(BLS12381G2_PREFIX_BYTES) - == BLS12381G2 - ) - assert ( - KeyType.from_multicodec_prefix(BLS12381G1G2_PREFIX_BYTES) - == BLS12381G1G2 - ) - assert KeyType.from_multicodec_prefix(b"\xef\x01") == None + assert key_types.from_multicodec_prefix(b"\xef\x01") == None def test_from_prefixed_bytes(self): - + key_types = KeyTypes() assert ( - KeyType.from_prefixed_bytes( + key_types.from_prefixed_bytes( b"".join([ED25519_PREFIX_BYTES, b"random-bytes"]) ) == ED25519 ) assert ( - KeyType.from_prefixed_bytes( + key_types.from_prefixed_bytes( b"".join([X25519_PREFIX_BYTES, b"random-bytes"]) ) == X25519 ) assert ( - KeyType.from_prefixed_bytes( + key_types.from_prefixed_bytes( b"".join([BLS12381G1_PREFIX_BYTES, b"random-bytes"]) ) == BLS12381G1 ) assert ( - KeyType.from_prefixed_bytes( + key_types.from_prefixed_bytes( b"".join([BLS12381G2_PREFIX_BYTES, b"random-bytes"]) ) == BLS12381G2 ) assert ( - KeyType.from_prefixed_bytes( + key_types.from_prefixed_bytes( b"".join([BLS12381G1G2_PREFIX_BYTES, b"random-bytes"]) ) == BLS12381G1G2 ) assert ( - KeyType.from_prefixed_bytes(b"".join([b"\xef\x01", b"other-random-bytes"])) + key_types.from_prefixed_bytes( + b"".join([b"\xef\x01", b"other-random-bytes"]) + ) == None ) diff --git a/aries_cloudagent/wallet/tests/test_in_memory_wallet.py b/aries_cloudagent/wallet/tests/test_in_memory_wallet.py index fd550cbf1f..bfcf2e0581 100644 --- a/aries_cloudagent/wallet/tests/test_in_memory_wallet.py +++ b/aries_cloudagent/wallet/tests/test_in_memory_wallet.py @@ -141,9 +141,7 @@ async def test_create_local_key_random_ed25519(self, wallet: InMemoryWallet): @pytest.mark.asyncio @pytest.mark.ursa_bbs_signatures async def test_create_local_key_random_bls12381g2(self, wallet: InMemoryWallet): - info = await wallet.create_local_did( - DIDMethod.KEY, BLS12381G2, None, None - ) + info = await wallet.create_local_did(DIDMethod.KEY, BLS12381G2, None, None) assert info and info.did and info.verkey @pytest.mark.asyncio @@ -179,9 +177,7 @@ async def test_create_local_key_seeded_bls12381g2(self, wallet: InMemoryWallet): assert info.verkey == self.test_bls12381g2_verkey # should not raise WalletDuplicateError - same verkey - await wallet.create_local_did( - DIDMethod.KEY, BLS12381G2, self.test_seed, None - ) + await wallet.create_local_did(DIDMethod.KEY, BLS12381G2, self.test_seed, None) with pytest.raises(WalletError): _ = await wallet.create_local_did( @@ -484,9 +480,7 @@ async def test_sign_verify(self, wallet: InMemoryWallet): @pytest.mark.asyncio @pytest.mark.ursa_bbs_signatures async def test_sign_verify_bbs(self, wallet: InMemoryWallet): - info = await wallet.create_local_did( - DIDMethod.KEY, BLS12381G2, self.test_seed - ) + info = await wallet.create_local_did(DIDMethod.KEY, BLS12381G2, self.test_seed) message_bin = self.test_message.encode("ascii") signature = await wallet.sign_message(message_bin, info.verkey) assert signature @@ -524,21 +518,15 @@ async def test_sign_verify_bbs(self, wallet: InMemoryWallet): assert "Verkey not provided" in str(excinfo.value) with pytest.raises(WalletError) as excinfo: - await wallet.verify_message( - message_bin, signature, None, BLS12381G2 - ) + await wallet.verify_message(message_bin, signature, None, BLS12381G2) assert "Verkey not provided" in str(excinfo.value) with pytest.raises(WalletError) as excinfo: - await wallet.verify_message( - message_bin, None, info.verkey, BLS12381G2 - ) + await wallet.verify_message(message_bin, None, info.verkey, BLS12381G2) assert "Signature not provided" in str(excinfo.value) with pytest.raises(WalletError) as excinfo: - await wallet.verify_message( - None, message_bin, info.verkey, BLS12381G2 - ) + await wallet.verify_message(None, message_bin, info.verkey, BLS12381G2) assert "Message not provided" in str(excinfo.value) @pytest.mark.asyncio From b4a6fd94088f1bfed3a0f369f284bb09407cb51a Mon Sep 17 00:00:00 2001 From: Adam Burdett Date: Thu, 22 Sep 2022 17:05:26 -0600 Subject: [PATCH 497/872] some passing tests Signed-off-by: Adam Burdett --- .../issue_credential/v2_0/formats/ld_proof/handler.py | 7 +++---- .../v2_0/formats/ld_proof/tests/test_handler.py | 4 ++-- .../present_proof/dif/tests/test_pres_exch_handler.py | 2 ++ .../vc/ld_proofs/crypto/tests/test_wallet_key_pair.py | 2 ++ .../ld_proofs/suites/tests/test_bbs_bls_signature_2020.py | 2 ++ .../suites/tests/test_bbs_bls_signature_proof_2020.py | 2 ++ .../ld_proofs/suites/tests/test_ed25519_signature_2018.py | 2 ++ aries_cloudagent/wallet/routes.py | 2 +- aries_cloudagent/wallet/tests/test_in_memory_wallet.py | 2 +- 9 files changed, 17 insertions(+), 8 deletions(-) diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py index 9863f018d0..2f3c8f7985 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py @@ -1,5 +1,6 @@ """V2.0 issue-credential linked data proof credential format handler.""" + from ......vc.ld_proofs.error import LinkedDataProofException from ......vc.ld_proofs.check import get_properties_without_context import logging @@ -73,10 +74,8 @@ SIGNATURE_SUITE_KEY_TYPE_MAPPING[BbsBlsSignature2020] = BLS12381G2 -PROOF_TYPE_SIGNATURE_SUITE_MAPPING = { - suite.signature_type: suite - for suite, key_type in SIGNATURE_SUITE_KEY_TYPE_MAPPING.items() -} +PROOF_TYPE_SIGNATURE_SUITE_MAPPING = {suite.signature_type: suite for suite in SIGNATURE_SUITE_KEY_TYPE_MAPPING} + KEY_TYPE_SIGNATURE_SUITE_MAPPING = { key_type: suite for suite, key_type in SIGNATURE_SUITE_KEY_TYPE_MAPPING.items() diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/tests/test_handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/tests/test_handler.py index 8283379059..8417b2f405 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/tests/test_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/tests/test_handler.py @@ -26,7 +26,7 @@ ) from .......vc.ld_proofs.constants import SECURITY_CONTEXT_BBS_URL from .......vc.tests.document_loader import custom_document_loader -from .......wallet.key_type import BLS12381G1G2, ED25519 +from .......wallet.key_type import BLS12381G1G2, BLS12381G2, ED25519 from .......wallet.error import WalletNotFoundError from .......wallet.did_method import DIDMethod from .......wallet.base import BaseWallet @@ -230,7 +230,7 @@ async def test_assert_can_issue_with_id_and_proof_type(self): verkey="verkey", metadata={}, method=DIDMethod.SOV, - key_type=BLS12381G1G2, + key_type=BLS12381G2, ) mock_did_info.return_value = invalid_did_info with self.assertRaises(V20CredFormatError) as context: diff --git a/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py b/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py index 110252fd97..78be6ffab7 100644 --- a/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py +++ b/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py @@ -6,6 +6,8 @@ import mock as async_mock import pytest +from aries_cloudagent.wallet.key_type import BLS12381G2, ED25519 + from .....core.in_memory import InMemoryProfile from .....did.did_key import DIDKey from .....resolver.did_resolver import DIDResolver diff --git a/aries_cloudagent/vc/ld_proofs/crypto/tests/test_wallet_key_pair.py b/aries_cloudagent/vc/ld_proofs/crypto/tests/test_wallet_key_pair.py index 4ae0a83753..6c82afad01 100644 --- a/aries_cloudagent/vc/ld_proofs/crypto/tests/test_wallet_key_pair.py +++ b/aries_cloudagent/vc/ld_proofs/crypto/tests/test_wallet_key_pair.py @@ -1,5 +1,7 @@ from asynctest import TestCase, mock as async_mock +from aries_cloudagent.wallet.key_type import ED25519 + from .....wallet.key_pair import KeyType from ...error import LinkedDataProofException diff --git a/aries_cloudagent/vc/ld_proofs/suites/tests/test_bbs_bls_signature_2020.py b/aries_cloudagent/vc/ld_proofs/suites/tests/test_bbs_bls_signature_2020.py index de3efe4475..bb3bc4d523 100644 --- a/aries_cloudagent/vc/ld_proofs/suites/tests/test_bbs_bls_signature_2020.py +++ b/aries_cloudagent/vc/ld_proofs/suites/tests/test_bbs_bls_signature_2020.py @@ -1,6 +1,8 @@ from asynctest import TestCase, mock as async_mock import pytest +from aries_cloudagent.wallet.key_type import BLS12381G2 + from .....did.did_key import DIDKey from .....wallet.key_pair import KeyType from .....wallet.in_memory import InMemoryWallet diff --git a/aries_cloudagent/vc/ld_proofs/suites/tests/test_bbs_bls_signature_proof_2020.py b/aries_cloudagent/vc/ld_proofs/suites/tests/test_bbs_bls_signature_proof_2020.py index e60f923983..67d027d770 100644 --- a/aries_cloudagent/vc/ld_proofs/suites/tests/test_bbs_bls_signature_proof_2020.py +++ b/aries_cloudagent/vc/ld_proofs/suites/tests/test_bbs_bls_signature_proof_2020.py @@ -1,6 +1,8 @@ from asynctest import TestCase, mock as async_mock import pytest +from aries_cloudagent.wallet.key_type import BLS12381G2 + from .....did.did_key import DIDKey from .....wallet.key_pair import KeyType from .....wallet.in_memory import InMemoryWallet diff --git a/aries_cloudagent/vc/ld_proofs/suites/tests/test_ed25519_signature_2018.py b/aries_cloudagent/vc/ld_proofs/suites/tests/test_ed25519_signature_2018.py index 46e9b02558..60bf0389f6 100644 --- a/aries_cloudagent/vc/ld_proofs/suites/tests/test_ed25519_signature_2018.py +++ b/aries_cloudagent/vc/ld_proofs/suites/tests/test_ed25519_signature_2018.py @@ -1,5 +1,7 @@ from asynctest import TestCase +from aries_cloudagent.wallet.key_type import ED25519 + from .....did.did_key import DIDKey from .....wallet.key_pair import KeyType diff --git a/aries_cloudagent/wallet/routes.py b/aries_cloudagent/wallet/routes.py index b3c9ff80df..fad1efd2fc 100644 --- a/aries_cloudagent/wallet/routes.py +++ b/aries_cloudagent/wallet/routes.py @@ -41,7 +41,7 @@ from .did_method import DIDMethod from .did_posture import DIDPosture from .error import WalletError, WalletNotFoundError -from .key_type import BLS12381G2, ED25519, KeyType, KeyTypes +from .key_type import BLS12381G2, ED25519, KeyTypes from .util import EVENT_LISTENER_PATTERN LOGGER = logging.getLogger(__name__) diff --git a/aries_cloudagent/wallet/tests/test_in_memory_wallet.py b/aries_cloudagent/wallet/tests/test_in_memory_wallet.py index bfcf2e0581..b5a68d5207 100644 --- a/aries_cloudagent/wallet/tests/test_in_memory_wallet.py +++ b/aries_cloudagent/wallet/tests/test_in_memory_wallet.py @@ -4,7 +4,7 @@ from ...core.in_memory import InMemoryProfile from ...messaging.decorators.signature_decorator import SignatureDecorator from ...wallet.in_memory import InMemoryWallet -from ...wallet.key_type import BLS12381G2, ED25519 +from ...wallet.key_type import BLS12381G1, BLS12381G1G2, BLS12381G2, ED25519, X25519 from ...wallet.did_method import DIDMethod from ...wallet.error import ( WalletError, From 0c3382372c1fa4c71241c17a490ae50a2615ad7a Mon Sep 17 00:00:00 2001 From: Adam Burdett Date: Thu, 22 Sep 2022 17:11:02 -0600 Subject: [PATCH 498/872] passing unit tests Signed-off-by: Adam Burdett --- aries_cloudagent/wallet/tests/test_routes.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/wallet/tests/test_routes.py b/aries_cloudagent/wallet/tests/test_routes.py index c657a99780..103a68d0b3 100644 --- a/aries_cloudagent/wallet/tests/test_routes.py +++ b/aries_cloudagent/wallet/tests/test_routes.py @@ -9,7 +9,7 @@ from ...ledger.base import BaseLedger from ...protocols.coordinate_mediation.v1_0.route_manager import RouteManager from ...wallet.did_method import DIDMethod -from ...wallet.key_type import ED25519 +from ...wallet.key_type import ED25519, KeyTypes from ..base import BaseWallet from ..did_info import DIDInfo from ..did_posture import DIDPosture @@ -23,6 +23,7 @@ def setUp(self): self.context = AdminRequestContext.test_context( self.session_inject, self.profile ) + self.context.injector.bind_instance(KeyTypes,KeyTypes()) self.request_dict = { "context": self.context, "outbound_message_router": async_mock.AsyncMock(), From 4764863959a405d10883c25c52a9cc1359816c12 Mon Sep 17 00:00:00 2001 From: Adam Burdett Date: Thu, 22 Sep 2022 17:11:46 -0600 Subject: [PATCH 499/872] formatting Signed-off-by: Adam Burdett --- .../issue_credential/v2_0/formats/ld_proof/handler.py | 4 +++- aries_cloudagent/wallet/tests/test_routes.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py index 2f3c8f7985..b67f9b7def 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py @@ -74,7 +74,9 @@ SIGNATURE_SUITE_KEY_TYPE_MAPPING[BbsBlsSignature2020] = BLS12381G2 -PROOF_TYPE_SIGNATURE_SUITE_MAPPING = {suite.signature_type: suite for suite in SIGNATURE_SUITE_KEY_TYPE_MAPPING} +PROOF_TYPE_SIGNATURE_SUITE_MAPPING = { + suite.signature_type: suite for suite in SIGNATURE_SUITE_KEY_TYPE_MAPPING +} KEY_TYPE_SIGNATURE_SUITE_MAPPING = { diff --git a/aries_cloudagent/wallet/tests/test_routes.py b/aries_cloudagent/wallet/tests/test_routes.py index 103a68d0b3..1cfd36e773 100644 --- a/aries_cloudagent/wallet/tests/test_routes.py +++ b/aries_cloudagent/wallet/tests/test_routes.py @@ -23,7 +23,7 @@ def setUp(self): self.context = AdminRequestContext.test_context( self.session_inject, self.profile ) - self.context.injector.bind_instance(KeyTypes,KeyTypes()) + self.context.injector.bind_instance(KeyTypes, KeyTypes()) self.request_dict = { "context": self.context, "outbound_message_router": async_mock.AsyncMock(), From 8d332a861c3c9095ed15eb0d79235ec66dbc4efd Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Fri, 23 Sep 2022 13:39:02 -0700 Subject: [PATCH 500/872] update test_dispatcher to use mock, asyncMock Signed-off-by: Shaanjot Gill --- .../core/tests/test_dispatcher.py | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/aries_cloudagent/core/tests/test_dispatcher.py b/aries_cloudagent/core/tests/test_dispatcher.py index 023665c0db..ffca07ce14 100644 --- a/aries_cloudagent/core/tests/test_dispatcher.py +++ b/aries_cloudagent/core/tests/test_dispatcher.py @@ -1,7 +1,7 @@ import json from async_case import IsolatedAsyncioTestCase -from asynctest import mock as async_mock +import mock as async_mock import pytest from marshmallow import EXCLUDE @@ -114,14 +114,14 @@ async def test_dispatch(self): ) as conn_mgr_mock, async_mock.patch.object( test_module, "get_version_from_message_type", - async_mock.CoroutineMock(return_value="1.1"), + async_mock.AsyncMock(return_value="1.1"), ), async_mock.patch.object( test_module, "validate_get_response_version", - async_mock.CoroutineMock(return_value=("1.1", None)), + async_mock.AsyncMock(return_value=("1.1", None)), ): conn_mgr_mock.return_value = async_mock.MagicMock( - find_inbound_connection=async_mock.CoroutineMock( + find_inbound_connection=async_mock.AsyncMock( return_value=async_mock.MagicMock(connection_id="dummy") ) ) @@ -163,11 +163,11 @@ async def test_dispatch_versioned_message(self): ) as handler_mock, async_mock.patch.object( test_module, "get_version_from_message_type", - async_mock.CoroutineMock(return_value="1.1"), + async_mock.AsyncMock(return_value="1.1"), ), async_mock.patch.object( test_module, "validate_get_response_version", - async_mock.CoroutineMock(return_value=("1.1", None)), + async_mock.AsyncMock(return_value=("1.1", None)), ): await dispatcher.queue_message( dispatcher.profile, make_inbound(message), rcv.send @@ -284,11 +284,11 @@ async def test_dispatch_versioned_message_handle_greater_succeeds(self): ) as handler_mock, async_mock.patch.object( test_module, "get_version_from_message_type", - async_mock.CoroutineMock(return_value="1.1"), + async_mock.AsyncMock(return_value="1.1"), ), async_mock.patch.object( test_module, "validate_get_response_version", - async_mock.CoroutineMock(return_value=("1.1", None)), + async_mock.AsyncMock(return_value=("1.1", None)), ): await dispatcher.queue_message( dispatcher.profile, make_inbound(message), rcv.send @@ -342,9 +342,9 @@ async def test_bad_message_dispatch_parse_x(self): rcv = Receiver() bad_messages = ["not even a dict", {"bad": "message"}] with async_mock.patch.object( - test_module, "get_version_from_message_type", async_mock.CoroutineMock() + test_module, "get_version_from_message_type", async_mock.AsyncMock() ), async_mock.patch.object( - test_module, "validate_get_response_version", async_mock.CoroutineMock() + test_module, "validate_get_response_version", async_mock.AsyncMock() ): for bad in bad_messages: await dispatcher.queue_message( @@ -416,7 +416,7 @@ async def test_create_send_outbound(self): message = StubAgentMessage() responder = test_module.DispatcherResponder(context, message, None) outbound_message = await responder.create_outbound(message) - with async_mock.patch.object(responder, "_send", async_mock.CoroutineMock()): + with async_mock.patch.object(responder, "_send", async_mock.AsyncMock()): await responder.send_outbound(outbound_message) async def test_create_send_webhook(self): @@ -433,7 +433,7 @@ async def test_create_enc_outbound(self): message = b"abc123xyz7890000" responder = test_module.DispatcherResponder(context, message, None) with async_mock.patch.object( - responder, "send_outbound", async_mock.CoroutineMock() + responder, "send_outbound", async_mock.AsyncMock() ) as mock_send_outbound: await responder.send(message) assert mock_send_outbound.called_once() @@ -474,11 +474,11 @@ def _smaller_scope(): # with async_mock.patch.object( # test_module, # "get_version_from_message_type", - # async_mock.CoroutineMock(return_value="1.1"), + # async_mock.AsyncMock(return_value="1.1"), # ), async_mock.patch.object( # test_module, # "validate_get_response_version", - # async_mock.CoroutineMock(return_value=("1.1", "fields-ignored-due-to-version-mismatch")), + # async_mock.AsyncMock(return_value=("1.1", "fields-ignored-due-to-version-mismatch")), # ): # await dispatcher.queue_message( # dispatcher.profile, make_inbound(message), rcv.send @@ -503,11 +503,11 @@ def _smaller_scope(): # with async_mock.patch.object( # test_module, # "get_version_from_message_type", - # async_mock.CoroutineMock(return_value="1.1"), + # async_mock.AsyncMock(return_value="1.1"), # ), async_mock.patch.object( # test_module, # "validate_get_response_version", - # async_mock.CoroutineMock(return_value=("1.1", "version-with-degraded-features")), + # async_mock.AsyncMock(return_value=("1.1", "version-with-degraded-features")), # ): # await dispatcher.queue_message( # dispatcher.profile, make_inbound(message), rcv.send @@ -532,11 +532,11 @@ def _smaller_scope(): # with async_mock.patch.object( # test_module, # "get_version_from_message_type", - # async_mock.CoroutineMock(return_value="1.1"), + # async_mock.AsyncMock(return_value="1.1"), # ), async_mock.patch.object( # test_module, # "validate_get_response_version", - # async_mock.CoroutineMock(return_value=("1.1", "version-not-supported")), + # async_mock.AsyncMock(return_value=("1.1", "version-not-supported")), # ): # with self.assertRaises(test_module.MessageParseError): # await dispatcher.queue_message( From 82c3e8c6742690e206568eab46203f4a1ab6a5c5 Mon Sep 17 00:00:00 2001 From: Anastasiia Date: Sun, 25 Sep 2022 13:47:22 +0200 Subject: [PATCH 501/872] Removed duplication Signed-off-by: Anastasiia --- .../v2_0/formats/dif/tests/test_handler.py | 89 +------------------ .../present_proof/v2_0/tests/test_manager.py | 48 ---------- .../present_proof/v2_0/tests/test_routes.py | 61 ------------- 3 files changed, 2 insertions(+), 196 deletions(-) diff --git a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py index b2f0393bd6..003cec94fe 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py @@ -179,7 +179,7 @@ }, { "id": "citizenship_input_2", - "name": "EU Driver's License", + "name": "EU Driver's License v2", "group": ["B"], "schema": [ {"uri": "https://www.w3.org/2018/credentials#VerifiableCredential"}, @@ -283,55 +283,7 @@ }, } -DIF_PRES_SEQUENCE = [{ - "@context": ["https://www.w3.org/2018/credentials/v1"], - "type": ["VerifiablePresentation"], - "verifiableCredential": [ - { - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://w3id.org/citizenship/v1", - "https://w3id.org/security/bbs/v1", - ], - "id": "https://issuer.oidp.uscis.gov/credentials/83627465", - "type": ["PermanentResidentCard", "VerifiableCredential"], - "credentialSubject": { - "id": "did:example:b34ca6cd37bbf23", - "type": ["Person", "PermanentResident"], - "givenName": "JOHN", - }, - "issuanceDate": "2010-01-01T19:53:24Z", - "issuer": "did:key:zUC74bgefTdc43KS1psXgXf4jLaHyaj2qCQqQTXrtmSYGf1PxiJhrH6LGpaBMyj6tqAKmjGyMaS4RfNo2an77vT1HfzJUNPk4H7TCuJvSp4vet4Cu67kn2JSegoQNFSA1tbwU8v", - "proof": { - "type": "BbsBlsSignatureProof2020", - "nonce": "3AuruhJQrXtEgiagiJ+FwVf2S0SnzUDJvnO61YecQsJ7ImR1mPcoVjJJ0HOhfkFpoYI=", - "proofValue": "ABkBuAaPlP5A7JWY78Xf69oBnsMLcD1RXbIFYhcLoXPXW12CG9glnnqnPLsGri5xsA3LcP0kg74X+sAjKXGRGy3uvp412Dm0FuohYNboQcLne5KOAa5AxU4bjmwQsxdfduVqhriro1N+YTkuB4SMmO/5ooL0N3OHsYdExg7nSzWqmZoqgp+3CwIxF0a/oyKTcxJORuIqAAAAdInlL9teSIX49NJGEZfBO7IrdjT2iggH/G0AlPWoEvrWIbuCRQ69K83n5o7oJVjqhAAAAAIaVmlAD6+FEKA4eg0OaWOKPrd5Kq8rv0vIwjJ71egxll0Fqq4zDWQ/+yl3Pteh0Wyuyvpm19/sj6tiCWj4PkA+rpxtR2bXpnrCTKUffFFNBjVvVziXDS0KWkGUB7XU9mjUa4USC7Iub3bZZCnFjQA5AAAADzkGwGD837r33e7OTrGEti8eAkvFDcyCgA4ck/X+5HJjAJclHWbl4SNQR8CiNZyzJpvxW+jbNBcwmEvocYArddk3F78Ki0Qnp6aU9eDgfOOx1iW2BXLUjrhq5I2hP5/WQF3CEDYRjczGjzM9T8/coeC36YAp0zJunIXUKb8SPDSOISafibYRYFB4xhlWKXWloDelafyujOBST8KZNM8FmF4DSbXrO8vmZbjuR/8ntUcUK7X2rNbuZ3M5eWZDF8pL+SA9gQitKfPHEocoYAdhgEAM7ZNAJ+TgOcx9gtZIhDWKDNnFxIeoOAylbD1xZd9xbWtq3Bk3R79xqsKxFRJRNxk/9b6fJruP292+qM5lxcZ1jUz/dJUYFI93hH4Mso75CjGRN78MAY9SNifl6H8qcxTpBn4332LlFhRznLbtnc4YSWA/fvVqaN9h2zCH/6AdbLKXGffV34EF7DadwJsi9jsc+YlSMn6qaIUIDTdGLwh4KKpSH5bVbg/mVCcXPTJplFgYwRsOdiQbZY/740dJyo1lPjQ0Lvdio8W2M8c73ujeJU70CNLkgjJAMUPGrCFtGxBH2eeLBQ0P95qRZAIcJ7U0MibZLaRjoUOuTla5BIt2038PJ6XhcY6BEJaLyJOPEQ==", - "verificationMethod": "did:key:zUC74bgefTdc43KS1psXgXf4jLaHyaj2qCQqQTXrtmSYGf1PxiJhrH6LGpaBMyj6tqAKmjGyMaS4RfNo2an77vT1HfzJUNPk4H7TCuJvSp4vet4Cu67kn2JSegoQNFSA1tbwU8v#zUC74bgefTdc43KS1psXgXf4jLaHyaj2qCQqQTXrtmSYGf1PxiJhrH6LGpaBMyj6tqAKmjGyMaS4RfNo2an77vT1HfzJUNPk4H7TCuJvSp4vet4Cu67kn2JSegoQNFSA1tbwU8v", - "proofPurpose": "assertionMethod", - "created": "2021-05-05T15:22:30.523465", - }, - } - ], - "presentation_submission": { - "id": "a5fcfe44-2c30-497d-af02-98e539da9a0f", - "definition_id": "32f54163-7166-48f1-93d8-ff217bdb0653", - "descriptor_map": [ - { - "id": "citizenship_input_1", - "format": "ldp_vp", - "path": "$.verifiableCredential[0]", - } - ], - }, - "proof": { - "type": "Ed25519Signature2018", - "verificationMethod": "did:sov:4QxzWk3ajdnEA37NdNU5Kt#key-1", - "created": "2021-05-05T15:23:03.023971", - "proofPurpose": "authentication", - "challenge": "40429d49-5e8f-4ffc-baf8-e332412f1247", - "jws": "eyJhbGciOiAiRWREU0EiLCAiYjY0IjogZmFsc2UsICJjcml0IjogWyJiNjQiXX0..2uBYmg7muE9ZPVeAGo_ibVfLkCjf2hGshr2o5i8pAwFyNBM-kDHXofuq1MzJgb19wzb01VIu91hY_ajjt9KFAA", - }, -}, +DIF_PRES_SEQUENCE = [DIF_PRES, { "@context": ["https://www.w3.org/2018/credentials/v1"], "type": ["VerifiablePresentation"], @@ -1217,43 +1169,6 @@ async def test_verify_pres_sequence(self): output = await self.handler.verify_pres(record) assert output.verified - async def test_verify_pres_sequence_one_pres_false(self): - dif_pres = V20Pres( - formats=[ - V20PresFormat( - attach_id="dif", - format_=ATTACHMENT_FORMAT[PRES_20][V20PresFormat.Format.DIF.api], - ) - ], - presentations_attach=[AttachDecorator.data_json([DIF_PRES, {}], ident="dif")], - ) - dif_pres_request = V20PresRequest( - formats=[ - V20PresFormat( - attach_id="dif", - format_=ATTACHMENT_FORMAT[PRES_20_REQUEST][ - V20PresFormat.Format.DIF.api - ], - ) - ], - request_presentations_attach=[ - AttachDecorator.data_json(DIF_PRES_REQUEST_SEQUENCE, ident="dif") - ], - ) - record = V20PresExRecord( - pres_ex_id="pxid", - thread_id="thid", - connection_id="conn_id", - initiator="init", - role="role", - state="state", - pres_request=dif_pres_request, - pres=dif_pres, - verified="false", - auto_present=True, - error_msg="error", - ) - with async_mock.patch.object( test_module, "verify_presentation", diff --git a/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py b/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py index b31428f22c..4c25dd609e 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py @@ -2188,53 +2188,6 @@ async def test_verify_pres_indy_and_dif(self): assert px_rec_out.state == (V20PresExRecord.STATE_DONE) - async def test_verify_pres_indy_and_dif_false(self): - pres_request = V20PresRequest( - formats=[ - V20PresFormat( - attach_id="indy", - format_=ATTACHMENT_FORMAT[PRES_20_REQUEST][ - V20PresFormat.Format.INDY.api - ], - ), - V20PresFormat( - attach_id="dif", - format_=ATTACHMENT_FORMAT[PRES_20_REQUEST][ - V20PresFormat.Format.DIF.api - ], - ) - ], - will_confirm=True, - request_presentations_attach=[ - AttachDecorator.data_base64(INDY_PROOF_REQ_NAME, ident="indy"), - AttachDecorator.data_json(DIF_PRES_REQ_ALT, ident="dif") - ], - ) - pres = V20Pres( - formats=[ - V20PresFormat( - attach_id="indy", - format_=ATTACHMENT_FORMAT[PRES_20][V20PresFormat.Format.INDY.api], - ), - V20PresFormat( - attach_id="dif", - format_=ATTACHMENT_FORMAT[PRES_20][V20PresFormat.Format.DIF.api], - ) - ], - presentations_attach=[ - AttachDecorator.data_base64(INDY_PROOF, ident="indy"), - AttachDecorator.data_json(DIF_PRES, ident="dif") - ], - ) - px_rec_in = V20PresExRecord( - pres_request=pres_request, - pres=pres, - ) - self.profile.context.injector.bind_instance(DocumentLoader, custom_document_loader) - self.profile.context.injector.bind_instance( - BaseMultitenantManager, - async_mock.MagicMock(MultitenantManager, autospec=True), - ) with async_mock.patch.object( IndyLedgerRequestsExecutor, "get_ledger_for_identifier", @@ -2255,7 +2208,6 @@ async def test_verify_pres_indy_and_dif_false(self): ) as save_ex: px_rec_out = await self.manager.verify_pres(px_rec_in) save_ex.assert_called_once() - assert px_rec_out.state == (V20PresExRecord.STATE_DONE) assert px_rec_out.verified == 'false' diff --git a/aries_cloudagent/protocols/present_proof/v2_0/tests/test_routes.py b/aries_cloudagent/protocols/present_proof/v2_0/tests/test_routes.py index 704c3c2751..0d13acc826 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/tests/test_routes.py @@ -1842,67 +1842,6 @@ async def test_present_proof_send_presentation_dif(self): mock_px_rec_inst.serialize.return_value ) - async def test_present_proof_send_presentation_indy_and_dif(self): - proof_req = deepcopy(DIF_PROOF_REQ) - proof_req["issuer_id"] = "test123" - self.request.json = async_mock.CoroutineMock( - return_value={ - "dif": proof_req, - "indy": { - "comment": "dummy", - "self_attested_attributes": {}, - "requested_attributes": {}, - "requested_predicates": {}, - } - } - ) - self.request.match_info = { - "pres_ex_id": "dummy", - } - self.profile.context.injector.bind_instance( - IndyVerifier, - async_mock.MagicMock( - verify_presentation=async_mock.CoroutineMock(), - ), - ) - - with async_mock.patch.object( - test_module, "ConnRecord", autospec=True - ) as mock_conn_rec_cls, async_mock.patch.object( - test_module, "V20PresManager", autospec=True - ) as mock_pres_mgr_cls, async_mock.patch.object( - test_module, "V20PresExRecord", autospec=True - ) as mock_px_rec_cls, async_mock.patch.object( - test_module.web, "json_response" - ) as mock_response: - mock_px_rec_inst = async_mock.MagicMock( - connection_id="dummy", - state=test_module.V20PresExRecord.STATE_REQUEST_RECEIVED, - serialize=async_mock.MagicMock( - return_value={"thread_id": "sample-thread-id"} - ), - ) - mock_px_rec_cls.retrieve_by_id = async_mock.CoroutineMock( - return_value=mock_px_rec_inst - ) - - mock_conn_rec_inst = async_mock.MagicMock(is_ready=True) - mock_conn_rec_cls.retrieve_by_id = async_mock.CoroutineMock( - return_value=mock_conn_rec_inst - ) - - mock_pres_mgr_inst = async_mock.MagicMock( - create_pres=async_mock.CoroutineMock( - return_value=(mock_px_rec_inst, async_mock.MagicMock()) - ) - ) - mock_pres_mgr_cls.return_value = mock_pres_mgr_inst - - await test_module.present_proof_send_presentation(self.request) - mock_response.assert_called_once_with( - mock_px_rec_inst.serialize.return_value - ) - async def test_present_proof_send_presentation_dif_error(self): self.request.json = async_mock.CoroutineMock( return_value={"dif": DIF_PROOF_REQ} From 43d08f36128137aadfa9e5b9ca6370c4f1f951ef Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Mon, 26 Sep 2022 10:55:16 -0400 Subject: [PATCH 502/872] test: base wallet route manager and recip to conn Signed-off-by: Daniel Bluhm --- .../multitenant/tests/test_route_manager.py | 14 ++++++-------- .../v1_0/tests/test_route_manager.py | 2 +- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/aries_cloudagent/multitenant/tests/test_route_manager.py b/aries_cloudagent/multitenant/tests/test_route_manager.py index ddcc528a2b..200bc62a7b 100644 --- a/aries_cloudagent/multitenant/tests/test_route_manager.py +++ b/aries_cloudagent/multitenant/tests/test_route_manager.py @@ -58,8 +58,8 @@ def route_manager(root_profile: Profile, sub_profile: Profile, wallet_id: str): @pytest.fixture -def base_route_manager(root_profile: Profile, sub_profile: Profile, wallet_id: str): - yield BaseWalletRouteManager(root_profile) +def base_route_manager(): + yield BaseWalletRouteManager() @pytest.mark.asyncio @@ -371,17 +371,15 @@ async def test_routing_info_with_base_mediator_and_sub_mediator( @pytest.mark.asyncio async def test_connection_from_recipient_key( - sub_profile: Profile, base_route_manager: MultitenantRouteManager + sub_profile: Profile, base_route_manager: BaseWalletRouteManager ): manager = mock.MagicMock() manager.get_profile_for_key = mock.CoroutineMock(return_value=sub_profile) - route_manager.root_profile.context.injector.bind_instance( - BaseMultitenantManager, manager - ) + sub_profile.context.injector.bind_instance(BaseMultitenantManager, manager) with mock.patch.object( RouteManager, "connection_from_recipient_key", mock.CoroutineMock() ) as mock_conn_for_recip: - result = await route_manager.connection_from_recipient_key( - route_manager.root_profile, TEST_VERKEY + result = await base_route_manager.connection_from_recipient_key( + sub_profile, TEST_VERKEY ) assert result == mock_conn_for_recip.return_value diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_route_manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_route_manager.py index bf5931c42e..fd8eedc05e 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_route_manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_route_manager.py @@ -487,7 +487,7 @@ async def test_connection_from_recipient_key_invite( ): with mock.patch.object( ConnRecord, - "retrieve_by_invitation_key", + "retrieve_by_tag_filter", mock.CoroutineMock(return_value=conn_record), ): result = await route_manager.connection_from_recipient_key(profile, TEST_VERKEY) From 4065c7842c73d5d1babec476a5683d8ca3e33006 Mon Sep 17 00:00:00 2001 From: Adam Burdett Date: Mon, 26 Sep 2022 11:44:12 -0600 Subject: [PATCH 503/872] move load did entry for registry injection Signed-off-by: Adam Burdett --- aries_cloudagent/wallet/askar.py | 35 ++++++++++++++++---------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/aries_cloudagent/wallet/askar.py b/aries_cloudagent/wallet/askar.py index b8b26bee93..62a3d0bb7c 100644 --- a/aries_cloudagent/wallet/askar.py +++ b/aries_cloudagent/wallet/askar.py @@ -33,7 +33,7 @@ ) from .did_method import DIDMethod from .error import WalletError, WalletDuplicateError, WalletNotFoundError -from .key_type import BLS12381G2, ED25519, KeyType +from .key_type import BLS12381G2, ED25519, KeyType, KeyTypes from .util import b58_to_bytes, bytes_to_b58 CATEGORY_DID = "did" @@ -257,7 +257,7 @@ async def get_local_dids(self) -> Sequence[DIDInfo]: ret = [] for item in await self._session.handle.fetch_all(CATEGORY_DID): - ret.append(_load_did_entry(item)) + ret.append(self._load_did_entry(item)) return ret async def get_local_did(self, did: str) -> DIDInfo: @@ -284,7 +284,7 @@ async def get_local_did(self, did: str) -> DIDInfo: raise WalletError("Error when fetching local DID") from err if not did: raise WalletNotFoundError("Unknown DID: {}".format(did)) - return _load_did_entry(did) + return self._load_did_entry(did) async def get_local_did_for_verkey(self, verkey: str) -> DIDInfo: """ @@ -308,7 +308,7 @@ async def get_local_did_for_verkey(self, verkey: str) -> DIDInfo: except AskarError as err: raise WalletError("Error when fetching local DID for verkey") from err if dids: - return _load_did_entry(dids[0]) + return self._load_did_entry(dids[0]) raise WalletNotFoundError("No DID defined for verkey: {}".format(verkey)) async def replace_local_did_metadata(self, did: str, metadata: dict): @@ -403,7 +403,7 @@ async def set_public_did(self, did: Union[str, DIDInfo]) -> DIDInfo: raise WalletError("Error when fetching local DID") from err if not item: raise WalletNotFoundError("Unknown DID: {}".format(did)) - info = _load_did_entry(item) + info = self._load_did_entry(item) else: info = did item = None @@ -727,6 +727,19 @@ async def unpack_message(self, enc_message: bytes) -> Tuple[str, str, str]: raise WalletError("Exception when unpacking message") from err return unpacked_json.decode("utf-8"), sender, recipient + def _load_did_entry(self, entry: Entry) -> DIDInfo: + """Convert a DID record into the expected DIDInfo format.""" + did_info = entry.value_json + key_types: KeyTypes = self._session.inject(KeyTypes) + return DIDInfo( + did=did_info["did"], + verkey=did_info["verkey"], + metadata=did_info.get("metadata"), + method=DIDMethod.from_method(did_info.get("method", "sov")), + key_type=key_types.from_key_type(did_info.get("verkey_type", "ed25519")) + or ED25519, + ) + def _create_keypair(key_type: KeyType, seed: Union[str, bytes] = None) -> Key: """Instantiate a new keypair with an optional seed value.""" @@ -755,15 +768,3 @@ def _create_keypair(key_type: KeyType, seed: Union[str, bytes] = None) -> Key: raise WalletError("Invalid seed for key generation") from None else: return Key.generate(alg) - - -def _load_did_entry(entry: Entry) -> DIDInfo: - """Convert a DID record into the expected DIDInfo format.""" - did_info = entry.value_json - return DIDInfo( - did=did_info["did"], - verkey=did_info["verkey"], - metadata=did_info.get("metadata"), - method=DIDMethod.from_method(did_info.get("method", "sov")), - key_type=KeyType.from_key_type(did_info.get("verkey_type", "ed25519")), - ) From a4cbef1d2db3e1fa1aa8dac50d93165f0929f3de Mon Sep 17 00:00:00 2001 From: Adam Burdett Date: Mon, 26 Sep 2022 12:03:23 -0600 Subject: [PATCH 504/872] add registry to indy wallet Signed-off-by: Adam Burdett --- aries_cloudagent/wallet/indy.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/wallet/indy.py b/aries_cloudagent/wallet/indy.py index 7e02feb6e9..b91ce355df 100644 --- a/aries_cloudagent/wallet/indy.py +++ b/aries_cloudagent/wallet/indy.py @@ -192,10 +192,12 @@ async def __get_keypair_signing_key(self, verkey: str) -> KeyInfo: try: key_pair_mgr = KeyPairStorageManager(IndySdkStorage(self.opened)) key_pair = await key_pair_mgr.get_key_pair(verkey) + # TODO: inject context to support more keytypes + key_types = KeyTypes() return KeyInfo( verkey=verkey, metadata=key_pair["metadata"], - key_type=KeyType.from_key_type(key_pair["key_type"]), + key_type=key_types.from_key_type(key_pair["key_type"]) or BLS12381G2, ) except (StorageNotFoundError): raise WalletNotFoundError(f"Unknown key: {verkey}") From 55ab05977d1006601077dbbd63d8d78a68d36e8a Mon Sep 17 00:00:00 2001 From: Adam Burdett Date: Mon, 26 Sep 2022 14:32:26 -0600 Subject: [PATCH 505/872] default SOV method Signed-off-by: Adam Burdett --- aries_cloudagent/wallet/askar.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aries_cloudagent/wallet/askar.py b/aries_cloudagent/wallet/askar.py index ae5f7c76be..195866817b 100644 --- a/aries_cloudagent/wallet/askar.py +++ b/aries_cloudagent/wallet/askar.py @@ -735,7 +735,7 @@ def _load_did_entry(self, entry: Entry) -> DIDInfo: did=did_info["did"], verkey=did_info["verkey"], metadata=did_info.get("metadata"), - method=did_methods.from_method(did_info.get("method", "sov")), + method=did_methods.from_method(did_info.get("method", "sov")) or SOV, key_type=KeyType.from_key_type(did_info.get("verkey_type", "ed25519")), ) From 9ec8330225371b00319d0820b6fe441c1836219e Mon Sep 17 00:00:00 2001 From: "Colton Wolkins (Indicio work address)" Date: Tue, 27 Sep 2022 09:25:49 -0600 Subject: [PATCH 506/872] fix: Safely shutdown when root_profile uninitialized If an exception was thrown during the initialization of the root_profile, we would throw an exception during shutdown and fail to end the process. This would leave ACA-Py running, without accepting/handling any further requests. Adding guards for shutdown code that uses the root_profile should avoid this situation Signed-off-by: Colton Wolkins (Indicio work address) --- aries_cloudagent/core/conductor.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/aries_cloudagent/core/conductor.py b/aries_cloudagent/core/conductor.py index c11327e5f9..f087e161b8 100644 --- a/aries_cloudagent/core/conductor.py +++ b/aries_cloudagent/core/conductor.py @@ -482,7 +482,8 @@ async def start(self) -> None: async def stop(self, timeout=1.0): """Stop the agent.""" # notify protcols that we are shutting down - await self.root_profile.notify(SHUTDOWN_EVENT_TOPIC, {}) + if self.root_profile: + await self.root_profile.notify(SHUTDOWN_EVENT_TOPIC, {}) shutdown = TaskQueue() if self.dispatcher: @@ -494,13 +495,13 @@ async def stop(self, timeout=1.0): if self.outbound_transport_manager: shutdown.run(self.outbound_transport_manager.stop()) - # close multitenant profiles - multitenant_mgr = self.context.inject_or(BaseMultitenantManager) - if multitenant_mgr: - for profile in multitenant_mgr.open_profiles: - shutdown.run(profile.close()) - if self.root_profile: + # close multitenant profiles + multitenant_mgr = self.context.inject_or(BaseMultitenantManager) + if multitenant_mgr: + for profile in multitenant_mgr.open_profiles: + shutdown.run(profile.close()) + shutdown.run(self.root_profile.close()) await shutdown.complete(timeout) From 38fe838708be9e74ad4d502bf1b2594dbe3b5724 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Tue, 27 Sep 2022 08:48:56 -0700 Subject: [PATCH 507/872] reduce complexity - protocol_registry Signed-off-by: Shaanjot Gill --- aries_cloudagent/core/protocol_registry.py | 49 ++++++++++++++-------- aries_cloudagent/core/util.py | 2 +- 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/aries_cloudagent/core/protocol_registry.py b/aries_cloudagent/core/protocol_registry.py index 42e2420411..d24ca8579c 100644 --- a/aries_cloudagent/core/protocol_registry.py +++ b/aries_cloudagent/core/protocol_registry.py @@ -87,29 +87,47 @@ def create_msg_types_for_minor_version(self, typesets, version_definition): Typesets mapping """ - updated_typeset = None + updated_typeset = {} curr_minor_version = version_definition["current_minor_version"] min_minor_version = version_definition["minimum_minor_version"] major_version = version_definition["major_version"] if curr_minor_version >= min_minor_version and curr_minor_version >= 1: for version_index in range(min_minor_version, curr_minor_version + 1): to_check = f"{str(major_version)}.{str(version_index)}" - for typeset in typesets: - for msg_type_string, module_path in typeset.items(): - updated_msg_type_string = Template(msg_type_string).substitute( - version=to_check - ) - if not updated_typeset: - updated_typeset = {} - updated_typeset[updated_msg_type_string] = module_path + updated_typeset.update( + self._get_updated_tyoeset_dict(typesets, to_check, updated_typeset) + ) return (updated_typeset,) - def _template_message_type_check(self, typeset) -> bool: - for msg_type_string, module_path in typeset.items(): + def _get_updated_tyoeset_dict(self, typesets, to_check, updated_typeset) -> dict: + for typeset in typesets: + for msg_type_string, module_path in typeset.items(): + updated_msg_type_string = Template(msg_type_string).substitute( + version=to_check + ) + updated_typeset[updated_msg_type_string] = module_path + return updated_typeset + + def _template_message_type_check(self, typeset, idx: int = 0) -> bool: + for msg_type_string, _ in typeset.items(): if "$version" in msg_type_string: return True return False + def _create_and_register_updated_typesets(self, typesets, version_definition): + updated_typesets = self.create_msg_types_for_minor_version( + typesets, version_definition + ) + update_flag = False + for typeset in updated_typesets: + if typeset: + self._typemap.update(typeset) + update_flag = True + if update_flag: + return updated_typesets + else: + return None + def register_message_types(self, *typesets, version_definition=None): """ Add new supported message types. @@ -121,23 +139,20 @@ def register_message_types(self, *typesets, version_definition=None): """ # Maintain support for versionless protocol modules - template_msg_type_version = False + template_msg_type_version = True for typeset in typesets: if not self._template_message_type_check(typeset): self._typemap.update(typeset) - else: - template_msg_type_version = True + template_msg_type_version = False # Track versioned modules for version routing if version_definition: # create updated typesets for minor versions and register them if template_msg_type_version: - updated_typesets = self.create_msg_types_for_minor_version( + updated_typesets = self._create_and_register_updated_typesets( typesets, version_definition ) if updated_typesets: - for typeset in updated_typesets: - self._typemap.update(typeset) typesets = updated_typesets for typeset in typesets: for message_type_string, module_path in typeset.items(): diff --git a/aries_cloudagent/core/util.py b/aries_cloudagent/core/util.py index 7db3bd8b64..f68e18eaff 100644 --- a/aries_cloudagent/core/util.py +++ b/aries_cloudagent/core/util.py @@ -97,7 +97,7 @@ def get_version_from_message(msg: AgentMessage) -> str: async def get_proto_default_version( profile: Profile, msg_class: type, major_version: int = 1 -): +) -> str: """Return default protocol version from version_definition.""" version_definition = await get_version_def_from_msg_class( profile, msg_class, major_version From cfb66b4a3223016a5e9bd2033fc1ae06babee92c Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Tue, 27 Sep 2022 09:11:39 -0700 Subject: [PATCH 508/872] reduce complexity - register_message_types Signed-off-by: Shaanjot Gill --- aries_cloudagent/core/protocol_registry.py | 32 +++++++++++++--------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/aries_cloudagent/core/protocol_registry.py b/aries_cloudagent/core/protocol_registry.py index d24ca8579c..89d131f164 100644 --- a/aries_cloudagent/core/protocol_registry.py +++ b/aries_cloudagent/core/protocol_registry.py @@ -128,6 +128,20 @@ def _create_and_register_updated_typesets(self, typesets, version_definition): else: return None + def _update_version_map(self, message_type_string, module_path, version_definition): + parsed_type_string = self.parse_type_string(message_type_string) + + if version_definition["major_version"] not in self._versionmap: + self._versionmap[version_definition["major_version"]] = [] + + self._versionmap[version_definition["major_version"]].append( + { + "parsed_type_string": parsed_type_string, + "version_definition": version_definition, + "message_module": module_path, + } + ) + def register_message_types(self, *typesets, version_definition=None): """ Add new supported message types. @@ -140,6 +154,7 @@ def register_message_types(self, *typesets, version_definition=None): # Maintain support for versionless protocol modules template_msg_type_version = True + updated_typesets = None for typeset in typesets: if not self._template_message_type_check(typeset): self._typemap.update(typeset) @@ -152,21 +167,12 @@ def register_message_types(self, *typesets, version_definition=None): updated_typesets = self._create_and_register_updated_typesets( typesets, version_definition ) - if updated_typesets: - typesets = updated_typesets + if updated_typesets: + typesets = updated_typesets for typeset in typesets: for message_type_string, module_path in typeset.items(): - parsed_type_string = self.parse_type_string(message_type_string) - - if version_definition["major_version"] not in self._versionmap: - self._versionmap[version_definition["major_version"]] = [] - - self._versionmap[version_definition["major_version"]].append( - { - "parsed_type_string": parsed_type_string, - "version_definition": version_definition, - "message_module": module_path, - } + self._update_version_map( + message_type_string, module_path, version_definition ) def register_controllers(self, *controller_sets, version_definition=None): From 84628f9d91762896c120f82c5ff3f0fe390c8d00 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Tue, 27 Sep 2022 09:26:35 -0700 Subject: [PATCH 509/872] cleanup Signed-off-by: Shaanjot Gill --- aries_cloudagent/core/protocol_registry.py | 2 +- aries_cloudagent/core/util.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/aries_cloudagent/core/protocol_registry.py b/aries_cloudagent/core/protocol_registry.py index 89d131f164..cd18a814dd 100644 --- a/aries_cloudagent/core/protocol_registry.py +++ b/aries_cloudagent/core/protocol_registry.py @@ -108,7 +108,7 @@ def _get_updated_tyoeset_dict(self, typesets, to_check, updated_typeset) -> dict updated_typeset[updated_msg_type_string] = module_path return updated_typeset - def _template_message_type_check(self, typeset, idx: int = 0) -> bool: + def _template_message_type_check(self, typeset) -> bool: for msg_type_string, _ in typeset.items(): if "$version" in msg_type_string: return True diff --git a/aries_cloudagent/core/util.py b/aries_cloudagent/core/util.py index f68e18eaff..ebe03de929 100644 --- a/aries_cloudagent/core/util.py +++ b/aries_cloudagent/core/util.py @@ -11,7 +11,7 @@ from ..messaging.agent_message import AgentMessage from ..utils.classloader import ClassLoader -from .error import ProtocolMinorVersionNotSupported +from .error import ProtocolMinorVersionNotSupported, ProtocolDefinitionValidationError CORE_EVENT_PREFIX = "acapy::core::" STARTUP_EVENT_TOPIC = CORE_EVENT_PREFIX + "startup" @@ -76,7 +76,7 @@ async def validate_get_response_version( + f" Received {rec_minor_version}." ) else: - raise Exception( + raise ProtocolMinorVersionNotSupported( f"Supported major version {proto_major_version}" " is not same as received major version" f" {rec_major_version}." @@ -135,7 +135,7 @@ async def get_version_def_from_msg_class( version_definition = protocol_version break if not version_definition: - raise Exception( + raise ProtocolDefinitionValidationError( f"Unable to load protocol version_definition for {str(msg_class)}" ) if cache: From 7bce03c4815cf655b24d1eaa4c400259157bbcba Mon Sep 17 00:00:00 2001 From: Anastasiia Date: Wed, 28 Sep 2022 13:44:29 +0200 Subject: [PATCH 510/872] Formatted code Signed-off-by: Anastasiia --- .../present_proof/dif/pres_exch_handler.py | 30 +++-- .../dif/tests/test_pres_exch_handler.py | 10 +- .../present_proof/v2_0/formats/dif/handler.py | 2 +- .../v2_0/formats/dif/tests/test_handler.py | 104 +++++++++--------- .../protocols/present_proof/v2_0/manager.py | 2 +- .../present_proof/v2_0/messages/pres.py | 2 +- .../v2_0/messages/tests/test_pres.py | 3 +- .../present_proof/v2_0/tests/test_manager.py | 23 ++-- demo/runners/agent_container.py | 14 ++- 9 files changed, 107 insertions(+), 83 deletions(-) diff --git a/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py b/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py index a625e3db02..0df31f37d8 100644 --- a/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py +++ b/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py @@ -1248,13 +1248,15 @@ async def create_vp( if req.nested_req: for nested_req in req.nested_req: res = await self.apply_requirements( - req=nested_req, credentials=credentials, records_filter=records_filter + req=nested_req, + credentials=credentials, + records_filter=records_filter ) result.append(res) else: res = await self.apply_requirements( - req=req, credentials=credentials, records_filter=records_filter - ) + req=req, credentials=credentials, records_filter=records_filter + ) result.append(res) result_vp = [] @@ -1286,7 +1288,9 @@ async def create_vp( applicable_creds=applicable_creds ) if not issuer_id and len(filtered_creds_list) == 0: - vp = await create_presentation(credentials=applicable_creds_list) + vp = await create_presentation( + credentials=applicable_creds_list + ) vp["presentation_submission"] = submission_property.serialize() if self.proof_type is BbsBlsSignature2020.signature_type: vp["@context"].append(SECURITY_CONTEXT_BBS_URL) @@ -1396,19 +1400,21 @@ async def verify_received_pres( input_descriptors = pd.input_descriptors if isinstance(pres, Sequence): for pr in pres: - descriptor_map_list = pr["presentation_submission"].get("descriptor_map") + descriptor_map_list = pr["presentation_submission"].get( + "descriptor_map" + ) await self.__verify_desc_map_list( - descriptor_map_list, - pr, - input_descriptors) + descriptor_map_list, pr, input_descriptors + ) else: descriptor_map_list = pres["presentation_submission"].get("descriptor_map") await self.__verify_desc_map_list( - descriptor_map_list, - pres, - input_descriptors) + descriptor_map_list, pres, input_descriptors + ) - async def __verify_desc_map_list(self, descriptor_map_list, pres, input_descriptors): + async def __verify_desc_map_list( + self, descriptor_map_list, pres, input_descriptors + ): inp_desc_id_contraint_map = {} inp_desc_id_schema_one_of_filter = set() inp_desc_id_schemas_map = {} diff --git a/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py b/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py index 312926f016..b3bdb1c255 100644 --- a/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py +++ b/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py @@ -108,7 +108,9 @@ async def test_load_cred_json_a(self, setup_tuple, profile): if isinstance(tmp_vp, Sequence): cred_count_list = [] for tmp_vp_single in tmp_vp: - cred_count_list.append(len(tmp_vp_single.get("verifiableCredential"))) + cred_count_list.append( + len(tmp_vp_single.get("verifiableCredential")) + ) assert min(cred_count_list) == tmp_pd[1] else: @@ -133,7 +135,9 @@ async def test_load_cred_json_b(self, setup_tuple, profile): if isinstance(tmp_vp, Sequence): cred_count_list = [] for tmp_vp_single in tmp_vp: - cred_count_list.append(len(tmp_vp_single.get("verifiableCredential"))) + cred_count_list.append( + len(tmp_vp_single.get("verifiableCredential")) + ) assert min(cred_count_list) == tmp_pd[1] else: @@ -2397,7 +2401,7 @@ async def test_create_vp_no_issuer_with_bbs_suite(self, profile, setup_tuple): pd=pd_list[0][0], challenge="3fa85f64-5717-4562-b3fc-2c963f66afa7", ) - #2 sub_reqs, vp is a sequence + # 2 sub_reqs, vp is a sequence for vp_single in vp: assert vp_single["test"] == "1" assert SECURITY_CONTEXT_BBS_URL in vp_single["@context"] diff --git a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/handler.py b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/handler.py index 2e9fff4b42..9c0d6d608a 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/handler.py @@ -474,7 +474,7 @@ async def verify_pres(self, pres_ex_record: V20PresExRecord) -> V20PresExRecord: challenge = pres_request["options"].get("challenge", str(uuid4())) if not challenge: challenge = str(uuid4()) - if(isinstance(dif_proof, Sequence)): + if isinstance(dif_proof, Sequence): for proof in dif_proof: pres_ver_result = await verify_presentation( presentation=proof, diff --git a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py index 003cec94fe..637e4ae9ef 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py @@ -152,7 +152,7 @@ "rule": "pick", "min": 1, "from": "B", - } + }, ], "input_descriptors": [ { @@ -198,7 +198,7 @@ } ], }, - } + }, ], }, } @@ -283,56 +283,58 @@ }, } -DIF_PRES_SEQUENCE = [DIF_PRES, -{ - "@context": ["https://www.w3.org/2018/credentials/v1"], - "type": ["VerifiablePresentation"], - "verifiableCredential": [ - { - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://w3id.org/citizenship/v1", - "https://w3id.org/security/bbs/v1", - ], - "id": "https://issuer.oidp.uscis.gov/credentials/83627465", - "type": ["PermanentResidentCard", "VerifiableCredential"], - "credentialSubject": { - "id": "did:example:b34ca6cd37bbf23", - "type": ["Person", "PermanentResident"], - "givenName": "JOHN", - }, - "issuanceDate": "2010-01-01T19:53:24Z", - "issuer": "did:key:zUC74bgefTdc43KS1psXgXf4jLaHyaj2qCQqQTXrtmSYGf1PxiJhrH6LGpaBMyj6tqAKmjGyMaS4RfNo2an77vT1HfzJUNPk4H7TCuJvSp4vet4Cu67kn2JSegoQNFSA1tbwU8v", - "proof": { - "type": "BbsBlsSignatureProof2020", - "nonce": "3AuruhJQrXtEgiagiJ+FwVf2S0SnzUDJvnO61YecQsJ7ImR1mPcoVjJJ0HOhfkFpoYI=", - "proofValue": "ABkBuAaPlP5A7JWY78Xf69oBnsMLcD1RXbIFYhcLoXPXW12CG9glnnqnPLsGri5xsA3LcP0kg74X+sAjKXGRGy3uvp412Dm0FuohYNboQcLne5KOAa5AxU4bjmwQsxdfduVqhriro1N+YTkuB4SMmO/5ooL0N3OHsYdExg7nSzWqmZoqgp+3CwIxF0a/oyKTcxJORuIqAAAAdInlL9teSIX49NJGEZfBO7IrdjT2iggH/G0AlPWoEvrWIbuCRQ69K83n5o7oJVjqhAAAAAIaVmlAD6+FEKA4eg0OaWOKPrd5Kq8rv0vIwjJ71egxll0Fqq4zDWQ/+yl3Pteh0Wyuyvpm19/sj6tiCWj4PkA+rpxtR2bXpnrCTKUffFFNBjVvVziXDS0KWkGUB7XU9mjUa4USC7Iub3bZZCnFjQA5AAAADzkGwGD837r33e7OTrGEti8eAkvFDcyCgA4ck/X+5HJjAJclHWbl4SNQR8CiNZyzJpvxW+jbNBcwmEvocYArddk3F78Ki0Qnp6aU9eDgfOOx1iW2BXLUjrhq5I2hP5/WQF3CEDYRjczGjzM9T8/coeC36YAp0zJunIXUKb8SPDSOISafibYRYFB4xhlWKXWloDelafyujOBST8KZNM8FmF4DSbXrO8vmZbjuR/8ntUcUK7X2rNbuZ3M5eWZDF8pL+SA9gQitKfPHEocoYAdhgEAM7ZNAJ+TgOcx9gtZIhDWKDNnFxIeoOAylbD1xZd9xbWtq3Bk3R79xqsKxFRJRNxk/9b6fJruP292+qM5lxcZ1jUz/dJUYFI93hH4Mso75CjGRN78MAY9SNifl6H8qcxTpBn4332LlFhRznLbtnc4YSWA/fvVqaN9h2zCH/6AdbLKXGffV34EF7DadwJsi9jsc+YlSMn6qaIUIDTdGLwh4KKpSH5bVbg/mVCcXPTJplFgYwRsOdiQbZY/740dJyo1lPjQ0Lvdio8W2M8c73ujeJU70CNLkgjJAMUPGrCFtGxBH2eeLBQ0P95qRZAIcJ7U0MibZLaRjoUOuTla5BIt2038PJ6XhcY6BEJaLyJOPEQ==", - "verificationMethod": "did:key:zUC74bgefTdc43KS1psXgXf4jLaHyaj2qCQqQTXrtmSYGf1PxiJhrH6LGpaBMyj6tqAKmjGyMaS4RfNo2an77vT1HfzJUNPk4H7TCuJvSp4vet4Cu67kn2JSegoQNFSA1tbwU8v#zUC74bgefTdc43KS1psXgXf4jLaHyaj2qCQqQTXrtmSYGf1PxiJhrH6LGpaBMyj6tqAKmjGyMaS4RfNo2an77vT1HfzJUNPk4H7TCuJvSp4vet4Cu67kn2JSegoQNFSA1tbwU8v", - "proofPurpose": "assertionMethod", - "created": "2021-05-05T15:22:30.523465", - }, - } - ], - "presentation_submission": { - "id": "a5fcfe44-2c30-497d-af02-98e539da9a0f", - "definition_id": "32f54163-7166-48f1-93d8-ff217bdb0653", - "descriptor_map": [ - { - "id": "citizenship_input_2", - "format": "ldp_vp", - "path": "$.verifiableCredential[0]", +DIF_PRES_SEQUENCE = [ + DIF_PRES, + { + "@context": ["https://www.w3.org/2018/credentials/v1"], + "type": ["VerifiablePresentation"], + "verifiableCredential": [ + { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://w3id.org/citizenship/v1", + "https://w3id.org/security/bbs/v1", + ], + "id": "https://issuer.oidp.uscis.gov/credentials/83627465", + "type": ["PermanentResidentCard", "VerifiableCredential"], + "credentialSubject": { + "id": "did:example:b34ca6cd37bbf23", + "type": ["Person", "PermanentResident"], + "givenName": "JOHN", + }, + "issuanceDate": "2010-01-01T19:53:24Z", + "issuer": "did:key:zUC74bgefTdc43KS1psXgXf4jLaHyaj2qCQqQTXrtmSYGf1PxiJhrH6LGpaBMyj6tqAKmjGyMaS4RfNo2an77vT1HfzJUNPk4H7TCuJvSp4vet4Cu67kn2JSegoQNFSA1tbwU8v", + "proof": { + "type": "BbsBlsSignatureProof2020", + "nonce": "3AuruhJQrXtEgiagiJ+FwVf2S0SnzUDJvnO61YecQsJ7ImR1mPcoVjJJ0HOhfkFpoYI=", + "proofValue": "ABkBuAaPlP5A7JWY78Xf69oBnsMLcD1RXbIFYhcLoXPXW12CG9glnnqnPLsGri5xsA3LcP0kg74X+sAjKXGRGy3uvp412Dm0FuohYNboQcLne5KOAa5AxU4bjmwQsxdfduVqhriro1N+YTkuB4SMmO/5ooL0N3OHsYdExg7nSzWqmZoqgp+3CwIxF0a/oyKTcxJORuIqAAAAdInlL9teSIX49NJGEZfBO7IrdjT2iggH/G0AlPWoEvrWIbuCRQ69K83n5o7oJVjqhAAAAAIaVmlAD6+FEKA4eg0OaWOKPrd5Kq8rv0vIwjJ71egxll0Fqq4zDWQ/+yl3Pteh0Wyuyvpm19/sj6tiCWj4PkA+rpxtR2bXpnrCTKUffFFNBjVvVziXDS0KWkGUB7XU9mjUa4USC7Iub3bZZCnFjQA5AAAADzkGwGD837r33e7OTrGEti8eAkvFDcyCgA4ck/X+5HJjAJclHWbl4SNQR8CiNZyzJpvxW+jbNBcwmEvocYArddk3F78Ki0Qnp6aU9eDgfOOx1iW2BXLUjrhq5I2hP5/WQF3CEDYRjczGjzM9T8/coeC36YAp0zJunIXUKb8SPDSOISafibYRYFB4xhlWKXWloDelafyujOBST8KZNM8FmF4DSbXrO8vmZbjuR/8ntUcUK7X2rNbuZ3M5eWZDF8pL+SA9gQitKfPHEocoYAdhgEAM7ZNAJ+TgOcx9gtZIhDWKDNnFxIeoOAylbD1xZd9xbWtq3Bk3R79xqsKxFRJRNxk/9b6fJruP292+qM5lxcZ1jUz/dJUYFI93hH4Mso75CjGRN78MAY9SNifl6H8qcxTpBn4332LlFhRznLbtnc4YSWA/fvVqaN9h2zCH/6AdbLKXGffV34EF7DadwJsi9jsc+YlSMn6qaIUIDTdGLwh4KKpSH5bVbg/mVCcXPTJplFgYwRsOdiQbZY/740dJyo1lPjQ0Lvdio8W2M8c73ujeJU70CNLkgjJAMUPGrCFtGxBH2eeLBQ0P95qRZAIcJ7U0MibZLaRjoUOuTla5BIt2038PJ6XhcY6BEJaLyJOPEQ==", + "verificationMethod": "did:key:zUC74bgefTdc43KS1psXgXf4jLaHyaj2qCQqQTXrtmSYGf1PxiJhrH6LGpaBMyj6tqAKmjGyMaS4RfNo2an77vT1HfzJUNPk4H7TCuJvSp4vet4Cu67kn2JSegoQNFSA1tbwU8v#zUC74bgefTdc43KS1psXgXf4jLaHyaj2qCQqQTXrtmSYGf1PxiJhrH6LGpaBMyj6tqAKmjGyMaS4RfNo2an77vT1HfzJUNPk4H7TCuJvSp4vet4Cu67kn2JSegoQNFSA1tbwU8v", + "proofPurpose": "assertionMethod", + "created": "2021-05-05T15:22:30.523465", + }, } ], + "presentation_submission": { + "id": "a5fcfe44-2c30-497d-af02-98e539da9a0f", + "definition_id": "32f54163-7166-48f1-93d8-ff217bdb0653", + "descriptor_map": [ + { + "id": "citizenship_input_2", + "format": "ldp_vp", + "path": "$.verifiableCredential[0]", + } + ], + }, + "proof": { + "type": "Ed25519Signature2018", + "verificationMethod": "did:sov:4QxzWk3ajdnEA37NdNU5Kt#key-1", + "created": "2021-05-05T15:23:03.023971", + "proofPurpose": "authentication", + "challenge": "40429d49-5e8f-4ffc-baf8-e332412f1247", + "jws": "eyJhbGciOiAiRWREU0EiLCAiYjY0IjogZmFsc2UsICJjcml0IjogWyJiNjQiXX0..2uBYmg7muE9ZPVeAGo_ibVfLkCjf2hGshr2o5i8pAwFyNBM-kDHXofuq1MzJgb19wzb01VIu91hY_ajjt9KFAA", + }, }, - "proof": { - "type": "Ed25519Signature2018", - "verificationMethod": "did:sov:4QxzWk3ajdnEA37NdNU5Kt#key-1", - "created": "2021-05-05T15:23:03.023971", - "proofPurpose": "authentication", - "challenge": "40429d49-5e8f-4ffc-baf8-e332412f1247", - "jws": "eyJhbGciOiAiRWREU0EiLCAiYjY0IjogZmFsc2UsICJjcml0IjogWyJiNjQiXX0..2uBYmg7muE9ZPVeAGo_ibVfLkCjf2hGshr2o5i8pAwFyNBM-kDHXofuq1MzJgb19wzb01VIu91hY_ajjt9KFAA", - }, -}] +] TEST_CRED = { @@ -1130,7 +1132,9 @@ async def test_verify_pres_sequence(self): format_=ATTACHMENT_FORMAT[PRES_20][V20PresFormat.Format.DIF.api], ) ], - presentations_attach=[AttachDecorator.data_json(DIF_PRES_SEQUENCE, ident="dif")], + presentations_attach=[ + AttachDecorator.data_json(DIF_PRES_SEQUENCE, ident="dif") + ], ) dif_pres_request = V20PresRequest( formats=[ diff --git a/aries_cloudagent/protocols/present_proof/v2_0/manager.py b/aries_cloudagent/protocols/present_proof/v2_0/manager.py index 1f41dd617a..ef542f0c29 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/manager.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/manager.py @@ -393,7 +393,7 @@ async def verify_pres(self, pres_ex_record: V20PresExRecord): ).verify_pres( pres_ex_record, ) - if pres_ex_record.verified == 'false': + if pres_ex_record.verified == "false": break pres_ex_record.state = V20PresExRecord.STATE_DONE diff --git a/aries_cloudagent/protocols/present_proof/v2_0/messages/pres.py b/aries_cloudagent/protocols/present_proof/v2_0/messages/pres.py index e3c1ba902b..ae44c5a4d5 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/messages/pres.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/messages/pres.py @@ -118,7 +118,7 @@ def get_attach_by_id(attach_id): atch = get_attach_by_id(fmt.attach_id) pres_format = V20PresFormat.Format.get(fmt.format) if pres_format: - if(isinstance(atch.content, Sequence)): + if isinstance(atch.content, Sequence): for el in atch.content: pres_format.validate_fields(PRES_20, el) else: diff --git a/aries_cloudagent/protocols/present_proof/v2_0/messages/tests/test_pres.py b/aries_cloudagent/protocols/present_proof/v2_0/messages/tests/test_pres.py index dfc79e2181..63381c1f62 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/messages/tests/test_pres.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/messages/tests/test_pres.py @@ -1828,7 +1828,7 @@ def test_init_type(self): assert len(PRES.formats) == len(PRES.presentations_attach) assert PRES.attachment(V20PresFormat.Format.INDY) == INDY_PROOF assert PRES._type == DIDCommPrefix.qualify_current(PRES_20) - + assert PRES_DIF.presentations_attach[0].content == DIF_PROOF assert len(PRES_DIF.formats) == len(PRES.presentations_attach) assert PRES_DIF.attachment(V20PresFormat.Format.DIF) == DIF_PROOF @@ -1891,4 +1891,3 @@ def test_serde_dif(self): pres_dict = PRES_DIF.serialize() pres_obj = V20Pres.deserialize(pres_dict) assert type(pres_obj) == V20Pres - diff --git a/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py b/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py index 4c25dd609e..f0a3663b3f 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py @@ -825,11 +825,11 @@ async def test_create_pres_indy_and_dif(self): format_=ATTACHMENT_FORMAT[PRES_20_REQUEST][ V20PresFormat.Format.DIF.api ], - ) + ), ], request_presentations_attach=[ AttachDecorator.data_base64(INDY_PROOF_REQ_NAME, ident="indy"), - AttachDecorator.data_json(DIF_PRES_REQ, ident="dif") + AttachDecorator.data_json(DIF_PRES_REQ, ident="dif"), ], ) @@ -854,7 +854,10 @@ async def test_create_pres_indy_and_dif(self): return_value=mock_attach_decorator_indy ) - mock_create_pres.return_value = (PRES_20, AttachDecorator.data_json(DIF_PRES, ident="dif")) + mock_create_pres.return_value = ( + PRES_20, + AttachDecorator.data_json(DIF_PRES, ident="dif") + ) req_creds = await indy_proof_req_preview2indy_requested_creds( INDY_PROOF_REQ_NAME, preview=None, holder=self.holder @@ -2144,12 +2147,12 @@ async def test_verify_pres_indy_and_dif(self): format_=ATTACHMENT_FORMAT[PRES_20_REQUEST][ V20PresFormat.Format.DIF.api ], - ) + ), ], will_confirm=True, request_presentations_attach=[ AttachDecorator.data_base64(INDY_PROOF_REQ_NAME, ident="indy"), - AttachDecorator.data_json(DIF_PRES_REQ, ident="dif") + AttachDecorator.data_json(DIF_PRES_REQ, ident="dif"), ], ) pres = V20Pres( @@ -2161,11 +2164,11 @@ async def test_verify_pres_indy_and_dif(self): V20PresFormat( attach_id="dif", format_=ATTACHMENT_FORMAT[PRES_20][V20PresFormat.Format.DIF.api], - ) + ), ], presentations_attach=[ AttachDecorator.data_base64(INDY_PROOF, ident="indy"), - AttachDecorator.data_json(DIF_PRES, ident="dif") + AttachDecorator.data_json(DIF_PRES, ident="dif"), ], ) px_rec_in = V20PresExRecord( @@ -2173,7 +2176,9 @@ async def test_verify_pres_indy_and_dif(self): pres=pres, ) - self.profile.context.injector.bind_instance(DocumentLoader, custom_document_loader) + self.profile.context.injector.bind_instance( + DocumentLoader, custom_document_loader + ) self.profile.context.injector.bind_instance( BaseMultitenantManager, async_mock.MagicMock(MultitenantManager, autospec=True), @@ -2209,7 +2214,7 @@ async def test_verify_pres_indy_and_dif(self): px_rec_out = await self.manager.verify_pres(px_rec_in) save_ex.assert_called_once() assert px_rec_out.state == (V20PresExRecord.STATE_DONE) - assert px_rec_out.verified == 'false' + assert px_rec_out.verified == "false" async def test_send_pres_ack(self): px_rec = V20PresExRecord() diff --git a/demo/runners/agent_container.py b/demo/runners/agent_container.py index c980d8c89f..0f49bd15e0 100644 --- a/demo/runners/agent_container.py +++ b/demo/runners/agent_container.py @@ -494,9 +494,13 @@ async def handle_present_proof_v2_0(self, message): input_descriptor_schema_uri.append(element["uri"]) for record in records: - if self.check_input_descriptor_record_id(input_descriptor_schema_uri, record): + if self.check_input_descriptor_record_id( + input_descriptor_schema_uri, record + ): record_id = record["record_id"] - dif_request["dif"]["record_ids"][input_descriptor["id"]] = [ + dif_request["dif"]["record_ids"][ + input_descriptor["id"] + ] = [ record_id, ] break @@ -626,7 +630,9 @@ async def create_schema_and_cred_def( ) return cred_def_id - def check_input_descriptor_record_id(self, input_descriptor_schema_uri, record) -> bool: + def check_input_descriptor_record_id( + self, input_descriptor_schema_uri, record + ) -> bool: result = False for uri in input_descriptor_schema_uri: for record_type in record["type"]: @@ -634,7 +640,7 @@ def check_input_descriptor_record_id(self, input_descriptor_schema_uri, record) result = True break result = False - + return result From e0caf065faf51b3a5e60ded1bf4d6c1bee4e2edf Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Sat, 1 Oct 2022 03:23:03 -0700 Subject: [PATCH 511/872] OOB v1.1 support w/ backward comp Signed-off-by: Shaanjot Gill --- aries_cloudagent/connections/base_manager.py | 8 ++-- aries_cloudagent/core/tests/test_util.py | 15 +++++-- aries_cloudagent/core/util.py | 36 +++++++++++----- aries_cloudagent/messaging/agent_message.py | 17 +++++++- aries_cloudagent/messaging/models/base.py | 15 ++++++- .../protocols/out_of_band/v1_0/manager.py | 34 +++++++++++---- .../out_of_band/v1_0/message_types.py | 7 ++++ .../out_of_band/v1_0/messages/invitation.py | 31 ++++++++------ .../v1_0/messages/problem_report.py | 21 +++++----- .../out_of_band/v1_0/messages/reuse.py | 22 +++++----- .../out_of_band/v1_0/messages/reuse_accept.py | 22 +++++----- .../v1_0/messages/tests/test_invitation.py | 3 +- .../protocols/out_of_band/v1_0/routes.py | 20 +++++---- .../out_of_band/v1_0/tests/test_manager.py | 25 +++++++---- .../out_of_band/v1_0/tests/test_routes.py | 41 +++++++++++++++++++ aries_cloudagent/resolver/base.py | 15 +++++-- aries_cloudagent/resolver/default/indy.py | 12 ++++-- aries_cloudagent/resolver/default/key.py | 6 ++- .../resolver/default/tests/test_indy.py | 9 ++++ .../resolver/default/universal.py | 6 ++- aries_cloudagent/resolver/default/web.py | 6 ++- aries_cloudagent/resolver/did_resolver.py | 17 ++++++-- aries_cloudagent/resolver/tests/test_base.py | 4 +- .../resolver/tests/test_did_resolver.py | 2 +- open-api/openapi.json | 16 ++++++++ 25 files changed, 295 insertions(+), 115 deletions(-) diff --git a/aries_cloudagent/connections/base_manager.py b/aries_cloudagent/connections/base_manager.py index 162fb26c39..79bef004bf 100644 --- a/aries_cloudagent/connections/base_manager.py +++ b/aries_cloudagent/connections/base_manager.py @@ -5,7 +5,7 @@ """ import logging -from typing import List, Sequence, Tuple +from typing import Optional, List, Sequence, Tuple, Text from pydid import ( BaseDIDDocument as ResolvedDocument, @@ -227,7 +227,9 @@ async def remove_keys_for_did(self, did: str): storage: BaseStorage = session.inject(BaseStorage) await storage.delete_all_records(self.RECORD_TYPE_DID_KEY, {"did": did}) - async def resolve_invitation(self, did: str): + async def resolve_invitation( + self, did: str, accept: Optional[Sequence[Text]] = None + ): """ Resolve invitation with the DID Resolver. @@ -241,7 +243,7 @@ async def resolve_invitation(self, did: str): resolver = self._profile.inject(DIDResolver) try: - doc_dict: dict = await resolver.resolve(self._profile, did) + doc_dict: dict = await resolver.resolve(self._profile, did, accept) doc: ResolvedDocument = pydid.deserialize_document(doc_dict, strict=True) except ResolverError as error: raise BaseConnectionManagerError( diff --git a/aries_cloudagent/core/tests/test_util.py b/aries_cloudagent/core/tests/test_util.py index 1b20487e71..c25f944d74 100644 --- a/aries_cloudagent/core/tests/test_util.py +++ b/aries_cloudagent/core/tests/test_util.py @@ -64,10 +64,19 @@ def test_get_version_from_message_type(self): ) def test_get_version_from_message(self): - assert test_module.get_version_from_message(HandshakeReuse()) == "1.0" + assert test_module.get_version_from_message(HandshakeReuse()) == "1.1" - async def test_get_proto_default_version(self): + async def test_get_proto_default_version_from_msg_class(self): profile = make_profile() assert ( - await test_module.get_proto_default_version(profile, HandshakeReuse) + await test_module.get_proto_default_version_from_msg_class( + profile, HandshakeReuse + ) + ) == "1.1" + + def test_get_proto_default_version(self): + assert ( + test_module.get_proto_default_version( + "aries_cloudagent.protocols.out_of_band.definition" + ) ) == "1.1" diff --git a/aries_cloudagent/core/util.py b/aries_cloudagent/core/util.py index ebe03de929..58b7713e8f 100644 --- a/aries_cloudagent/core/util.py +++ b/aries_cloudagent/core/util.py @@ -95,16 +95,20 @@ def get_version_from_message(msg: AgentMessage) -> str: return get_version_from_message_type(msg_type) -async def get_proto_default_version( +async def get_proto_default_version_from_msg_class( profile: Profile, msg_class: type, major_version: int = 1 ) -> str: """Return default protocol version from version_definition.""" version_definition = await get_version_def_from_msg_class( profile, msg_class, major_version ) - default_major_version = version_definition["major_version"] - default_minor_version = version_definition["current_minor_version"] - return f"{default_major_version}.{default_minor_version}" + return _get_default_version_from_version_def(version_definition) + + +def get_proto_default_version(def_path: str, major_version: int = 1) -> str: + """Return default protocol version from version_definition.""" + version_definition = _get_version_def_from_path(def_path, major_version) + return _get_default_version_from_version_def(version_definition) def _get_path_from_msg_class(msg_class: type) -> str: @@ -116,10 +120,26 @@ def _get_path_from_msg_class(msg_class: type) -> str: return (path.replace("/", ".")) + "definition" +def _get_version_def_from_path(definition_path: str, major_version: int = 1): + version_definition = None + definition = ClassLoader.load_module(definition_path) + for protocol_version in definition.versions: + if major_version == protocol_version["major_version"]: + version_definition = protocol_version + break + return version_definition + + +def _get_default_version_from_version_def(version_definition) -> str: + default_major_version = version_definition["major_version"] + default_minor_version = version_definition["current_minor_version"] + return f"{default_major_version}.{default_minor_version}" + + async def get_version_def_from_msg_class( profile: Profile, msg_class: type, major_version: int = 1 ): - """Return version_definition of a protocol.""" + """Return version_definition of a protocol from msg_class.""" cache = profile.inject_or(BaseCache) version_definition = None if cache: @@ -129,11 +149,7 @@ async def get_version_def_from_msg_class( if version_definition: return version_definition definition_path = _get_path_from_msg_class(msg_class) - definition = ClassLoader.load_module(definition_path) - for protocol_version in definition.versions: - if major_version == protocol_version["major_version"]: - version_definition = protocol_version - break + version_definition = _get_version_def_from_path(definition_path, major_version) if not version_definition: raise ProtocolDefinitionValidationError( f"Unable to load protocol version_definition for {str(msg_class)}" diff --git a/aries_cloudagent/messaging/agent_message.py b/aries_cloudagent/messaging/agent_message.py index ea39f14712..b6e64f9b8a 100644 --- a/aries_cloudagent/messaging/agent_message.py +++ b/aries_cloudagent/messaging/agent_message.py @@ -1,8 +1,11 @@ """Agent message base class and schema.""" +import re +import uuid + from collections import OrderedDict from typing import Mapping, Union -import uuid +from string import Template from marshmallow import ( EXCLUDE, @@ -98,6 +101,18 @@ def _get_handler_class(cls): """ return resolve_class(cls.Meta.handler_class, cls) + @classmethod + def assign_version_to_message_type(cls, version: str): + """Assign version to Meta.message_type.""" + if "$version" in cls.Meta.message_type: + cls.Meta.message_type = Template(cls.Meta.message_type).substitute( + version=version + ) + else: + cls.Meta.message_type = re.sub( + r"(\d+\.)?(\*|\d+)", version, cls.Meta.message_type + ) + @property def Handler(self) -> type: """ diff --git a/aries_cloudagent/messaging/models/base.py b/aries_cloudagent/messaging/models/base.py index fd00c7d68d..b11cc8cd81 100644 --- a/aries_cloudagent/messaging/models/base.py +++ b/aries_cloudagent/messaging/models/base.py @@ -1,5 +1,6 @@ """Base classes for Models and Schemas.""" +import re import logging import json @@ -319,7 +320,19 @@ def make_model(self, data: dict, **kwargs): A model instance """ - return self.Model(**data) + try: + cls_inst = self.Model(**data) + except TypeError as err: + msg_type_version = None + if "_type" in str(err) and "_type" in data: + match = re.search(r"(\d+\.)?(\*|\d+)", data["_type"]) + if match: + msg_type_version = match.group() + del data["_type"] + cls_inst = self.Model(**data) + if msg_type_version: + cls_inst.assign_version_to_message_type(msg_type_version) + return cls_inst @post_dump def remove_skipped_values(self, data, **kwargs): diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py index 1679069e1a..1932055dd9 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py @@ -3,7 +3,7 @@ import asyncio import logging import re -from typing import Mapping, Optional, Sequence, Union +from typing import Mapping, Optional, Sequence, Union, Text import uuid @@ -87,6 +87,7 @@ async def create_invitation( attachments: Sequence[Mapping] = None, metadata: dict = None, mediation_id: str = None, + accept: Optional[Sequence[Text]] = None, ) -> InvitationRecord: """ Generate new connection invitation. @@ -105,6 +106,8 @@ async def create_invitation( multi_use: set to True to create an invitation for multiple-use connection alias: optional alias to apply to connection for later use attachments: list of dicts in form of {"id": ..., "type": ...} + accept: Optional list of mime types in the order of preference of the sender + that the receiver can use in responding to the message Returns: Invitation record @@ -122,7 +125,7 @@ async def create_invitation( "request attachments, or both" ) - accept = bool( + auto_accept = bool( auto_accept or ( auto_accept is None @@ -227,6 +230,7 @@ async def create_invitation( handshake_protocols=handshake_protocols, requests_attach=message_attachments, services=[f"did:sov:{public_did.did}"], + accept=accept, ) our_recipient_key = public_did.verkey @@ -242,7 +246,7 @@ async def create_invitation( their_role=ConnRecord.Role.REQUESTER.rfc23, state=ConnRecord.State.INVITATION.rfc23, accept=ConnRecord.ACCEPT_AUTO - if accept + if auto_accept else ConnRecord.ACCEPT_MANUAL, alias=alias, connection_protocol=connection_protocol, @@ -286,7 +290,7 @@ async def create_invitation( their_role=ConnRecord.Role.REQUESTER.rfc23, state=ConnRecord.State.INVITATION.rfc23, accept=ConnRecord.ACCEPT_AUTO - if accept + if auto_accept else ConnRecord.ACCEPT_MANUAL, invitation_mode=invitation_mode, alias=alias, @@ -322,6 +326,7 @@ async def create_invitation( invi_msg.label = my_label or self.profile.settings.get("default_label") invi_msg.handshake_protocols = handshake_protocols invi_msg.requests_attach = message_attachments + invi_msg.accept = accept invi_msg.services = [ ServiceMessage( _id="#inline", @@ -415,6 +420,9 @@ async def receive_invitation( # Get the single service item oob_service_item = invitation.services[0] + # accept + accept = invitation.accept + # Get the DID public did, if any public_did = None if isinstance(oob_service_item, str): @@ -446,7 +454,9 @@ async def receive_invitation( # Try to reuse the connection. If not accepted sets the conn_rec to None if conn_rec and not invitation.requests_attach: - oob_record = await self._handle_hanshake_reuse(oob_record, conn_rec) + oob_record = await self._handle_hanshake_reuse( + oob_record, conn_rec, get_version_from_message(invitation) + ) LOGGER.warning( f"Connection reuse request finished with state {oob_record.state}" @@ -467,6 +477,7 @@ async def receive_invitation( alias=alias, auto_accept=auto_accept, mediation_id=mediation_id, + accept=accept, ) LOGGER.debug( f"Performed handshake with connection {oob_record.connection_id}" @@ -674,10 +685,12 @@ async def _wait_for_state() -> ConnRecord: return None async def _handle_hanshake_reuse( - self, oob_record: OobRecord, conn_record: ConnRecord + self, oob_record: OobRecord, conn_record: ConnRecord, version: str ) -> OobRecord: # Send handshake reuse - oob_record = await self._create_handshake_reuse_message(oob_record, conn_record) + oob_record = await self._create_handshake_reuse_message( + oob_record, conn_record, version + ) # Wait for the reuse accepted message oob_record = await self._wait_for_reuse_response(oob_record.oob_id) @@ -719,6 +732,7 @@ async def _perform_handshake( alias: Optional[str] = None, auto_accept: Optional[bool] = None, mediation_id: Optional[str] = None, + accept: Optional[Sequence[Text]] = None, ) -> OobRecord: invitation = oob_record.invitation @@ -746,7 +760,8 @@ async def _perform_handshake( # or something else that includes the key type. We now assume # ED25519 keys endpoint, recipient_keys, routing_keys = await self.resolve_invitation( - service + service, + accept=accept, ) service = ServiceMessage.deserialize( { @@ -824,6 +839,7 @@ async def _create_handshake_reuse_message( self, oob_record: OobRecord, conn_record: ConnRecord, + version: str, ) -> OobRecord: """ Create and Send a Handshake Reuse message under RFC 0434. @@ -840,7 +856,7 @@ async def _create_handshake_reuse_message( """ try: - reuse_msg = HandshakeReuse() + reuse_msg = HandshakeReuse(version=version) reuse_msg.assign_thread_id(thid=reuse_msg._id, pthid=oob_record.invi_msg_id) connection_targets = await self.fetch_connection_targets( diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/message_types.py b/aries_cloudagent/protocols/out_of_band/v1_0/message_types.py index c130b92e4f..e2d539c906 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/message_types.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/message_types.py @@ -1,5 +1,7 @@ """Message and inner object type identifiers for Out of Band messages.""" +from ....core.util import get_proto_default_version + from ...didcomm_prefix import DIDCommPrefix SPEC_URI = ( @@ -13,6 +15,11 @@ MESSAGE_REUSE_ACCEPT = "out-of-band/$version/handshake-reuse-accepted" PROBLEM_REPORT = "out-of-band/$version/problem_report" +# Default Version +DEFAULT_VERSION = get_proto_default_version( + "aries_cloudagent.protocols.out_of_band.definition", 1 +) + PROTOCOL_PACKAGE = "aries_cloudagent.protocols.out_of_band.v1_0" MESSAGE_TYPES = DIDCommPrefix.qualify_all( diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/messages/invitation.py b/aries_cloudagent/protocols/out_of_band/v1_0/messages/invitation.py index 14271ccd01..9229ab4921 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/messages/invitation.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/messages/invitation.py @@ -3,8 +3,7 @@ from collections import namedtuple from enum import Enum from re import sub -from string import Template -from typing import Sequence, Text, Union +from typing import Optional, Sequence, Text, Union from urllib.parse import parse_qs, urljoin, urlparse from marshmallow import ( @@ -27,11 +26,10 @@ from ....didexchange.v1_0.message_types import ARIES_PROTOCOL as DIDX_PROTO from ....connections.v1_0.message_types import ARIES_PROTOCOL as CONN_PROTO -from ..message_types import INVITATION +from ..message_types import INVITATION, DEFAULT_VERSION from .service import Service -BASE_PROTO_VERSION = "1.0" HSProtoSpec = namedtuple("HSProtoSpec", "rfc name aka") @@ -125,7 +123,8 @@ def __init__( handshake_protocols: Sequence[Text] = None, requests_attach: Sequence[AttachDecorator] = None, services: Sequence[Union[Service, Text]] = None, - version: str = BASE_PROTO_VERSION, + accept: Optional[Sequence[Text]] = None, + version: str = DEFAULT_VERSION, **kwargs, ): """ @@ -143,20 +142,14 @@ def __init__( ) self.requests_attach = list(requests_attach) if requests_attach else [] self.services = services - self.assign_version_to_message_type(version=version) + self.assign_version_to_message_type(version) + self.accept = accept @classmethod def wrap_message(cls, message: dict) -> AttachDecorator: """Convert an aries message to an attachment decorator.""" return AttachDecorator.data_json(mapping=message, ident="request-0") - @classmethod - def assign_version_to_message_type(cls, version: str): - """Assign version to Meta.message_type.""" - cls.Meta.message_type = Template(cls.Meta.message_type).substitute( - version=version - ) - def to_url(self, base_url: str = None) -> str: """ Convert an invitation message to URL format for sharing. @@ -208,6 +201,12 @@ class Meta: model_class = InvitationMessage unknown = EXCLUDE + _type = fields.Str( + data_key="@type", + required=False, + description="Message type", + example="https://didcomm.org/my-family/1.0/my-message-type", + ) label = fields.Str(required=False, description="Optional label", example="Bob") handshake_protocols = fields.List( fields.Str( @@ -219,6 +218,12 @@ class Meta: ), required=False, ) + accept = fields.List( + fields.Str(), + example=["didcomm/aip1", "didcomm/aip2;env=rfc19"], + description=("List of mime type in order of preference"), + required=False, + ) requests_attach = fields.Nested( AttachDecoratorSchema, required=False, diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/messages/problem_report.py b/aries_cloudagent/protocols/out_of_band/v1_0/messages/problem_report.py index 679adacb5a..3adc645746 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/messages/problem_report.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/messages/problem_report.py @@ -6,15 +6,15 @@ from marshmallow import ( EXCLUDE, + fields, pre_dump, validates_schema, ValidationError, ) -from string import Template from ....problem_report.v1_0.message import ProblemReport, ProblemReportSchema -from ..message_types import PROBLEM_REPORT, PROTOCOL_PACKAGE +from ..message_types import PROBLEM_REPORT, PROTOCOL_PACKAGE, DEFAULT_VERSION HANDLER_CLASS = ( f"{PROTOCOL_PACKAGE}.handlers" @@ -22,7 +22,6 @@ ) LOGGER = logging.getLogger(__name__) -BASE_PROTO_VERSION = "1.0" class ProblemReportReason(Enum): @@ -42,18 +41,11 @@ class Meta: message_type = PROBLEM_REPORT schema_class = "OOBProblemReportSchema" - def __init__(self, version: str = BASE_PROTO_VERSION, *args, **kwargs): + def __init__(self, version: str = DEFAULT_VERSION, *args, **kwargs): """Initialize a ProblemReport message instance.""" super().__init__(*args, **kwargs) self.assign_version_to_message_type(version=version) - @classmethod - def assign_version_to_message_type(cls, version: str): - """Assign version to Meta.message_type.""" - cls.Meta.message_type = Template(cls.Meta.message_type).substitute( - version=version - ) - class OOBProblemReportSchema(ProblemReportSchema): """Schema for ProblemReport base class.""" @@ -64,6 +56,13 @@ class Meta: model_class = OOBProblemReport unknown = EXCLUDE + _type = fields.Str( + data_key="@type", + required=False, + description="Message type", + example="https://didcomm.org/my-family/1.0/my-message-type", + ) + @pre_dump def check_thread_deco(self, obj, **kwargs): """Thread decorator, and its thid and pthid, are mandatory.""" diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/messages/reuse.py b/aries_cloudagent/protocols/out_of_band/v1_0/messages/reuse.py index f6b24d0e8b..c43d958060 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/messages/reuse.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/messages/reuse.py @@ -1,16 +1,14 @@ """Represents a Handshake Reuse message under RFC 0434.""" -from marshmallow import EXCLUDE, pre_dump, ValidationError -from string import Template +from marshmallow import EXCLUDE, fields, pre_dump, ValidationError from .....messaging.agent_message import AgentMessage, AgentMessageSchema -from ..message_types import MESSAGE_REUSE, PROTOCOL_PACKAGE +from ..message_types import MESSAGE_REUSE, PROTOCOL_PACKAGE, DEFAULT_VERSION HANDLER_CLASS = ( f"{PROTOCOL_PACKAGE}.handlers.reuse_handler.HandshakeReuseMessageHandler" ) -BASE_PROTO_VERSION = "1.0" class HandshakeReuse(AgentMessage): @@ -25,20 +23,13 @@ class Meta: def __init__( self, - version: str = BASE_PROTO_VERSION, + version: str = DEFAULT_VERSION, **kwargs, ): """Initialize Handshake Reuse message object.""" super().__init__(**kwargs) self.assign_version_to_message_type(version=version) - @classmethod - def assign_version_to_message_type(cls, version: str): - """Assign version to Meta.message_type.""" - cls.Meta.message_type = Template(cls.Meta.message_type).substitute( - version=version - ) - class HandshakeReuseSchema(AgentMessageSchema): """Handshake Reuse schema class.""" @@ -49,6 +40,13 @@ class Meta: model_class = HandshakeReuse unknown = EXCLUDE + _type = fields.Str( + data_key="@type", + required=False, + description="Message type", + example="https://didcomm.org/my-family/1.0/my-message-type", + ) + @pre_dump def check_thread_deco(self, obj, **kwargs): """Thread decorator, and its thid and pthid, are mandatory.""" diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/messages/reuse_accept.py b/aries_cloudagent/protocols/out_of_band/v1_0/messages/reuse_accept.py index e920506205..244271ccc8 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/messages/reuse_accept.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/messages/reuse_accept.py @@ -1,17 +1,15 @@ """Represents a Handshake Reuse Accept message under RFC 0434.""" -from marshmallow import EXCLUDE, pre_dump, ValidationError -from string import Template +from marshmallow import EXCLUDE, fields, pre_dump, ValidationError from .....messaging.agent_message import AgentMessage, AgentMessageSchema -from ..message_types import MESSAGE_REUSE_ACCEPT, PROTOCOL_PACKAGE +from ..message_types import MESSAGE_REUSE_ACCEPT, PROTOCOL_PACKAGE, DEFAULT_VERSION HANDLER_CLASS = ( f"{PROTOCOL_PACKAGE}.handlers" ".reuse_accept_handler.HandshakeReuseAcceptMessageHandler" ) -BASE_PROTO_VERSION = "1.0" class HandshakeReuseAccept(AgentMessage): @@ -26,20 +24,13 @@ class Meta: def __init__( self, - version: str = BASE_PROTO_VERSION, + version: str = DEFAULT_VERSION, **kwargs, ): """Initialize Handshake Reuse Accept object.""" super().__init__(**kwargs) self.assign_version_to_message_type(version=version) - @classmethod - def assign_version_to_message_type(cls, version: str): - """Assign version to Meta.message_type.""" - cls.Meta.message_type = Template(cls.Meta.message_type).substitute( - version=version - ) - class HandshakeReuseAcceptSchema(AgentMessageSchema): """Handshake Reuse Accept schema class.""" @@ -50,6 +41,13 @@ class Meta: model_class = HandshakeReuseAccept unknown = EXCLUDE + _type = fields.Str( + data_key="@type", + required=False, + description="Message type", + example="https://didcomm.org/my-family/1.0/my-message-type", + ) + @pre_dump def check_thread_deco(self, obj, **kwargs): """Thread decorator, and its thid and pthid, are mandatory.""" diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/messages/tests/test_invitation.py b/aries_cloudagent/protocols/out_of_band/v1_0/messages/tests/test_invitation.py index 83b80773a6..4d5e1be494 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/messages/tests/test_invitation.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/messages/tests/test_invitation.py @@ -53,7 +53,7 @@ def test_init(self): ) assert invi.services == [TEST_DID] assert invi._type == DIDCommPrefix.qualify_current( - Template(INVITATION).substitute(version="1.0") + Template(INVITATION).substitute(version="1.1") ) service = Service(_id="#inline", _type=DID_COMM, did=TEST_DID) @@ -62,6 +62,7 @@ def test_init(self): label="A label", handshake_protocols=[DIDCommPrefix.qualify_current(DIDX_PROTO)], services=[service], + version="1.0", ) assert invi_msg.services == [service] assert invi_msg._type == DIDCommPrefix.qualify_current( diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/routes.py b/aries_cloudagent/protocols/out_of_band/v1_0/routes.py index 59454a69d2..73e68d97ee 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/routes.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/routes.py @@ -75,15 +75,15 @@ class AttachmentDefSchema(OpenAPISchema): ), required=False, ) - # accept = fields.List( - # fields.Str(), - # description=( - # "List of mime type in order of preference that should be" - # " use in responding to the message" - # ), - # example="['didcomm/aip1', 'didcomm/aip2;env=rfc19']", - # required=False, - # ) + accept = fields.List( + fields.Str(), + description=( + "List of mime type in order of preference that should be" + " use in responding to the message" + ), + example=["didcomm/aip1", "didcomm/aip2;env=rfc19"], + required=False, + ) use_public_did = fields.Boolean( default=False, description="Whether to use public DID in invitation", @@ -160,6 +160,7 @@ async def invitation_create(request: web.BaseRequest): body = await request.json() if request.body_exists else {} attachments = body.get("attachments") handshake_protocols = body.get("handshake_protocols", []) + accept = body.get("accept") use_public_did = body.get("use_public_did", False) metadata = body.get("metadata") my_label = body.get("my_label") @@ -184,6 +185,7 @@ async def invitation_create(request: web.BaseRequest): metadata=metadata, alias=alias, mediation_id=mediation_id, + accept=accept, ) except (StorageNotFoundError, ValidationError, OutOfBandManagerError) as e: raise web.HTTPBadRequest(reason=e.roll_up) diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py index df39e85088..7cc72f28d6 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py @@ -14,6 +14,7 @@ from .....connections.models.diddoc import DIDDoc, PublicKey, PublicKeyType, Service from .....core.event_bus import EventBus from .....core.in_memory import InMemoryProfile +from .....core.util import get_version_from_message from .....core.oob_processor import OobMessageProcessor from .....did.did_key import DIDKey from .....messaging.decorators.attach_decorator import AttachDecorator @@ -389,7 +390,7 @@ async def test_create_invitation_handshake_succeeds(self): ) assert invi_rec.invitation._type == DIDCommPrefix.qualify_current( - Template(INVITATION).substitute(version="1.0") + Template(INVITATION).substitute(version="1.1") ) assert not invi_rec.invitation.requests_attach assert ( @@ -476,7 +477,7 @@ async def test_create_invitation_mediation_overwrites_routing_and_endpoint(self) ) assert isinstance(invite, InvitationRecord) assert invite.invitation._type == DIDCommPrefix.qualify_current( - Template(INVITATION).substitute(version="1.0") + Template(INVITATION).substitute(version="1.1") ) assert invite.invitation.label == "test123" assert ( @@ -790,12 +791,13 @@ async def test_create_invitation_peer_did(self): public=False, hs_protos=[test_module.HSProto.RFC23], multi_use=False, + accept=["didcomm/aip1", "didcomm/aip2;env=rfc19"], ) assert invi_rec._invitation.ser[ "@type" ] == DIDCommPrefix.qualify_current( - Template(INVITATION).substitute(version="1.0") + Template(INVITATION).substitute(version="1.1") ) assert not invi_rec._invitation.ser.get("requests~attach") assert invi_rec.invitation.label == "That guy" @@ -894,7 +896,7 @@ async def test_create_handshake_reuse_msg(self): ) oob_record = await self.manager._create_handshake_reuse_message( - oob_record, self.test_conn_rec + oob_record, self.test_conn_rec, get_version_from_message(invitation) ) _, kwargs = self.responder.send.call_args @@ -904,7 +906,7 @@ async def test_create_handshake_reuse_msg(self): # Assert responder has been called with the reuse message assert reuse_message._type == DIDCommPrefix.qualify_current( - Template(MESSAGE_REUSE).substitute(version="1.0") + Template(MESSAGE_REUSE).substitute(version="1.1") ) assert oob_record.reuse_msg_id == reuse_message._id @@ -918,7 +920,7 @@ async def test_create_handshake_reuse_msg_catch_exception(self): oob_mgr_fetch_conn.side_effect = StorageNotFoundError() with self.assertRaises(OutOfBandManagerError) as context: await self.manager._create_handshake_reuse_message( - async_mock.MagicMock(), self.test_conn_rec + async_mock.MagicMock(), self.test_conn_rec, "1.0" ) assert "Error on creating and sending a handshake reuse message" in str( context.exception @@ -989,7 +991,7 @@ async def test_receive_reuse_message_existing_found_multi_use(self): recipient_did_public=False, ) - reuse_msg = HandshakeReuse() + reuse_msg = HandshakeReuse(version="1.0") reuse_msg.assign_thread_id(thid="the-thread-id", pthid="the-pthid") self.test_conn_rec.invitation_msg_id = "test_123" @@ -1445,7 +1447,9 @@ async def test_receive_invitation_handshake_reuse(self): ) perform_handshake.assert_not_called() - handle_handshake_reuse.assert_called_once_with(ANY, test_exist_conn) + handle_handshake_reuse.assert_called_once_with( + ANY, test_exist_conn, get_version_from_message(oob_invitation) + ) assert result.state == OobRecord.STATE_ACCEPTED @@ -1504,12 +1508,15 @@ async def test_receive_invitation_handshake_reuse_failed(self): mediation_id="mediation_id", ) - handle_handshake_reuse.assert_called_once_with(ANY, test_exist_conn) + handle_handshake_reuse.assert_called_once_with( + ANY, test_exist_conn, get_version_from_message(oob_invitation) + ) perform_handshake.assert_called_once_with( oob_record=ANY, alias="alias", auto_accept=True, mediation_id="mediation_id", + accept=None, ) assert mock_oob.state == OobRecord.STATE_DONE diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_routes.py index d7aaffabf8..9b7b463bae 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_routes.py @@ -57,6 +57,47 @@ async def test_invitation_create(self): metadata=body["metadata"], alias=None, mediation_id=None, + accept=None, + ) + mock_json_response.assert_called_once_with({"abc": "123"}) + + async def test_invitation_create_with_accept(self): + self.request.query = { + "multi_use": "true", + "auto_accept": "true", + } + body = { + "attachments": async_mock.MagicMock(), + "handshake_protocols": [test_module.HSProto.RFC23.name], + "accept": ["didcomm/aip1", "didcomm/aip2;env=rfc19"], + "use_public_did": True, + "metadata": {"hello": "world"}, + } + self.request.json = async_mock.CoroutineMock(return_value=body) + + with async_mock.patch.object( + test_module, "OutOfBandManager", autospec=True + ) as mock_oob_mgr, async_mock.patch.object( + test_module.web, "json_response", async_mock.Mock() + ) as mock_json_response: + mock_oob_mgr.return_value.create_invitation = async_mock.CoroutineMock( + return_value=async_mock.MagicMock( + serialize=async_mock.MagicMock(return_value={"abc": "123"}) + ) + ) + + result = await test_module.invitation_create(self.request) + mock_oob_mgr.return_value.create_invitation.assert_called_once_with( + my_label=None, + auto_accept=True, + public=True, + multi_use=True, + hs_protos=[test_module.HSProto.RFC23], + attachments=body["attachments"], + metadata=body["metadata"], + alias=None, + mediation_id=None, + accept=["didcomm/aip1", "didcomm/aip2;env=rfc19"], ) mock_json_response.assert_called_once_with({"abc": "123"}) diff --git a/aries_cloudagent/resolver/base.py b/aries_cloudagent/resolver/base.py index 994bfe94b2..973482f417 100644 --- a/aries_cloudagent/resolver/base.py +++ b/aries_cloudagent/resolver/base.py @@ -5,7 +5,7 @@ from abc import ABC, abstractmethod from enum import Enum -from typing import NamedTuple, Pattern, Sequence, Union +from typing import Optional, NamedTuple, Pattern, Sequence, Union, Text from pydid import DID @@ -132,7 +132,12 @@ async def supports(self, profile: Profile, did: str) -> bool: return bool(supported_did_regex.match(did)) - async def resolve(self, profile: Profile, did: Union[str, DID]) -> dict: + async def resolve( + self, + profile: Profile, + did: Union[str, DID], + accept: Optional[Sequence[Text]] = None, + ) -> dict: """Resolve a DID using this resolver.""" if isinstance(did, DID): did = str(did) @@ -143,8 +148,10 @@ async def resolve(self, profile: Profile, did: Union[str, DID]) -> dict: f"{self.__class__.__name__} does not support DID method for: {did}" ) - return await self._resolve(profile, did) + return await self._resolve(profile, did, accept) @abstractmethod - async def _resolve(self, profile: Profile, did: str) -> dict: + async def _resolve( + self, profile: Profile, did: str, accept: Optional[Sequence[Text]] = None + ) -> dict: """Resolve a DID using this resolver.""" diff --git a/aries_cloudagent/resolver/default/indy.py b/aries_cloudagent/resolver/default/indy.py index e3e66688e6..38a19a85ad 100644 --- a/aries_cloudagent/resolver/default/indy.py +++ b/aries_cloudagent/resolver/default/indy.py @@ -4,7 +4,7 @@ """ import logging -from typing import Optional, Pattern +from typing import Optional, Pattern, Sequence, Text from pydid import DID, DIDDocumentBuilder from pydid.verification_method import Ed25519VerificationKey2018, VerificationMethod @@ -69,6 +69,7 @@ def add_services( builder: DIDDocumentBuilder, endpoints: Optional[dict], recipient_key: VerificationMethod = None, + accept: Optional[Sequence[Text]] = None, ): """Add services.""" if not endpoints: @@ -102,7 +103,7 @@ def add_services( priority=1, routing_keys=routing_keys, recipient_keys=[recipient_key.id], - accept=["didcomm/aip2;env=rfc19"], + accept=(accept if accept else ["didcomm/aip2;env=rfc19"]), ) if self.SERVICE_TYPE_DIDCOMM in types: @@ -112,6 +113,7 @@ def add_services( service_endpoint=endpoint, recipient_keys=[recipient_key.id], routing_keys=routing_keys, + # CHECKME accept=(accept if accept else ["didcomm/v2"]), accept=["didcomm/v2"], ) builder.context.append(self.CONTEXT_DIDCOMM_V2) @@ -128,7 +130,9 @@ def add_services( service_endpoint=endpoint, ) - async def _resolve(self, profile: Profile, did: str) -> dict: + async def _resolve( + self, profile: Profile, did: str, accept: Optional[Sequence[Text]] = None + ) -> dict: """Resolve an indy DID.""" multitenant_mgr = profile.inject_or(BaseMultitenantManager) if multitenant_mgr: @@ -158,7 +162,7 @@ async def _resolve(self, profile: Profile, did: str) -> dict: ) builder.authentication.reference(vmethod.id) builder.assertion_method.reference(vmethod.id) - self.add_services(builder, endpoints, vmethod) + self.add_services(builder, endpoints, vmethod, accept) result = builder.build() return result.serialize() diff --git a/aries_cloudagent/resolver/default/key.py b/aries_cloudagent/resolver/default/key.py index 2f9f10edf9..1f7e027dd0 100644 --- a/aries_cloudagent/resolver/default/key.py +++ b/aries_cloudagent/resolver/default/key.py @@ -3,7 +3,7 @@ Resolution is performed using the IndyLedger class. """ -from typing import Pattern +from typing import Optional, Pattern, Sequence, Text from ...did.did_key import DIDKey from ...config.injection_context import InjectionContext @@ -28,7 +28,9 @@ def supported_did_regex(self) -> Pattern: """Return supported_did_regex of Key DID Resolver.""" return DIDKeyType.PATTERN - async def _resolve(self, profile: Profile, did: str) -> dict: + async def _resolve( + self, profile: Profile, did: str, accept: Optional[Sequence[Text]] = None + ) -> dict: """Resolve a Key DID.""" try: did_key = DIDKey.from_did(did) diff --git a/aries_cloudagent/resolver/default/tests/test_indy.py b/aries_cloudagent/resolver/default/tests/test_indy.py index d1a17dd357..c2c53a3e92 100644 --- a/aries_cloudagent/resolver/default/tests/test_indy.py +++ b/aries_cloudagent/resolver/default/tests/test_indy.py @@ -70,6 +70,15 @@ async def test_resolve(self, profile: Profile, resolver: IndyDIDResolver): """Test resolve method.""" assert await resolver.resolve(profile, TEST_DID0) + @pytest.mark.asyncio + async def test_resolve_with_accept( + self, profile: Profile, resolver: IndyDIDResolver + ): + """Test resolve method.""" + assert await resolver.resolve( + profile, TEST_DID0, ["didcomm/aip1", "didcomm/aip2;env=rfc19"] + ) + @pytest.mark.asyncio async def test_resolve_multitenant( self, profile: Profile, resolver: IndyDIDResolver, ledger: BaseLedger diff --git a/aries_cloudagent/resolver/default/universal.py b/aries_cloudagent/resolver/default/universal.py index 6846d7f016..a43aee0e8e 100644 --- a/aries_cloudagent/resolver/default/universal.py +++ b/aries_cloudagent/resolver/default/universal.py @@ -2,7 +2,7 @@ import logging import re -from typing import Iterable, Optional, Pattern, Union +from typing import Iterable, Optional, Pattern, Sequence, Union, Text import aiohttp @@ -83,7 +83,9 @@ def supported_did_regex(self) -> Pattern: return self._supported_did_regex - async def _resolve(self, _profile: Profile, did: str) -> dict: + async def _resolve( + self, _profile: Profile, did: str, accept: Optional[Sequence[Text]] = None + ) -> dict: """Resolve DID through remote universal resolver.""" async with aiohttp.ClientSession() as session: diff --git a/aries_cloudagent/resolver/default/web.py b/aries_cloudagent/resolver/default/web.py index 30aa5ce440..987048209d 100644 --- a/aries_cloudagent/resolver/default/web.py +++ b/aries_cloudagent/resolver/default/web.py @@ -2,7 +2,7 @@ import urllib.parse -from typing import Pattern +from typing import Optional, Pattern, Sequence, Text import aiohttp @@ -57,7 +57,9 @@ def __transform_to_url(self, did): return "https://" + url + "/did.json" - async def _resolve(self, profile: Profile, did: str) -> dict: + async def _resolve( + self, profile: Profile, did: str, accept: Optional[Sequence[Text]] = None + ) -> dict: """Resolve did:web DIDs.""" url = self.__transform_to_url(did) diff --git a/aries_cloudagent/resolver/did_resolver.py b/aries_cloudagent/resolver/did_resolver.py index bf52959043..fb8a3f83fd 100644 --- a/aries_cloudagent/resolver/did_resolver.py +++ b/aries_cloudagent/resolver/did_resolver.py @@ -8,7 +8,7 @@ from datetime import datetime from itertools import chain import logging -from typing import List, Sequence, Tuple, Type, TypeVar, Union +from typing import Optional, List, Sequence, Tuple, Text, Type, TypeVar, Union from pydid import DID, DIDError, DIDUrl, Resource, NonconformantDocument from pydid.doc.doc import IDNotFoundError @@ -41,7 +41,10 @@ def register_resolver(self, resolver: BaseDIDResolver): self.resolvers.append(resolver) async def _resolve( - self, profile: Profile, did: Union[str, DID] + self, + profile: Profile, + did: Union[str, DID], + accept: Optional[Sequence[Text]] = None, ) -> Tuple[BaseDIDResolver, dict]: """Retrieve doc and return with resolver.""" # TODO Cache results @@ -55,6 +58,7 @@ async def _resolve( document = await resolver.resolve( profile, did, + accept, ) return resolver, document except DIDNotFound: @@ -62,9 +66,14 @@ async def _resolve( raise DIDNotFound(f"DID {did} could not be resolved") - async def resolve(self, profile: Profile, did: Union[str, DID]) -> dict: + async def resolve( + self, + profile: Profile, + did: Union[str, DID], + accept: Optional[Sequence[Text]] = None, + ) -> dict: """Resolve a DID.""" - _, doc = await self._resolve(profile, did) + _, doc = await self._resolve(profile, did, accept) return doc async def resolve_with_metadata( diff --git a/aries_cloudagent/resolver/tests/test_base.py b/aries_cloudagent/resolver/tests/test_base.py index cdae76a91e..2108468458 100644 --- a/aries_cloudagent/resolver/tests/test_base.py +++ b/aries_cloudagent/resolver/tests/test_base.py @@ -22,7 +22,7 @@ async def setup(self, context): def supported_did_regex(self): return re.compile("^did:example:[a-zA-Z0-9_.-]+$") - async def _resolve(self, profile, did) -> DIDDocument: + async def _resolve(self, profile, did, accept) -> DIDDocument: return DIDDocument("did:example:123") @@ -74,7 +74,7 @@ async def setup(self, context): def supported_methods(self): return ["example"] - async def _resolve(self, profile, did) -> DIDDocument: + async def _resolve(self, profile, did, accept) -> DIDDocument: return DIDDocument("did:example:123") with pytest.deprecated_call(): diff --git a/aries_cloudagent/resolver/tests/test_did_resolver.py b/aries_cloudagent/resolver/tests/test_did_resolver.py index f2c4ba9ab5..2bcbb4ed42 100644 --- a/aries_cloudagent/resolver/tests/test_did_resolver.py +++ b/aries_cloudagent/resolver/tests/test_did_resolver.py @@ -79,7 +79,7 @@ def supported_did_regex(self) -> Pattern: async def setup(self, context): pass - async def _resolve(self, profile, did): + async def _resolve(self, profile, did, accept): if isinstance(self.resolved, Exception): raise self.resolved return self.resolved.serialize() diff --git a/open-api/openapi.json b/open-api/openapi.json index e673680e53..d7a5599378 100644 --- a/open-api/openapi.json +++ b/open-api/openapi.json @@ -7328,6 +7328,14 @@ "description" : "Handshake protocol to specify in invitation" } }, + "accept" : { + "type" : "array", + "items" : { + "type" : "string", + "example" : "didcomm/aip1", + "description" : "Mime type list in order of preference to be used in response" + } + }, "mediation_id" : { "type" : "string", "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", @@ -7373,6 +7381,14 @@ "description" : "Handshake protocol" } }, + "accept" : { + "type" : "array", + "items" : { + "type" : "string", + "example" : "didcomm/aip1", + "description" : "Mime type list in order of preference to be used in response" + } + }, "label" : { "type" : "string", "example" : "Bob", From 348e665b5fab37ab0f3fdbe69fcc348a1fde2346 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Mon, 3 Oct 2022 14:29:11 -0700 Subject: [PATCH 512/872] rework based upon feedback Signed-off-by: Shaanjot Gill --- aries_cloudagent/core/protocol_registry.py | 6 ++--- aries_cloudagent/messaging/agent_message.py | 26 +++++++++---------- aries_cloudagent/messaging/models/base.py | 7 +---- .../out_of_band/v1_0/message_types.py | 13 +++++----- .../out_of_band/v1_0/messages/invitation.py | 6 ++++- .../v1_0/messages/problem_report.py | 14 ++++++++-- .../out_of_band/v1_0/messages/reuse.py | 7 ++++- .../out_of_band/v1_0/messages/reuse_accept.py | 7 ++++- .../v1_0/messages/tests/test_invitation.py | 26 ++++++++++++------- .../messages/tests/test_problem_report.py | 8 ++++++ .../v1_0/messages/tests/test_reuse.py | 8 ++++++ .../v1_0/messages/tests/test_reuse_accept.py | 20 ++++++++++++++ .../out_of_band/v1_0/tests/test_manager.py | 11 +++----- 13 files changed, 109 insertions(+), 50 deletions(-) diff --git a/aries_cloudagent/core/protocol_registry.py b/aries_cloudagent/core/protocol_registry.py index cd18a814dd..e0096e4b0f 100644 --- a/aries_cloudagent/core/protocol_registry.py +++ b/aries_cloudagent/core/protocol_registry.py @@ -2,7 +2,7 @@ import logging -from string import Template +from re import sub from typing import Mapping, Sequence from ..config.injection_context import InjectionContext @@ -102,8 +102,8 @@ def create_msg_types_for_minor_version(self, typesets, version_definition): def _get_updated_tyoeset_dict(self, typesets, to_check, updated_typeset) -> dict: for typeset in typesets: for msg_type_string, module_path in typeset.items(): - updated_msg_type_string = Template(msg_type_string).substitute( - version=to_check + updated_msg_type_string = sub( + r"(\d+\.)?(\*|\d+)", to_check, msg_type_string ) updated_typeset[updated_msg_type_string] = module_path return updated_typeset diff --git a/aries_cloudagent/messaging/agent_message.py b/aries_cloudagent/messaging/agent_message.py index b6e64f9b8a..130a2a1cbe 100644 --- a/aries_cloudagent/messaging/agent_message.py +++ b/aries_cloudagent/messaging/agent_message.py @@ -4,8 +4,8 @@ import uuid from collections import OrderedDict +from re import sub from typing import Mapping, Union -from string import Template from marshmallow import ( EXCLUDE, @@ -84,6 +84,7 @@ def __init__(self, _id: str = None, _decorators: BaseDecoratorSet = None): self.__class__.__name__ ) ) + self._message_type = self.Meta.message_type # Not required for now # if not self.Meta.handler_class: # raise TypeError( @@ -101,18 +102,6 @@ def _get_handler_class(cls): """ return resolve_class(cls.Meta.handler_class, cls) - @classmethod - def assign_version_to_message_type(cls, version: str): - """Assign version to Meta.message_type.""" - if "$version" in cls.Meta.message_type: - cls.Meta.message_type = Template(cls.Meta.message_type).substitute( - version=version - ) - else: - cls.Meta.message_type = re.sub( - r"(\d+\.)?(\*|\d+)", version, cls.Meta.message_type - ) - @property def Handler(self) -> type: """ @@ -133,7 +122,12 @@ def _type(self) -> str: Current DIDComm prefix, slash, message type defined on `Meta.message_type` """ - return DIDCommPrefix.qualify_current(self.Meta.message_type) + return DIDCommPrefix.qualify_current(self._message_type) + + @_type.setter + def _type(self, msg_type: str): + """Set the message type identifier.""" + self._message_type = msg_type @property def _id(self) -> str: @@ -161,6 +155,10 @@ def _decorators(self, value: BaseDecoratorSet): """Fetch the message's decorator set.""" self._message_decorators = value + def get_updated_msg_type(self, version: str) -> str: + """Update version to Meta.message_type.""" + return sub(r"(\d+\.)?(\*|\d+)", version, self.Meta.message_type) + def get_signature(self, field_name: str) -> SignatureDecorator: """ Get the signature for a named field. diff --git a/aries_cloudagent/messaging/models/base.py b/aries_cloudagent/messaging/models/base.py index b11cc8cd81..a4bae2c6ff 100644 --- a/aries_cloudagent/messaging/models/base.py +++ b/aries_cloudagent/messaging/models/base.py @@ -323,15 +323,10 @@ def make_model(self, data: dict, **kwargs): try: cls_inst = self.Model(**data) except TypeError as err: - msg_type_version = None if "_type" in str(err) and "_type" in data: - match = re.search(r"(\d+\.)?(\*|\d+)", data["_type"]) - if match: - msg_type_version = match.group() + data["msg_type"] = data["_type"] del data["_type"] cls_inst = self.Model(**data) - if msg_type_version: - cls_inst.assign_version_to_message_type(msg_type_version) return cls_inst @post_dump diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/message_types.py b/aries_cloudagent/protocols/out_of_band/v1_0/message_types.py index e2d539c906..e8fcd09a94 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/message_types.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/message_types.py @@ -9,17 +9,18 @@ "2da7fc4ee043effa3a9960150e7ba8c9a4628b68/features/0434-outofband" ) -# Message types -INVITATION = "out-of-band/$version/invitation" -MESSAGE_REUSE = "out-of-band/$version/handshake-reuse" -MESSAGE_REUSE_ACCEPT = "out-of-band/$version/handshake-reuse-accepted" -PROBLEM_REPORT = "out-of-band/$version/problem_report" - # Default Version DEFAULT_VERSION = get_proto_default_version( "aries_cloudagent.protocols.out_of_band.definition", 1 ) +# Message types +INVITATION = f"out-of-band/{DEFAULT_VERSION}/invitation" +MESSAGE_REUSE = f"out-of-band/{DEFAULT_VERSION}/handshake-reuse" +MESSAGE_REUSE_ACCEPT = f"out-of-band/{DEFAULT_VERSION}/handshake-reuse-accepted" +PROBLEM_REPORT = f"out-of-band/{DEFAULT_VERSION}/problem_report" + + PROTOCOL_PACKAGE = "aries_cloudagent.protocols.out_of_band.v1_0" MESSAGE_TYPES = DIDCommPrefix.qualify_all( diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/messages/invitation.py b/aries_cloudagent/protocols/out_of_band/v1_0/messages/invitation.py index 9229ab4921..99d0863572 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/messages/invitation.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/messages/invitation.py @@ -125,6 +125,7 @@ def __init__( services: Sequence[Union[Service, Text]] = None, accept: Optional[Sequence[Text]] = None, version: str = DEFAULT_VERSION, + msg_type: Optional[Text] = None, **kwargs, ): """ @@ -142,7 +143,10 @@ def __init__( ) self.requests_attach = list(requests_attach) if requests_attach else [] self.services = services - self.assign_version_to_message_type(version) + if msg_type: + self._type = msg_type + else: + self._type = self.get_updated_msg_type(version) self.accept = accept @classmethod diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/messages/problem_report.py b/aries_cloudagent/protocols/out_of_band/v1_0/messages/problem_report.py index 3adc645746..a4126dd348 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/messages/problem_report.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/messages/problem_report.py @@ -3,6 +3,7 @@ import logging from enum import Enum +from typing import Optional, Text from marshmallow import ( EXCLUDE, @@ -41,10 +42,19 @@ class Meta: message_type = PROBLEM_REPORT schema_class = "OOBProblemReportSchema" - def __init__(self, version: str = DEFAULT_VERSION, *args, **kwargs): + def __init__( + self, + version: str = DEFAULT_VERSION, + msg_type: Optional[Text] = None, + *args, + **kwargs, + ): """Initialize a ProblemReport message instance.""" super().__init__(*args, **kwargs) - self.assign_version_to_message_type(version=version) + if msg_type: + self._type = msg_type + else: + self._type = self.get_updated_msg_type(version) class OOBProblemReportSchema(ProblemReportSchema): diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/messages/reuse.py b/aries_cloudagent/protocols/out_of_band/v1_0/messages/reuse.py index c43d958060..6fc52064ce 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/messages/reuse.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/messages/reuse.py @@ -1,6 +1,7 @@ """Represents a Handshake Reuse message under RFC 0434.""" from marshmallow import EXCLUDE, fields, pre_dump, ValidationError +from typing import Optional, Text from .....messaging.agent_message import AgentMessage, AgentMessageSchema @@ -24,11 +25,15 @@ class Meta: def __init__( self, version: str = DEFAULT_VERSION, + msg_type: Optional[Text] = None, **kwargs, ): """Initialize Handshake Reuse message object.""" super().__init__(**kwargs) - self.assign_version_to_message_type(version=version) + if msg_type: + self._type = msg_type + else: + self._type = self.get_updated_msg_type(version) class HandshakeReuseSchema(AgentMessageSchema): diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/messages/reuse_accept.py b/aries_cloudagent/protocols/out_of_band/v1_0/messages/reuse_accept.py index 244271ccc8..977c2ea026 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/messages/reuse_accept.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/messages/reuse_accept.py @@ -1,6 +1,7 @@ """Represents a Handshake Reuse Accept message under RFC 0434.""" from marshmallow import EXCLUDE, fields, pre_dump, ValidationError +from typing import Optional, Text from .....messaging.agent_message import AgentMessage, AgentMessageSchema @@ -25,11 +26,15 @@ class Meta: def __init__( self, version: str = DEFAULT_VERSION, + msg_type: Optional[Text] = None, **kwargs, ): """Initialize Handshake Reuse Accept object.""" super().__init__(**kwargs) - self.assign_version_to_message_type(version=version) + if msg_type: + self._type = msg_type + else: + self._type = self.get_updated_msg_type(version) class HandshakeReuseAcceptSchema(AgentMessageSchema): diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/messages/tests/test_invitation.py b/aries_cloudagent/protocols/out_of_band/v1_0/messages/tests/test_invitation.py index 4d5e1be494..d00da45914 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/messages/tests/test_invitation.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/messages/tests/test_invitation.py @@ -1,6 +1,5 @@ import pytest -from string import Template from unittest import TestCase from ......messaging.models.base import BaseModelError @@ -10,6 +9,7 @@ from .....connections.v1_0.message_types import ARIES_PROTOCOL as CONN_PROTO from .....didcomm_prefix import DIDCommPrefix from .....didexchange.v1_0.message_types import ARIES_PROTOCOL as DIDX_PROTO +from .....didexchange.v1_0.messages.request import DIDXRequest from ...message_types import INVITATION @@ -45,16 +45,14 @@ def test_properties(self): class TestInvitationMessage(TestCase): def test_init(self): """Test initialization message.""" - invi = InvitationMessage( + invi_msg = InvitationMessage( comment="Hello", label="A label", handshake_protocols=[DIDCommPrefix.qualify_current(DIDX_PROTO)], services=[TEST_DID], ) - assert invi.services == [TEST_DID] - assert invi._type == DIDCommPrefix.qualify_current( - Template(INVITATION).substitute(version="1.1") - ) + assert invi_msg.services == [TEST_DID] + assert "out-of-band/1.1/invitation" in invi_msg._type service = Service(_id="#inline", _type=DID_COMM, did=TEST_DID) invi_msg = InvitationMessage( @@ -65,9 +63,7 @@ def test_init(self): version="1.0", ) assert invi_msg.services == [service] - assert invi_msg._type == DIDCommPrefix.qualify_current( - Template(INVITATION).substitute(version="1.0") - ) + assert "out-of-band/1.0/invitation" in invi_msg._type def test_wrap_serde(self): """Test conversion of aries message to attachment decorator.""" @@ -150,3 +146,15 @@ def test_invalid_invi_wrong_type_services(self): invi_schema = InvitationMessageSchema() with pytest.raises(test_module.ValidationError): invi_schema.validate_fields(obj_x) + + def test_assign_msg_type_version_to_model_inst(self): + test_msg = InvitationMessage() + assert "1.1" in test_msg._type + assert "1.1" in InvitationMessage.Meta.message_type + test_msg = InvitationMessage(version="1.2") + assert "1.2" in test_msg._type + assert "1.1" in InvitationMessage.Meta.message_type + test_req = DIDXRequest() + assert "1.0" in test_req._type + assert "1.2" in test_msg._type + assert "1.1" in InvitationMessage.Meta.message_type diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/messages/tests/test_problem_report.py b/aries_cloudagent/protocols/out_of_band/v1_0/messages/tests/test_problem_report.py index 0b605b179f..c594ad146b 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/messages/tests/test_problem_report.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/messages/tests/test_problem_report.py @@ -73,3 +73,11 @@ def test_validate_and_logger(self): self._caplog.set_level(logging.WARNING) OOBProblemReportSchema().validate_fields(data) assert "Unexpected error code received" in self._caplog.text + + def test_assign_msg_type_version_to_model_inst(self): + test_msg = OOBProblemReport() + assert "1.1" in test_msg._type + assert "1.1" in OOBProblemReport.Meta.message_type + test_msg = OOBProblemReport(version="1.2") + assert "1.2" in test_msg._type + assert "1.1" in OOBProblemReport.Meta.message_type diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/messages/tests/test_reuse.py b/aries_cloudagent/protocols/out_of_band/v1_0/messages/tests/test_reuse.py index 5cd79750d0..837698e718 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/messages/tests/test_reuse.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/messages/tests/test_reuse.py @@ -35,3 +35,11 @@ def test_pre_dump_x(self): """Exercise pre-dump serialization requirements.""" with pytest.raises(BaseModelError): data = self.reuse_msg.serialize() + + def test_assign_msg_type_version_to_model_inst(self): + test_msg = HandshakeReuse() + assert "1.1" in test_msg._type + assert "1.1" in HandshakeReuse.Meta.message_type + test_msg = HandshakeReuse(version="1.2") + assert "1.2" in test_msg._type + assert "1.1" in HandshakeReuse.Meta.message_type diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/messages/tests/test_reuse_accept.py b/aries_cloudagent/protocols/out_of_band/v1_0/messages/tests/test_reuse_accept.py index 3feca5439a..556493c618 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/messages/tests/test_reuse_accept.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/messages/tests/test_reuse_accept.py @@ -6,6 +6,8 @@ from ......messaging.models.base import BaseModelError +from .....didcomm_prefix import DIDCommPrefix + from ..reuse_accept import HandshakeReuseAccept, HandshakeReuseAcceptSchema @@ -31,7 +33,25 @@ def test_make_model(self): model_instance = HandshakeReuseAccept.deserialize(data) assert isinstance(model_instance, HandshakeReuseAccept) + def test_make_model_backward_comp(self): + """Make reuse-accept model.""" + self.reuse_accept_msg.assign_thread_id(thid="test_thid", pthid="test_pthid") + data = self.reuse_accept_msg.serialize() + data["@type"] = DIDCommPrefix.qualify_current( + "out-of-band/1.0/handshake-reuse-accepted" + ) + model_instance = HandshakeReuseAccept.deserialize(data) + assert isinstance(model_instance, HandshakeReuseAccept) + def test_pre_dump_x(self): """Exercise pre-dump serialization requirements.""" with pytest.raises(BaseModelError): data = self.reuse_accept_msg.serialize() + + def test_assign_msg_type_version_to_model_inst(self): + test_msg = HandshakeReuseAccept() + assert "1.1" in test_msg._type + assert "1.1" in HandshakeReuseAccept.Meta.message_type + test_msg = HandshakeReuseAccept(version="1.2") + assert "1.2" in test_msg._type + assert "1.1" in HandshakeReuseAccept.Meta.message_type diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py index 7cc72f28d6..2043d8d071 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py @@ -3,7 +3,6 @@ import json from copy import deepcopy from datetime import datetime, timedelta, timezone -from string import Template from typing import List from unittest.mock import ANY @@ -390,7 +389,7 @@ async def test_create_invitation_handshake_succeeds(self): ) assert invi_rec.invitation._type == DIDCommPrefix.qualify_current( - Template(INVITATION).substitute(version="1.1") + "out-of-band/1.1/invitation" ) assert not invi_rec.invitation.requests_attach assert ( @@ -477,7 +476,7 @@ async def test_create_invitation_mediation_overwrites_routing_and_endpoint(self) ) assert isinstance(invite, InvitationRecord) assert invite.invitation._type == DIDCommPrefix.qualify_current( - Template(INVITATION).substitute(version="1.1") + "out-of-band/1.1/invitation" ) assert invite.invitation.label == "test123" assert ( @@ -796,9 +795,7 @@ async def test_create_invitation_peer_did(self): assert invi_rec._invitation.ser[ "@type" - ] == DIDCommPrefix.qualify_current( - Template(INVITATION).substitute(version="1.1") - ) + ] == DIDCommPrefix.qualify_current("out-of-band/1.1/invitation") assert not invi_rec._invitation.ser.get("requests~attach") assert invi_rec.invitation.label == "That guy" assert ( @@ -906,7 +903,7 @@ async def test_create_handshake_reuse_msg(self): # Assert responder has been called with the reuse message assert reuse_message._type == DIDCommPrefix.qualify_current( - Template(MESSAGE_REUSE).substitute(version="1.1") + "out-of-band/1.1/handshake-reuse" ) assert oob_record.reuse_msg_id == reuse_message._id From 984de6787533c74a3082cc4d316b10d47d1db96b Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Mon, 3 Oct 2022 16:21:48 -0700 Subject: [PATCH 513/872] update protocol_registry and cleanup Signed-off-by: Shaanjot Gill --- aries_cloudagent/core/protocol_registry.py | 35 +++-- .../core/tests/test_protocol_registry.py | 123 ++++++++++++++++-- aries_cloudagent/messaging/agent_message.py | 1 - aries_cloudagent/messaging/models/base.py | 1 - 4 files changed, 132 insertions(+), 28 deletions(-) diff --git a/aries_cloudagent/core/protocol_registry.py b/aries_cloudagent/core/protocol_registry.py index e0096e4b0f..af941d085d 100644 --- a/aries_cloudagent/core/protocol_registry.py +++ b/aries_cloudagent/core/protocol_registry.py @@ -1,14 +1,14 @@ """Handle registration and publication of supported protocols.""" import logging +import re -from re import sub from typing import Mapping, Sequence from ..config.injection_context import InjectionContext from ..utils.classloader import ClassLoader -from .error import ProtocolMinorVersionNotSupported +from .error import ProtocolMinorVersionNotSupported, ProtocolDefinitionValidationError LOGGER = logging.getLogger(__name__) @@ -91,28 +91,34 @@ def create_msg_types_for_minor_version(self, typesets, version_definition): curr_minor_version = version_definition["current_minor_version"] min_minor_version = version_definition["minimum_minor_version"] major_version = version_definition["major_version"] - if curr_minor_version >= min_minor_version and curr_minor_version >= 1: + if curr_minor_version >= min_minor_version: for version_index in range(min_minor_version, curr_minor_version + 1): to_check = f"{str(major_version)}.{str(version_index)}" updated_typeset.update( self._get_updated_tyoeset_dict(typesets, to_check, updated_typeset) ) + else: + raise ProtocolDefinitionValidationError( + "min_minor_version is greater than curr_minor_version for the" + f" following typeset: {str(typesets)}" + ) return (updated_typeset,) def _get_updated_tyoeset_dict(self, typesets, to_check, updated_typeset) -> dict: for typeset in typesets: for msg_type_string, module_path in typeset.items(): - updated_msg_type_string = sub( + updated_msg_type_string = re.sub( r"(\d+\.)?(\*|\d+)", to_check, msg_type_string ) updated_typeset[updated_msg_type_string] = module_path return updated_typeset - def _template_message_type_check(self, typeset) -> bool: - for msg_type_string, _ in typeset.items(): - if "$version" in msg_type_string: - return True - return False + def _message_type_check_for_minor_verssion(self, version_definition) -> bool: + if not version_definition: + return False + curr_minor_version = version_definition["current_minor_version"] + min_minor_version = version_definition["minimum_minor_version"] + return bool(curr_minor_version >= 1 and curr_minor_version >= min_minor_version) def _create_and_register_updated_typesets(self, typesets, version_definition): updated_typesets = self.create_msg_types_for_minor_version( @@ -153,17 +159,18 @@ def register_message_types(self, *typesets, version_definition=None): """ # Maintain support for versionless protocol modules - template_msg_type_version = True updated_typesets = None - for typeset in typesets: - if not self._template_message_type_check(typeset): + minor_versions_supported = self._message_type_check_for_minor_verssion( + version_definition + ) + if not minor_versions_supported: + for typeset in typesets: self._typemap.update(typeset) - template_msg_type_version = False # Track versioned modules for version routing if version_definition: # create updated typesets for minor versions and register them - if template_msg_type_version: + if minor_versions_supported: updated_typesets = self._create_and_register_updated_typesets( typesets, version_definition ) diff --git a/aries_cloudagent/core/tests/test_protocol_registry.py b/aries_cloudagent/core/tests/test_protocol_registry.py index 15d99cbc36..d3b094f51f 100644 --- a/aries_cloudagent/core/tests/test_protocol_registry.py +++ b/aries_cloudagent/core/tests/test_protocol_registry.py @@ -47,16 +47,16 @@ def test_message_type_query(self): def test_create_msg_types_for_minor_version(self): test_typesets = ( { - "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/$version/fake-forward-invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.forward_invitation.ForwardInvitation", - "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/$version/fake-invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation.Invitation", - "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/$version/fake-invitation-request": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation_request.InvitationRequest", - "https://didcom.org/introduction-service/$version/fake-forward-invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.forward_invitation.ForwardInvitation", - "https://didcom.org/introduction-service/$version/fake-invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation.Invitation", - "https://didcom.org/introduction-service/$version/fake-invitation-request": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation_request.InvitationRequest", + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/1.0/fake-forward-invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.forward_invitation.ForwardInvitation", + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/1.0/fake-invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation.Invitation", + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/1.0/fake-invitation-request": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation_request.InvitationRequest", + "https://didcom.org/introduction-service/1.0/fake-forward-invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.forward_invitation.ForwardInvitation", + "https://didcom.org/introduction-service/1.0/fake-invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation.Invitation", + "https://didcom.org/introduction-service/1.0/fake-invitation-request": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation_request.InvitationRequest", }, ) test_version_def = { - "current_minor_version": 1, + "current_minor_version": 0, "major_version": 1, "minimum_minor_version": 0, "path": "v0_1", @@ -78,23 +78,122 @@ def test_create_msg_types_for_minor_version(self): in updated_typeset ) assert ( - "https://didcom.org/introduction-service/1.1/fake-forward-invitation" + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/1.0/fake-forward-invitation" in updated_typeset ) + + def test_introduction_create_msg_types_for_minor_version(self): + test_typesets = ( + { + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/0.1/invitation-request": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation_request.InvitationRequest", + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/0.1/invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation.Invitation", + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/0.1/forward-invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation_messages.forward_invitation.ForwardInvitation", + "https://didcom.org/introduction-service/0.1/invitation-request": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation_request.InvitationRequest", + "https://didcom.org/introduction-service/0.1/invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation.Invitation", + "https://didcom.org/introduction-service/0.1/forward-invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.forward_invitation.ForwardInvitation", + }, + ) + test_version_def = { + "current_minor_version": 1, + "major_version": 0, + "minimum_minor_version": 1, + "path": "v0_1", + } + updated_typesets = self.registry.create_msg_types_for_minor_version( + test_typesets, test_version_def + ) + updated_typeset = updated_typesets[0] assert ( - "https://didcom.org/introduction-service/1.1/fake-invitation" + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/0.1/invitation-request" in updated_typeset ) assert ( - "https://didcom.org/introduction-service/1.1/fake-invitation-request" + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/0.1/invitation" in updated_typeset ) assert ( - "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/1.0/fake-forward-invitation" + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/0.1/forward-invitation" + in updated_typeset + ) + assert ( + "https://didcom.org/introduction-service/0.1/invitation-request" + in updated_typeset + ) + assert ( + "https://didcom.org/introduction-service/0.1/invitation" in updated_typeset + ) + assert ( + "https://didcom.org/introduction-service/0.1/forward-invitation" + in updated_typeset + ) + + def test_oob_create_msg_types_for_minor_version(self): + test_typesets = ( + { + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/out-of-band/1.1/invitation": "aries_cloudagent.protocols.out_of_band.v1_0.messages.invitation.Invitation", + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/out-of-band/1.1/handshake-reuse": "aries_cloudagent.protocols.out_of_band.v1_0.messages.reuse.HandshakeReuse", + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/out-of-band/1.1/handshake-reuse-accepted": "aries_cloudagent.protocols.out_of_band.v1_0.messages.reuse_accept.HandshakeReuseAccept", + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/out-of-band/1.1/problem_report": "aries_cloudagent.protocols.out_of_band.v1_0.messages.problem_report.OOBProblemReport", + "https://didcom.org/out-of-band/1.1/invitation": "aries_cloudagent.protocols.out_of_band.v1_0.messages.invitation.Invitation", + "https://didcom.org/out-of-band/1.1/handshake-reuse": "aries_cloudagent.protocols.out_of_band.v1_0.messages.reuse.HandshakeReuse", + "https://didcom.org/out-of-band/1.1/handshake-reuse-accepted": "aries_cloudagent.protocols.out_of_band.v1_0.messages.reuse_accept.HandshakeReuseAccept", + "https://didcom.org/out-of-band/1.1/problem_report": "aries_cloudagent.protocols.out_of_band.v1_0.messages.problem_report.OOBProblemReport", + }, + ) + test_version_def = { + "current_minor_version": 1, + "major_version": 1, + "minimum_minor_version": 0, + "path": "v0_1", + } + updated_typesets = self.registry.create_msg_types_for_minor_version( + test_typesets, test_version_def + ) + updated_typeset = updated_typesets[0] + assert "https://didcom.org/out-of-band/1.0/invitation" in updated_typeset + assert "https://didcom.org/out-of-band/1.0/handshake-reuse" in updated_typeset + assert ( + "https://didcom.org/out-of-band/1.0/handshake-reuse-accepted" + in updated_typeset + ) + assert "https://didcom.org/out-of-band/1.0/problem_report" in updated_typeset + assert "https://didcom.org/out-of-band/1.1/invitation" in updated_typeset + assert "https://didcom.org/out-of-band/1.1/handshake-reuse" in updated_typeset + assert ( + "https://didcom.org/out-of-band/1.1/handshake-reuse-accepted" + in updated_typeset + ) + assert "https://didcom.org/out-of-band/1.1/problem_report" in updated_typeset + assert ( + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/out-of-band/1.0/invitation" + in updated_typeset + ) + assert ( + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/out-of-band/1.0/handshake-reuse" + in updated_typeset + ) + assert ( + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/out-of-band/1.0/handshake-reuse-accepted" + in updated_typeset + ) + assert ( + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/out-of-band/1.0/problem_report" + in updated_typeset + ) + assert ( + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/out-of-band/1.1/invitation" + in updated_typeset + ) + assert ( + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/out-of-band/1.1/handshake-reuse" + in updated_typeset + ) + assert ( + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/out-of-band/1.1/handshake-reuse-accepted" in updated_typeset ) assert ( - "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/1.1/fake-invitation-request" + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/out-of-band/1.1/problem_report" in updated_typeset ) diff --git a/aries_cloudagent/messaging/agent_message.py b/aries_cloudagent/messaging/agent_message.py index 130a2a1cbe..c13351ef67 100644 --- a/aries_cloudagent/messaging/agent_message.py +++ b/aries_cloudagent/messaging/agent_message.py @@ -1,6 +1,5 @@ """Agent message base class and schema.""" -import re import uuid from collections import OrderedDict diff --git a/aries_cloudagent/messaging/models/base.py b/aries_cloudagent/messaging/models/base.py index a4bae2c6ff..8eb0601a6c 100644 --- a/aries_cloudagent/messaging/models/base.py +++ b/aries_cloudagent/messaging/models/base.py @@ -1,6 +1,5 @@ """Base classes for Models and Schemas.""" -import re import logging import json From f5a74e7c21a4b87e8598e586740e40925ca0c444 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Tue, 4 Oct 2022 06:25:47 -0700 Subject: [PATCH 514/872] create 1.0 OOB invitation Signed-off-by: Shaanjot Gill --- .../protocols/out_of_band/v1_0/manager.py | 12 +++++++++--- .../protocols/out_of_band/v1_0/routes.py | 7 +++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py index 1932055dd9..56cb6b7c3a 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py @@ -37,6 +37,7 @@ from .models.invitation import InvitationRecord from .models.oob_record import OobRecord from .messages.service import Service +from .message_types import DEFAULT_VERSION LOGGER = logging.getLogger(__name__) REUSE_WEBHOOK_TOPIC = "acapy::webhook::connection_reuse" @@ -88,6 +89,7 @@ async def create_invitation( metadata: dict = None, mediation_id: str = None, accept: Optional[Sequence[Text]] = None, + protocol_version: Optional[Text] = None, ) -> InvitationRecord: """ Generate new connection invitation. @@ -108,6 +110,7 @@ async def create_invitation( attachments: list of dicts in form of {"id": ..., "type": ...} accept: Optional list of mime types in the order of preference of the sender that the receiver can use in responding to the message + protocol_version: OOB protocol version [1.0, 1.1] Returns: Invitation record @@ -230,7 +233,8 @@ async def create_invitation( handshake_protocols=handshake_protocols, requests_attach=message_attachments, services=[f"did:sov:{public_did.did}"], - accept=accept, + accept=accept if protocol_version != "1.0" else None, + version=protocol_version or DEFAULT_VERSION, ) our_recipient_key = public_did.verkey @@ -276,7 +280,9 @@ async def create_invitation( # Initializing InvitationMessage here to include # invitation_msg_id in webhook poyload - invi_msg = InvitationMessage(_id=invitation_message_id) + invi_msg = InvitationMessage( + _id=invitation_message_id, version=protocol_version or DEFAULT_VERSION + ) if handshake_protocols: invitation_mode = ( @@ -326,7 +332,7 @@ async def create_invitation( invi_msg.label = my_label or self.profile.settings.get("default_label") invi_msg.handshake_protocols = handshake_protocols invi_msg.requests_attach = message_attachments - invi_msg.accept = accept + invi_msg.accept = accept if protocol_version != "1.0" else None invi_msg.services = [ ServiceMessage( _id="#inline", diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/routes.py b/aries_cloudagent/protocols/out_of_band/v1_0/routes.py index 73e68d97ee..89e3e2cb0b 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/routes.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/routes.py @@ -101,6 +101,11 @@ class AttachmentDefSchema(OpenAPISchema): required=False, example="Invitation to Barry", ) + protocol_version = fields.Str( + description="OOB protocol version", + required=False, + example="1.1", + ) alias = fields.Str( description="Alias for connection", required=False, @@ -166,6 +171,7 @@ async def invitation_create(request: web.BaseRequest): my_label = body.get("my_label") alias = body.get("alias") mediation_id = body.get("mediation_id") + protocol_version = body.get("protocol_version") multi_use = json.loads(request.query.get("multi_use", "false")) auto_accept = json.loads(request.query.get("auto_accept", "null")) @@ -186,6 +192,7 @@ async def invitation_create(request: web.BaseRequest): alias=alias, mediation_id=mediation_id, accept=accept, + protocol_version=protocol_version, ) except (StorageNotFoundError, ValidationError, OutOfBandManagerError) as e: raise web.HTTPBadRequest(reason=e.roll_up) From 8bf9a69bae862882b293858d5817640adbde5f88 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Tue, 4 Oct 2022 06:50:41 -0700 Subject: [PATCH 515/872] test fix and sonar check cleanup Signed-off-by: Shaanjot Gill --- .../core/tests/test_protocol_registry.py | 43 ++++++++++--------- .../out_of_band/v1_0/tests/test_manager.py | 7 +-- .../out_of_band/v1_0/tests/test_routes.py | 4 +- 3 files changed, 30 insertions(+), 24 deletions(-) diff --git a/aries_cloudagent/core/tests/test_protocol_registry.py b/aries_cloudagent/core/tests/test_protocol_registry.py index d3b094f51f..623ad1d808 100644 --- a/aries_cloudagent/core/tests/test_protocol_registry.py +++ b/aries_cloudagent/core/tests/test_protocol_registry.py @@ -45,14 +45,15 @@ def test_message_type_query(self): assert matches == () def test_create_msg_types_for_minor_version(self): + MSG_PATH = "aries_cloudagent.protocols.introduction.v0_1.messages" test_typesets = ( { - "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/1.0/fake-forward-invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.forward_invitation.ForwardInvitation", - "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/1.0/fake-invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation.Invitation", - "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/1.0/fake-invitation-request": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation_request.InvitationRequest", - "https://didcom.org/introduction-service/1.0/fake-forward-invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.forward_invitation.ForwardInvitation", - "https://didcom.org/introduction-service/1.0/fake-invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation.Invitation", - "https://didcom.org/introduction-service/1.0/fake-invitation-request": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation_request.InvitationRequest", + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/1.0/fake-forward-invitation": f"{MSG_PATH}.forward_invitation.ForwardInvitation", + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/1.0/fake-invitation": f"{MSG_PATH}.invitation.Invitation", + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/1.0/fake-invitation-request": f"{MSG_PATH}.invitation_request.InvitationRequest", + "https://didcom.org/introduction-service/1.0/fake-forward-invitation": f"{MSG_PATH}.forward_invitation.ForwardInvitation", + "https://didcom.org/introduction-service/1.0/fake-invitation": f"{MSG_PATH}.invitation.Invitation", + "https://didcom.org/introduction-service/1.0/fake-invitation-request": f"{MSG_PATH}.invitation_request.InvitationRequest", }, ) test_version_def = { @@ -83,14 +84,15 @@ def test_create_msg_types_for_minor_version(self): ) def test_introduction_create_msg_types_for_minor_version(self): + MSG_PATH = "aries_cloudagent.protocols.introduction.v0_1.messages" test_typesets = ( { - "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/0.1/invitation-request": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation_request.InvitationRequest", - "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/0.1/invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation.Invitation", - "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/0.1/forward-invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation_messages.forward_invitation.ForwardInvitation", - "https://didcom.org/introduction-service/0.1/invitation-request": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation_request.InvitationRequest", - "https://didcom.org/introduction-service/0.1/invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.invitation.Invitation", - "https://didcom.org/introduction-service/0.1/forward-invitation": "aries_cloudagent.protocols.introduction.v0_1.messages.forward_invitation.ForwardInvitation", + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/0.1/invitation-request": f"{MSG_PATH}.invitation_request.InvitationRequest", + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/0.1/invitation": f"{MSG_PATH}.invitation.Invitation", + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/introduction-service/0.1/forward-invitation": f"{MSG_PATH}.invitation_messages.forward_invitation.ForwardInvitation", + "https://didcom.org/introduction-service/0.1/invitation-request": f"{MSG_PATH}.invitation_request.InvitationRequest", + "https://didcom.org/introduction-service/0.1/invitation": f"{MSG_PATH}.invitation.Invitation", + "https://didcom.org/introduction-service/0.1/forward-invitation": f"{MSG_PATH}.forward_invitation.ForwardInvitation", }, ) test_version_def = { @@ -128,16 +130,17 @@ def test_introduction_create_msg_types_for_minor_version(self): ) def test_oob_create_msg_types_for_minor_version(self): + MSG_PATH = "aries_cloudagent.protocols.out_of_band.v1_0.messages" test_typesets = ( { - "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/out-of-band/1.1/invitation": "aries_cloudagent.protocols.out_of_band.v1_0.messages.invitation.Invitation", - "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/out-of-band/1.1/handshake-reuse": "aries_cloudagent.protocols.out_of_band.v1_0.messages.reuse.HandshakeReuse", - "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/out-of-band/1.1/handshake-reuse-accepted": "aries_cloudagent.protocols.out_of_band.v1_0.messages.reuse_accept.HandshakeReuseAccept", - "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/out-of-band/1.1/problem_report": "aries_cloudagent.protocols.out_of_band.v1_0.messages.problem_report.OOBProblemReport", - "https://didcom.org/out-of-band/1.1/invitation": "aries_cloudagent.protocols.out_of_band.v1_0.messages.invitation.Invitation", - "https://didcom.org/out-of-band/1.1/handshake-reuse": "aries_cloudagent.protocols.out_of_band.v1_0.messages.reuse.HandshakeReuse", - "https://didcom.org/out-of-band/1.1/handshake-reuse-accepted": "aries_cloudagent.protocols.out_of_band.v1_0.messages.reuse_accept.HandshakeReuseAccept", - "https://didcom.org/out-of-band/1.1/problem_report": "aries_cloudagent.protocols.out_of_band.v1_0.messages.problem_report.OOBProblemReport", + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/out-of-band/1.1/invitation": f"{MSG_PATH}.invitation.Invitation", + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/out-of-band/1.1/handshake-reuse": f"{MSG_PATH}.reuse.HandshakeReuse", + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/out-of-band/1.1/handshake-reuse-accepted": f"{MSG_PATH}.reuse_accept.HandshakeReuseAccept", + "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/out-of-band/1.1/problem_report": f"{MSG_PATH}.problem_report.OOBProblemReport", + "https://didcom.org/out-of-band/1.1/invitation": f"{MSG_PATH}.invitation.Invitation", + "https://didcom.org/out-of-band/1.1/handshake-reuse": f"{MSG_PATH}.reuse.HandshakeReuse", + "https://didcom.org/out-of-band/1.1/handshake-reuse-accepted": f"{MSG_PATH}.reuse_accept.HandshakeReuseAccept", + "https://didcom.org/out-of-band/1.1/problem_report": f"{MSG_PATH}.problem_report.OOBProblemReport", }, ) test_version_def = { diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py index 2043d8d071..07c632e5eb 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py @@ -103,6 +103,7 @@ class TestConfig: service_endpoint=test_endpoint, ) NOW_8601 = datetime.utcnow().replace(tzinfo=timezone.utc).isoformat(" ", "seconds") + TEST_INVI_MESSAGE_TYPE = "out-of-band/1.1/invitation" NOW_EPOCH = str_to_epoch(NOW_8601) CD_ID = "GMm4vMw8LLrLJjp81kRRLp:3:CL:12:tag" INDY_PROOF_REQ = json.loads( @@ -389,7 +390,7 @@ async def test_create_invitation_handshake_succeeds(self): ) assert invi_rec.invitation._type == DIDCommPrefix.qualify_current( - "out-of-band/1.1/invitation" + self.TEST_INVI_MESSAGE_TYPE ) assert not invi_rec.invitation.requests_attach assert ( @@ -476,7 +477,7 @@ async def test_create_invitation_mediation_overwrites_routing_and_endpoint(self) ) assert isinstance(invite, InvitationRecord) assert invite.invitation._type == DIDCommPrefix.qualify_current( - "out-of-band/1.1/invitation" + self.TEST_INVI_MESSAGE_TYPE ) assert invite.invitation.label == "test123" assert ( @@ -795,7 +796,7 @@ async def test_create_invitation_peer_did(self): assert invi_rec._invitation.ser[ "@type" - ] == DIDCommPrefix.qualify_current("out-of-band/1.1/invitation") + ] == DIDCommPrefix.qualify_current(self.TEST_INVI_MESSAGE_TYPE) assert not invi_rec._invitation.ser.get("requests~attach") assert invi_rec.invitation.label == "That guy" assert ( diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_routes.py index 9b7b463bae..9006a27021 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_routes.py @@ -58,6 +58,7 @@ async def test_invitation_create(self): alias=None, mediation_id=None, accept=None, + protocol_version=None, ) mock_json_response.assert_called_once_with({"abc": "123"}) @@ -86,7 +87,7 @@ async def test_invitation_create_with_accept(self): ) ) - result = await test_module.invitation_create(self.request) + await test_module.invitation_create(self.request) mock_oob_mgr.return_value.create_invitation.assert_called_once_with( my_label=None, auto_accept=True, @@ -98,6 +99,7 @@ async def test_invitation_create_with_accept(self): alias=None, mediation_id=None, accept=["didcomm/aip1", "didcomm/aip2;env=rfc19"], + protocol_version=None, ) mock_json_response.assert_called_once_with({"abc": "123"}) From 006b8f63c9d22bc5a4bc3f797dfb70bba50f3f34 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Tue, 4 Oct 2022 09:15:43 -0700 Subject: [PATCH 516/872] change parameter to service_accept and typo fix Signed-off-by: Shaanjot Gill --- aries_cloudagent/connections/base_manager.py | 4 ++-- aries_cloudagent/core/protocol_registry.py | 4 ++-- .../protocols/out_of_band/v1_0/manager.py | 20 ++++++++++--------- .../out_of_band/v1_0/messages/invitation.py | 6 +++--- .../protocols/out_of_band/v1_0/routes.py | 4 ++-- .../out_of_band/v1_0/tests/test_manager.py | 4 ++-- .../out_of_band/v1_0/tests/test_routes.py | 4 ++-- aries_cloudagent/resolver/base.py | 9 ++++++--- aries_cloudagent/resolver/default/indy.py | 15 +++++++++----- aries_cloudagent/resolver/default/key.py | 5 ++++- .../resolver/default/universal.py | 5 ++++- aries_cloudagent/resolver/default/web.py | 5 ++++- aries_cloudagent/resolver/did_resolver.py | 8 ++++---- 13 files changed, 56 insertions(+), 37 deletions(-) diff --git a/aries_cloudagent/connections/base_manager.py b/aries_cloudagent/connections/base_manager.py index 79bef004bf..4308705c31 100644 --- a/aries_cloudagent/connections/base_manager.py +++ b/aries_cloudagent/connections/base_manager.py @@ -228,7 +228,7 @@ async def remove_keys_for_did(self, did: str): await storage.delete_all_records(self.RECORD_TYPE_DID_KEY, {"did": did}) async def resolve_invitation( - self, did: str, accept: Optional[Sequence[Text]] = None + self, did: str, service_accept: Optional[Sequence[Text]] = None ): """ Resolve invitation with the DID Resolver. @@ -243,7 +243,7 @@ async def resolve_invitation( resolver = self._profile.inject(DIDResolver) try: - doc_dict: dict = await resolver.resolve(self._profile, did, accept) + doc_dict: dict = await resolver.resolve(self._profile, did, service_accept) doc: ResolvedDocument = pydid.deserialize_document(doc_dict, strict=True) except ResolverError as error: raise BaseConnectionManagerError( diff --git a/aries_cloudagent/core/protocol_registry.py b/aries_cloudagent/core/protocol_registry.py index af941d085d..90175f9109 100644 --- a/aries_cloudagent/core/protocol_registry.py +++ b/aries_cloudagent/core/protocol_registry.py @@ -95,7 +95,7 @@ def create_msg_types_for_minor_version(self, typesets, version_definition): for version_index in range(min_minor_version, curr_minor_version + 1): to_check = f"{str(major_version)}.{str(version_index)}" updated_typeset.update( - self._get_updated_tyoeset_dict(typesets, to_check, updated_typeset) + self._get_updated_typeset_dict(typesets, to_check, updated_typeset) ) else: raise ProtocolDefinitionValidationError( @@ -104,7 +104,7 @@ def create_msg_types_for_minor_version(self, typesets, version_definition): ) return (updated_typeset,) - def _get_updated_tyoeset_dict(self, typesets, to_check, updated_typeset) -> dict: + def _get_updated_typeset_dict(self, typesets, to_check, updated_typeset) -> dict: for typeset in typesets: for msg_type_string, module_path in typeset.items(): updated_msg_type_string = re.sub( diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py index 56cb6b7c3a..3420e57213 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py @@ -88,7 +88,7 @@ async def create_invitation( attachments: Sequence[Mapping] = None, metadata: dict = None, mediation_id: str = None, - accept: Optional[Sequence[Text]] = None, + service_accept: Optional[Sequence[Text]] = None, protocol_version: Optional[Text] = None, ) -> InvitationRecord: """ @@ -108,7 +108,7 @@ async def create_invitation( multi_use: set to True to create an invitation for multiple-use connection alias: optional alias to apply to connection for later use attachments: list of dicts in form of {"id": ..., "type": ...} - accept: Optional list of mime types in the order of preference of the sender + service_accept: Optional list of mime types in the order of preference of the sender that the receiver can use in responding to the message protocol_version: OOB protocol version [1.0, 1.1] @@ -233,7 +233,7 @@ async def create_invitation( handshake_protocols=handshake_protocols, requests_attach=message_attachments, services=[f"did:sov:{public_did.did}"], - accept=accept if protocol_version != "1.0" else None, + service_accept=service_accept if protocol_version != "1.0" else None, version=protocol_version or DEFAULT_VERSION, ) @@ -332,7 +332,9 @@ async def create_invitation( invi_msg.label = my_label or self.profile.settings.get("default_label") invi_msg.handshake_protocols = handshake_protocols invi_msg.requests_attach = message_attachments - invi_msg.accept = accept if protocol_version != "1.0" else None + invi_msg.service_accept = ( + service_accept if protocol_version != "1.0" else None + ) invi_msg.services = [ ServiceMessage( _id="#inline", @@ -426,8 +428,8 @@ async def receive_invitation( # Get the single service item oob_service_item = invitation.services[0] - # accept - accept = invitation.accept + # service_accept + service_accept = invitation.service_accept # Get the DID public did, if any public_did = None @@ -483,7 +485,7 @@ async def receive_invitation( alias=alias, auto_accept=auto_accept, mediation_id=mediation_id, - accept=accept, + service_accept=service_accept, ) LOGGER.debug( f"Performed handshake with connection {oob_record.connection_id}" @@ -738,7 +740,7 @@ async def _perform_handshake( alias: Optional[str] = None, auto_accept: Optional[bool] = None, mediation_id: Optional[str] = None, - accept: Optional[Sequence[Text]] = None, + service_accept: Optional[Sequence[Text]] = None, ) -> OobRecord: invitation = oob_record.invitation @@ -767,7 +769,7 @@ async def _perform_handshake( # ED25519 keys endpoint, recipient_keys, routing_keys = await self.resolve_invitation( service, - accept=accept, + service_accept=service_accept, ) service = ServiceMessage.deserialize( { diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/messages/invitation.py b/aries_cloudagent/protocols/out_of_band/v1_0/messages/invitation.py index 99d0863572..18adebbf8b 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/messages/invitation.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/messages/invitation.py @@ -123,7 +123,7 @@ def __init__( handshake_protocols: Sequence[Text] = None, requests_attach: Sequence[AttachDecorator] = None, services: Sequence[Union[Service, Text]] = None, - accept: Optional[Sequence[Text]] = None, + service_accept: Optional[Sequence[Text]] = None, version: str = DEFAULT_VERSION, msg_type: Optional[Text] = None, **kwargs, @@ -147,7 +147,7 @@ def __init__( self._type = msg_type else: self._type = self.get_updated_msg_type(version) - self.accept = accept + self.service_accept = service_accept @classmethod def wrap_message(cls, message: dict) -> AttachDecorator: @@ -222,7 +222,7 @@ class Meta: ), required=False, ) - accept = fields.List( + service_accept = fields.List( fields.Str(), example=["didcomm/aip1", "didcomm/aip2;env=rfc19"], description=("List of mime type in order of preference"), diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/routes.py b/aries_cloudagent/protocols/out_of_band/v1_0/routes.py index 89e3e2cb0b..3b9b489500 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/routes.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/routes.py @@ -165,7 +165,7 @@ async def invitation_create(request: web.BaseRequest): body = await request.json() if request.body_exists else {} attachments = body.get("attachments") handshake_protocols = body.get("handshake_protocols", []) - accept = body.get("accept") + service_accept = body.get("accept") use_public_did = body.get("use_public_did", False) metadata = body.get("metadata") my_label = body.get("my_label") @@ -191,7 +191,7 @@ async def invitation_create(request: web.BaseRequest): metadata=metadata, alias=alias, mediation_id=mediation_id, - accept=accept, + service_accept=service_accept, protocol_version=protocol_version, ) except (StorageNotFoundError, ValidationError, OutOfBandManagerError) as e: diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py index 07c632e5eb..a9c03cc3da 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py @@ -791,7 +791,7 @@ async def test_create_invitation_peer_did(self): public=False, hs_protos=[test_module.HSProto.RFC23], multi_use=False, - accept=["didcomm/aip1", "didcomm/aip2;env=rfc19"], + service_accept=["didcomm/aip1", "didcomm/aip2;env=rfc19"], ) assert invi_rec._invitation.ser[ @@ -1514,7 +1514,7 @@ async def test_receive_invitation_handshake_reuse_failed(self): alias="alias", auto_accept=True, mediation_id="mediation_id", - accept=None, + service_accept=None, ) assert mock_oob.state == OobRecord.STATE_DONE diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_routes.py index 9006a27021..cf0349c34a 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_routes.py @@ -57,7 +57,7 @@ async def test_invitation_create(self): metadata=body["metadata"], alias=None, mediation_id=None, - accept=None, + service_accept=None, protocol_version=None, ) mock_json_response.assert_called_once_with({"abc": "123"}) @@ -98,7 +98,7 @@ async def test_invitation_create_with_accept(self): metadata=body["metadata"], alias=None, mediation_id=None, - accept=["didcomm/aip1", "didcomm/aip2;env=rfc19"], + service_accept=["didcomm/aip1", "didcomm/aip2;env=rfc19"], protocol_version=None, ) mock_json_response.assert_called_once_with({"abc": "123"}) diff --git a/aries_cloudagent/resolver/base.py b/aries_cloudagent/resolver/base.py index 973482f417..df31cf4dd3 100644 --- a/aries_cloudagent/resolver/base.py +++ b/aries_cloudagent/resolver/base.py @@ -136,7 +136,7 @@ async def resolve( self, profile: Profile, did: Union[str, DID], - accept: Optional[Sequence[Text]] = None, + service_accept: Optional[Sequence[Text]] = None, ) -> dict: """Resolve a DID using this resolver.""" if isinstance(did, DID): @@ -148,10 +148,13 @@ async def resolve( f"{self.__class__.__name__} does not support DID method for: {did}" ) - return await self._resolve(profile, did, accept) + return await self._resolve(profile, did, service_accept) @abstractmethod async def _resolve( - self, profile: Profile, did: str, accept: Optional[Sequence[Text]] = None + self, + profile: Profile, + did: str, + service_accept: Optional[Sequence[Text]] = None, ) -> dict: """Resolve a DID using this resolver.""" diff --git a/aries_cloudagent/resolver/default/indy.py b/aries_cloudagent/resolver/default/indy.py index 38a19a85ad..3645274a84 100644 --- a/aries_cloudagent/resolver/default/indy.py +++ b/aries_cloudagent/resolver/default/indy.py @@ -69,7 +69,7 @@ def add_services( builder: DIDDocumentBuilder, endpoints: Optional[dict], recipient_key: VerificationMethod = None, - accept: Optional[Sequence[Text]] = None, + service_accept: Optional[Sequence[Text]] = None, ): """Add services.""" if not endpoints: @@ -103,7 +103,9 @@ def add_services( priority=1, routing_keys=routing_keys, recipient_keys=[recipient_key.id], - accept=(accept if accept else ["didcomm/aip2;env=rfc19"]), + accept=( + service_accept if service_accept else ["didcomm/aip2;env=rfc19"] + ), ) if self.SERVICE_TYPE_DIDCOMM in types: @@ -113,7 +115,7 @@ def add_services( service_endpoint=endpoint, recipient_keys=[recipient_key.id], routing_keys=routing_keys, - # CHECKME accept=(accept if accept else ["didcomm/v2"]), + # CHECKME accept=(service_accept if service_accept else ["didcomm/v2"]), accept=["didcomm/v2"], ) builder.context.append(self.CONTEXT_DIDCOMM_V2) @@ -131,7 +133,10 @@ def add_services( ) async def _resolve( - self, profile: Profile, did: str, accept: Optional[Sequence[Text]] = None + self, + profile: Profile, + did: str, + service_accept: Optional[Sequence[Text]] = None, ) -> dict: """Resolve an indy DID.""" multitenant_mgr = profile.inject_or(BaseMultitenantManager) @@ -162,7 +167,7 @@ async def _resolve( ) builder.authentication.reference(vmethod.id) builder.assertion_method.reference(vmethod.id) - self.add_services(builder, endpoints, vmethod, accept) + self.add_services(builder, endpoints, vmethod, service_accept) result = builder.build() return result.serialize() diff --git a/aries_cloudagent/resolver/default/key.py b/aries_cloudagent/resolver/default/key.py index 1f7e027dd0..0217156f81 100644 --- a/aries_cloudagent/resolver/default/key.py +++ b/aries_cloudagent/resolver/default/key.py @@ -29,7 +29,10 @@ def supported_did_regex(self) -> Pattern: return DIDKeyType.PATTERN async def _resolve( - self, profile: Profile, did: str, accept: Optional[Sequence[Text]] = None + self, + profile: Profile, + did: str, + service_accept: Optional[Sequence[Text]] = None, ) -> dict: """Resolve a Key DID.""" try: diff --git a/aries_cloudagent/resolver/default/universal.py b/aries_cloudagent/resolver/default/universal.py index a43aee0e8e..85ca9e2dba 100644 --- a/aries_cloudagent/resolver/default/universal.py +++ b/aries_cloudagent/resolver/default/universal.py @@ -84,7 +84,10 @@ def supported_did_regex(self) -> Pattern: return self._supported_did_regex async def _resolve( - self, _profile: Profile, did: str, accept: Optional[Sequence[Text]] = None + self, + _profile: Profile, + did: str, + service_accept: Optional[Sequence[Text]] = None, ) -> dict: """Resolve DID through remote universal resolver.""" diff --git a/aries_cloudagent/resolver/default/web.py b/aries_cloudagent/resolver/default/web.py index 987048209d..df2d99e6cc 100644 --- a/aries_cloudagent/resolver/default/web.py +++ b/aries_cloudagent/resolver/default/web.py @@ -58,7 +58,10 @@ def __transform_to_url(self, did): return "https://" + url + "/did.json" async def _resolve( - self, profile: Profile, did: str, accept: Optional[Sequence[Text]] = None + self, + profile: Profile, + did: str, + service_accept: Optional[Sequence[Text]] = None, ) -> dict: """Resolve did:web DIDs.""" diff --git a/aries_cloudagent/resolver/did_resolver.py b/aries_cloudagent/resolver/did_resolver.py index fb8a3f83fd..a2bdf48cbc 100644 --- a/aries_cloudagent/resolver/did_resolver.py +++ b/aries_cloudagent/resolver/did_resolver.py @@ -44,7 +44,7 @@ async def _resolve( self, profile: Profile, did: Union[str, DID], - accept: Optional[Sequence[Text]] = None, + service_accept: Optional[Sequence[Text]] = None, ) -> Tuple[BaseDIDResolver, dict]: """Retrieve doc and return with resolver.""" # TODO Cache results @@ -58,7 +58,7 @@ async def _resolve( document = await resolver.resolve( profile, did, - accept, + service_accept, ) return resolver, document except DIDNotFound: @@ -70,10 +70,10 @@ async def resolve( self, profile: Profile, did: Union[str, DID], - accept: Optional[Sequence[Text]] = None, + service_accept: Optional[Sequence[Text]] = None, ) -> dict: """Resolve a DID.""" - _, doc = await self._resolve(profile, did, accept) + _, doc = await self._resolve(profile, did, service_accept) return doc async def resolve_with_metadata( From 2f8db62e147b62bac97810b76f117ccb415e43fc Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Tue, 4 Oct 2022 10:29:16 -0700 Subject: [PATCH 517/872] include _type and _version in AgentMessage Signed-off-by: Shaanjot Gill --- aries_cloudagent/messaging/agent_message.py | 17 ++++++++++++++--- .../protocols/out_of_band/v1_0/manager.py | 12 +++++------- .../out_of_band/v1_0/messages/invitation.py | 12 ++++-------- .../out_of_band/v1_0/messages/problem_report.py | 6 +----- .../out_of_band/v1_0/messages/reuse.py | 6 +----- .../out_of_band/v1_0/messages/reuse_accept.py | 6 +----- aries_cloudagent/resolver/default/indy.py | 3 ++- 7 files changed, 28 insertions(+), 34 deletions(-) diff --git a/aries_cloudagent/messaging/agent_message.py b/aries_cloudagent/messaging/agent_message.py index c13351ef67..2d17f1d35a 100644 --- a/aries_cloudagent/messaging/agent_message.py +++ b/aries_cloudagent/messaging/agent_message.py @@ -4,7 +4,7 @@ from collections import OrderedDict from re import sub -from typing import Mapping, Union +from typing import Mapping, Optional, Union, Text from marshmallow import ( EXCLUDE, @@ -55,7 +55,13 @@ class Meta: schema_class = None message_type = None - def __init__(self, _id: str = None, _decorators: BaseDecoratorSet = None): + def __init__( + self, + _id: str = None, + _type: Optional[Text] = None, + _version: Optional[Text] = None, + _decorators: BaseDecoratorSet = None, + ): """ Initialize base agent message object. @@ -83,7 +89,12 @@ def __init__(self, _id: str = None, _decorators: BaseDecoratorSet = None): self.__class__.__name__ ) ) - self._message_type = self.Meta.message_type + if _type: + self._message_type = _type + elif _version: + self._message_type = self.get_updated_msg_type(_version) + else: + self._message_type = self.Meta.message_type # Not required for now # if not self.Meta.handler_class: # raise TypeError( diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py index 3420e57213..7f160ed63c 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py @@ -108,8 +108,8 @@ async def create_invitation( multi_use: set to True to create an invitation for multiple-use connection alias: optional alias to apply to connection for later use attachments: list of dicts in form of {"id": ..., "type": ...} - service_accept: Optional list of mime types in the order of preference of the sender - that the receiver can use in responding to the message + service_accept: Optional list of mime types in the order of preference of + the sender that the receiver can use in responding to the message protocol_version: OOB protocol version [1.0, 1.1] Returns: @@ -233,7 +233,7 @@ async def create_invitation( handshake_protocols=handshake_protocols, requests_attach=message_attachments, services=[f"did:sov:{public_did.did}"], - service_accept=service_accept if protocol_version != "1.0" else None, + accept=service_accept if protocol_version != "1.0" else None, version=protocol_version or DEFAULT_VERSION, ) @@ -332,9 +332,7 @@ async def create_invitation( invi_msg.label = my_label or self.profile.settings.get("default_label") invi_msg.handshake_protocols = handshake_protocols invi_msg.requests_attach = message_attachments - invi_msg.service_accept = ( - service_accept if protocol_version != "1.0" else None - ) + invi_msg.accept = service_accept if protocol_version != "1.0" else None invi_msg.services = [ ServiceMessage( _id="#inline", @@ -429,7 +427,7 @@ async def receive_invitation( oob_service_item = invitation.services[0] # service_accept - service_accept = invitation.service_accept + service_accept = invitation.accept # Get the DID public did, if any public_did = None diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/messages/invitation.py b/aries_cloudagent/protocols/out_of_band/v1_0/messages/invitation.py index 18adebbf8b..14b6eaaff6 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/messages/invitation.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/messages/invitation.py @@ -123,7 +123,7 @@ def __init__( handshake_protocols: Sequence[Text] = None, requests_attach: Sequence[AttachDecorator] = None, services: Sequence[Union[Service, Text]] = None, - service_accept: Optional[Sequence[Text]] = None, + accept: Optional[Sequence[Text]] = None, version: str = DEFAULT_VERSION, msg_type: Optional[Text] = None, **kwargs, @@ -136,18 +136,14 @@ def __init__( """ # super().__init__(_id=_id, **kwargs) - super().__init__(**kwargs) + super().__init__(_type=msg_type, _version=version, **kwargs) self.label = label self.handshake_protocols = ( list(handshake_protocols) if handshake_protocols else [] ) self.requests_attach = list(requests_attach) if requests_attach else [] self.services = services - if msg_type: - self._type = msg_type - else: - self._type = self.get_updated_msg_type(version) - self.service_accept = service_accept + self.accept = accept @classmethod def wrap_message(cls, message: dict) -> AttachDecorator: @@ -222,7 +218,7 @@ class Meta: ), required=False, ) - service_accept = fields.List( + accept = fields.List( fields.Str(), example=["didcomm/aip1", "didcomm/aip2;env=rfc19"], description=("List of mime type in order of preference"), diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/messages/problem_report.py b/aries_cloudagent/protocols/out_of_band/v1_0/messages/problem_report.py index a4126dd348..fc2e01039e 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/messages/problem_report.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/messages/problem_report.py @@ -50,11 +50,7 @@ def __init__( **kwargs, ): """Initialize a ProblemReport message instance.""" - super().__init__(*args, **kwargs) - if msg_type: - self._type = msg_type - else: - self._type = self.get_updated_msg_type(version) + super().__init__(_type=msg_type, _version=version, *args, **kwargs) class OOBProblemReportSchema(ProblemReportSchema): diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/messages/reuse.py b/aries_cloudagent/protocols/out_of_band/v1_0/messages/reuse.py index 6fc52064ce..d53f5aeddb 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/messages/reuse.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/messages/reuse.py @@ -29,11 +29,7 @@ def __init__( **kwargs, ): """Initialize Handshake Reuse message object.""" - super().__init__(**kwargs) - if msg_type: - self._type = msg_type - else: - self._type = self.get_updated_msg_type(version) + super().__init__(_type=msg_type, _version=version, **kwargs) class HandshakeReuseSchema(AgentMessageSchema): diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/messages/reuse_accept.py b/aries_cloudagent/protocols/out_of_band/v1_0/messages/reuse_accept.py index 977c2ea026..0b0e21a58e 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/messages/reuse_accept.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/messages/reuse_accept.py @@ -30,11 +30,7 @@ def __init__( **kwargs, ): """Initialize Handshake Reuse Accept object.""" - super().__init__(**kwargs) - if msg_type: - self._type = msg_type - else: - self._type = self.get_updated_msg_type(version) + super().__init__(_type=msg_type, _version=version, **kwargs) class HandshakeReuseAcceptSchema(AgentMessageSchema): diff --git a/aries_cloudagent/resolver/default/indy.py b/aries_cloudagent/resolver/default/indy.py index 3645274a84..ad34b90487 100644 --- a/aries_cloudagent/resolver/default/indy.py +++ b/aries_cloudagent/resolver/default/indy.py @@ -115,7 +115,8 @@ def add_services( service_endpoint=endpoint, recipient_keys=[recipient_key.id], routing_keys=routing_keys, - # CHECKME accept=(service_accept if service_accept else ["didcomm/v2"]), + # CHECKME + # accept=(service_accept if service_accept else ["didcomm/v2"]), accept=["didcomm/v2"], ) builder.context.append(self.CONTEXT_DIDCOMM_V2) From dbcac2c61c0f4c884e42b630d697cc53af711786 Mon Sep 17 00:00:00 2001 From: Pritam Singh Date: Thu, 6 Oct 2022 12:24:01 +0530 Subject: [PATCH 518/872] include image_url in oob invitation Signed-off-by: Pritam Singh --- aries_cloudagent/protocols/out_of_band/v1_0/manager.py | 3 +++ .../protocols/out_of_band/v1_0/messages/invitation.py | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py index 7f160ed63c..dd0843045a 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py @@ -121,6 +121,7 @@ async def create_invitation( mediation_id, or_default=True, ) + image_url = self.profile.context.settings.get("image_url") if not (hs_protos or attachments): raise OutOfBandManagerError( @@ -235,6 +236,7 @@ async def create_invitation( services=[f"did:sov:{public_did.did}"], accept=service_accept if protocol_version != "1.0" else None, version=protocol_version or DEFAULT_VERSION, + image_url=image_url, ) our_recipient_key = public_did.verkey @@ -333,6 +335,7 @@ async def create_invitation( invi_msg.handshake_protocols = handshake_protocols invi_msg.requests_attach = message_attachments invi_msg.accept = service_accept if protocol_version != "1.0" else None + invi_msg.image_url = image_url invi_msg.services = [ ServiceMessage( _id="#inline", diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/messages/invitation.py b/aries_cloudagent/protocols/out_of_band/v1_0/messages/invitation.py index 14b6eaaff6..e461201975 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/messages/invitation.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/messages/invitation.py @@ -120,6 +120,7 @@ def __init__( *, comment: str = None, label: str = None, + image_url: str = None, handshake_protocols: Sequence[Text] = None, requests_attach: Sequence[AttachDecorator] = None, services: Sequence[Union[Service, Text]] = None, @@ -138,6 +139,7 @@ def __init__( # super().__init__(_id=_id, **kwargs) super().__init__(_type=msg_type, _version=version, **kwargs) self.label = label + self.image_url = image_url self.handshake_protocols = ( list(handshake_protocols) if handshake_protocols else [] ) @@ -208,6 +210,13 @@ class Meta: example="https://didcomm.org/my-family/1.0/my-message-type", ) label = fields.Str(required=False, description="Optional label", example="Bob") + image_url = fields.URL( + data_key="imageUrl", + required=False, + allow_none=True, + description="Optional image URL for out-of-band invitation", + example="http://192.168.56.101/img/logo.jpg", + ) handshake_protocols = fields.List( fields.Str( description="Handshake protocol", From 0f47f705baf4323d38770155454c34eef4ce6e5e Mon Sep 17 00:00:00 2001 From: Anastasiia Date: Mon, 10 Oct 2022 17:14:14 +0200 Subject: [PATCH 519/872] Formatted code Signed-off-by: Anastasiia --- .../protocols/present_proof/dif/pres_exch_handler.py | 6 ++---- .../present_proof/v2_0/formats/dif/tests/test_handler.py | 2 +- .../present_proof/v2_0/messages/tests/test_pres.py | 1 - .../protocols/present_proof/v2_0/tests/test_manager.py | 5 ++--- 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py b/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py index 0df31f37d8..153325c2a8 100644 --- a/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py +++ b/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py @@ -1250,7 +1250,7 @@ async def create_vp( res = await self.apply_requirements( req=nested_req, credentials=credentials, - records_filter=records_filter + records_filter=records_filter, ) result.append(res) else: @@ -1288,9 +1288,7 @@ async def create_vp( applicable_creds=applicable_creds ) if not issuer_id and len(filtered_creds_list) == 0: - vp = await create_presentation( - credentials=applicable_creds_list - ) + vp = await create_presentation(credentials=applicable_creds_list) vp["presentation_submission"] = submission_property.serialize() if self.proof_type is BbsBlsSignature2020.signature_type: vp["@context"].append(SECURITY_CONTEXT_BBS_URL) diff --git a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py index 637e4ae9ef..9234d2e3e1 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py @@ -289,7 +289,7 @@ "@context": ["https://www.w3.org/2018/credentials/v1"], "type": ["VerifiablePresentation"], "verifiableCredential": [ - { + { "@context": [ "https://www.w3.org/2018/credentials/v1", "https://w3id.org/citizenship/v1", diff --git a/aries_cloudagent/protocols/present_proof/v2_0/messages/tests/test_pres.py b/aries_cloudagent/protocols/present_proof/v2_0/messages/tests/test_pres.py index 63381c1f62..ccd14b0896 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/messages/tests/test_pres.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/messages/tests/test_pres.py @@ -1818,7 +1818,6 @@ ) - class TestV20Pres(TestCase): """Presentation tests.""" diff --git a/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py b/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py index f0a3663b3f..9ac7fbd6f5 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py @@ -831,7 +831,6 @@ async def test_create_pres_indy_and_dif(self): AttachDecorator.data_base64(INDY_PROOF_REQ_NAME, ident="indy"), AttachDecorator.data_json(DIF_PRES_REQ, ident="dif"), ], - ) px_rec_in = V20PresExRecord(pres_request=pres_request.serialize()) more_magic_rr = async_mock.MagicMock( @@ -856,7 +855,7 @@ async def test_create_pres_indy_and_dif(self): mock_create_pres.return_value = ( PRES_20, - AttachDecorator.data_json(DIF_PRES, ident="dif") + AttachDecorator.data_json(DIF_PRES, ident="dif"), ) req_creds = await indy_proof_req_preview2indy_requested_creds( @@ -2175,7 +2174,7 @@ async def test_verify_pres_indy_and_dif(self): pres_request=pres_request, pres=pres, ) - + self.profile.context.injector.bind_instance( DocumentLoader, custom_document_loader ) From 20c811ceafc34a05e75cf463c215c4be1ae04ee9 Mon Sep 17 00:00:00 2001 From: Anastasiia Date: Mon, 10 Oct 2022 18:51:36 +0200 Subject: [PATCH 520/872] Formatted code Signed-off-by: Anastasiia --- .../protocols/present_proof/dif/pres_exch_handler.py | 4 +++- .../protocols/present_proof/v2_0/tests/test_manager.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py b/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py index 153325c2a8..2d137effcb 100644 --- a/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py +++ b/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py @@ -1305,7 +1305,9 @@ async def create_vp( applicable_creds=applicable_creds ) if not issuer_id: - vp = await create_presentation(credentials=applicable_creds_list) + vp = await create_presentation( + credentials=applicable_creds_list + ) vp["presentation_submission"] = submission_property.serialize() if self.proof_type is BbsBlsSignature2020.signature_type: vp["@context"].append(SECURITY_CONTEXT_BBS_URL) diff --git a/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py b/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py index 9ac7fbd6f5..14a22ce4d8 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py @@ -854,7 +854,7 @@ async def test_create_pres_indy_and_dif(self): ) mock_create_pres.return_value = ( - PRES_20, + PRES_20, AttachDecorator.data_json(DIF_PRES, ident="dif"), ) From 42b50295aa01bd67a5bc20fe483ac2dc0ad285de Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Wed, 12 Oct 2022 08:01:25 -0700 Subject: [PATCH 521/872] fix Signed-off-by: Shaanjot Gill --- aries_cloudagent/core/conductor.py | 24 +++++++++++++------ aries_cloudagent/core/dispatcher.py | 2 +- aries_cloudagent/core/tests/test_conductor.py | 11 ++++++--- .../protocols/connections/v1_0/routes.py | 17 ++++++++----- .../endorse_transaction/v1_0/routes.py | 6 ++++- 5 files changed, 42 insertions(+), 18 deletions(-) diff --git a/aries_cloudagent/core/conductor.py b/aries_cloudagent/core/conductor.py index f087e161b8..0586ef9955 100644 --- a/aries_cloudagent/core/conductor.py +++ b/aries_cloudagent/core/conductor.py @@ -26,6 +26,7 @@ from ..config.logging import LoggingConfigurator from ..config.provider import ClassProvider from ..config.wallet import wallet_config +from ..connections.models.conn_record import ConnRecord from ..core.profile import Profile from ..indy.verifier import IndyVerifier from ..ledger.base import BaseLedger @@ -450,14 +451,23 @@ async def start(self) -> None: if mediation_connections_invite else OutOfBandManager(self.root_profile) ) - - conn_record = await mgr.receive_invitation( - invitation=invitation_handler.from_url( - mediation_invite_record.invite - ), - auto_accept=True, - ) async with self.root_profile.session() as session: + invitation = invitation_handler.from_url( + mediation_invite_record.invite + ) + if isinstance(mgr, OutOfBandManager): + oob_record = await mgr.receive_invitation( + invitation=invitation, + auto_accept=True, + ) + conn_record = await ConnRecord.retrieve_by_id( + session, oob_record.connection_id + ) + else: + conn_record = await mgr.receive_invitation( + invitation=invitation, + auto_accept=True, + ) await ( MediationInviteStore( session.context.inject(BaseStorage) diff --git a/aries_cloudagent/core/dispatcher.py b/aries_cloudagent/core/dispatcher.py index 2193dd20b9..bfc6f95e4a 100644 --- a/aries_cloudagent/core/dispatcher.py +++ b/aries_cloudagent/core/dispatcher.py @@ -296,10 +296,10 @@ async def make_message( if not isinstance(parsed_msg, dict): raise MessageParseError("Expected a JSON object") message_type = parsed_msg.get("@type") - message_type_rec_version = get_version_from_message_type(message_type) if not message_type: raise MessageParseError("Message does not contain '@type' parameter") + message_type_rec_version = get_version_from_message_type(message_type) registry: ProtocolRegistry = self.profile.inject(ProtocolRegistry) try: diff --git a/aries_cloudagent/core/tests/test_conductor.py b/aries_cloudagent/core/tests/test_conductor.py index 203ca1b3b4..890e60b6ab 100644 --- a/aries_cloudagent/core/tests/test_conductor.py +++ b/aries_cloudagent/core/tests/test_conductor.py @@ -1161,7 +1161,9 @@ async def test_mediator_invitation_0434(self, mock_from_url, _): "test": async_mock.MagicMock(schemes=["http"]) } await conductor.setup() - + conductor.root_profile.context.update_settings( + {"mediation.connections_invite": False} + ) conn_record = ConnRecord( invitation_key="3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx", their_label="Hello", @@ -1170,12 +1172,15 @@ async def test_mediator_invitation_0434(self, mock_from_url, _): ) conn_record.accept = ConnRecord.ACCEPT_MANUAL await conn_record.save(await conductor.root_profile.session()) + oob_record = async_mock.MagicMock( + connection_id=conn_record.connection_id, + ) with async_mock.patch.object( test_module, "OutOfBandManager", async_mock.MagicMock( return_value=async_mock.MagicMock( - receive_invitation=async_mock.AsyncMock(return_value=conn_record) + receive_invitation=async_mock.AsyncMock(return_value=oob_record) ) ), ) as mock_mgr, async_mock.patch.object( @@ -1185,10 +1190,10 @@ async def test_mediator_invitation_0434(self, mock_from_url, _): return_value=async_mock.MagicMock(value=f"v{__version__}") ), ): + assert not conductor.root_profile.settings["mediation.connections_invite"] await conductor.start() await conductor.stop() mock_from_url.assert_called_once_with("test-invite") - mock_mgr.return_value.receive_invitation.assert_called_once() @async_mock.patch.object(test_module, "MediationInviteStore") @async_mock.patch.object(test_module.ConnectionInvitation, "from_url") diff --git a/aries_cloudagent/protocols/connections/v1_0/routes.py b/aries_cloudagent/protocols/connections/v1_0/routes.py index 41d7306b26..3da5e71f95 100644 --- a/aries_cloudagent/protocols/connections/v1_0/routes.py +++ b/aries_cloudagent/protocols/connections/v1_0/routes.py @@ -10,7 +10,7 @@ request_schema, response_schema, ) - +from typing import cast from marshmallow import fields, validate, validates_schema from ....admin.request_context import AdminRequestContext @@ -115,7 +115,7 @@ class CreateInvitationRequestSchema(OpenAPISchema): mediation_id = fields.Str( required=False, description="Identifier for active mediation record to be used", - **MEDIATION_ID_SCHEMA + **MEDIATION_ID_SCHEMA, ) @@ -247,7 +247,7 @@ class ReceiveInvitationQueryStringSchema(OpenAPISchema): mediation_id = fields.Str( required=False, description="Identifier for active mediation record to be used", - **MEDIATION_ID_SCHEMA + **MEDIATION_ID_SCHEMA, ) @@ -261,7 +261,7 @@ class AcceptInvitationQueryStringSchema(OpenAPISchema): mediation_id = fields.Str( required=False, description="Identifier for active mediation record to be used", - **MEDIATION_ID_SCHEMA + **MEDIATION_ID_SCHEMA, ) @@ -536,11 +536,16 @@ async def connections_create_invitation(request: web.BaseRequest): metadata=metadata, mediation_id=mediation_id, ) - + invitation_url = invitation.to_url(base_url) + base_endpoint = service_endpoint or cast( + str, profile.settings.get("default_endpoint") + ) result = { "connection_id": connection and connection.connection_id, "invitation": invitation.serialize(), - "invitation_url": invitation.to_url(base_url), + "invitation_url": f"{base_endpoint}{invitation_url}" + if invitation_url.startswith("?") + else invitation_url, } except (ConnectionManagerError, StorageError, BaseModelError) as err: raise web.HTTPBadRequest(reason=err.roll_up) from err diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py index 7e3bc6820e..c43aa1f45f 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py @@ -764,11 +764,15 @@ async def on_startup_event(profile: Profile, event: Event): invite = InvitationMessage.from_url(endorser_invitation) if invite: oob_mgr = OutOfBandManager(profile) - conn_record = await oob_mgr.receive_invitation( + oob_record = await oob_mgr.receive_invitation( invitation=invite, auto_accept=True, alias=endorser_alias, ) + async with profile.session() as session: + conn_record = await ConnRecord.retrieve_by_id( + session, oob_record.connection_id + ) else: invite = ConnectionInvitation.from_url(endorser_invitation) if invite: From 9cdb75ed2f738c66193bdd903bff33ec10b71b3c Mon Sep 17 00:00:00 2001 From: Micah Peltier Date: Wed, 12 Oct 2022 11:49:52 -0600 Subject: [PATCH 522/872] feat: add flag for unsolicited requests Signed-off-by: Micah Peltier --- aries_cloudagent/config/argparse.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index c3cc337f6b..90b2abb2a0 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -1014,7 +1014,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." ), ) @@ -1109,6 +1119,11 @@ 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: From ff3edd851012c3774f622fdb4997c05c83936897 Mon Sep 17 00:00:00 2001 From: Micah Peltier Date: Wed, 12 Oct 2022 11:57:56 -0600 Subject: [PATCH 523/872] feat: allow metadata on public invites Signed-off-by: Micah Peltier --- aries_cloudagent/protocols/out_of_band/v1_0/manager.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py index 1fa0756740..29ba806f5c 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py @@ -132,11 +132,6 @@ async def create_invitation( raise OutOfBandManagerError( "Cannot store metadata without handshake protocols" ) - if public: - if metadata: - raise OutOfBandManagerError( - "Cannot store metadata on public invitations" - ) if attachments and multi_use: raise OutOfBandManagerError( From 91ba45ba7cb7ef8d3025751cf6289948a0c6b1e4 Mon Sep 17 00:00:00 2001 From: Micah Peltier Date: Wed, 12 Oct 2022 12:05:01 -0600 Subject: [PATCH 524/872] feat: check for new flag Signed-off-by: Micah Peltier --- aries_cloudagent/protocols/didexchange/v1_0/manager.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/aries_cloudagent/protocols/didexchange/v1_0/manager.py b/aries_cloudagent/protocols/didexchange/v1_0/manager.py index 587e6b56b5..523d307362 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/manager.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/manager.py @@ -483,6 +483,9 @@ async def receive_request( ) else: # request is against implicit invitation on public DID + if not self.profile.settings.get("requests_through_public_did"): + raise DIDXManagerError("Unsolicited connection requests to " + "public DID is not enabled") async with self.profile.session() as session: wallet = session.inject(BaseWallet) my_info = await wallet.create_local_did( From 55e52d62b39a34f40c11ed16a19b7f9d1d7b2556 Mon Sep 17 00:00:00 2001 From: Micah Peltier Date: Wed, 12 Oct 2022 12:06:48 -0600 Subject: [PATCH 525/872] test: implicit invites Signed-off-by: Micah Peltier --- .../didexchange/v1_0/tests/test_manager.py | 143 ++++++++++++++++++ 1 file changed, 143 insertions(+) diff --git a/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py index 131d87670e..a59c52c10a 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py @@ -971,6 +971,149 @@ async def test_receive_request_public_did_no_auto_accept(self): messages = self.responder.messages assert not messages + async def test_receive_request_implicit_public_did_not_enabled(self): + async with self.profile.session() as session: + mock_request = async_mock.MagicMock( + did=TestConfig.test_did, + did_doc_attach=async_mock.MagicMock( + data=async_mock.MagicMock( + verify=async_mock.CoroutineMock(return_value=True), + signed=async_mock.MagicMock( + decode=async_mock.MagicMock(return_value="dummy-did-doc") + ), + ) + ), + _thread=async_mock.MagicMock(pthid="did:sov:publicdid0000000000000"), + ) + mediation_record = MediationRecord( + role=MediationRecord.ROLE_CLIENT, + state=MediationRecord.STATE_GRANTED, + connection_id=self.test_mediator_conn_id, + routing_keys=self.test_mediator_routing_keys, + endpoint=self.test_mediator_endpoint, + ) + await mediation_record.save(session) + + await session.wallet.create_local_did( + method=DIDMethod.SOV, + key_type=KeyType.ED25519, + seed=None, + did=TestConfig.test_did, + ) + + self.profile.context.update_settings({"public_invites": True}) + + with async_mock.patch.object( + test_module, "ConnRecord", async_mock.MagicMock() + ) as mock_conn_rec_cls, async_mock.patch.object( + test_module, "DIDDoc", autospec=True + ) as mock_did_doc, async_mock.patch.object( + test_module, "DIDPosture", autospec=True + ) as mock_did_posture, async_mock.patch.object( + self.manager, + "verify_diddoc", + async_mock.CoroutineMock(return_value=DIDDoc(TestConfig.test_did))): + mock_did_posture.get = async_mock.MagicMock( + return_value=test_module.DIDPosture.PUBLIC + ) + mock_conn_rec_cls.retrieve_by_invitation_key = async_mock.CoroutineMock( + side_effect=StorageNotFoundError() + ) + mock_conn_rec_cls.retrieve_by_invitation_msg_id = ( + async_mock.CoroutineMock(return_value=None) + ) + + with self.assertRaises(DIDXManagerError) as context: + await self.manager.receive_request( + request=mock_request, + recipient_did=TestConfig.test_did, + my_endpoint=None, + alias=None, + auto_accept_implicit=None, + ) + assert "Unsolicited connection requests" in str(context.exception) + + async def test_receive_request_implicit_public_did(self): + async with self.profile.session() as session: + mock_request = async_mock.MagicMock( + did=TestConfig.test_did, + did_doc_attach=async_mock.MagicMock( + data=async_mock.MagicMock( + verify=async_mock.CoroutineMock(return_value=True), + signed=async_mock.MagicMock( + decode=async_mock.MagicMock(return_value="dummy-did-doc") + ), + ) + ), + _thread=async_mock.MagicMock(pthid="did:sov:publicdid0000000000000"), + ) + mediation_record = MediationRecord( + role=MediationRecord.ROLE_CLIENT, + state=MediationRecord.STATE_GRANTED, + connection_id=self.test_mediator_conn_id, + routing_keys=self.test_mediator_routing_keys, + endpoint=self.test_mediator_endpoint, + ) + await mediation_record.save(session) + + await session.wallet.create_local_did( + method=DIDMethod.SOV, + key_type=KeyType.ED25519, + seed=None, + did=TestConfig.test_did, + ) + + self.profile.context.update_settings({"public_invites": True}) + self.profile.context.update_settings({"requests_through_public_did": True}) + ACCEPT_AUTO = ConnRecord.ACCEPT_AUTO + STATE_REQUEST = ConnRecord.State.REQUEST + + with async_mock.patch.object( + test_module, "ConnRecord", async_mock.MagicMock() + ) as mock_conn_rec_cls, async_mock.patch.object( + test_module, "DIDDoc", autospec=True + ) as mock_did_doc, async_mock.patch.object( + test_module, "DIDPosture", autospec=True + ) as mock_did_posture, async_mock.patch.object( + self.manager, + "verify_diddoc", + async_mock.CoroutineMock(return_value=DIDDoc(TestConfig.test_did))): + mock_did_posture.get = async_mock.MagicMock( + return_value=test_module.DIDPosture.PUBLIC + ) + mock_conn_rec_cls.retrieve_by_invitation_key = async_mock.CoroutineMock( + side_effect=StorageNotFoundError() + ) + mock_conn_rec_cls.retrieve_by_invitation_msg_id = ( + async_mock.CoroutineMock(return_value=None) + ) + + mock_conn_record = async_mock.MagicMock( + accept=ACCEPT_AUTO, + my_did=None, + state=STATE_REQUEST.rfc23, + attach_request=async_mock.CoroutineMock(), + retrieve_request=async_mock.CoroutineMock(), + metadata_get_all=async_mock.CoroutineMock(return_value={}), + metadata_get=async_mock.CoroutineMock(return_value=True), + save=async_mock.CoroutineMock(), + ) + + mock_conn_rec_cls.return_value = mock_conn_record + + conn_rec = await self.manager.receive_request( + request=mock_request, + recipient_did=TestConfig.test_did, + recipient_verkey=None, + my_endpoint=None, + alias=None, + auto_accept_implicit=None, + ) + assert conn_rec + self.oob_mock.clean_finished_oob_record.assert_called_once_with( + self.profile, mock_request + ) + async def test_receive_request_peer_did(self): async with self.profile.session() as session: mock_request = async_mock.MagicMock( From 6ecd8e6f87eec05cab5d0de99ba4070e4133f5c1 Mon Sep 17 00:00:00 2001 From: Micah Peltier Date: Wed, 12 Oct 2022 12:12:13 -0600 Subject: [PATCH 526/872] feat: allow public did + multi-use conn invites Signed-off-by: Micah Peltier --- .../protocols/connections/v1_0/manager.py | 146 ++++++++++-------- 1 file changed, 78 insertions(+), 68 deletions(-) diff --git a/aries_cloudagent/protocols/connections/v1_0/manager.py b/aries_cloudagent/protocols/connections/v1_0/manager.py index d937b7ecb2..6f5f07a861 100644 --- a/aries_cloudagent/protocols/connections/v1_0/manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/manager.py @@ -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=KeyType.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") @@ -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=KeyType.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) @@ -531,6 +538,9 @@ 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 From e640a4e61957b225315396e09c1c5a4a1fbdf280 Mon Sep 17 00:00:00 2001 From: Micah Peltier Date: Wed, 12 Oct 2022 12:13:19 -0600 Subject: [PATCH 527/872] tests: check new flags, conn manager changes Signed-off-by: Micah Peltier --- .../connections/v1_0/tests/test_manager.py | 113 +++++++++++++----- 1 file changed, 80 insertions(+), 33 deletions(-) diff --git a/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py index d7c3836236..15d6e18454 100644 --- a/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py @@ -35,6 +35,7 @@ from ....discovery.v2_0.manager import V20DiscoveryMgr from ..manager import ConnectionManager, ConnectionManagerError +from .. import manager as test_module from ..messages.connection_invitation import ConnectionInvitation from ..messages.connection_request import ConnectionRequest from ..messages.connection_response import ConnectionResponse @@ -112,21 +113,6 @@ async def setUp(self): self.manager = ConnectionManager(self.profile) assert self.manager.profile - async def test_create_invitation_public_and_multi_use_fails(self): - self.context.update_settings({"public_invites": True}) - with async_mock.patch.object( - InMemoryWallet, "get_public_did", autospec=True - ) as mock_wallet_get_public_did: - mock_wallet_get_public_did.return_value = DIDInfo( - self.test_did, - self.test_verkey, - None, - method=DIDMethod.SOV, - key_type=KeyType.ED25519, - ) - with self.assertRaises(ConnectionManagerError): - await self.manager.create_invitation(public=True, multi_use=True) - async def test_create_invitation_non_multi_use_invitation_fails_on_reuse(self): connect_record, connect_invite = await self.manager.create_invitation() @@ -174,7 +160,7 @@ async def test_create_invitation_public(self): public=True, my_endpoint="testendpoint" ) - assert connect_record is None + assert connect_record assert connect_invite.did.endswith(self.test_did) self.route_manager.route_public_did.assert_called_once_with( self.profile, self.test_verkey @@ -266,23 +252,6 @@ async def test_create_invitation_metadata_assigned(self): assert await record.metadata_get_all(session) == {"hello": "world"} - async def test_create_invitation_public_and_metadata_fails(self): - self.context.update_settings({"public_invites": True}) - with async_mock.patch.object( - InMemoryWallet, "get_public_did", autospec=True - ) as mock_wallet_get_public_did: - mock_wallet_get_public_did.return_value = DIDInfo( - self.test_did, - self.test_verkey, - None, - method=DIDMethod.SOV, - key_type=KeyType.ED25519, - ) - with self.assertRaises(ConnectionManagerError): - await self.manager.create_invitation( - public=True, metadata={"hello": "world"} - ) - async def test_create_invitation_multi_use_metadata_transfers_to_connection(self): async with self.profile.session() as session: connect_record, _ = await self.manager.create_invitation( @@ -643,7 +612,84 @@ async def test_receive_request_public_did_oob_invite(self): self.profile, mock_request ) + async def test_receive_request_public_did_unsolicited_fails(self): + async with self.profile.session() as session: + mock_request = async_mock.MagicMock() + mock_request.connection = async_mock.MagicMock() + mock_request.connection.did = self.test_did + mock_request.connection.did_doc = async_mock.MagicMock() + mock_request.connection.did_doc.did = self.test_did + + receipt = MessageReceipt( + recipient_did=self.test_did, recipient_did_public=True + ) + await session.wallet.create_local_did( + method=DIDMethod.SOV, + key_type=KeyType.ED25519, + seed=None, + did=self.test_did, + ) + + self.context.update_settings({"public_invites": True}) + with self.assertRaises( + ConnectionManagerError + ), async_mock.patch.object( + ConnRecord, "connection_id", autospec=True + ), async_mock.patch.object( + ConnRecord, "save", autospec=True + ) as mock_conn_rec_save, async_mock.patch.object( + ConnRecord, "attach_request", autospec=True + ) as mock_conn_attach_request, async_mock.patch.object( + ConnRecord, "retrieve_by_id", autospec=True + ) as mock_conn_retrieve_by_id, async_mock.patch.object( + ConnRecord, "retrieve_request", autospec=True + ), async_mock.patch.object( + ConnRecord, "retrieve_by_invitation_msg_id", async_mock.CoroutineMock() + ) as mock_conn_retrieve_by_invitation_msg_id: + mock_conn_retrieve_by_invitation_msg_id.return_value = None + conn_rec = await self.manager.receive_request(mock_request, receipt) + async def test_receive_request_public_did_conn_invite(self): + async with self.profile.session() as session: + mock_request = async_mock.MagicMock() + mock_request.connection = async_mock.MagicMock() + mock_request.connection.did = self.test_did + mock_request.connection.did_doc = async_mock.MagicMock() + mock_request.connection.did_doc.did = self.test_did + + receipt = MessageReceipt( + recipient_did=self.test_did, recipient_did_public=True + ) + await session.wallet.create_local_did( + method=DIDMethod.SOV, + key_type=KeyType.ED25519, + seed=None, + did=self.test_did, + ) + + mock_connection_record = async_mock.MagicMock() + mock_connection_record.save = async_mock.CoroutineMock() + mock_connection_record.attach_request = async_mock.CoroutineMock() + + + self.context.update_settings({"public_invites": True}) + with async_mock.patch.object( + ConnRecord, "connection_id", autospec=True + ), async_mock.patch.object( + ConnRecord, "save", autospec=True + ) as mock_conn_rec_save, async_mock.patch.object( + ConnRecord, "attach_request", autospec=True + ) as mock_conn_attach_request, async_mock.patch.object( + ConnRecord, "retrieve_by_id", autospec=True + ) as mock_conn_retrieve_by_id, async_mock.patch.object( + ConnRecord, "retrieve_request", autospec=True + ), async_mock.patch.object( + ConnRecord, "retrieve_by_invitation_msg_id", async_mock.CoroutineMock(return_value=mock_connection_record) + ) as mock_conn_retrieve_by_invitation_msg_id: + conn_rec = await self.manager.receive_request(mock_request, receipt) + assert conn_rec + + async def test_receive_request_public_did_unsolicited(self): async with self.profile.session() as session: mock_request = async_mock.MagicMock() mock_request.connection = async_mock.MagicMock() @@ -662,6 +708,7 @@ async def test_receive_request_public_did_conn_invite(self): ) self.context.update_settings({"public_invites": True}) + self.context.update_settings({"requests_through_public_did": True}) with async_mock.patch.object( ConnRecord, "connection_id", autospec=True ), async_mock.patch.object( From 0542fa04247956442fe559b820f23c13a3e277cf Mon Sep 17 00:00:00 2001 From: Tobias Wich Date: Tue, 23 Aug 2022 11:06:00 +0200 Subject: [PATCH 528/872] Leave credentialStatus element in the LD credential This change makes sure that the comparison of the provided reference data is comparable with the credential content. Signed-off-by: Tobias Wich --- .../protocols/issue_credential/v2_0/formats/ld_proof/handler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py index 796e71e67f..0efaf0c767 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py @@ -503,7 +503,7 @@ async def receive_credential( # Remove values from cred that are not part of detail cred_dict.pop("proof") - credential_status = cred_dict.pop("credentialStatus", None) + credential_status = cred_dict.get("credentialStatus", None) detail_status = detail.options.credential_status if cred_dict != detail_dict["credential"]: From 9cf558e19a3ffe020982e8f5acd38123fc05bdd3 Mon Sep 17 00:00:00 2001 From: Tobias Wich Date: Fri, 2 Sep 2022 12:24:04 +0200 Subject: [PATCH 529/872] Adapt test to changed handling of credentialStatus values Signed-off-by: Tobias Wich --- .../v2_0/formats/ld_proof/tests/test_handler.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/tests/test_handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/tests/test_handler.py index c4666a679e..b78e1ab835 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/tests/test_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/tests/test_handler.py @@ -770,13 +770,14 @@ async def test_receive_credential_x_credential_status_ne(self): async def test_receive_credential_x_credential_status_ne_both_set(self): detail = deepcopy(LD_PROOF_VC_DETAIL) + statusEntry = {"type": "SomeRandomType"} - # Set credential status so it's only set on the detail - # not the issued credential + # Set credential status in both request and reference credential detail["options"]["credentialStatus"] = {"type": "CredentialStatusType"} + detail["credential"]["credentialStatus"] = deepcopy(statusEntry) vc = deepcopy(LD_PROOF_VC) - vc["credentialStatus"] = {"type": "SomeRandomType"} + vc["credentialStatus"] = deepcopy(statusEntry) cred_issue = V20CredIssue( formats=[ From 2807458973728e11dad84d2b651b0c6a1b8fadf4 Mon Sep 17 00:00:00 2001 From: Tobias Wich Date: Wed, 14 Sep 2022 12:04:57 +0200 Subject: [PATCH 530/872] Rename variable in test causing code smell alert The variable statusEntry in the function test_receive_credential_x_credential_status_ne_both_set has been renamed to status_entry in order to comply with the linting rules. Signed-off-by: Tobias Wich --- .../v2_0/formats/ld_proof/tests/test_handler.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/tests/test_handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/tests/test_handler.py index b78e1ab835..a2f7cbeccb 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/tests/test_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/tests/test_handler.py @@ -770,14 +770,14 @@ async def test_receive_credential_x_credential_status_ne(self): async def test_receive_credential_x_credential_status_ne_both_set(self): detail = deepcopy(LD_PROOF_VC_DETAIL) - statusEntry = {"type": "SomeRandomType"} + status_entry = {"type": "SomeRandomType"} # Set credential status in both request and reference credential detail["options"]["credentialStatus"] = {"type": "CredentialStatusType"} - detail["credential"]["credentialStatus"] = deepcopy(statusEntry) + detail["credential"]["credentialStatus"] = deepcopy(status_entry) vc = deepcopy(LD_PROOF_VC) - vc["credentialStatus"] = deepcopy(statusEntry) + vc["credentialStatus"] = deepcopy(status_entry) cred_issue = V20CredIssue( formats=[ From 2cebc373aec36218f0ee54e551573a086db3b7b4 Mon Sep 17 00:00:00 2001 From: Adam Burdett Date: Thu, 13 Oct 2022 10:11:49 -0600 Subject: [PATCH 531/872] happy flake8 Signed-off-by: Adam Burdett --- aries_cloudagent/config/default_context.py | 3 +- aries_cloudagent/core/tests/test_conductor.py | 13 ++++---- .../decorators/tests/test_attach_decorator.py | 13 +++++--- .../protocols/connections/v1_0/manager.py | 8 ++--- .../handlers/tests/test_request_handler.py | 4 +-- .../v1_0/messages/tests/test_request.py | 3 +- .../v1_0/messages/tests/test_response.py | 3 +- .../didexchange/v1_0/tests/test_manager.py | 6 ++-- .../transport/tests/test_pack_format.py | 3 +- .../utils/tests/test_outofband.py | 4 +-- aries_cloudagent/wallet/routes.py | 1 - .../wallet/tests/test_in_memory_wallet.py | 30 +++++-------------- .../wallet/tests/test_indy_wallet.py | 3 +- 13 files changed, 35 insertions(+), 59 deletions(-) diff --git a/aries_cloudagent/config/default_context.py b/aries_cloudagent/config/default_context.py index 72159ece64..360f103fa1 100644 --- a/aries_cloudagent/config/default_context.py +++ b/aries_cloudagent/config/default_context.py @@ -70,8 +70,7 @@ async def bind_providers(self, context: InjectionContext): # so it can be shared by all wallet instances. If we set it in the indy sdk # profile provider it could mean other wallets won't have access to the provider if is_indy_sdk_module_installed(): - from ..ledger.indy import (IndySdkLedgerPool, - IndySdkLedgerPoolProvider) + from ..ledger.indy import IndySdkLedgerPool, IndySdkLedgerPoolProvider context.injector.bind_provider( IndySdkLedgerPool, diff --git a/aries_cloudagent/core/tests/test_conductor.py b/aries_cloudagent/core/tests/test_conductor.py index 22ab676930..85992f0d75 100644 --- a/aries_cloudagent/core/tests/test_conductor.py +++ b/aries_cloudagent/core/tests/test_conductor.py @@ -8,18 +8,19 @@ from ...config.injection_context import InjectionContext from ...connections.models.conn_record import ConnRecord from ...connections.models.connection_target import ConnectionTarget -from ...connections.models.diddoc import (DIDDoc, PublicKey, PublicKeyType, - Service) +from ...connections.models.diddoc import DIDDoc, PublicKey, PublicKeyType, Service from ...core.event_bus import EventBus, MockEventBus from ...core.in_memory import InMemoryProfileManager from ...core.profile import ProfileManager from ...core.protocol_registry import ProtocolRegistry from ...multitenant.base import BaseMultitenantManager from ...multitenant.manager import MultitenantManager -from ...protocols.coordinate_mediation.mediation_invite_store import \ - MediationInviteRecord -from ...protocols.coordinate_mediation.v1_0.models.mediation_record import \ - MediationRecord +from ...protocols.coordinate_mediation.mediation_invite_store import ( + MediationInviteRecord, +) +from ...protocols.coordinate_mediation.v1_0.models.mediation_record import ( + MediationRecord, +) from ...resolver.did_resolver import DIDResolver from ...storage.base import BaseStorage from ...storage.error import StorageNotFoundError diff --git a/aries_cloudagent/messaging/decorators/tests/test_attach_decorator.py b/aries_cloudagent/messaging/decorators/tests/test_attach_decorator.py index f86197d02e..c0f1f26169 100644 --- a/aries_cloudagent/messaging/decorators/tests/test_attach_decorator.py +++ b/aries_cloudagent/messaging/decorators/tests/test_attach_decorator.py @@ -12,10 +12,15 @@ from ....wallet.indy import IndySdkWallet from ....wallet.key_type import ED25519 from ....wallet.util import b64_to_bytes, bytes_to_b64 -from ..attach_decorator import (AttachDecorator, AttachDecoratorData, - AttachDecoratorData1JWS, - AttachDecoratorDataJWS, - AttachDecoratorDataJWSHeader, did_key, raw_key) +from ..attach_decorator import ( + AttachDecorator, + AttachDecoratorData, + AttachDecoratorData1JWS, + AttachDecoratorDataJWS, + AttachDecoratorDataJWSHeader, + did_key, + raw_key, +) KID = "did:sov:LjgpST2rjsoxYegQDRm7EL#keys-4" INDY_CRED = { diff --git a/aries_cloudagent/protocols/connections/v1_0/manager.py b/aries_cloudagent/protocols/connections/v1_0/manager.py index f262b6baf7..23c8057645 100644 --- a/aries_cloudagent/protocols/connections/v1_0/manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/manager.py @@ -828,9 +828,7 @@ async def create_static_connection( async with self.profile.session() as session: wallet = session.inject(BaseWallet) # seed and DID optional - my_info = await wallet.create_local_did( - SOV, ED25519, my_seed, my_did - ) + my_info = await wallet.create_local_did(SOV, ED25519, my_seed, my_did) # must provide their DID and verkey if the seed is not known if (not their_did or not their_verkey) and not their_seed: @@ -842,9 +840,7 @@ async def create_static_connection( if not their_verkey: their_verkey_bin, _ = create_keypair(ED25519, their_seed.encode()) their_verkey = bytes_to_b58(their_verkey_bin) - their_info = DIDInfo( - their_did, their_verkey, {}, method=SOV, key_type=ED25519 - ) + their_info = DIDInfo(their_did, their_verkey, {}, method=SOV, key_type=ED25519) # Create connection record connection = ConnRecord( diff --git a/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_request_handler.py b/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_request_handler.py index ad35aa636c..8f8c4381b1 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_request_handler.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_request_handler.py @@ -87,9 +87,7 @@ async def setUp(self): await self.conn_rec.save(self.session) wallet = self.session.wallet - self.did_info = await wallet.create_local_did( - method=SOV, key_type=ED25519 - ) + self.did_info = await wallet.create_local_did(method=SOV, key_type=ED25519) self.did_doc_attach = AttachDecorator.data_base64(self.did_doc().serialize()) await self.did_doc_attach.data.sign(self.did_info.verkey, wallet) diff --git a/aries_cloudagent/protocols/didexchange/v1_0/messages/tests/test_request.py b/aries_cloudagent/protocols/didexchange/v1_0/messages/tests/test_request.py index 5a2e4abaee..37569858e6 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/messages/tests/test_request.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/messages/tests/test_request.py @@ -2,8 +2,7 @@ from asynctest import TestCase as AsyncTestCase -from ......connections.models.diddoc import (DIDDoc, PublicKey, PublicKeyType, - Service) +from ......connections.models.diddoc import DIDDoc, PublicKey, PublicKeyType, Service from ......core.in_memory import InMemoryProfile from ......messaging.decorators.attach_decorator import AttachDecorator from ......wallet.did_method import SOV diff --git a/aries_cloudagent/protocols/didexchange/v1_0/messages/tests/test_response.py b/aries_cloudagent/protocols/didexchange/v1_0/messages/tests/test_response.py index ca3cbcea07..59657ef2dc 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/messages/tests/test_response.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/messages/tests/test_response.py @@ -2,8 +2,7 @@ from asynctest import TestCase as AsyncTestCase -from ......connections.models.diddoc import (DIDDoc, PublicKey, PublicKeyType, - Service) +from ......connections.models.diddoc import DIDDoc, PublicKey, PublicKeyType, Service from ......core.in_memory import InMemoryProfile from ......messaging.decorators.attach_decorator import AttachDecorator from ......wallet.did_method import SOV diff --git a/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py index 839d9cd139..18f87d6b30 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py @@ -9,8 +9,7 @@ from .....connections.base_manager import BaseConnectionManagerError from .....connections.models.conn_record import ConnRecord from .....connections.models.connection_target import ConnectionTarget -from .....connections.models.diddoc import (DIDDoc, PublicKey, PublicKeyType, - Service) +from .....connections.models.diddoc import DIDDoc, PublicKey, PublicKeyType, Service from .....core.in_memory import InMemoryProfile from .....core.oob_processor import OobMessageProcessor from .....did.did_key import DIDKey @@ -30,8 +29,7 @@ from .....wallet.in_memory import InMemoryWallet from .....wallet.key_type import ED25519 from ....coordinate_mediation.v1_0.manager import MediationManager -from ....coordinate_mediation.v1_0.models.mediation_record import \ - MediationRecord +from ....coordinate_mediation.v1_0.models.mediation_record import MediationRecord from ....coordinate_mediation.v1_0.route_manager import RouteManager from ....didcomm_prefix import DIDCommPrefix from ....discovery.v2_0.manager import V20DiscoveryMgr diff --git a/aries_cloudagent/transport/tests/test_pack_format.py b/aries_cloudagent/transport/tests/test_pack_format.py index 16f5bcc0b2..02a7712e1c 100644 --- a/aries_cloudagent/transport/tests/test_pack_format.py +++ b/aries_cloudagent/transport/tests/test_pack_format.py @@ -12,8 +12,7 @@ from ...wallet.error import WalletError from ...wallet.key_type import ED25519 from .. import pack_format as test_module -from ..error import (RecipientKeysError, WireFormatEncodeError, - WireFormatParseError) +from ..error import RecipientKeysError, WireFormatEncodeError, WireFormatParseError from ..pack_format import PackWireFormat diff --git a/aries_cloudagent/utils/tests/test_outofband.py b/aries_cloudagent/utils/tests/test_outofband.py index 9e2975fcc5..6e3821ff57 100644 --- a/aries_cloudagent/utils/tests/test_outofband.py +++ b/aries_cloudagent/utils/tests/test_outofband.py @@ -10,9 +10,7 @@ class TestOutOfBand(TestCase): test_did = "55GkHamhTU1ZbTbV2ab9DE" test_verkey = "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx" - test_did_info = DIDInfo( - test_did, test_verkey, None, method=SOV, key_type=ED25519 - ) + test_did_info = DIDInfo(test_did, test_verkey, None, method=SOV, key_type=ED25519) def test_serialize_oob(self): invi = InvitationMessage( diff --git a/aries_cloudagent/wallet/routes.py b/aries_cloudagent/wallet/routes.py index 7fe07a00a1..26362dc703 100644 --- a/aries_cloudagent/wallet/routes.py +++ b/aries_cloudagent/wallet/routes.py @@ -354,7 +354,6 @@ async def wallet_create_did(request: web.BaseRequest): # set default method and key type for backwards compat - seed = body.get("seed") or None if seed and not context.settings.get("wallet.allow_insecure_seed"): raise web.HTTPBadRequest(reason="Seed support is not enabled") diff --git a/aries_cloudagent/wallet/tests/test_in_memory_wallet.py b/aries_cloudagent/wallet/tests/test_in_memory_wallet.py index c06f889b4a..ecb3c97263 100644 --- a/aries_cloudagent/wallet/tests/test_in_memory_wallet.py +++ b/aries_cloudagent/wallet/tests/test_in_memory_wallet.py @@ -5,11 +5,9 @@ from ...core.in_memory import InMemoryProfile from ...messaging.decorators.signature_decorator import SignatureDecorator from ...wallet.did_method import KEY, SOV, DIDMethods -from ...wallet.error import (WalletDuplicateError, WalletError, - WalletNotFoundError) +from ...wallet.error import WalletDuplicateError, WalletError, WalletNotFoundError from ...wallet.in_memory import InMemoryWallet -from ...wallet.key_type import (BLS12381G1, BLS12381G1G2, BLS12381G2, ED25519, - X25519) +from ...wallet.key_type import BLS12381G1, BLS12381G1G2, BLS12381G2, ED25519, X25519 @pytest.fixture() @@ -160,16 +158,12 @@ async def test_create_local_sov_seeded(self, wallet: InMemoryWallet): await wallet.create_local_did(SOV, ED25519, self.test_seed, None) with pytest.raises(WalletError): - _ = await wallet.create_local_did( - SOV, ED25519, "invalid-seed", None - ) + _ = await wallet.create_local_did(SOV, ED25519, "invalid-seed", None) @pytest.mark.asyncio @pytest.mark.ursa_bbs_signatures async def test_create_local_key_seeded_bls12381g2(self, wallet: InMemoryWallet): - info = await wallet.create_local_did( - KEY, BLS12381G2, self.test_seed, None - ) + info = await wallet.create_local_did(KEY, BLS12381G2, self.test_seed, None) assert info.did == self.test_key_bls12381g2_did assert info.verkey == self.test_bls12381g2_verkey @@ -177,9 +171,7 @@ async def test_create_local_key_seeded_bls12381g2(self, wallet: InMemoryWallet): await wallet.create_local_did(KEY, BLS12381G2, self.test_seed, None) with pytest.raises(WalletError): - _ = await wallet.create_local_did( - KEY, BLS12381G2, "invalid-seed", None - ) + _ = await wallet.create_local_did(KEY, BLS12381G2, "invalid-seed", None) @pytest.mark.asyncio async def test_create_local_key_seeded_ed25519(self, wallet: InMemoryWallet): @@ -191,9 +183,7 @@ async def test_create_local_key_seeded_ed25519(self, wallet: InMemoryWallet): await wallet.create_local_did(KEY, ED25519, self.test_seed, None) with pytest.raises(WalletError): - _ = await wallet.create_local_did( - KEY, ED25519, "invalid-seed", None - ) + _ = await wallet.create_local_did(KEY, ED25519, "invalid-seed", None) @pytest.mark.asyncio async def test_rotate_did_keypair(self, wallet: InMemoryWallet): @@ -227,9 +217,7 @@ async def test_rotate_did_keypair(self, wallet: InMemoryWallet): @pytest.mark.asyncio async def test_create_local_with_did(self, wallet: InMemoryWallet): - info = await wallet.create_local_did( - SOV, ED25519, None, self.test_sov_did - ) + info = await wallet.create_local_did(SOV, ED25519, None, self.test_sov_did) assert info.did == self.test_sov_did with pytest.raises(WalletDuplicateError): @@ -524,9 +512,7 @@ async def test_sign_verify_bbs(self, wallet: InMemoryWallet): @pytest.mark.asyncio async def test_pack_unpack(self, wallet: InMemoryWallet): - await wallet.create_local_did( - SOV, ED25519, self.test_seed, self.test_sov_did - ) + await wallet.create_local_did(SOV, ED25519, self.test_seed, self.test_sov_did) packed_anon = await wallet.pack_message( self.test_message, [self.test_ed25519_verkey] diff --git a/aries_cloudagent/wallet/tests/test_indy_wallet.py b/aries_cloudagent/wallet/tests/test_indy_wallet.py index 46f43c802b..8dfe821e58 100644 --- a/aries_cloudagent/wallet/tests/test_indy_wallet.py +++ b/aries_cloudagent/wallet/tests/test_indy_wallet.py @@ -10,8 +10,7 @@ from asynctest import mock as async_mock from ...config.injection_context import InjectionContext -from ...core.error import (ProfileDuplicateError, ProfileError, - ProfileNotFoundError) +from ...core.error import ProfileDuplicateError, ProfileError, ProfileNotFoundError from ...core.in_memory import InMemoryProfile from ...indy.sdk import wallet_setup as test_setup_module from ...indy.sdk.profile import IndySdkProfile, IndySdkProfileManager From 349a59cc46cf467d90122793998eef4f4be2c54c Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Thu, 13 Oct 2022 12:09:21 -0700 Subject: [PATCH 532/872] PR#1970 revert updates + int test fix Signed-off-by: Shaanjot Gill --- aries_cloudagent/core/conductor.py | 27 ++++++------------- aries_cloudagent/core/tests/test_conductor.py | 9 ++++++- demo/requirements.txt | 2 +- 3 files changed, 17 insertions(+), 21 deletions(-) diff --git a/aries_cloudagent/core/conductor.py b/aries_cloudagent/core/conductor.py index 0586ef9955..c24862a4e9 100644 --- a/aries_cloudagent/core/conductor.py +++ b/aries_cloudagent/core/conductor.py @@ -26,7 +26,6 @@ from ..config.logging import LoggingConfigurator from ..config.provider import ClassProvider from ..config.wallet import wallet_config -from ..connections.models.conn_record import ConnRecord from ..core.profile import Profile from ..indy.verifier import IndyVerifier from ..ledger.base import BaseLedger @@ -451,33 +450,23 @@ async def start(self) -> None: if mediation_connections_invite else OutOfBandManager(self.root_profile) ) - async with self.root_profile.session() as session: - invitation = invitation_handler.from_url( + record = await mgr.receive_invitation( + invitation=invitation_handler.from_url( mediation_invite_record.invite - ) - if isinstance(mgr, OutOfBandManager): - oob_record = await mgr.receive_invitation( - invitation=invitation, - auto_accept=True, - ) - conn_record = await ConnRecord.retrieve_by_id( - session, oob_record.connection_id - ) - else: - conn_record = await mgr.receive_invitation( - invitation=invitation, - auto_accept=True, - ) + ), + auto_accept=True, + ) + async with self.root_profile.session() as session: await ( MediationInviteStore( session.context.inject(BaseStorage) ).mark_default_invite_as_used() ) - await conn_record.metadata_set( + await record.metadata_set( session, MediationManager.SEND_REQ_AFTER_CONNECTION, True ) - await conn_record.metadata_set( + await record.metadata_set( session, MediationManager.SET_TO_DEFAULT_ON_GRANTED, True ) diff --git a/aries_cloudagent/core/tests/test_conductor.py b/aries_cloudagent/core/tests/test_conductor.py index 890e60b6ab..fe94ad97ad 100644 --- a/aries_cloudagent/core/tests/test_conductor.py +++ b/aries_cloudagent/core/tests/test_conductor.py @@ -24,6 +24,7 @@ from ...protocols.coordinate_mediation.v1_0.models.mediation_record import ( MediationRecord, ) +from ...protocols.out_of_band.v1_0.models.oob_record import OobRecord from ...resolver.did_resolver import DIDResolver from ...multitenant.base import BaseMultitenantManager from ...multitenant.manager import MultitenantManager @@ -1172,8 +1173,13 @@ async def test_mediator_invitation_0434(self, mock_from_url, _): ) conn_record.accept = ConnRecord.ACCEPT_MANUAL await conn_record.save(await conductor.root_profile.session()) - oob_record = async_mock.MagicMock( + invitation = test_module.InvitationMessage() + oob_record = OobRecord( + invitation=invitation, + invi_msg_id=invitation._id, + role=OobRecord.ROLE_RECEIVER, connection_id=conn_record.connection_id, + state=OobRecord.STATE_INITIAL, ) with async_mock.patch.object( test_module, @@ -1194,6 +1200,7 @@ async def test_mediator_invitation_0434(self, mock_from_url, _): await conductor.start() await conductor.stop() mock_from_url.assert_called_once_with("test-invite") + mock_mgr.return_value.receive_invitation.assert_called_once() @async_mock.patch.object(test_module, "MediationInviteStore") @async_mock.patch.object(test_module.ConnectionInvitation, "from_url") diff --git a/demo/requirements.txt b/demo/requirements.txt index 276088d6b5..26f58df943 100644 --- a/demo/requirements.txt +++ b/demo/requirements.txt @@ -1,5 +1,5 @@ asyncpg~=0.25.0 prompt_toolkit~=2.0.9 -git+https://github.com/webpy/webpy.git#egg=web.py +web.py~=0.62 pygments~=2.10 qrcode[pil]~=6.1 From d71e9e29f6bbf93e9f5ea90232850bb83ea04c99 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Thu, 13 Oct 2022 13:45:30 -0700 Subject: [PATCH 533/872] Demo of an author-configured agent via docker Signed-off-by: Ian Costanzo --- demo/docker-agent/Dockerfile.acapy | 10 ++++++ demo/docker-agent/docker-compose.yml | 47 ++++++++++++++++++++++++++ demo/docker-agent/ngrok-wait.sh | 49 ++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+) create mode 100644 demo/docker-agent/Dockerfile.acapy create mode 100644 demo/docker-agent/docker-compose.yml create mode 100755 demo/docker-agent/ngrok-wait.sh diff --git a/demo/docker-agent/Dockerfile.acapy b/demo/docker-agent/Dockerfile.acapy new file mode 100644 index 0000000000..a8eee30ae0 --- /dev/null +++ b/demo/docker-agent/Dockerfile.acapy @@ -0,0 +1,10 @@ +FROM bcgovimages/aries-cloudagent:py36-1.16-1_1.0.0-rc0 + +USER root + +ADD https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64 ./jq +RUN chmod +x ./jq +COPY ngrok-wait.sh ngrok-wait.sh +RUN chmod +x ./ngrok-wait.sh + +USER $user diff --git a/demo/docker-agent/docker-compose.yml b/demo/docker-agent/docker-compose.yml new file mode 100644 index 0000000000..2857ce219a --- /dev/null +++ b/demo/docker-agent/docker-compose.yml @@ -0,0 +1,47 @@ +# Sample docker-compose to start a local aca-py author agent +# To start aca-py and the postgres database, just run `docker-compose up` +# To shut down the services run `docker-compose rm` - this will retain the postgres database, so you can change aca-py startup parameters +# and restart the docker containers without losing your wallet data +# If you want to delete your wallet data just run `docker volume ls -q | xargs docker volume rm` +version: "3" +services: + ngrok-agent: + image: wernight/ngrok + ports: + - 4057:4040 + command: ngrok http author-agent:8001 --log stdout + + author-agent: + build: + context: . + dockerfile: Dockerfile.acapy + environment: + - NGROK_NAME=ngrok-agent + ports: + - 8010:8010 + - 8001:8001 + depends_on: + - wallet-db + entrypoint: /bin/bash + command: [ + "-c", + "sleep 5; \ + ./ngrok-wait.sh" + ] + volumes: + - ./ngrok-wait.sh:/home/indy/ngrok-wait.sh + + wallet-db: + image: vcr-postgresql + environment: + - POSTGRESQL_USER=DB_USER + - POSTGRESQL_PASSWORD=DB_PASSWORD + - POSTGRESQL_DATABASE=DB_USER + - POSTGRESQL_ADMIN_PASSWORD=mysecretpassword + ports: + - 5433:5432 + volumes: + - wallet-db-data:/var/lib/pgsql/data + +volumes: + wallet-db-data: diff --git a/demo/docker-agent/ngrok-wait.sh b/demo/docker-agent/ngrok-wait.sh new file mode 100755 index 0000000000..f7e3fb07b8 --- /dev/null +++ b/demo/docker-agent/ngrok-wait.sh @@ -0,0 +1,49 @@ +#!/bin/bash + +# based on code developed by Sovrin: https://github.com/hyperledger/aries-acapy-plugin-toolbox + +echo "using ngrok end point [$NGROK_NAME]" + +NGROK_ENDPOINT=null +while [ -z "$NGROK_ENDPOINT" ] || [ "$NGROK_ENDPOINT" = "null" ] +do + echo "Fetching end point from ngrok service" + NGROK_ENDPOINT=$(curl --silent $NGROK_NAME:4040/api/tunnels | ./jq -r '.tunnels[] | select(.proto=="https") | .public_url') + + if [ -z "$NGROK_ENDPOINT" ] || [ "$NGROK_ENDPOINT" = "null" ]; then + echo "ngrok not ready, sleeping 5 seconds...." + sleep 5 + fi +done + +export ACAPY_ENDPOINT=$NGROK_ENDPOINT + +echo "Starting aca-py agent with endpoint [$ACAPY_ENDPOINT]" + +# ... if you want to echo the aca-py startup command ... +set -x + +exec aca-py start \ + --auto-provision \ + --inbound-transport http '0.0.0.0' 8001 \ + --outbound-transport http \ + --genesis-url "https://raw.githubusercontent.com/ICCS-ISAC/dtrust-reconu/main/CANdy/dev/pool_transactions_genesis" \ + --endpoint "${ACAPY_ENDPOINT}" \ + --auto-ping-connection \ + --monitor-ping \ + --public-invites \ + --wallet-type "askar" \ + --wallet-name "test_author" \ + --wallet-key "secret_key" \ + --wallet-storage-type "postgres_storage" \ + --wallet-storage-config "{\"url\":\"wallet-db:5432\",\"max_connections\":5}" \ + --wallet-storage-creds "{\"account\":\"DB_USER\",\"password\":\"DB_PASSWORD\",\"admin_account\":\"postgres\",\"admin_password\":\"mysecretpassword\"}" \ + --admin '0.0.0.0' 8010 \ + --label "test_author" \ + --admin-insecure-mode \ + --endorser-protocol-role author \ + --endorser-alias 'Endorser' \ + --auto-request-endorsement \ + --auto-write-transactions \ + --auto-create-revocation-transactions \ + --log-level "error" From 6a8546d46b43f7e3a27b9a5932ee9d5d2f13da26 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Thu, 13 Oct 2022 17:12:05 -0700 Subject: [PATCH 534/872] web.py fix Signed-off-by: Shaanjot Gill --- demo/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/requirements.txt b/demo/requirements.txt index 276088d6b5..26f58df943 100644 --- a/demo/requirements.txt +++ b/demo/requirements.txt @@ -1,5 +1,5 @@ asyncpg~=0.25.0 prompt_toolkit~=2.0.9 -git+https://github.com/webpy/webpy.git#egg=web.py +web.py~=0.62 pygments~=2.10 qrcode[pil]~=6.1 From 70f5e1b6ab665ad53e8caf183aba78b114055565 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Fri, 14 Oct 2022 10:23:23 -0700 Subject: [PATCH 535/872] Add README Signed-off-by: Ian Costanzo --- demo/docker-agent/Dockerfile.acapy | 4 ++ demo/docker-agent/README.md | 75 ++++++++++++++++++++++++++++ demo/docker-agent/docker-compose.yml | 2 +- demo/docker-agent/ngrok-wait.sh | 6 ++- 4 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 demo/docker-agent/README.md diff --git a/demo/docker-agent/Dockerfile.acapy b/demo/docker-agent/Dockerfile.acapy index a8eee30ae0..aa9e5593de 100644 --- a/demo/docker-agent/Dockerfile.acapy +++ b/demo/docker-agent/Dockerfile.acapy @@ -8,3 +8,7 @@ COPY ngrok-wait.sh ngrok-wait.sh RUN chmod +x ./ngrok-wait.sh USER $user + +# temporary until this PR gets merged/released +RUN pip uninstall -y aries-cloudagent +RUN pip install aries-cloudagent[indy,bbs,askar]@git+https://github.com/ianco/aries-cloudagent-python@endorser-write-did diff --git a/demo/docker-agent/README.md b/demo/docker-agent/README.md new file mode 100644 index 0000000000..a2700b1882 --- /dev/null +++ b/demo/docker-agent/README.md @@ -0,0 +1,75 @@ +# Running an Author Agent and connecting to an Endorser + +This directory contains scripts to run an aca-py agent as an Author, that can conenct to an Endorser service. + +## Running the Author Agent + +The docker-compose script runs ngrok to expose the agent's port publicly, and stores wallet data in a postgres database. + +To run the Author agent in this repo, open a command shell in this directory and run: + +- to build the containers: + +```bash +docker-compose build +``` + +- to run the author agent: + +```bash +docker-compose up +``` + +You can connect to the [agent's api service here](http://localhost:8010). + +Note that all the configuration settings are hard-coded in the docker-compose file and ngrok-wait.sh script, so if you change any configs you need to rebuild the docker images. + +- to shut down the agent: + +```bash +docker-compose stop +docker-compose rm -f +``` + +This will leave the agent's wallet data, so if you restart the agent it will maintain any created data. + +- to remove the agent's wallet: + +```bash +docker volume rm docker-agent_wallet-db-data +``` + +## Connecting to an Endorser Service + +For this example, we will connect to [this endorser service](https://github.com/bcgov/aries-endorser-service), which you can connect to locally at `http://localhost:5050/endorser/docs`. + +Make sure you start the endorser service on the same ledger as your author, and make sure the endorser has a public DID with ENDORSER role. + +For example start the endorser service as `LEDGER_URL=http://test.bcovrin.vonx.io TAILS_SERVER_URL=https://tails-test.vonx.io ./manage start --logs` and then make sure the Author agent is started with `--genesis_url http://test.bcovrin.vonx.io/genesis`. + +### Connecting the Author to the Endorser + +Endorser Service: Use the `GET /v1/admin/config` endpoint to fetch the endorser's configuration, including the public DID (which the author will need to know). Also confirm whether the `ENDORSER_AUTO_ACCEPT_CONNECTIONS` and `ENDORSER_AUTO_ENDORSE_REQUESTS` settings are `True` or `False` - for the following we will assume that both are `False` and the endorser must explicitely respond to all requests. + +Author Agent: Use the `POST /didexchange/create-request` to request a connection with the endorser, using the endorser's public DID. Set the `alias` to `Endorser` - this *MUST* match the `--endorser-alias 'Endorser'` setting (in the ngrok-wait.sh script). Use the `GET /connections` endpoint to verify the connection is in `request` state. + +Endorser Service: Use the `GET /v1/connections` endpoint to see the connection request (state `request`). Using the `connection_id`, call the `POST /connections/{connection_id}/accept` endpoint to accept the request. Verify that the connection state goes to `active`. + +Author Agent: Verify the connection state goes to `active`. Use the `POST /transactions/{conn_id}/set-endorser-role` to set the connection role to `TRANSACTION_AUTHOR`, and then use `POST /transactions/{conn_id}/set-endorser-info` to set the endorser's alias to `Endorser` and the public DID to the endorser's public DID. Verify the settings using the `GET /connections/{conn_id}/meta-data` endpoint. + +The connection is now setup between the two agents! + +### Creating a Public Author DID + +Author Agent: Use the `POST /wallet/did/create` (use an empty `{}` POST body) to create a local did. Then use `POST /ledger/register-nym` to send the data to the ledger - this will create a transaction and send it to the endorser service. + +Endorser Service: Use the `GET /v1/endorse/transactions` endpoint to see the endorse request - it should be in state `request_received`. Using the `POST /v1/endorse/transactions/{transaction_id}/endorse` endpoint and the `transaction_id`, approve the request. The state should now (eventually) go to `transaction_acked`. + +Author Service: Use the `GET /transactions` endpoint to verify the transaction is in `transaction_acked` state. Then use the `POST /wallet/did/public` to set the new DID to be the Author's public DID. This will generate another endorser transaction to set the DID's endpoint (ATTRIB transaction) on the ledger. + +Endorser Service: Use the same endpoints as above (`GET /v1/endorse/transactions` and then `POST /v1/endorse/transactions/{transaction_id}/endorse`) to view the endorse request and approve it. + +### Endorsing Author Requests + +Author requests to create schema, create credential definition and create revocation registries will all now generate endorse requests to the endorser. + diff --git a/demo/docker-agent/docker-compose.yml b/demo/docker-agent/docker-compose.yml index 2857ce219a..8dd07c428f 100644 --- a/demo/docker-agent/docker-compose.yml +++ b/demo/docker-agent/docker-compose.yml @@ -8,7 +8,7 @@ services: ngrok-agent: image: wernight/ngrok ports: - - 4057:4040 + - 4067:4040 command: ngrok http author-agent:8001 --log stdout author-agent: diff --git a/demo/docker-agent/ngrok-wait.sh b/demo/docker-agent/ngrok-wait.sh index f7e3fb07b8..70c50ce6f3 100755 --- a/demo/docker-agent/ngrok-wait.sh +++ b/demo/docker-agent/ngrok-wait.sh @@ -27,12 +27,12 @@ exec aca-py start \ --auto-provision \ --inbound-transport http '0.0.0.0' 8001 \ --outbound-transport http \ - --genesis-url "https://raw.githubusercontent.com/ICCS-ISAC/dtrust-reconu/main/CANdy/dev/pool_transactions_genesis" \ + --genesis-url "http://test.bcovrin.vonx.io/genesis" \ --endpoint "${ACAPY_ENDPOINT}" \ --auto-ping-connection \ --monitor-ping \ --public-invites \ - --wallet-type "askar" \ + --wallet-type "indy" \ --wallet-name "test_author" \ --wallet-key "secret_key" \ --wallet-storage-type "postgres_storage" \ @@ -47,3 +47,5 @@ exec aca-py start \ --auto-write-transactions \ --auto-create-revocation-transactions \ --log-level "error" + +# --genesis-url "https://raw.githubusercontent.com/ICCS-ISAC/dtrust-reconu/main/CANdy/dev/pool_transactions_genesis" \ From 29f4bb70e3b2a19372d1e7b560dfca9e81b0f1e5 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Fri, 14 Oct 2022 10:59:43 -0700 Subject: [PATCH 536/872] Updated README Signed-off-by: Ian Costanzo --- demo/docker-agent/README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/demo/docker-agent/README.md b/demo/docker-agent/README.md index a2700b1882..c5fb56dc59 100644 --- a/demo/docker-agent/README.md +++ b/demo/docker-agent/README.md @@ -39,6 +39,8 @@ This will leave the agent's wallet data, so if you restart the agent it will mai docker volume rm docker-agent_wallet-db-data ``` +Note that the Author agent is not (yet) configured with revocations enabled or a tails server, so revocation is not supported. + ## Connecting to an Endorser Service For this example, we will connect to [this endorser service](https://github.com/bcgov/aries-endorser-service), which you can connect to locally at `http://localhost:5050/endorser/docs`. @@ -73,3 +75,13 @@ Endorser Service: Use the same endpoints as above (`GET /v1/endorse/transaction Author requests to create schema, create credential definition and create revocation registries will all now generate endorse requests to the endorser. +Author Agent: To create a schema use the `POST /schemas` endpoint. This will create an endorse request. + +Endorser Service: Use the same endpoints as above (`GET /v1/endorse/transactions` and then `POST /v1/endorse/transactions/{transaction_id}/endorse`) to view the endorse request and approve it. + +Author Agent: To create a cred def use the `POST /credential-definitions` endpoint. This will create an endorse request. + +Endorser Service: Use the same endpoints as above (`GET /v1/endorse/transactions` and then `POST /v1/endorse/transactions/{transaction_id}/endorse`) to view the endorse request and approve it. + + + From ad44b028053c38eae85ba774c0f2def33eda178f Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Fri, 14 Oct 2022 16:30:40 -0400 Subject: [PATCH 537/872] fix: public did mediator routing keys as did keys Squash of multiple commits: fix: revert changes to manager fix: don't use dereference_as fix: correct typo in method fix: remove stray print statement Signed-off-by: Daniel Bluhm --- aries_cloudagent/connections/base_manager.py | 8 +++-- .../protocols/out_of_band/v1_0/manager.py | 1 + aries_cloudagent/resolver/default/indy.py | 17 ++++++++-- aries_cloudagent/resolver/did_resolver.py | 31 +++++++++++-------- 4 files changed, 38 insertions(+), 19 deletions(-) diff --git a/aries_cloudagent/connections/base_manager.py b/aries_cloudagent/connections/base_manager.py index 4308705c31..53172b9966 100644 --- a/aries_cloudagent/connections/base_manager.py +++ b/aries_cloudagent/connections/base_manager.py @@ -269,16 +269,18 @@ async def resolve_invitation( endpoint = first_didcomm_service.service_endpoint recipient_keys: List[VerificationMethod] = [ - doc.dereference(url) for url in first_didcomm_service.recipient_keys + await resolver.dereference(self._profile, url, document=doc) + for url in first_didcomm_service.recipient_keys ] routing_keys: List[VerificationMethod] = [ - doc.dereference(url) for url in first_didcomm_service.routing_keys + await resolver.dereference(self._profile, url, document=doc) + for url in first_didcomm_service.routing_keys ] for key in [*recipient_keys, *routing_keys]: if not isinstance(key, self.SUPPORTED_KEY_TYPES): raise BaseConnectionManagerError( - f"Key type {key.type} is not supported" + f"Key type {type(key).__name__} is not supported" ) return ( diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py index 7f160ed63c..3c9e300b95 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py @@ -222,6 +222,7 @@ async def create_invitation( async with self.profile.session() as session: wallet = session.inject(BaseWallet) public_did = await wallet.get_public_did() + if not public_did: raise OutOfBandManagerError( "Cannot create public invitation with no public DID" diff --git a/aries_cloudagent/resolver/default/indy.py b/aries_cloudagent/resolver/default/indy.py index ad34b90487..7f86025ed2 100644 --- a/aries_cloudagent/resolver/default/indy.py +++ b/aries_cloudagent/resolver/default/indy.py @@ -11,6 +11,7 @@ from ...config.injection_context import InjectionContext from ...core.profile import Profile +from ...did.did_key import DIDKey from ...ledger.endpoint_type import EndpointType from ...ledger.error import LedgerError from ...ledger.multiple_ledger.ledger_requests_executor import ( @@ -19,7 +20,7 @@ ) from ...messaging.valid import IndyDID from ...multitenant.base import BaseMultitenantManager - +from ...wallet.key_type import KeyType from ..base import BaseDIDResolver, DIDNotFound, ResolverError, ResolverType LOGGER = logging.getLogger(__name__) @@ -29,6 +30,16 @@ class NoIndyLedger(ResolverError): """Raised when there is no Indy ledger instance configured.""" +def _routing_keys_as_did_key_urls(routing_keys: Sequence[str]) -> Sequence[str]: + """Convert raw base58 keys to did:key values.""" + return [ + DIDKey.from_public_key_b58(routing_key, KeyType.ED25519).key_id + if not routing_key.startswith("did:key:") + else routing_key + for routing_key in routing_keys + ] + + class IndyDIDResolver(BaseDIDResolver): """Indy DID Resolver.""" @@ -101,7 +112,7 @@ def add_services( type_=self.SERVICE_TYPE_DID_COMMUNICATION, service_endpoint=endpoint, priority=1, - routing_keys=routing_keys, + routing_keys=_routing_keys_as_did_key_urls(routing_keys), recipient_keys=[recipient_key.id], accept=( service_accept if service_accept else ["didcomm/aip2;env=rfc19"] @@ -114,7 +125,7 @@ def add_services( type_=self.SERVICE_TYPE_DIDCOMM, service_endpoint=endpoint, recipient_keys=[recipient_key.id], - routing_keys=routing_keys, + routing_keys=_routing_keys_as_did_key_urls(routing_keys), # CHECKME # accept=(service_accept if service_accept else ["didcomm/v2"]), accept=["didcomm/v2"], diff --git a/aries_cloudagent/resolver/did_resolver.py b/aries_cloudagent/resolver/did_resolver.py index a2bdf48cbc..b523b59607 100644 --- a/aries_cloudagent/resolver/did_resolver.py +++ b/aries_cloudagent/resolver/did_resolver.py @@ -8,10 +8,11 @@ from datetime import datetime from itertools import chain import logging -from typing import Optional, List, Sequence, Tuple, Text, Type, TypeVar, Union +from typing import List, Optional, Sequence, Text, Tuple, Union -from pydid import DID, DIDError, DIDUrl, Resource, NonconformantDocument -from pydid.doc.doc import IDNotFoundError +from pydid import DID, DIDError, DIDUrl, Resource +import pydid +from pydid.doc.doc import BaseDIDDocument, IDNotFoundError from ..core.profile import Profile from .base import ( @@ -26,9 +27,6 @@ LOGGER = logging.getLogger(__name__) -ResourceType = TypeVar("ResourceType", bound=Resource) - - class DIDResolver: """did resolver singleton.""" @@ -115,8 +113,12 @@ async def _match_did_to_resolver( return resolvers async def dereference( - self, profile: Profile, did_url: str, *, cls: Type[ResourceType] = Resource - ) -> ResourceType: + self, + profile: Profile, + did_url: str, + *, + document: Optional[BaseDIDDocument] = None, + ) -> Resource: """Dereference a DID URL to its corresponding DID Doc object.""" # TODO Use cached DID Docs when possible try: @@ -128,12 +130,15 @@ async def dereference( "Failed to parse DID URL from {}".format(did_url) ) from err - doc_dict = await self.resolve(profile, parsed.did) - # Use non-conformant doc as the "least common denominator" + if document and parsed.did != document.id: + document = None + + if not document: + doc_dict = await self.resolve(profile, parsed.did) + document = pydid.deserialize_document(doc_dict) + try: - return NonconformantDocument.deserialize(doc_dict).dereference_as( - cls, parsed - ) + return document.dereference(parsed) except IDNotFoundError as error: raise ResolverError( "Failed to dereference DID URL: {}".format(error) From 83a0a6b04bfe756d8a109629670bda061be4f86a Mon Sep 17 00:00:00 2001 From: Moriarty Date: Mon, 17 Oct 2022 15:08:34 +0200 Subject: [PATCH 538/872] chore: fix ACAPY_PROMOTE-AUTHOR-DID flag using underscore instead of hyphon Signed-off-by: Moriarty --- aries_cloudagent/config/argparse.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index c3cc337f6b..81ecbf7853 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -1837,7 +1837,7 @@ def add_arguments(self, parser: ArgumentParser): parser.add_argument( "--auto-promote-author-did", action="store_true", - env_var="ACAPY_PROMOTE-AUTHOR-DID", + env_var="ACAPY_PROMOTE_AUTHOR_DID", help="For Authors, specify whether to automatically promote" " a DID to the wallet public DID after writing to the ledger.", ) From 8b1298e2ea84fbbeed3c91a7099a9e518b625b30 Mon Sep 17 00:00:00 2001 From: Moriarty Date: Mon, 17 Oct 2022 16:36:33 +0200 Subject: [PATCH 539/872] chore: also update docs Signed-off-by: Moriarty --- Endorser.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Endorser.md b/Endorser.md index bf3ec5da8c..a911c77d03 100644 --- a/Endorser.md +++ b/Endorser.md @@ -56,7 +56,7 @@ Endorsement: For Authors, specify whether to automatically create transactions for a cred def's revocation registry. (If not specified, the controller must invoke the endpoints required to create the revocation registry and assign to the cred def.) [env var: ACAPY_CREATE_REVOCATION_TRANSACTIONS] --auto-promote-author-did - For Authors, specify whether to automatically promote a DID to the wallet public DID after writing to the ledger. + For Authors, specify whether to automatically promote a DID to the wallet public DID after writing to the ledger. [env var: ACAPY_PROMOTE_AUTHOR_DID] ``` ## How Aca-py Handles Endorsements From 55d0682dd952910843551425c756bc490ff95d79 Mon Sep 17 00:00:00 2001 From: Moriarty Date: Mon, 17 Oct 2022 21:06:07 +0200 Subject: [PATCH 540/872] chore: code review feedback Signed-off-by: Moriarty --- Endorser.md | 2 +- aries_cloudagent/config/argparse.py | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Endorser.md b/Endorser.md index a911c77d03..b1db873c26 100644 --- a/Endorser.md +++ b/Endorser.md @@ -56,7 +56,7 @@ Endorsement: For Authors, specify whether to automatically create transactions for a cred def's revocation registry. (If not specified, the controller must invoke the endpoints required to create the revocation registry and assign to the cred def.) [env var: ACAPY_CREATE_REVOCATION_TRANSACTIONS] --auto-promote-author-did - For Authors, specify whether to automatically promote a DID to the wallet public DID after writing to the ledger. [env var: ACAPY_PROMOTE_AUTHOR_DID] + For Authors, specify whether to automatically promote a DID to the wallet public DID after writing to the ledger. [env var: ACAPY_AUTO_PROMOTE_AUTHOR_DID] ``` ## How Aca-py Handles Endorsements diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index 81ecbf7853..b402661f6d 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -13,12 +13,12 @@ from configargparse import ArgumentParser, Namespace, YAMLConfigFileParser -from ..utils.tracing import trace_event +from aries_cloudagent.utils.tracing import trace_event -from .error import ArgsParseError -from .util import BoundedInt, ByteSize +from aries_cloudagent.config.error import ArgsParseError +from aries_cloudagent.config.util import BoundedInt, ByteSize -from .plugin_settings import PLUGIN_CONFIG_KEY +from aries_cloudagent.config.plugin_settings import PLUGIN_CONFIG_KEY CAT_PROVISION = "general" CAT_START = "start" @@ -1837,7 +1837,7 @@ def add_arguments(self, parser: ArgumentParser): parser.add_argument( "--auto-promote-author-did", action="store_true", - env_var="ACAPY_PROMOTE_AUTHOR_DID", + env_var="ACAPY_AUTO_PROMOTE_AUTHOR_DID", help="For Authors, specify whether to automatically promote" " a DID to the wallet public DID after writing to the ledger.", ) From 098701e89bbddba20788ae9600f52a8458374774 Mon Sep 17 00:00:00 2001 From: Moriarty Date: Tue, 18 Oct 2022 09:27:53 +0200 Subject: [PATCH 541/872] chore: remove auto-formatted imports Signed-off-by: Moriarty --- aries_cloudagent/config/argparse.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index b402661f6d..6822f72c2f 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -13,12 +13,12 @@ from configargparse import ArgumentParser, Namespace, YAMLConfigFileParser -from aries_cloudagent.utils.tracing import trace_event +from ..utils.tracing import trace_event -from aries_cloudagent.config.error import ArgsParseError -from aries_cloudagent.config.util import BoundedInt, ByteSize +from .error import ArgsParseError +from .util import BoundedInt, ByteSize -from aries_cloudagent.config.plugin_settings import PLUGIN_CONFIG_KEY +from .plugin_settings import PLUGIN_CONFIG_KEY CAT_PROVISION = "general" CAT_START = "start" From be53f255847c9470899ae027a3da979c47882a3e Mon Sep 17 00:00:00 2001 From: Moriarty Date: Tue, 18 Oct 2022 15:28:07 +0200 Subject: [PATCH 542/872] feat: update pynacl version from 1.4.0 to 1.50 this fixes install for m1 mac arm64 arch Signed-off-by: Moriarty --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 70bc72e54e..fe33a1021c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,7 +13,7 @@ markupsafe==2.0.1 marshmallow==3.5.1 msgpack~=1.0 prompt_toolkit~=2.0.9 -pynacl~=1.4.0 +pynacl~=1.5.0 requests~=2.25.0 packaging~=20.4 pyld~=2.0.3 From 2777d020a364d9e1776d671e9b60dfcb47951dae Mon Sep 17 00:00:00 2001 From: pco Date: Tue, 18 Oct 2022 19:00:23 +0200 Subject: [PATCH 543/872] fixed issue when passing debug options through the run_demo script Signed-off-by: pco --- demo/run_demo | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/demo/run_demo b/demo/run_demo index b9c5714ded..088685eab3 100755 --- a/demo/run_demo +++ b/demo/run_demo @@ -21,8 +21,10 @@ if [ -z "$DOCKER_NET" ]; then fi DOCKER_VOL="" +j=1 for i in "$@" do + ((j++)) if [ ! -z "$SKIP" ]; then SKIP="" continue @@ -65,12 +67,12 @@ do continue ;; --debug-pycharm-controller-port) - PYDEVD_PYCHARM_CONTROLLER_PORT=$2 + PYDEVD_PYCHARM_CONTROLLER_PORT=${!j} SKIP=1 continue ;; --debug-pycharm-agent-port) - PYDEVD_PYCHARM_AGENT_PORT=$2 + PYDEVD_PYCHARM_AGENT_PORT=${!j} SKIP=1 continue ;; @@ -154,7 +156,7 @@ if [ ! -z "$DOCKERHOST" ]; then export RUNMODE="docker" elif [ -z "${PWD_HOST_FQDN}" ]; then # getDockerHost; for details refer to https://github.com/bcgov/DITP-DevOps/tree/main/code/snippets#getdockerhost - . /dev/stdin <<<"$(cat <(curl -s --raw https://raw.githubusercontent.com/bcgov/DITP-DevOps/main/code/snippets/getDockerHost))" + . /dev/stdin <<<"$(cat <(curl -s --raw https://raw.githubusercontent.com/bcgov/DITP-DevOps/main/code/snippets/getDockerHost))" export DOCKERHOST=$(getDockerHost) export RUNMODE="docker" else From 1e85957d509be753a5383bffb60dc2ba6c4b7788 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Tue, 18 Oct 2022 22:52:09 -0400 Subject: [PATCH 544/872] Simplify actions and images * test: build and run without push Signed-off-by: Daniel Bluhm * test: further slim tests-indy Signed-off-by: Daniel Bluhm * fix: store image in docker Signed-off-by: Daniel Bluhm * fix: load true for build of images Signed-off-by: Daniel Bluhm * fix: bad workflow for tests-indy Signed-off-by: Daniel Bluhm * refactor: indy images all from one Dockerfile And don't publish indy-python Signed-off-by: Daniel Bluhm * fix: no tty in gha Signed-off-by: Daniel Bluhm * fix: clean up workflows Signed-off-by: Daniel Bluhm * fix: id of jobs and conditions Signed-off-by: Daniel Bluhm * fix: publish indy meta and condition Signed-off-by: Daniel Bluhm * fix: tags for release on indy image publish Signed-off-by: Daniel Bluhm * refactor: move local actions to .github folder Signed-off-by: Daniel Bluhm * feat: create nightly testing workflows Signed-off-by: Daniel Bluhm * ci: use reusable workflows to DRY things up Signed-off-by: Daniel Bluhm * fix: env values Signed-off-by: Daniel Bluhm * fix: env values again Signed-off-by: Daniel Bluhm * fix: don't use env Signed-off-by: Daniel Bluhm * refactor: unified pr and nightly workflows Signed-off-by: Daniel Bluhm * fix: run on python 3.6 Signed-off-by: Daniel Bluhm * docs: update docs Signed-off-by: Daniel Bluhm * chore: update label on workflow Signed-off-by: Daniel Bluhm * chore: fix typo in docs Signed-off-by: Daniel Bluhm Signed-off-by: Daniel Bluhm --- .../actions}/run-indy-tails-server/action.yml | 0 .../actions}/run-integration-tests/action.yml | 0 .../actions}/run-von-network/action.yml | 0 .github/workflows/integrationtests.yml | 2 +- .github/workflows/nightly-indy.yml | 86 ------- .github/workflows/nightly-tests.yml | 29 +++ .github/workflows/nightly.yml | 84 ------- .github/workflows/pr-tests.yml | 18 ++ .github/workflows/publish-indy-python.yml | 101 -------- .github/workflows/publish-indy.yml | 50 ++-- .github/workflows/publish.yml | 47 ++-- .github/workflows/tests-indy.yml | 183 ++------------- .github/workflows/tests.yml | 17 +- ContainerImagesAndGithubActions.md | 105 +++------ docker/Dockerfile | 2 +- docker/Dockerfile.indy | 220 +++++++++++++++++- docker/Dockerfile.indy-base | 170 -------------- scripts/run_tests_indy | 7 +- 18 files changed, 387 insertions(+), 734 deletions(-) rename {actions => .github/actions}/run-indy-tails-server/action.yml (100%) rename {actions => .github/actions}/run-integration-tests/action.yml (100%) rename {actions => .github/actions}/run-von-network/action.yml (100%) delete mode 100644 .github/workflows/nightly-indy.yml create mode 100644 .github/workflows/nightly-tests.yml delete mode 100644 .github/workflows/nightly.yml create mode 100644 .github/workflows/pr-tests.yml delete mode 100644 .github/workflows/publish-indy-python.yml delete mode 100644 docker/Dockerfile.indy-base diff --git a/actions/run-indy-tails-server/action.yml b/.github/actions/run-indy-tails-server/action.yml similarity index 100% rename from actions/run-indy-tails-server/action.yml rename to .github/actions/run-indy-tails-server/action.yml diff --git a/actions/run-integration-tests/action.yml b/.github/actions/run-integration-tests/action.yml similarity index 100% rename from actions/run-integration-tests/action.yml rename to .github/actions/run-integration-tests/action.yml diff --git a/actions/run-von-network/action.yml b/.github/actions/run-von-network/action.yml similarity index 100% rename from actions/run-von-network/action.yml rename to .github/actions/run-von-network/action.yml diff --git a/.github/workflows/integrationtests.yml b/.github/workflows/integrationtests.yml index db62b14a34..404f7c562a 100644 --- a/.github/workflows/integrationtests.yml +++ b/.github/workflows/integrationtests.yml @@ -21,7 +21,7 @@ jobs: #- name: run-indy-tails-server # uses: ./acapy/actions/run-indy-tails-server - name: run-integration-tests - uses: ./acapy/actions/run-integration-tests + uses: ./acapy/.github/actions/run-integration-tests # to run with a specific set of tests include the following parameter: # with: # TEST_SCOPE: "-t @T001-RFC0037" diff --git a/.github/workflows/nightly-indy.yml b/.github/workflows/nightly-indy.yml deleted file mode 100644 index 82288898b0..0000000000 --- a/.github/workflows/nightly-indy.yml +++ /dev/null @@ -1,86 +0,0 @@ -name: Nightly Build (Indy) -on: - schedule: - - cron: '0 0 * * *' - workflow_dispatch: - -env: - NAME: aries-cloudagent-python - PYTHON_VERSION: 3.7 - INDY_VERSION: 1.16.0 - -jobs: - nightly: - name: Nightly (Indy) - runs-on: ubuntu-latest - steps: - - name: Gather image info - id: info - run: | - echo "::set-output name=repo-owner::${GITHUB_REPOSITORY_OWNER,,}" - - - name: Check image exists - id: image-exists - uses: dbluhm/image-tag-exists@257851f02e3473a75719e26b5a566ea5457da4ef - with: - tag: ghcr.io/${{ steps.info.outputs.repo-owner }}/${{ env.NAME }}:py${{ env.PYTHON_VERSION }}-indy-${{ env.INDY_VERSION }}-nightly-${{ github.sha }} - token: ${{ secrets.GITHUB_TOKEN }} - - - uses: actions/checkout@v3 - if: steps.image-exists.outputs.exists != 'true' - - - name: Cache Docker layers - if: steps.image-exists.outputs.exists != 'true' - uses: actions/cache@v3 - with: - path: /tmp/.buildx-cache - key: ${{ runner.os }}-buildx-${{ github.sha }} - restore-keys: | - ${{ runner.os }}-buildx- - - - name: Set up Docker Buildx - if: steps.image-exists.outputs.exists != 'true' - uses: docker/setup-buildx-action@v1 - - - name: Log in to the GitHub Container Registry - if: steps.image-exists.outputs.exists != 'true' - uses: docker/login-action@v1 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Setup Base Image Metadata - if: steps.image-exists.outputs.exists != 'true' - id: base-meta - uses: docker/metadata-action@v3 - with: - images: | - ghcr.io/${{ steps.info.outputs.repo-owner }}/${{ env.NAME }} - tags: | - type=raw,value=py${{ env.PYTHON_VERSION }}-indy-${{ env.INDY_VERSION }}-nightly - type=sha,format=long,prefix=py${{ env.PYTHON_VERSION }}-indy-${{ env.INDY_VERSION }}-nightly- - - - name: Build and Push Base Image to ghcr.io - if: steps.image-exists.outputs.exists != 'true' - uses: docker/build-push-action@v3 - with: - push: true - context: . - file: docker/Dockerfile.indy - tags: ${{ steps.base-meta.outputs.tags }} - labels: ${{ steps.base-meta.outputs.labels }} - build-args: | - python_version=${{ env.PYTHON_VERSION }} - cache-from: type=local,src=/tmp/.buildx-cache - cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max - - # Temp fix - # https://github.com/docker/build-push-action/issues/252 - # https://github.com/moby/buildkit/issues/1896 - - name: Move cache - if: steps.image-exists.outputs.exists != 'true' - run: | - rm -rf /tmp/.buildx-cache - mv /tmp/.buildx-cache-new /tmp/.buildx-cache-base - diff --git a/.github/workflows/nightly-tests.yml b/.github/workflows/nightly-tests.yml new file mode 100644 index 0000000000..72e51140fb --- /dev/null +++ b/.github/workflows/nightly-tests.yml @@ -0,0 +1,29 @@ +name: Nightly Tests + +on: + schedule: + - cron: '0 0 * * *' + workflow_dispatch: + +jobs: + tests: + name: Tests + strategy: + fail-fast: false + matrix: + python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"] + uses: ./.github/workflows/tests.yml + with: + python-version: ${{ matrix.python-version }} + + tests-indy: + name: Tests (Indy) + strategy: + fail-fast: false + matrix: + python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"] + + uses: ./.github/workflows/tests-indy.yml + with: + python-version: ${{ matrix.python-version }} + indy-version: "1.16.0" diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml deleted file mode 100644 index 4a02ae3f1e..0000000000 --- a/.github/workflows/nightly.yml +++ /dev/null @@ -1,84 +0,0 @@ -name: Nightly Build -on: - schedule: - - cron: '0 0 * * *' - workflow_dispatch: - -env: - NAME: aries-cloudagent-python - PYTHON_VERSION: 3.7 - -jobs: - nightly: - name: Nightly - runs-on: ubuntu-latest - steps: - - name: Gather image info - id: info - run: | - echo "::set-output name=repo-owner::${GITHUB_REPOSITORY_OWNER,,}" - - - name: Check image exists - id: image-exists - uses: dbluhm/image-tag-exists@257851f02e3473a75719e26b5a566ea5457da4ef - with: - tag: ghcr.io/${{ steps.info.outputs.repo-owner }}/${{ env.NAME }}:py${{ env.PYTHON_VERSION }}-nightly-${{ github.sha }} - token: ${{ secrets.GITHUB_TOKEN }} - - - uses: actions/checkout@v3 - if: steps.image-exists.outputs.exists != 'true' - - - name: Cache Docker layers - if: steps.image-exists.outputs.exists != 'true' - uses: actions/cache@v3 - with: - path: /tmp/.buildx-cache - key: ${{ runner.os }}-buildx-${{ github.sha }} - restore-keys: | - ${{ runner.os }}-buildx- - - - name: Set up Docker Buildx - if: steps.image-exists.outputs.exists != 'true' - uses: docker/setup-buildx-action@v1 - - - name: Log in to the GitHub Container Registry - if: steps.image-exists.outputs.exists != 'true' - uses: docker/login-action@v1 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Setup Base Image Metadata - if: steps.image-exists.outputs.exists != 'true' - id: base-meta - uses: docker/metadata-action@v3 - with: - images: | - ghcr.io/${{ steps.info.outputs.repo-owner }}/${{ env.NAME }} - tags: | - type=raw,value=py${{ env.PYTHON_VERSION }}-nightly - type=sha,format=long,prefix=py${{ env.PYTHON_VERSION }}-nightly- - - - name: Build and Push Base Image to ghcr.io - if: steps.image-exists.outputs.exists != 'true' - uses: docker/build-push-action@v3 - with: - push: true - context: . - file: docker/Dockerfile - tags: ${{ steps.base-meta.outputs.tags }} - labels: ${{ steps.base-meta.outputs.labels }} - build-args: | - python_version=${{ env.PYTHON_VERSION }} - cache-from: type=local,src=/tmp/.buildx-cache - cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max - - # Temp fix - # https://github.com/docker/build-push-action/issues/252 - # https://github.com/moby/buildkit/issues/1896 - - name: Move cache - if: steps.image-exists.outputs.exists != 'true' - run: | - rm -rf /tmp/.buildx-cache - mv /tmp/.buildx-cache-new /tmp/.buildx-cache-base diff --git a/.github/workflows/pr-tests.yml b/.github/workflows/pr-tests.yml new file mode 100644 index 0000000000..228ec7dd7f --- /dev/null +++ b/.github/workflows/pr-tests.yml @@ -0,0 +1,18 @@ +name: PR Tests + +on: + pull_request: + +jobs: + tests: + name: Tests + uses: ./.github/workflows/tests.yml + with: + python-version: "3.6" + + tests-indy: + name: Tests (Indy) + uses: ./.github/workflows/tests-indy.yml + with: + python-version: "3.6" + indy-version: "1.16.0" diff --git a/.github/workflows/publish-indy-python.yml b/.github/workflows/publish-indy-python.yml deleted file mode 100644 index f2b0bea15b..0000000000 --- a/.github/workflows/publish-indy-python.yml +++ /dev/null @@ -1,101 +0,0 @@ -name: Publish Indy Python -on: - workflow_dispatch: - inputs: - indy_sdk_url: - description: 'Indy SDK download URL' - required: false - type: string - indy_postgres_url: - description: 'Indy postgres download URL' - required: false - type: string - indy_version: - description: 'Indy SDK Version' - required: false - type: string - default: '1.16.0' - tag: - description: 'Image tag' - required: false - type: string - -env: - INDY_SDK_TAG_URL: "https://codeload.github.com/hyperledger/indy-sdk/tar.gz/refs/tags/" - -jobs: - publish-image: - strategy: - fail-fast: false - matrix: - python-version: ['3.7', '3.8', '3.9', '3.10'] - - name: Publish Indy Python - runs-on: ubuntu-latest - steps: - - name: Gather image info - id: info - run: | - echo "::set-output name=repo-owner::${GITHUB_REPOSITORY_OWNER,,}" - - [ -n "${{ inputs.indy_sdk_url }}"] && echo "::set-output name=indy-sdk-url::${{ inputs.indy_sdk_url }}" - [ -z "${{ inputs.indy_sdk_url }}"] && echo "::set-output name=indy-sdk-url::${{ env.INDY_SDK_TAG_URL }}v${{ inputs.indy_version }}" - - [ -n "${{ inputs.indy_postgres_url }}"] && echo "::set-output name=indy-postgres-url::${{ inputs.indy_postgres_url }}" - [ -z "${{ inputs.indy_postgres_url }}"] && echo "::set-output name=indy-postgres-url::${{ env.INDY_SDK_TAG_URL }}v${{ inputs.indy_version }}" - - [ -n "${{ inputs.tag }}" ] && echo "::set-output name=tag::${{ inputs.tag }}" - [ -z "${{ inputs.tag }}" ] && echo "::set-output name=tag::py${{ matrix.python-version }}-${{ inputs.indy_version }}" - - - uses: actions/checkout@v3 - - - name: Cache Docker layers - uses: actions/cache@v3 - with: - path: /tmp/.buildx-cache-base - key: ${{ runner.os }}-buildx-base-${{ github.sha }} - restore-keys: | - ${{ runner.os }}-buildx-base- - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 - - - name: Log in to the GitHub Container Registry - uses: docker/login-action@v1 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Setup Image Metadata - id: base-meta - uses: docker/metadata-action@v3 - with: - images: | - ghcr.io/${{ steps.info.outputs.repo-owner }}/indy-python - tags: | - type=raw,value=${{ steps.info.outputs.tag }} - - - name: Build and Push Image to ghcr.io - uses: docker/build-push-action@v3 - with: - push: true - context: . - file: docker/Dockerfile.indy-base - tags: ${{ steps.base-meta.outputs.tags }} - labels: ${{ steps.base-meta.outputs.labels }} - build-args: | - python_version=${{ matrix.python-version }} - indy_version=${{ inputs.indy_version }} - indy_sdk_url=${{ steps.info.outputs.indy-sdk-url }} - indy_postgres_url=${{ steps.info.outputs.indy-postgres-url }} - cache-from: type=local,src=/tmp/.buildx-cache-base - cache-to: type=local,dest=/tmp/.buildx-cache-base-new,mode=max - - # Temp fix - # https://github.com/docker/build-push-action/issues/252 - # https://github.com/moby/buildkit/issues/1896 - - name: Move cache - run: | - rm -rf /tmp/.buildx-cache-base - mv /tmp/.buildx-cache-base-new /tmp/.buildx-cache-base diff --git a/.github/workflows/publish-indy.yml b/.github/workflows/publish-indy.yml index 058187d030..dd7c23cdc7 100644 --- a/.github/workflows/publish-indy.yml +++ b/.github/workflows/publish-indy.yml @@ -1,20 +1,24 @@ name: Publish ACA-Py (Indy) on: + release: + types: [released] workflow_dispatch: inputs: indy_version: description: 'Indy SDK Version' required: false type: string - default: '1.16.0' tag: description: 'Image tag' - required: false + required: true + type: string + version: + description: "Version label in image" + required: true type: string env: - ACAPY_REQS: '[askar,bbs]' - + INDY_VERSION: 1.16.0 jobs: publish-image: @@ -32,21 +36,14 @@ jobs: id: info run: | echo "::set-output name=repo-owner::${GITHUB_REPOSITORY_OWNER,,}" - echo "::set-output name=acapy-version::$(sed -ne 's/__version__ = \"\([0-9.]\+\)\"/\1/p' aries_cloudagent/version.py)" - - - name: Tag image - id: tag - run: | - [ -n "${{ inputs.tag }}" ] && echo "::set-output name=tag::${{ inputs.tag }}" - [ -z "${{ inputs.tag }}" ] && echo "::set-output name=tag::py${{ matrix.python-version }}-indy-${{ inputs.indy_version }}-${{ steps.info.outputs.acapy-version }}" - name: Cache Docker layers uses: actions/cache@v3 with: path: /tmp/.buildx-cache - key: ${{ runner.os }}-buildx-base-${{ github.sha }} + key: ${{ runner.os }}-buildx-${{ github.sha }} restore-keys: | - ${{ runner.os }}-buildx-base- + ${{ runner.os }}-buildx- - name: Set up Docker Buildx uses: docker/setup-buildx-action@v1 @@ -58,14 +55,25 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Setup Image Metadata - id: base-meta + - name: Setup Image Metadata (manual) + if: github.event_name == 'workflow_dispatch' + id: dispatch-meta + uses: docker/metadata-action@v3 + with: + images: | + ghcr.io/${{ steps.info.outputs.repo-owner }}/aries-cloudagent-python + tags: | + type=raw,value=${{ inputs.tag }} + + - name: Setup Image Metadata (release) + if: github.event_name == 'release' + id: meta uses: docker/metadata-action@v3 with: images: | ghcr.io/${{ steps.info.outputs.repo-owner }}/aries-cloudagent-python tags: | - type=raw,value=${{ steps.tag.outputs.tag }} + type=semver,pattern=py${{ matrix.python-version }}-indy-{{ inputs.indy_version || env.INDY_VERSION }}-{{version}} - name: Build and Push Image to ghcr.io uses: docker/build-push-action@v3 @@ -73,14 +81,12 @@ jobs: push: true context: . file: docker/Dockerfile.indy - tags: ${{ steps.base-meta.outputs.tags }} - labels: ${{ steps.base-meta.outputs.labels }} + tags: ${{ steps.dispatch-meta.outputs.tags || steps.meta.outputs.tags }} + target: main build-args: | python_version=${{ matrix.python-version }} - indy_version=${{ inputs.indy_version }} - acapy_version=${{ steps.info.outputs.acapy-version }} - acapy_reqs=${{ env.ACAPY_REQS }} - org=${{ steps.info.outputs.repo-owner }} + indy_version=${{ inputs.indy_version || env.INDY_VERSION }} + acapy_version=${{ inputs.version || github.event.release.tag_name }} cache-from: type=local,src=/tmp/.buildx-cache cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 15449f4350..9ecfc8df35 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,15 +1,17 @@ name: Publish ACA-Py on: + release: + types: [released] workflow_dispatch: inputs: tag: description: 'Image tag' - required: false + required: true + type: string + version: + description: "Version label in image" + required: true type: string - -env: - ACAPY_REQS: '[askar,bbs]' - jobs: publish-image: @@ -27,21 +29,14 @@ jobs: id: info run: | echo "::set-output name=repo-owner::${GITHUB_REPOSITORY_OWNER,,}" - echo "::set-output name=acapy-version::$(sed -ne 's/__version__ = \"\([0-9.]\+\)\"/\1/p' aries_cloudagent/version.py)" - - - name: Tag image - id: tag - run: | - [ -n "${{ inputs.tag }}" ] && echo "::set-output name=tag::${{ inputs.tag }}" - [ -z "${{ inputs.tag }}" ] && echo "::set-output name=tag::py${{ matrix.python-version }}-${{ steps.info.outputs.acapy-version }}" - name: Cache Docker layers uses: actions/cache@v3 with: path: /tmp/.buildx-cache - key: ${{ runner.os }}-buildx-base-${{ github.sha }} + key: ${{ runner.os }}-buildx-${{ github.sha }} restore-keys: | - ${{ runner.os }}-buildx-base- + ${{ runner.os }}-buildx- - name: Set up Docker Buildx uses: docker/setup-buildx-action@v1 @@ -53,14 +48,25 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Setup Image Metadata - id: base-meta + - name: Setup Image Metadata (manual) + if: github.event_name == 'workflow_dispatch' + id: dispatch-meta + uses: docker/metadata-action@v3 + with: + images: | + ghcr.io/${{ steps.info.outputs.repo-owner }}/aries-cloudagent-python + tags: | + type=raw,value=${{ inputs.tag }} + + - name: Setup Image Metadata (release) + if: github.event_name == 'release' + id: meta uses: docker/metadata-action@v3 with: images: | ghcr.io/${{ steps.info.outputs.repo-owner }}/aries-cloudagent-python tags: | - type=raw,value=${{ steps.tag.outputs.tag }} + type=semver,pattern=py${{ matrix.python-version }}-{{version}} - name: Build and Push Image to ghcr.io uses: docker/build-push-action@v3 @@ -68,12 +74,11 @@ jobs: push: true context: . file: docker/Dockerfile - tags: ${{ steps.base-meta.outputs.tags }} - labels: ${{ steps.base-meta.outputs.labels }} + tags: ${{ steps.dispatch-meta.outputs.tags || steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} build-args: | python_version=${{ matrix.python-version }} - acapy_version=${{ steps.info.outputs.acapy-version }} - acapy_reqs=${{ env.ACAPY_REQS }} + acapy_version=${{ inputs.version || github.event.release.tag_name }} cache-from: type=local,src=/tmp/.buildx-cache cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max diff --git a/.github/workflows/tests-indy.yml b/.github/workflows/tests-indy.yml index 86d5c38601..3037354fd4 100644 --- a/.github/workflows/tests-indy.yml +++ b/.github/workflows/tests-indy.yml @@ -1,131 +1,23 @@ name: Tests (Indy) -on: - pull_request: -env: - INDY_VERSION: 1.16.0 +on: + workflow_call: + inputs: + python-version: + required: true + type: string + indy-version: + required: true + type: string jobs: - info: - name: Gather image info - runs-on: ubuntu-latest - outputs: - repo-owner: ${{ steps.info.outputs.owner-lc }} - indy-version: ${{ steps.info.outputs.indy-version }} - indy-sdk-url: ${{ steps.info.outputs.indy-sdk-url }} - base-dep-hash: ${{ steps.info.outputs.base-hash }} - test-dep-hash: ${{ steps.info.outputs.test-hash }} - steps: - - uses: actions/checkout@v3 - - name: Gather image info - id: info - run: | - echo "::set-output name=owner-lc::${GITHUB_REPOSITORY_OWNER,,}" - echo "::set-output name=indy-version::${{env.INDY_VERSION}}" - echo "::set-output name=indy-sdk-url::https://codeload.github.com/hyperledger/indy-sdk/tar.gz/refs/tags/v${{ env.INDY_VERSION }}" - echo "::set-output name=base-hash::${{ hashFiles('docker/Dockerfile.indy-base') }}" - echo "::set-output name=test-hash::${{ hashFiles('requirements*.txt', 'docker/Dockerfile.test-indy') }}" - - base-image: - name: Publish base image - needs: info - runs-on: ubuntu-latest - strategy: - fail-fast: true - matrix: - python-version: ["3.7", "3.8", "3.9", "3.10"] - - steps: - - name: Check image exists - id: image-exists - uses: dbluhm/image-tag-exists@257851f02e3473a75719e26b5a566ea5457da4ef - with: - tag: ghcr.io/${{ needs.info.outputs.repo-owner }}/indy-python-test:py${{ matrix.python-version }}-${{ env.INDY_VERSION }}-${{ needs.info.outputs.base-dep-hash }} - token: ${{ secrets.GITHUB_TOKEN }} - - - uses: actions/checkout@v3 - if: steps.image-exists.outputs.exists != 'true' - - - name: Cache Docker layers - if: steps.image-exists.outputs.exists != 'true' - uses: actions/cache@v3 - with: - path: /tmp/.buildx-cache-base - key: ${{ runner.os }}-buildx-base-${{ github.sha }} - restore-keys: | - ${{ runner.os }}-buildx-base- - - - name: Set up Docker Buildx - if: steps.image-exists.outputs.exists != 'true' - uses: docker/setup-buildx-action@v1 - - - name: Log in to the GitHub Container Registry - if: steps.image-exists.outputs.exists != 'true' - uses: docker/login-action@v1 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Setup Base Image Metadata - if: steps.image-exists.outputs.exists != 'true' - id: base-meta - uses: docker/metadata-action@v3 - with: - images: | - ghcr.io/${{ needs.info.outputs.repo-owner }}/indy-python-test - tags: | - type=raw,value=py${{ matrix.python-version }}-${{ env.INDY_VERSION }}-${{ needs.info.outputs.base-dep-hash }} - - - name: Build and Push Base Image to ghcr.io - if: steps.image-exists.outputs.exists != 'true' - uses: docker/build-push-action@v3 - with: - push: true - context: . - file: docker/Dockerfile.indy-base - tags: ${{ steps.base-meta.outputs.tags }} - labels: ${{ steps.base-meta.outputs.labels }} - build-args: | - python_version=${{ matrix.python-version }} - indy_version=${{ needs.info.outputs.indy_version }} - indy_sdk_url=${{ needs.info.outputs.indy-sdk-url }} - cache-from: type=local,src=/tmp/.buildx-cache-base - cache-to: type=local,dest=/tmp/.buildx-cache-base-new,mode=max - - # Temp fix - # https://github.com/docker/build-push-action/issues/252 - # https://github.com/moby/buildkit/issues/1896 - - name: Move cache - if: steps.image-exists.outputs.exists != 'true' - run: | - rm -rf /tmp/.buildx-cache-base - mv /tmp/.buildx-cache-base-new /tmp/.buildx-cache-base - - - test-image: - name: Publish test image - needs: ["info", "base-image"] + tests: + name: Test Python ${{ inputs.python-version }} on Indy ${{ inputs.indy-version }} runs-on: ubuntu-latest - strategy: - fail-fast: true - matrix: - python-version: ["3.7", "3.8", "3.9", "3.10"] - steps: - - - name: Check image exists - id: image-exists - uses: dbluhm/image-tag-exists@257851f02e3473a75719e26b5a566ea5457da4ef - with: - tag: ghcr.io/${{ needs.info.outputs.repo-owner }}/acapy-test:py${{ matrix.python-version }}-${{ needs.info.outputs.indy-version }}-${{ needs.info.outputs.test-dep-hash }} - token: ${{ secrets.GITHUB_TOKEN }} - - uses: actions/checkout@v3 - if: steps.image-exists.outputs.exists != 'true' - - name: Cache Docker layers - if: steps.image-exists.outputs.exists != 'true' + - name: Cache image layers uses: actions/cache@v3 with: path: /tmp/.buildx-cache-test @@ -134,38 +26,19 @@ jobs: ${{ runner.os }}-buildx-test- - name: Set up Docker Buildx - if: steps.image-exists.outputs.exists != 'true' uses: docker/setup-buildx-action@v1 - - name: Log in to the GitHub Container Registry - if: steps.image-exists.outputs.exists != 'true' - uses: docker/login-action@v1 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Setup Test Image Metadata - if: steps.image-exists.outputs.exists != 'true' - id: test-meta - uses: docker/metadata-action@v3 - with: - images: | - ghcr.io/${{ needs.info.outputs.repo-owner }}/acapy-test - tags: | - type=raw,value=py${{ matrix.python-version }}-${{ env.INDY_VERSION }}-${{ needs.info.outputs.test-dep-hash }} - - - name: Build and Push Test Image to ghcr.io - if: steps.image-exists.outputs.exists != 'true' + - name: Build test image uses: docker/build-push-action@v3 with: - push: true + load: true context: . - file: docker/Dockerfile.test-indy - tags: ${{ steps.test-meta.outputs.tags }} - labels: ${{ steps.test-meta.outputs.labels }} + file: docker/Dockerfile.indy + target: acapy-test + tags: acapy-test:latest build-args: | - base_image=ghcr.io/${{ needs.info.outputs.repo-owner }}/indy-python-test:py${{ matrix.python-version }}-${{ needs.info.outputs.indy-version }}-${{ needs.info.outputs.base-dep-hash }} + python_version=${{ inputs.python-version }} + indy_version=${{ inputs.indy-version }} cache-from: type=local,src=/tmp/.buildx-cache-test cache-to: type=local,dest=/tmp/.buildx-cache-test-new,mode=max @@ -173,26 +46,10 @@ jobs: # https://github.com/docker/build-push-action/issues/252 # https://github.com/moby/buildkit/issues/1896 - name: Move cache - if: steps.image-exists.outputs.exists != 'true' run: | rm -rf /tmp/.buildx-cache-test mv /tmp/.buildx-cache-test-new /tmp/.buildx-cache-test - tests: - name: Tests (Indy) - needs: - - info - - base-image - - test-image - runs-on: ubuntu-latest - container: ghcr.io/${{ needs.info.outputs.repo-owner }}/acapy-test:py${{ matrix.python-version }}-${{ needs.info.outputs.indy-version }}-${{ needs.info.outputs.test-dep-hash }} - strategy: - fail-fast: false - matrix: - python-version: ["3.7", "3.8", "3.9", "3.10"] - - steps: - - uses: actions/checkout@v3 - name: Run pytest run: | - pytest + docker run --rm acapy-test:latest diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6205a865fc..086f026172 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,23 +1,22 @@ name: Tests on: - pull_request: + workflow_call: + inputs: + python-version: + required: true + type: string jobs: tests: - name: Tests + name: Test Python ${{ inputs.python-version }} runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - python-version: ["3.7", "3.8", "3.9", "3.10"] - steps: - uses: actions/checkout@v3 - - name: Set up Python ${{ matrix.python-version }} + - name: Set up Python ${{ inputs.python-version }} uses: actions/setup-python@v4 with: - python-version: ${{ matrix.python-version }} + python-version: ${{ inputs.python-version }} cache: 'pip' cache-dependency-path: 'requirements*.txt' - name: Install dependencies diff --git a/ContainerImagesAndGithubActions.md b/ContainerImagesAndGithubActions.md index e5e8ccf277..ec4a2f32db 100644 --- a/ContainerImagesAndGithubActions.md +++ b/ContainerImagesAndGithubActions.md @@ -14,16 +14,10 @@ repository and made available through the [Github Packages Container Registry](https://ghcr.io). -## Images - -The following images are built from this project - -- `ghcr.io/hyperledger/aries-cloudagent-python` - multiple variants are built - from this project; see [Tags](#tags). -- `ghcr.io/hyperledger/indy-python` - this image is used as a base for the - ACA-Py Indy variant (see [Tags](#tags)). This may be moved to a more - appropriate project in the future. +## Image +This project builds and publishes the `ghcr.io/hyperledger/aries-cloudagent-python` image. +Multiple variants are available; see [Tags](#tags). ### Tags @@ -47,12 +41,13 @@ original BC Gov images) to the Standard image is outside of the scope of this document. The ACA-Py images built by this project are tagged to indicate which of the -above variants it is. Other tags are also generated for use by developers. +above variants it is. Other tags may also be generated for use by developers. Below is a table of all generated images and their tags: Tag | Variant | Example | Description | ------------------------|----------|--------------------------|-------------------------------------------------------------------------------------------------| +py3.6-X.Y.Z | Standard | py3.6-0.7.4 | Standard image variant built on Python 3.6 for ACA-Py version X.Y.Z | py3.7-X.Y.Z | Standard | py3.7-0.7.4 | Standard image variant built on Python 3.7 for ACA-Py version X.Y.Z | py3.8-X.Y.Z | Standard | py3.8-0.7.4 | Standard image variant built on Python 3.8 for ACA-Py version X.Y.Z | py3.9-X.Y.Z | Standard | py3.9-0.7.4 | Standard image variant built on Python 3.9 for ACA-Py version X.Y.Z | @@ -62,66 +57,10 @@ py3.8-indy-A.B.C-X.Y.Z | Indy | py3.8-indy-1.16.0-0.7.4 | Standard image v py3.9-indy-A.B.C-X.Y.Z | Indy | py3.9-indy-1.16.0-0.7.4 | Standard image variant built on Python 3.9 for ACA-Py version X.Y.Z and Indy SDK Version A.B.C | py3.10-indy-A.B.C-X.Y.Z | Indy | py3.10-indy-1.16.0-0.7.4 | Standard image variant built on Python 3.10 for ACA-Py version X.Y.Z and Indy SDK Version A.B.C | - -#### Indy Python - -**Image Name:** `ghcr.io/hyperledger/indy-python` - -The Indy Python image is used as a base for the Indy variant of ACA-Py. It is a -debian based image with `libindy` and the Indy SDK Python wrapper installed. - -Below is a table of all generated Indy Python images and their tags: - -Tag | Example | Description | -------------------------|--------------------------|-----------------------------------------| -py3.7-X.Y.Z | py3.7-1.16.0 | Python 3.7 with Indy SDK version X.Y.Z | -py3.8-X.Y.Z | py3.8-1.16.0 | Python 3.8 with Indy SDK version X.Y.Z | -py3.9-X.Y.Z | py3.9-1.16.0 | Python 3.9 with Indy SDK version X.Y.Z | -py3.10-X.Y.Z | py3.10-1.16.0 | Python 3.10 with Indy SDK version X.Y.Z | - - -#### Nightly - -The Github Actions will also produce Nightly builds of ACA-Py. If a nightly -build at the current hash of the repo doesn't yet exist, GHA will build a -standard and Indy ACA-Py image at midnight each day. Nightly builds are produced -only for the current "active" python version. - -Below is a table of all generated Nightly images and their tags: - -Tag | Variant | Example | Description | --------------------------------------------|----------|--------------------------------------------------------------------|---------------------------------------| -py3.7-nightly | Standard | py3.7-nightly | Standard image latest nightly | -py3.7-indy-A.B.C-nightly | Indy | py3.7-indy-1.16.0-nightly | Indy image latest nightly | -py3.7-nightly-{{ commit hash }} | Standard | py3.7-nightly-96bc6a8938f0c0e2a487a069d63bcb6c8172b320 | Standard image nightly at commit hash | -py3.7-indy-A.B.C-nightly-{{ commit hash }} | Indy | py3.7-indy-1.16.0-nightly-96bc6a8938f0c0e2a487a069d63bcb6c8172b320 | Indy image nightly at commit hash | - - -#### Testing - -The Github Actions will produce images used in CI/CD checks for Indy (Indy image -tests require `libindy` which is not available on Github runners; these tests -must be run inside of a container with `libindy`). These images are only -intended for use by these checks. - -Below is a table of all generated test images and their tags: - -Image + Tag | Description | ---------------------------------------------------------------------------------------------------------|------------------------------------| -indy-python-test:py{{python-version}}-{{indy-version}}-{{hash of indy base Dockerfile}} | Base Indy Python image for testing | -acapy-test:py{{python-version}}-{{indy-version}}-{{hash of requirements*.txt and indy test Dockerfile}} | ACA-Py test image | - -## Github Actions - -Several Github Actions are used to produce the above described images. - -**TODO:** Add descriptions of actions - -## Key Differences +### Key Image Differences There are several key differences that should be noted between the two image -variants and between the BC Gov ACA-Py images and VON images and the images -produced by this project. +variants and between the BC Gov ACA-Py images. - Standard Image - Based on slim variant of Debian @@ -151,3 +90,33 @@ produced by this project. - Askar and Indy Shared libraries built from source - Built from ACA-Py python package uploaded to PyPI - Includes Indy postgres storage plugin + +## Github Actions + +- Tests (`.github/workflows/tests.yml`) - A reusable workflow that runs tests + for the Standard ACA-Py variant for a given python version. +- Tests (Indy) (`.github/workflows/tests-indy.yml`) - A reusable workflow that + runs tests for the Indy ACA-Py variant for a given python and indy version. +- PR Tests (`.github/workflows/pr-tests.yml`) - Run on pull requests; runs tests + for the Standard and Indy ACA-Py variants for a "default" python version. + Check this workflow for the current default python and Indy versions in use. +- Nightly Tests (`.github/workflows/nightly-tests.yml`) - Run nightly; runs + tests for the Standard and Indy ACA-Py variants for all currently supported + python versions. Check this workflow for the set of currently supported + versions and Indy version(s) in use. +- Publish (`.github/workflows/publish.yml`) - Run on new release published or + when manually triggered; builds and pushes the Standard ACA-Py variant to the + Github Container Registry. +- Publish (Indy) (`.github/workflows/publish-indy.yml`) - Run on new release + published or when manually triggered; builds and pushes the Indy ACA-Py + variant to the Github Container Registry. +- Integration Tests (`.github/workflows/integrationtests.yml`) - Run on pull + requests (to the hyperledger fork only); runs BDD integration tests. +- Black Format (`.github/workflows/blackformat.yml`) - Run on pull requests; + checks formatting of files modified by the PR. +- CodeQL (`.github/workflows/codeql.yml`) - Run on pull requests; performs + CodeQL analysis. +- Python Publish (`.github/workflows/pythonpublish.yml`) - Run on release + created; publishes ACA-Py python package to PyPI. +- PIP Audit (`.github/workflows/pipaudit.yml`) - Run when manually triggered; + performs pip audit. diff --git a/docker/Dockerfile b/docker/Dockerfile index 0a40fbbe40..955479f307 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -13,7 +13,7 @@ FROM python:${python_version}-slim-buster AS main ARG uid=1001 ARG user=aries ARG acapy_version -ARG acapy_reqs +ARG acapy_reqs=[askar,bbs] ENV HOME="/home/$user" \ APP_ROOT="$HOME" \ diff --git a/docker/Dockerfile.indy b/docker/Dockerfile.indy index 9fd35fd5b6..1072998985 100644 --- a/docker/Dockerfile.indy +++ b/docker/Dockerfile.indy @@ -1,7 +1,210 @@ ARG python_version=3.6.13 +ARG rust_version=1.46 + +# This image could be replaced with an "indy" image from another repo, +# such as the indy-sdk +FROM rust:${rust_version}-slim-buster as indy-builder + +ARG user=indy +ENV HOME="/home/$user" +WORKDIR $HOME +RUN mkdir -p .local/bin .local/etc .local/lib + +# Install environment +RUN apt-get update -y && \ + apt-get install -y --no-install-recommends \ + automake \ + build-essential \ + ca-certificates \ + cmake \ + curl \ + git \ + libbz2-dev \ + libffi-dev \ + libgmp-dev \ + liblzma-dev \ + libncurses5-dev \ + libncursesw5-dev \ + libsecp256k1-dev \ + libsodium-dev \ + libsqlite3-dev \ + libssl-dev \ + libtool \ + libzmq3-dev \ + pkg-config \ + zlib1g-dev && \ + rm -rf /var/lib/apt/lists/* + +# set to --release for smaller, optimized library +ARG indy_build_flags=--release + +ARG indy_version=1.16.0 +ARG indy_sdk_url=https://codeload.github.com/hyperledger/indy-sdk/tar.gz/refs/tags/v${indy_version} + +# make local libs and binaries accessible +ENV PATH="$HOME/.local/bin:$PATH" +ENV LIBRARY_PATH="$HOME/.local/lib:$LIBRARY_PATH" + +# Download and extract indy-sdk +RUN mkdir indy-sdk && \ + curl "${indy_sdk_url}" | tar -xz -C indy-sdk + +# Build and install indy-sdk +WORKDIR $HOME/indy-sdk +RUN cd indy-sdk*/libindy && \ + cargo build ${indy_build_flags} && \ + cp target/*/libindy.so "$HOME/.local/lib" && \ + cargo clean + +# Package python3-indy +RUN tar czvf ../python3-indy.tgz -C indy-sdk*/wrappers/python . + +# grab the latest sdk code for the postgres plug-in +WORKDIR $HOME +ARG indy_postgres_url=${indy_sdk_url} +RUN mkdir indy-postgres && \ + curl "${indy_postgres_url}" | tar -xz -C indy-postgres + +# Build and install postgres_storage plugin +WORKDIR $HOME/indy-postgres +RUN cd indy-sdk*/experimental/plugins/postgres_storage && \ + cargo build ${indy_build_flags} && \ + cp target/*/libindystrgpostgres.so "$HOME/.local/lib" && \ + cargo clean + +# Clean up SDK +WORKDIR $HOME +RUN rm -rf indy-sdk indy-postgres + + +# Indy Base Image +# This image could be replaced with an "indy-python" image from another repo, +# such as the indy-sdk +FROM python:${python_version}-slim-buster as indy-base + +ARG uid=1001 +ARG user=indy ARG indy_version -ARG org=hyperledger -FROM python:${python_version}-slim-buster AS build + +ENV HOME="/home/$user" \ + APP_ROOT="$HOME" \ + LC_ALL=C.UTF-8 \ + LANG=C.UTF-8 \ + PIP_NO_CACHE_DIR=off \ + PYTHONUNBUFFERED=1 \ + PYTHONIOENCODING=UTF-8 \ + RUST_LOG=warning \ + SHELL=/bin/bash \ + SUMMARY="indy-python base image" \ + DESCRIPTION="aries-cloudagent provides a base image for running Hyperledger Aries agents in Docker. \ + This image provides all the necessary dependencies to use the indy-sdk in python. Based on Debian Buster." + +LABEL summary="$SUMMARY" \ + description="$DESCRIPTION" \ + io.k8s.description="$DESCRIPTION" \ + io.k8s.display-name="indy-python $indy_version" \ + name="indy-python" \ + version="$indy_version" \ + maintainer="" + +# Add indy user +RUN useradd -U -ms /bin/bash -u $uid $user + +# Install environment +RUN apt-get update -y && \ + apt-get install -y --no-install-recommends \ + apt-transport-https \ + ca-certificates \ + bzip2 \ + curl \ + git \ + less \ + libffi6 \ + libgmp10 \ + liblzma5 \ + libncurses5 \ + libncursesw5 \ + libsecp256k1-0 \ + libzmq5 \ + net-tools \ + openssl \ + sqlite3 \ + vim-tiny \ + zlib1g && \ + rm -rf /var/lib/apt/lists/* /usr/share/doc/* + +WORKDIR $HOME + +# Copy build results +COPY --from=indy-builder --chown=$user:$user $HOME . + +RUN mkdir -p $HOME/.local/bin + +# Add local binaries and aliases to path +ENV PATH="$HOME/.local/bin:$PATH" + +# Make libraries resolvable by python +ENV LD_LIBRARY_PATH="$HOME/.local/lib:$LD_LIBRARY_PATH" +RUN echo "$HOME/.local/lib" > /etc/ld.so.conf.d/local.conf && ldconfig + +# Install python3-indy +RUN pip install --no-cache-dir python3-indy.tgz && rm python3-indy.tgz + +# - In order to drop the root user, we have to make some directories writable +# to the root group as OpenShift default security model is to run the container +# under random UID. +RUN usermod -a -G 0 $user + +# Create standard directories to allow volume mounting and set permissions +# Note: PIP_NO_CACHE_DIR environment variable should be cleared to allow caching +RUN mkdir -p \ + $HOME/.cache/pip/http \ + $HOME/.indy-cli/networks \ + $HOME/.indy_client/wallet \ + $HOME/.indy_client/pool \ + $HOME/.indy_client/ledger-cache \ + $HOME/ledger/sandbox/data \ + $HOME/log + +# The root group needs access the directories under $HOME/.indy_client for the container to function in OpenShift. +# Also ensure the permissions on the python 'site-packages' folder are set correctly. +RUN chown -R $user:root $HOME/.indy_client \ + && chmod -R ug+rw $HOME/log $HOME/ledger $HOME/.cache $HOME/.indy_client + +USER $user + +CMD ["bash"] + + +# ACA-Py Test +# Used to run ACA-Py unit tests with Indy +FROM indy-base as acapy-test + +USER indy + +RUN mkdir src test-reports + +WORKDIR /home/indy/src + +RUN mkdir -p test-reports && chown -R indy:indy test-reports && chmod -R ug+rw test-reports + +ADD requirements*.txt ./ + +USER root +RUN pip3 install --no-cache-dir \ + -r requirements.txt \ + -r requirements.askar.txt \ + -r requirements.bbs.txt \ + -r requirements.dev.txt + +ADD --chown=indy:root . . +USER indy + +ENTRYPOINT ["/bin/bash", "-c", "pytest \"$@\"", "--"] + +# ACA-Py Builder +# Build ACA-Py wheel using setuptools +FROM python:${python_version}-slim-buster AS acapy-builder WORKDIR /src @@ -10,12 +213,15 @@ ADD . . RUN pip install setuptools wheel RUN python setup.py sdist bdist_wheel -FROM ghcr.io/${org}/indy-python:py${python_version}-${indy_version} AS main + +# ACA-Py Indy +# Install wheel from builder and commit final image +FROM indy-base AS main ARG uid=1001 ARG user=indy ARG acapy_version -ARG acapy_reqs +ARG acapy_reqs=[askar,bbs] ENV HOME="/home/$user" \ APP_ROOT="$HOME" \ @@ -28,8 +234,8 @@ ENV HOME="/home/$user" \ SHELL=/bin/bash \ SUMMARY="aries-cloudagent image" \ DESCRIPTION="aries-cloudagent provides a base image for running Hyperledger Aries agents in Docker. \ - This image layers the python implementation of aries-cloudagent $acapy_version. Based on indy-python, \ - this image includes indy-sdk and supporting libraries." + This image layers the python implementation of aries-cloudagent $acapy_version. \ + This image includes indy-sdk and supporting libraries." LABEL summary="$SUMMARY" \ description="$DESCRIPTION" \ @@ -47,7 +253,7 @@ RUN mkdir -p $HOME/.aries_cloudagent # Also ensure the permissions on the python 'site-packages' folder are set correctly. RUN chmod -R ug+rw $HOME/.aries_cloudagent -COPY --from=build /src/dist/aries_cloudagent*.whl . +COPY --from=acapy-builder /src/dist/aries_cloudagent*.whl . RUN pip install --no-cache-dir --find-links=. aries_cloudagent${acapy_reqs} && rm aries_cloudagent*.whl diff --git a/docker/Dockerfile.indy-base b/docker/Dockerfile.indy-base deleted file mode 100644 index fb53a59b81..0000000000 --- a/docker/Dockerfile.indy-base +++ /dev/null @@ -1,170 +0,0 @@ -ARG python_version=3.6.13 -ARG rust_version=1.46 -FROM rust:${rust_version}-slim-buster as builder - -ARG user=indy -ENV HOME="/home/$user" -WORKDIR $HOME -RUN mkdir -p .local/bin .local/etc .local/lib - -# Install environment -RUN apt-get update -y && \ - apt-get install -y --no-install-recommends \ - automake \ - build-essential \ - ca-certificates \ - cmake \ - curl \ - git \ - libbz2-dev \ - libffi-dev \ - libgmp-dev \ - liblzma-dev \ - libncurses5-dev \ - libncursesw5-dev \ - libsecp256k1-dev \ - libsodium-dev \ - libsqlite3-dev \ - libssl-dev \ - libtool \ - libzmq3-dev \ - pkg-config \ - zlib1g-dev && \ - rm -rf /var/lib/apt/lists/* - -# set to --release for smaller, optimized library -ARG indy_build_flags=--release - -ARG indy_sdk_url - -# make local libs and binaries accessible -ENV PATH="$HOME/.local/bin:$PATH" -ENV LIBRARY_PATH="$HOME/.local/lib:$LIBRARY_PATH" - -# Download and extract indy-sdk -RUN mkdir indy-sdk && \ - curl "${indy_sdk_url}" | tar -xz -C indy-sdk - -# Build and install indy-sdk -WORKDIR $HOME/indy-sdk -RUN cd indy-sdk*/libindy && \ - cargo build ${indy_build_flags} && \ - cp target/*/libindy.so "$HOME/.local/lib" && \ - cargo clean - -# Package python3-indy -RUN tar czvf ../python3-indy.tgz -C indy-sdk*/wrappers/python . - -# grab the latest sdk code for the postgres plug-in -WORKDIR $HOME -ARG indy_postgres_url=${indy_sdk_url} -RUN mkdir indy-postgres && \ - curl "${indy_postgres_url}" | tar -xz -C indy-postgres - -# Build and install postgres_storage plugin -WORKDIR $HOME/indy-postgres -RUN cd indy-sdk*/experimental/plugins/postgres_storage && \ - cargo build ${indy_build_flags} && \ - cp target/*/libindystrgpostgres.so "$HOME/.local/lib" && \ - cargo clean - -# Clean up SDK -WORKDIR $HOME -RUN rm -rf indy-sdk indy-postgres - -## Start fresh (new image) ## -FROM python:${python_version}-slim-buster - - -ARG uid=1001 -ARG user=indy -ARG indy_version - -ENV HOME="/home/$user" \ - APP_ROOT="$HOME" \ - LC_ALL=C.UTF-8 \ - LANG=C.UTF-8 \ - PIP_NO_CACHE_DIR=off \ - PYTHONUNBUFFERED=1 \ - PYTHONIOENCODING=UTF-8 \ - RUST_LOG=warning \ - SHELL=/bin/bash \ - SUMMARY="indy-python base image" \ - DESCRIPTION="aries-cloudagent provides a base image for running Hyperledger Aries agents in Docker. \ - This image provides all the necessary dependencies to use the indy-sdk in python. Based on Debian Buster." - -LABEL summary="$SUMMARY" \ - description="$DESCRIPTION" \ - io.k8s.description="$DESCRIPTION" \ - io.k8s.display-name="indy-python $indy_version" \ - name="indy-python" \ - version="$indy_version" \ - maintainer="" - -# Add indy user -RUN useradd -U -ms /bin/bash -u $uid $user - -# Install environment -RUN apt-get update -y && \ - apt-get install -y --no-install-recommends \ - apt-transport-https \ - ca-certificates \ - bzip2 \ - curl \ - git \ - less \ - libffi6 \ - libgmp10 \ - liblzma5 \ - libncurses5 \ - libncursesw5 \ - libsecp256k1-0 \ - libzmq5 \ - net-tools \ - openssl \ - sqlite3 \ - vim-tiny \ - zlib1g && \ - rm -rf /var/lib/apt/lists/* /usr/share/doc/* - -WORKDIR $HOME - -# Copy build results -COPY --from=builder --chown=$user:$user $HOME . - -RUN mkdir -p $HOME/.local/bin - -# Add local binaries and aliases to path -ENV PATH="$HOME/.local/bin:$PATH" - -# Make libraries resolvable by python -ENV LD_LIBRARY_PATH="$HOME/.local/lib:$LD_LIBRARY_PATH" -RUN echo "$HOME/.local/lib" > /etc/ld.so.conf.d/local.conf && ldconfig - -# Install python3-indy -RUN pip install --no-cache-dir python3-indy.tgz && rm python3-indy.tgz - -# - In order to drop the root user, we have to make some directories writable -# to the root group as OpenShift default security model is to run the container -# under random UID. -RUN usermod -a -G 0 $user - -# Create standard directories to allow volume mounting and set permissions -# Note: PIP_NO_CACHE_DIR environment variable should be cleared to allow caching -RUN mkdir -p \ - $HOME/.cache/pip/http \ - $HOME/.indy-cli/networks \ - $HOME/.indy_client/wallet \ - $HOME/.indy_client/pool \ - $HOME/.indy_client/ledger-cache \ - $HOME/ledger/sandbox/data \ - $HOME/log - -# The root group needs access the directories under $HOME/.indy_client for the container to function in OpenShift. -# Also ensure the permissions on the python 'site-packages' folder are set correctly. -RUN chown -R $user:root $HOME/.indy_client \ - && chmod -R ug+rw $HOME/log $HOME/ledger $HOME/.cache $HOME/.indy_client - -USER $user - -CMD ["bash"] diff --git a/scripts/run_tests_indy b/scripts/run_tests_indy index 6efa27b215..37fab8d5e7 100755 --- a/scripts/run_tests_indy +++ b/scripts/run_tests_indy @@ -3,7 +3,12 @@ cd "$(dirname "$0")" || exit CONTAINER_RUNTIME="${CONTAINER_RUNTIME:-docker}" -$CONTAINER_RUNTIME build -t aries-cloudagent-test -f ../docker/Dockerfile.test-indy .. || exit 1 +DOCKER_BUILDKIT=1 $CONTAINER_RUNTIME build \ + -t aries-cloudagent-test \ + -f ../docker/Dockerfile.indy \ + --target acapy-test .. \ + --build-arg indy_version=1.16.0 \ + || exit 1 if [ ! -d ../test-reports ]; then mkdir ../test-reports; fi From 42cf9d578235a4765004786deee1b8d03ec05f77 Mon Sep 17 00:00:00 2001 From: Char Howland Date: Thu, 20 Oct 2022 12:06:41 -0400 Subject: [PATCH 545/872] fix: remove cls from resolver.dereference() call Signed-off-by: Char Howland --- aries_cloudagent/messaging/jsonld/routes.py | 2 -- .../messaging/jsonld/tests/test_routes.py | 26 +++++++++---------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/aries_cloudagent/messaging/jsonld/routes.py b/aries_cloudagent/messaging/jsonld/routes.py index f0ff1b8c29..eb3b7b48c2 100644 --- a/aries_cloudagent/messaging/jsonld/routes.py +++ b/aries_cloudagent/messaging/jsonld/routes.py @@ -5,7 +5,6 @@ from marshmallow import INCLUDE, Schema, fields from pydid.verification_method import ( Ed25519VerificationKey2018, - KnownVerificationMethods, ) from ...admin.request_context import AdminRequestContext @@ -148,7 +147,6 @@ async def verify(request: web.BaseRequest): vmethod = await resolver.dereference( profile, doc["proof"]["verificationMethod"], - cls=KnownVerificationMethods, ) if not isinstance(vmethod, SUPPORTED_VERIFICATION_METHOD_TYPES): diff --git a/aries_cloudagent/messaging/jsonld/tests/test_routes.py b/aries_cloudagent/messaging/jsonld/tests/test_routes.py index 129ac26515..1302749fc1 100644 --- a/aries_cloudagent/messaging/jsonld/tests/test_routes.py +++ b/aries_cloudagent/messaging/jsonld/tests/test_routes.py @@ -234,22 +234,22 @@ async def test_verify_bad_ver_meth_deref_req_error( assert "error" in mock_response.call_args[0][0] -@pytest.mark.asyncio -async def test_verify_bad_ver_meth_not_ver_meth( - mock_resolver, mock_verify_request, mock_response, request_body -): - request_body["doc"]["proof"][ - "verificationMethod" - ] = "did:example:1234abcd#did-communication" - await test_module.verify(mock_verify_request(request_body)) - assert "error" in mock_response.call_args[0][0] - - +@pytest.mark.parametrize( + "vmethod", + [ + "did:example:1234abcd#key-2", + "did:example:1234abcd#did-communication", + ], +) @pytest.mark.asyncio async def test_verify_bad_vmethod_unsupported( - mock_resolver, mock_verify_request, mock_response, request_body + mock_resolver, + mock_verify_request, + mock_response, + request_body, + vmethod, ): - request_body["doc"]["proof"]["verificationMethod"] = "did:example:1234abcd#key-2" + request_body["doc"]["proof"]["verificationMethod"] = vmethod with pytest.raises(web.HTTPBadRequest): await test_module.verify(mock_verify_request(request_body)) From 2bf13c73230d5be0edc4633cf28a65b63e483424 Mon Sep 17 00:00:00 2001 From: Char Howland Date: Thu, 20 Oct 2022 12:07:41 -0400 Subject: [PATCH 546/872] fix: mock dereference Signed-off-by: Char Howland --- .../protocols/connections/v1_0/tests/test_manager.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py index 80efb456bf..e3631478c1 100644 --- a/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py @@ -2068,6 +2068,9 @@ async def test_fetch_connection_targets_conn_invitation_did_resolver(self): return_value=self.test_endpoint ) self.resolver.resolve = async_mock.CoroutineMock(return_value=did_doc) + self.resolver.dereference = async_mock.CoroutineMock( + return_value = did_doc.verification_method[0] + ) self.context.injector.bind_instance(DIDResolver, self.resolver) local_did = await session.wallet.create_local_did( @@ -2137,6 +2140,9 @@ async def test_fetch_connection_targets_conn_invitation_btcr_resolver(self): return_value=self.test_endpoint ) self.resolver.resolve = async_mock.CoroutineMock(return_value=did_doc) + self.resolver.dereference = async_mock.CoroutineMock( + return_value = did_doc.verification_method[0] + ) self.context.injector.bind_instance(DIDResolver, self.resolver) local_did = await session.wallet.create_local_did( method=DIDMethod.SOV, @@ -2288,6 +2294,9 @@ async def test_fetch_connection_targets_conn_invitation_unsupported_key_type(sel return_value=self.test_endpoint ) self.resolver.resolve = async_mock.CoroutineMock(return_value=did_doc) + self.resolver.dereference = async_mock.CoroutineMock( + return_value = did_doc.verification_method[0] + ) self.context.injector.bind_instance(DIDResolver, self.resolver) local_did = await session.wallet.create_local_did( method=DIDMethod.SOV, @@ -2357,6 +2366,9 @@ async def test_fetch_connection_targets_oob_invitation_svc_did_resolver(self): self.resolver = async_mock.MagicMock() self.resolver.resolve = async_mock.CoroutineMock(return_value=did_doc) + self.resolver.dereference = async_mock.CoroutineMock( + return_value = did_doc.verification_method[0] + ) self.context.injector.bind_instance(DIDResolver, self.resolver) local_did = await session.wallet.create_local_did( From 206ccda1ea0fdcc5fb47e79880da43397205408d Mon Sep 17 00:00:00 2001 From: Char Howland Date: Thu, 20 Oct 2022 12:09:17 -0400 Subject: [PATCH 547/872] feat: convert did key to did key url Signed-off-by: Char Howland --- aries_cloudagent/resolver/default/indy.py | 26 +++++++++++++++++------ 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/aries_cloudagent/resolver/default/indy.py b/aries_cloudagent/resolver/default/indy.py index 7f86025ed2..2bdef2cf8d 100644 --- a/aries_cloudagent/resolver/default/indy.py +++ b/aries_cloudagent/resolver/default/indy.py @@ -31,13 +31,25 @@ class NoIndyLedger(ResolverError): def _routing_keys_as_did_key_urls(routing_keys: Sequence[str]) -> Sequence[str]: - """Convert raw base58 keys to did:key values.""" - return [ - DIDKey.from_public_key_b58(routing_key, KeyType.ED25519).key_id - if not routing_key.startswith("did:key:") - else routing_key - for routing_key in routing_keys - ] + """Convert raw base58 keys to did:key values. + + If a did:key is passed in, convert to a did:key URL. + """ + + did_key_urls = [] + for routing_key in routing_keys: + if not routing_key.startswith("did:key:"): + did_key_urls.append( + DIDKey.from_public_key_b58(routing_key, KeyType.ED25519).key_id + ) + else: + if "#" not in routing_key: + did_key_urls.append( + f"{routing_key}#{DIDKey.from_did(routing_key).fingerprint}" + ) + else: + return routing_keys + return did_key_urls class IndyDIDResolver(BaseDIDResolver): From 795c58f9089a2eb6d7f353e2d4d5170a121f232f Mon Sep 17 00:00:00 2001 From: Char Howland Date: Thu, 20 Oct 2022 12:10:29 -0400 Subject: [PATCH 548/872] test: _routing_keys_as_did_key_urls Signed-off-by: Char Howland --- .../resolver/default/tests/test_indy.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/resolver/default/tests/test_indy.py b/aries_cloudagent/resolver/default/tests/test_indy.py index c2c53a3e92..6287fb5352 100644 --- a/aries_cloudagent/resolver/default/tests/test_indy.py +++ b/aries_cloudagent/resolver/default/tests/test_indy.py @@ -17,7 +17,7 @@ from ....multitenant.manager import MultitenantManager from ...base import DIDNotFound, ResolverError -from ..indy import IndyDIDResolver +from ..indy import IndyDIDResolver, _routing_keys_as_did_key_urls # pylint: disable=W0621 TEST_DID0 = "did:sov:WgWxqztrNooG92RXvxSTWv" @@ -127,7 +127,7 @@ async def test_supports_updated_did_sov_rules( """Test that new attrib structure is supported.""" example = { "endpoint": "https://example.com/endpoint", - "routingKeys": ["a-routing-key"], + "routingKeys": ["HQhjaj4mcaS3Xci27a9QhnBrNpS91VNFUU4TDrtMxa9j"], "types": ["DIDComm", "did-communication", "endpoint"], "profile": "https://example.com", "linked_domains": "https://example.com", @@ -177,3 +177,18 @@ async def test_supports_updated_did_sov_rules_no_endpoint_url( ) def test_process_endpoint_types(self, resolver: IndyDIDResolver, types, result): assert resolver.process_endpoint_types(types) == result + + @pytest.mark.parametrize( + "keys", + [ + ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"], + ["did:key:z6MkgzZFYHiH9RhyMmkoyvNvVwnvgLxkVrJbureLx9HXsuKA"], + [ + "did:key:z6MkgzZFYHiH9RhyMmkoyvNvVwnvgLxkVrJbureLx9HXsuKA#z6MkgzZFYHiH9RhyMmkoyvNvVwnvgLxkVrJbureLx9HXsuKA" + ], + ], + ) + def test_routing_keys_as_did_key_urls(self, keys): + for key in _routing_keys_as_did_key_urls(keys): + assert key.startswith("did:key:") + assert "#" in key From 1f1c19c40c546b81288ff08e67d52d55b8268b8a Mon Sep 17 00:00:00 2001 From: Char Howland Date: Thu, 20 Oct 2022 12:11:30 -0400 Subject: [PATCH 549/872] test: resolver.dereference() with diddoc passed in Signed-off-by: Char Howland --- .../resolver/tests/test_did_resolver.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/resolver/tests/test_did_resolver.py b/aries_cloudagent/resolver/tests/test_did_resolver.py index 2bcbb4ed42..58c459b307 100644 --- a/aries_cloudagent/resolver/tests/test_did_resolver.py +++ b/aries_cloudagent/resolver/tests/test_did_resolver.py @@ -7,7 +7,7 @@ import pytest from asynctest import mock as async_mock -from pydid import DID, DIDDocument, VerificationMethod +from pydid import DID, DIDDocument, VerificationMethod, BasicDIDDocument from ..base import ( BaseDIDResolver, @@ -153,6 +153,17 @@ async def test_dereference(resolver, profile): assert expected == actual.serialize() +@pytest.mark.asyncio +async def test_dereference_diddoc(resolver, profile): + url = "did:example:1234abcd#4" + doc = BasicDIDDocument( + id="did:example:z6Mkmpe2DyE4NsDiAb58d75hpi1BjqbH6wYMschUkjWDEEuR" + ) + result = await resolver.dereference(profile, url, document=doc) + assert isinstance(result, VerificationMethod) + assert result.id == url + + @pytest.mark.asyncio async def test_dereference_x(resolver, profile): url = "non-did" From d669be25e9ddd4e7941d201895326670aeb5a211 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Thu, 20 Oct 2022 15:06:48 -0400 Subject: [PATCH 550/872] style: fix formatting Signed-off-by: Daniel Bluhm --- .../protocols/connections/v1_0/tests/test_manager.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py index e3631478c1..285d3e9ff8 100644 --- a/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py @@ -2069,7 +2069,7 @@ async def test_fetch_connection_targets_conn_invitation_did_resolver(self): ) self.resolver.resolve = async_mock.CoroutineMock(return_value=did_doc) self.resolver.dereference = async_mock.CoroutineMock( - return_value = did_doc.verification_method[0] + return_value=did_doc.verification_method[0] ) self.context.injector.bind_instance(DIDResolver, self.resolver) @@ -2141,7 +2141,7 @@ async def test_fetch_connection_targets_conn_invitation_btcr_resolver(self): ) self.resolver.resolve = async_mock.CoroutineMock(return_value=did_doc) self.resolver.dereference = async_mock.CoroutineMock( - return_value = did_doc.verification_method[0] + return_value=did_doc.verification_method[0] ) self.context.injector.bind_instance(DIDResolver, self.resolver) local_did = await session.wallet.create_local_did( @@ -2295,7 +2295,7 @@ async def test_fetch_connection_targets_conn_invitation_unsupported_key_type(sel ) self.resolver.resolve = async_mock.CoroutineMock(return_value=did_doc) self.resolver.dereference = async_mock.CoroutineMock( - return_value = did_doc.verification_method[0] + return_value=did_doc.verification_method[0] ) self.context.injector.bind_instance(DIDResolver, self.resolver) local_did = await session.wallet.create_local_did( @@ -2367,7 +2367,7 @@ async def test_fetch_connection_targets_oob_invitation_svc_did_resolver(self): self.resolver = async_mock.MagicMock() self.resolver.resolve = async_mock.CoroutineMock(return_value=did_doc) self.resolver.dereference = async_mock.CoroutineMock( - return_value = did_doc.verification_method[0] + return_value=did_doc.verification_method[0] ) self.context.injector.bind_instance(DIDResolver, self.resolver) From cbf78019d037922becb521308f7ea8ea52025614 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Tue, 25 Oct 2022 10:26:51 -0400 Subject: [PATCH 551/872] chore: type hints on init for methods and key types Signed-off-by: Daniel Bluhm --- aries_cloudagent/wallet/did_method.py | 2 +- aries_cloudagent/wallet/key_type.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/wallet/did_method.py b/aries_cloudagent/wallet/did_method.py index 861ad002ba..af971ea019 100644 --- a/aries_cloudagent/wallet/did_method.py +++ b/aries_cloudagent/wallet/did_method.py @@ -9,7 +9,7 @@ class DIDMethod: """Class to represent a did method.""" - def __init__(self, name, key_types, rotation) -> None: + def __init__(self, name: str, key_types: List[KeyType], rotation: bool = False): """Construct did method class.""" self._method_name: str = name self._supported_key_types: List[KeyType] = key_types diff --git a/aries_cloudagent/wallet/key_type.py b/aries_cloudagent/wallet/key_type.py index bb8f2c6718..9e16bb1dde 100644 --- a/aries_cloudagent/wallet/key_type.py +++ b/aries_cloudagent/wallet/key_type.py @@ -6,7 +6,7 @@ class KeyType: """Key Type class.""" - def __init__(self, key_type, multicodec_name, multicodec_prefix: bytes) -> None: + def __init__(self, key_type: str, multicodec_name: str, multicodec_prefix: bytes): """Construct key type.""" self._type: str = key_type self._name: str = multicodec_name From 925cdc61762a5c9d60fbf6e323b51e06ada741a6 Mon Sep 17 00:00:00 2001 From: Anastasiia Date: Wed, 26 Oct 2022 15:25:36 +0200 Subject: [PATCH 552/872] Added Multicredentials.md Signed-off-by: Anastasiia --- Multicredentials.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 Multicredentials.md diff --git a/Multicredentials.md b/Multicredentials.md new file mode 100644 index 0000000000..1c9e1f72ab --- /dev/null +++ b/Multicredentials.md @@ -0,0 +1,9 @@ +# Multi-Credentials + +It is a known fact that multiple AnonCreds can be combined to present a presentation proof with an "and" logical operator: For instance, a verifier can ask for the "name" claim from an eID and the "address" claim from a bank statement to have a single proof that is either valid or invalid. With the Present Proof Protocol v2, it is possible to have "and" and "or" logical operators for AnonCreds and/or W3C Verifiable Credentials. + +With the Present Proof Protocol v2, verifiers can ask for a combination of credentials as proof. For instance, a Verifier can ask a claim from an AnonCreds **and** a verifiable presentation from a W3C Verifiable Credential, which would open the possibilities of Aries Cloud Agent Python being used for rather complex presentation proof requests that wouldn't be possible without the support of AnonCreds or W3C Verifiable Credentials. + +Moreover, it is possible to make similar presentation proof requests using the or logical operator. For instance, a verifier can ask for either an eID in AnonCreds format or an eID in W3C Verifiable Credential format. This has the potential to solve the interoperability problem of different credential formats and ecosystems from a user point of view by shifting the requirement of holding/accepting different credential formats from identity holders to verifiers. Here again, using Aries Cloud Agent Python as the underlying verifier agent can tackle such complex presentation proof requests since the agent is capable of verifying both type of credential formats and proof types. + +In the future, it would be even possible to put mDoc as an attachment with an and or or logical operation, along with AnonCreds and/or W3C Verifiable Credentials. For this to happen, Aca-Py either needs the capabilities to validate mDocs internally or to connect third-party endpoints to validate and get a response. \ No newline at end of file From f7558aff5050bbd3f6a78bc5ecd2dccaf0f7ddc1 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Wed, 26 Oct 2022 11:36:13 -0700 Subject: [PATCH 553/872] Fixes to acme exercise code Signed-off-by: Ian Costanzo --- demo/AcmeDemoWorkshop.md | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/demo/AcmeDemoWorkshop.md b/demo/AcmeDemoWorkshop.md index e8ec561a72..05a956c71e 100644 --- a/demo/AcmeDemoWorkshop.md +++ b/demo/AcmeDemoWorkshop.md @@ -61,9 +61,7 @@ from uuid import uuid4 ``` python TAILS_FILE_COUNT = int(os.getenv("TAILS_FILE_COUNT", 100)) -CRED_PREVIEW_TYPE = ( - "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/issue-credential/2.0/credential-preview" -) +CRED_PREVIEW_TYPE = "https://didcomm.org/issue-credential/2.0/credential-preview" ``` Next locate the code that is triggered by option ```2```: @@ -142,10 +140,16 @@ then replace the ```# TODO``` comment and the ```pass``` statement: if is_proof_of_education: log_status("#28.1 Received proof of education, check claims") for (referent, attr_spec) in pres_req["requested_attributes"].items(): - self.log( - f"{attr_spec['name']}: " - f"{pres['requested_proof']['revealed_attrs'][referent]['raw']}" - ) + if referent in pres['requested_proof']['revealed_attrs']: + self.log( + f"{attr_spec['name']}: " + f"{pres['requested_proof']['revealed_attrs'][referent]['raw']}" + ) + else: + self.log( + f"{attr_spec['name']}: " + "(attribute not revealed)" + ) for id_spec in pres["identifiers"]: # just print out the schema/cred def id's of presented claims self.log(f"schema_id: {id_spec['schema_id']}") @@ -153,7 +157,7 @@ then replace the ```# TODO``` comment and the ```pass``` statement: # TODO placeholder for the next step else: # in case there are any other kinds of proofs received - self.log("#28.1 Received ", message["presentation_request"]["name"]) + self.log("#28.1 Received ", pres_req["name"]) ``` Right now this just verifies the proof received and prints out the attributes it reveals, but in "real life" your application could do something useful with this information. From 997d263c564e921b0f985f2f53703a2caf7d6585 Mon Sep 17 00:00:00 2001 From: Char Howland Date: Thu, 27 Oct 2022 11:12:11 -0600 Subject: [PATCH 554/872] fix: account for changes to KeyType Signed-off-by: Char Howland --- aries_cloudagent/resolver/default/indy.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/aries_cloudagent/resolver/default/indy.py b/aries_cloudagent/resolver/default/indy.py index 2bdef2cf8d..8c98778d57 100644 --- a/aries_cloudagent/resolver/default/indy.py +++ b/aries_cloudagent/resolver/default/indy.py @@ -20,7 +20,7 @@ ) from ...messaging.valid import IndyDID from ...multitenant.base import BaseMultitenantManager -from ...wallet.key_type import KeyType +from ...wallet.key_type import ED25519 from ..base import BaseDIDResolver, DIDNotFound, ResolverError, ResolverType LOGGER = logging.getLogger(__name__) @@ -39,9 +39,10 @@ def _routing_keys_as_did_key_urls(routing_keys: Sequence[str]) -> Sequence[str]: did_key_urls = [] for routing_key in routing_keys: if not routing_key.startswith("did:key:"): - did_key_urls.append( - DIDKey.from_public_key_b58(routing_key, KeyType.ED25519).key_id - ) + did_key_urls.append(DIDKey.from_public_key_b58(routing_key, ED25519).key_id) + # NOTE: using hardcoded prefix ED25519 because py_multicodec library + # is outdated. Update after this PR gets released: + # https://github.com/multiformats/py-multicodec/pull/14 else: if "#" not in routing_key: did_key_urls.append( From 87905a86319466e086a506124a732076294d4a05 Mon Sep 17 00:00:00 2001 From: Char Howland Date: Thu, 27 Oct 2022 11:29:48 -0600 Subject: [PATCH 555/872] fix: remove note Signed-off-by: Char Howland --- aries_cloudagent/resolver/default/indy.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/aries_cloudagent/resolver/default/indy.py b/aries_cloudagent/resolver/default/indy.py index 8c98778d57..ce18f75c67 100644 --- a/aries_cloudagent/resolver/default/indy.py +++ b/aries_cloudagent/resolver/default/indy.py @@ -40,9 +40,6 @@ def _routing_keys_as_did_key_urls(routing_keys: Sequence[str]) -> Sequence[str]: for routing_key in routing_keys: if not routing_key.startswith("did:key:"): did_key_urls.append(DIDKey.from_public_key_b58(routing_key, ED25519).key_id) - # NOTE: using hardcoded prefix ED25519 because py_multicodec library - # is outdated. Update after this PR gets released: - # https://github.com/multiformats/py-multicodec/pull/14 else: if "#" not in routing_key: did_key_urls.append( From 5ab77b6753a11d14b8b43c59eb5bd05f9441083b Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Thu, 27 Oct 2022 10:36:17 -0700 Subject: [PATCH 556/872] Remove connection 'ready' check when synchronizing txn connection state Signed-off-by: Ian Costanzo --- .../test_transaction_job_to_send_handler.py | 20 ------------------- .../transaction_job_to_send_handler.py | 4 ---- 2 files changed, 24 deletions(-) diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/tests/test_transaction_job_to_send_handler.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/tests/test_transaction_job_to_send_handler.py index 9a1546fefa..41c66d4a78 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/tests/test_transaction_job_to_send_handler.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/tests/test_transaction_job_to_send_handler.py @@ -34,26 +34,6 @@ async def test_called(self): ) assert not responder.messages - async def test_called_not_ready(self): - request_context = RequestContext.test_context() - request_context.message_receipt = MessageReceipt() - request_context.connection_record = async_mock.MagicMock() - - with async_mock.patch.object( - test_module, "TransactionManager", autospec=True - ) as mock_tran_mgr: - mock_tran_mgr.return_value.set_transaction_their_job = ( - async_mock.CoroutineMock() - ) - request_context.message = TransactionJobToSend() - request_context.connection_ready = False - handler = test_module.TransactionJobToSendHandler() - responder = MockResponder() - with self.assertRaises(test_module.HandlerException): - await handler.handle(request_context, responder) - - assert not responder.messages - async def test_called_x(self): request_context = RequestContext.test_context() request_context.message_receipt = MessageReceipt() diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/transaction_job_to_send_handler.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/transaction_job_to_send_handler.py index 4e42cb63a8..68a8ce8ab5 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/transaction_job_to_send_handler.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/transaction_job_to_send_handler.py @@ -3,7 +3,6 @@ from .....messaging.base_handler import ( BaseHandler, BaseResponder, - HandlerException, RequestContext, ) @@ -26,9 +25,6 @@ async def handle(self, context: RequestContext, responder: BaseResponder): self._logger.debug(f"TransactionJobToSendHandler called with context {context}") assert isinstance(context.message, TransactionJobToSend) - if not context.connection_ready: - raise HandlerException("No connection established") - mgr = TransactionManager(context.profile) try: await mgr.set_transaction_their_job( From 23dd05bdb942d130058b3807317f91812e9926e1 Mon Sep 17 00:00:00 2001 From: PeterStrob Date: Thu, 27 Oct 2022 21:02:09 +0000 Subject: [PATCH 557/872] fix: update issue-credential endpoint summaries Signed-off-by: PeterStrob --- aries_cloudagent/protocols/issue_credential/v1_0/routes.py | 2 +- aries_cloudagent/protocols/issue_credential/v2_0/routes.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/routes.py b/aries_cloudagent/protocols/issue_credential/v1_0/routes.py index 381ce0401f..3994fa7c6a 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/routes.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/routes.py @@ -382,7 +382,7 @@ async def credential_exchange_retrieve(request: web.BaseRequest): @docs( tags=["issue-credential v1.0"], - summary="Send holder a credential, automating entire flow", + summary="Create a credential record without sending (generally for use with Out-Of-Band)", ) @request_schema(V10CredentialCreateSchema()) @response_schema(V10CredentialExchangeSchema(), 200, description="") diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/routes.py b/aries_cloudagent/protocols/issue_credential/v2_0/routes.py index f4147808c2..4f5099f893 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/routes.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/routes.py @@ -521,7 +521,7 @@ async def credential_exchange_retrieve(request: web.BaseRequest): @docs( tags=["issue-credential v2.0"], - summary="Create credential from attribute values", + summary="Create a credential record without sending (generally for use with Out-Of-Band)", ) @request_schema(V20IssueCredSchemaCore()) @response_schema(V20CredExRecordSchema(), 200, description="") From b3214c7ac9c5d2cd28e07bca9bdd247d48670381 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Fri, 28 Oct 2022 13:15:02 -0700 Subject: [PATCH 558/872] Add 0.7.5 patch Changelog entry to main branch Changelog Signed-off-by: Stephen Curran --- CHANGELOG.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index befc3bd50f..291b594b46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,30 @@ more work is still to be done on that before the final v1.0.0 release. - Release management pull requests - Release 1.0.0-rc0 [\#1904](https://github.com/hyperledger/aries-cloudagent-python/pull/1904) ([swcurran](https://github.com/swcurran)) +# 0.7.5 + +## October 26, 2022 + +0.7.5 is a patch release to deal primarily to add [PR #1881 DID Exchange in +ACA-Py 0.7.4 with explicit invitations and without auto-accept +broken](https://github.com/hyperledger/aries-cloudagent-python/pull/1881). A +couple of other PRs were added to the release, as listed below, and in +[Milestone 0.7.5](https://github.com/hyperledger/aries-cloudagent-python/milestone/6). + +### List of Pull Requests + +- Changelog and version updates for version 0.7.5-rc1 [\#1985](https://github.com/hyperledger/aries-cloudagent-python/pull/1985) ([swcurran](https://github.com/swcurran)) +- Endorser doc updates and some bug fixes [\#1926](https://github.com/hyperledger/aries-cloudagent-python/pull/1926) ([ianco](https://github.com/ianco)) +- Fix: web.py dependency - integration tests & demos [\#1973](https://github.com/hyperledger/aries-cloudagent-python/pull/1973) ([shaangill025](https://github.com/shaangill025)) +- Endorser write DID transaction [\#1938](https://github.com/hyperledger/aries-cloudagent-python/pull/1938) ([ianco](https://github.com/ianco)) +- fix: didx request cannot be accepted [\#1881](https://github.com/hyperledger/aries-cloudagent-python/pull/1881) ([rmnre](https://github.com/rmnre)) +- Fix: OOB - Handling of minor versions [\#1940](https://github.com/hyperledger/aries-cloudagent-python/pull/1940) ([shaangill025](https://github.com/shaangill025)) +- fix: Safely shutdown when root_profile uninitialized [\#1960](https://github.com/hyperledger/aries-cloudagent-python/pull/1960) ([frostyfrog](https://github.com/frostyfrog)) +- feat: 00B v1.1 support [\#1962](https://github.com/hyperledger/aries-cloudagent-python/pull/1962) ([shaangill025](https://github.com/shaangill025)) +- 0.7.5 Cherry Picks [\#1967](https://github.com/hyperledger/aries-cloudagent-python/pull/1967) ([frostyfrog](https://github.com/frostyfrog)) +- Changelog and version updates for version 0.7.5-rc0 [\#1969](https://github.com/hyperledger/aries-cloudagent-python/pull/1969) ([swcurran](https://github.com/swcurran)) +- Final 0.7.5 changes [\#1991](https://github.com/hyperledger/aries-cloudagent-python/pull/1991) ([swcurran](https://github.com/swcurran)) + # 0.7.4 ## June 30, 2022 From b5bc338235c4bdc8db08dffb5b01b459b6e3b6fe Mon Sep 17 00:00:00 2001 From: PeterStrob Date: Mon, 31 Oct 2022 18:48:57 +0000 Subject: [PATCH 559/872] fix: format error Signed-off-by: PeterStrob --- aries_cloudagent/protocols/issue_credential/v1_0/routes.py | 3 ++- aries_cloudagent/protocols/issue_credential/v2_0/routes.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/routes.py b/aries_cloudagent/protocols/issue_credential/v1_0/routes.py index 3994fa7c6a..0ceed22df4 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/routes.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/routes.py @@ -382,7 +382,8 @@ async def credential_exchange_retrieve(request: web.BaseRequest): @docs( tags=["issue-credential v1.0"], - summary="Create a credential record without sending (generally for use with Out-Of-Band)", + summary=("Create a credential record without " + "sending (generally for use with Out-Of-Band)"), ) @request_schema(V10CredentialCreateSchema()) @response_schema(V10CredentialExchangeSchema(), 200, description="") diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/routes.py b/aries_cloudagent/protocols/issue_credential/v2_0/routes.py index 4f5099f893..6ad10aefaf 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/routes.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/routes.py @@ -521,7 +521,8 @@ async def credential_exchange_retrieve(request: web.BaseRequest): @docs( tags=["issue-credential v2.0"], - summary="Create a credential record without sending (generally for use with Out-Of-Band)", + summary=("Create a credential record without " + "sending (generally for use with Out-Of-Band)"), ) @request_schema(V20IssueCredSchemaCore()) @response_schema(V20CredExRecordSchema(), 200, description="") From d935d0601b214314c49c601d29b39a67db6437bb Mon Sep 17 00:00:00 2001 From: PeterStrob Date: Tue, 1 Nov 2022 17:36:10 +0000 Subject: [PATCH 560/872] fix: more format errors Signed-off-by: PeterStrob --- aries_cloudagent/protocols/issue_credential/v1_0/routes.py | 2 +- aries_cloudagent/protocols/issue_credential/v2_0/routes.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/routes.py b/aries_cloudagent/protocols/issue_credential/v1_0/routes.py index 0ceed22df4..90853c598b 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/routes.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/routes.py @@ -383,7 +383,7 @@ async def credential_exchange_retrieve(request: web.BaseRequest): @docs( tags=["issue-credential v1.0"], summary=("Create a credential record without " - "sending (generally for use with Out-Of-Band)"), + "sending (generally for use with Out-Of-Band)"), ) @request_schema(V10CredentialCreateSchema()) @response_schema(V10CredentialExchangeSchema(), 200, description="") diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/routes.py b/aries_cloudagent/protocols/issue_credential/v2_0/routes.py index 6ad10aefaf..f35745f7ab 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/routes.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/routes.py @@ -522,7 +522,7 @@ async def credential_exchange_retrieve(request: web.BaseRequest): @docs( tags=["issue-credential v2.0"], summary=("Create a credential record without " - "sending (generally for use with Out-Of-Band)"), + "sending (generally for use with Out-Of-Band)"), ) @request_schema(V20IssueCredSchemaCore()) @response_schema(V20CredExRecordSchema(), 200, description="") From 1fc714eb614593ea86bb2453adc4250c03e363da Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Fri, 4 Nov 2022 14:19:46 -0700 Subject: [PATCH 561/872] Changelog and other updates for ACA-Py 1.0.0-rc1 Signed-off-by: Stephen Curran --- CHANGELOG.md | 76 ++++++++++++++++--- aries_cloudagent/version.py | 2 +- ...nt.protocols.coordinate_mediation.v1_0.rst | 8 ++ docs/generated/aries_cloudagent.resolver.rst | 8 -- open-api/openapi.json | 2 +- 5 files changed, 75 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 291b594b46..8a646cac3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,38 +1,72 @@ -# 1.0.0-rc0 +# 1.0.0-rc1 -## August 18, 2022 +## November 6, 2022 1.0.0 is a breaking update to ACA-Py whose version is intended to indicate the maturity of the implementation. The final 1.0.0 release will be Aries Interop -Profile 2.0-complete, and based on Python 3.7 or higher. The initial (rc0) -release candidate is for early adopters to provide feedback. +Profile 2.0-complete, and based on Python 3.7 or higher. ### Breaking Changes -As of rc0, there are no breaking updates in the release from the previous v0.7.4. -However, we know that there are some pending updates that will be breaking, hence -the bumps to the major/minor version elements. +As of release candidate 1.0.0-rc1, the only identified breaking change is the +handling of "unrevealed attributes" during verification (see +[\#1913](https://github.com/hyperledger/aries-cloudagent-python/pull/1913) for +details). As few implementations of Aries Wallets support unrevealed attributes +in an AnonCreds presentation, this is unlikely to impact any deployments. ### Categorized List of Pull Requests -In rc0, there are not a lot of new features, as the focus is on cleanup and +In rc1, there are not a lot of new features, as the focus is on cleanup and optimization. The biggest is the inclusion with ACA-Py of a universal resolver interface, allowing an instance to have both local resolvers for some DID Methods and a call out to an external universal resolver for other DID Methods. -While some work has been done on moving the default Python version beyond 3.6, -more work is still to be done on that before the final v1.0.0 release. +Another significant feature is full support for Hyperledger Indy transaction +endorsement for Authors and Endorsers. A new repo +[aries-endorser-service](https://github.com/hyperledger/aries-endorser-service) +has been created that is a pre-configured instance of ACA-Py for use as an +Endorser service. While some work has been done on moving the default Python +version beyond 3.6, more work is still to be done on that before the final +v1.0.0 release. ### Categorized List of Pull Requests -- Verifiable credential and revocation handling updates +- Verifiable credential, presentation and revocation handling updates - Refactor ledger correction code and insert into revocation error handling [\#1892](https://github.com/hyperledger/aries-cloudagent-python/pull/1892) ([ianco](https://github.com/ianco)) - Indy ledger fixes and cleanups [\#1870](https://github.com/hyperledger/aries-cloudagent-python/pull/1870) ([andrewwhitehead](https://github.com/andrewwhitehead)) - Refactoring of revocation registry creation [\#1813](https://github.com/hyperledger/aries-cloudagent-python/pull/1813) ([andrewwhitehead](https://github.com/andrewwhitehead)) + - Fix: the type of tails file path to string. [\#1925](https://github.com/hyperledger/aries-cloudagent-python/pull/1925) ([baegjae](https://github.com/baegjae)) + - Pre-populate revoc\_reg\_id on IssuerRevRegRecord [\#1924](https://github.com/hyperledger/aries-cloudagent-python/pull/1924) ([andrewwhitehead](https://github.com/andrewwhitehead)) + - Leave credentialStatus element in the LD credential [\#1921](https://github.com/hyperledger/aries-cloudagent-python/pull/1921) ([tsabolov](https://github.com/tsabolov)) + - **BREAKING:** Remove aca-py check for unrevealed revealed attrs on proof validation [\#1913](https://github.com/hyperledger/aries-cloudagent-python/pull/1913) ([ianco](https://github.com/ianco)) + - Send webhooks upon record/credential deletion [\#1906](https://github.com/hyperledger/aries-cloudagent-python/pull/1906) ([frostyfrog](https://github.com/frostyfrog)) + +- Out of Band (OOB) and DID Exchange / Connection Handling + - Fix: `--mediator-invitation` with OOB invitation + cleanup [\#1970](https://github.com/hyperledger/aries-cloudagent-python/pull/1970) ([shaangill025](https://github.com/shaangill025)) + - include image\_url in oob invitation [\#1966](https://github.com/hyperledger/aries-cloudagent-python/pull/1966) ([Zzocker](https://github.com/Zzocker)) + - feat: 00B v1.1 support [\#1962](https://github.com/hyperledger/aries-cloudagent-python/pull/1962) ([shaangill025](https://github.com/shaangill025)) + - Fix: OOB - Handling of minor versions [\#1940](https://github.com/hyperledger/aries-cloudagent-python/pull/1940) ([shaangill025](https://github.com/shaangill025)) + - fix: failed connectionless proof request on some case [\#1933](https://github.com/hyperledger/aries-cloudagent-python/pull/1933) ([kukgini](https://github.com/kukgini)) + - fix: propagate endpoint from mediation record [\#1922](https://github.com/hyperledger/aries-cloudagent-python/pull/1922) ([cjhowland](https://github.com/cjhowland)) + - Feat/public did endpoints for agents behind mediators [\#1899](https://github.com/hyperledger/aries-cloudagent-python/pull/1899) ([cjhowland](https://github.com/cjhowland)) - DID Registration and Resolution related updates - feat: add universal resolver [\#1866](https://github.com/hyperledger/aries-cloudagent-python/pull/1866) ([dbluhm](https://github.com/dbluhm)) - fix: resolve dids following new endpoint rules [\#1863](https://github.com/hyperledger/aries-cloudagent-python/pull/1863) ([dbluhm](https://github.com/dbluhm)) - fix: didx request cannot be accepted [\#1881](https://github.com/hyperledger/aries-cloudagent-python/pull/1881) ([rmnre](https://github.com/rmnre)) + - did method & key type registry [\#1986](https://github.com/hyperledger/aries-cloudagent-python/pull/1986) ([burdettadam](https://github.com/burdettadam)) + - Fix/endpoint attrib structure [\#1934](https://github.com/hyperledger/aries-cloudagent-python/pull/1934) ([cjhowland](https://github.com/cjhowland)) + - Simple did registry [\#1920](https://github.com/hyperledger/aries-cloudagent-python/pull/1920) ([burdettadam](https://github.com/burdettadam)) + - Use did:key for recipient keys [\#1886](https://github.com/hyperledger/aries-cloudagent-python/pull/1886) ([frostyfrog](https://github.com/frostyfrog)) + +- Hyperledger Indy Endorser/Author Transaction Handling + - Fix/txn job setting [\#1994](https://github.com/hyperledger/aries-cloudagent-python/pull/1994) ([ianco](https://github.com/ianco)) + - chore: fix ACAPY\_PROMOTE-AUTHOR-DID flag [\#1978](https://github.com/hyperledger/aries-cloudagent-python/pull/1978) ([morrieinmaas](https://github.com/morrieinmaas)) + + - Endorser write DID transaction [\#1938](https://github.com/hyperledger/aries-cloudagent-python/pull/1938) ([ianco](https://github.com/ianco)) + - Endorser doc updates and some bug fixes [\#1926](https://github.com/hyperledger/aries-cloudagent-python/pull/1926) ([ianco](https://github.com/ianco)) + +- Startup Command Line / Environment / YAML Parameter Updates + - Add seed command line parameter but use only if also an "allow insecure seed" parameter is set [\#1714](https://github.com/hyperledger/aries-cloudagent-python/pull/1714) ([DaevMithran](https://github.com/DaevMithran)) - Internal Aries framework data handling updates - fix: update RouteManager methods use to pass profile as parameter [\#1902](https://github.com/hyperledger/aries-cloudagent-python/pull/1902) ([chumbert](https://github.com/chumbert)) @@ -41,14 +75,34 @@ more work is still to be done on that before the final v1.0.0 release. - Enable manually triggering keylist updates during connection [\#1851](https://github.com/hyperledger/aries-cloudagent-python/pull/1851) ([dbluhm](https://github.com/dbluhm)) - feat: make base wallet route access configurable [\#1836](https://github.com/hyperledger/aries-cloudagent-python/pull/1836) ([dbluhm](https://github.com/dbluhm)) - feat: event and webhook on keylist update stored [\#1769](https://github.com/hyperledger/aries-cloudagent-python/pull/1769) ([dbluhm](https://github.com/dbluhm)) + - fix: Safely shutdown when root\_profile uninitialized [\#1960](https://github.com/hyperledger/aries-cloudagent-python/pull/1960) ([frostyfrog](https://github.com/frostyfrog)) + - feat: include connection ids in keylist update webhook [\#1914](https://github.com/hyperledger/aries-cloudagent-python/pull/1914) ([dbluhm](https://github.com/dbluhm)) + - fix: incorrect response schema for discover features [\#1912](https://github.com/hyperledger/aries-cloudagent-python/pull/1912) ([dbluhm](https://github.com/dbluhm)) + - Fix: SchemasInputDescriptorFilter: broken deserialization renders generated clients unusable [\#1894](https://github.com/hyperledger/aries-cloudagent-python/pull/1894) ([rmnre](https://github.com/rmnre)) + - fix: schema class can set Meta.unknown [\#1885](https://github.com/hyperledger/aries-cloudagent-python/pull/1885) ([dbluhm](https://github.com/dbluhm)) - Unit, Integration and Aries Agent Test Harness Test updates - Fixes a few AATH failures [\#1897](https://github.com/hyperledger/aries-cloudagent-python/pull/1897) ([ianco](https://github.com/ianco)) - fix: warnings in tests from IndySdkProfile [\#1865](https://github.com/hyperledger/aries-cloudagent-python/pull/1865) ([dbluhm](https://github.com/dbluhm)) - Unit test fixes for python 3.9 [\#1858](https://github.com/hyperledger/aries-cloudagent-python/pull/1858) ([andrewwhitehead](https://github.com/andrewwhitehead)) + - Update pip-audit.yml [\#1945](https://github.com/hyperledger/aries-cloudagent-python/pull/1945) ([ryjones](https://github.com/ryjones)) + - Update pip-audit.yml [\#1944](https://github.com/hyperledger/aries-cloudagent-python/pull/1944) ([ryjones](https://github.com/ryjones)) + +- Dependency Updates + - feat: update pynacl version from 1.4.0 to 1.50 [\#1981](https://github.com/hyperledger/aries-cloudagent-python/pull/1981) ([morrieinmaas](https://github.com/morrieinmaas)) + - Fix: web.py dependency - integration tests & demos [\#1973](https://github.com/hyperledger/aries-cloudagent-python/pull/1973) ([shaangill025](https://github.com/shaangill025)) + - chore: update pydid [\#1915](https://github.com/hyperledger/aries-cloudagent-python/pull/1915) ([dbluhm](https://github.com/dbluhm)) + +- Demo and Documentation Updates + - Fixes to acme exercise code [\#1990](https://github.com/hyperledger/aries-cloudagent-python/pull/1990) ([ianco](https://github.com/ianco)) + - Fixed bug in run\_demo script [\#1982](https://github.com/hyperledger/aries-cloudagent-python/pull/1982) ([pasquale95](https://github.com/pasquale95)) + - Transaction Author with Endorser demo [\#1975](https://github.com/hyperledger/aries-cloudagent-python/pull/1975) ([ianco](https://github.com/ianco)) + - Redis Plugins \[redis\_cache & redis\_queue\] related updates [\#1937](https://github.com/hyperledger/aries-cloudagent-python/pull/1937) ([shaangill025](https://github.com/shaangill025)) - Release management pull requests - Release 1.0.0-rc0 [\#1904](https://github.com/hyperledger/aries-cloudagent-python/pull/1904) ([swcurran](https://github.com/swcurran)) + - Add 0.7.5 patch Changelog entry to main branch Changelog [\#1996](https://github.com/hyperledger/aries-cloudagent-python/pull/1996) ([swcurran](https://github.com/swcurran)) + # 0.7.5 diff --git a/aries_cloudagent/version.py b/aries_cloudagent/version.py index dd12aefad4..7e5ca62006 100644 --- a/aries_cloudagent/version.py +++ b/aries_cloudagent/version.py @@ -1,4 +1,4 @@ """Library version information.""" -__version__ = "1.0.0-rc0" +__version__ = "1.0.0-rc1" RECORD_TYPE_ACAPY_VERSION = "acapy_version" diff --git a/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.rst b/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.rst index ca2bc3dae6..fb05fd04d8 100644 --- a/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.rst @@ -43,6 +43,14 @@ aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.message\_types module :undoc-members: :show-inheritance: +aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.normalization module +---------------------------------------------------------------------------- + +.. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.normalization + :members: + :undoc-members: + :show-inheritance: + aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.route\_manager module ----------------------------------------------------------------------------- diff --git a/docs/generated/aries_cloudagent.resolver.rst b/docs/generated/aries_cloudagent.resolver.rst index c3aa28279c..7732543e90 100644 --- a/docs/generated/aries_cloudagent.resolver.rst +++ b/docs/generated/aries_cloudagent.resolver.rst @@ -33,14 +33,6 @@ aries\_cloudagent.resolver.did\_resolver module :undoc-members: :show-inheritance: -aries\_cloudagent.resolver.did\_resolver\_registry module ---------------------------------------------------------- - -.. automodule:: aries_cloudagent.resolver.did_resolver_registry - :members: - :undoc-members: - :show-inheritance: - aries\_cloudagent.resolver.routes module ---------------------------------------- diff --git a/open-api/openapi.json b/open-api/openapi.json index d7a5599378..2be5598c83 100644 --- a/open-api/openapi.json +++ b/open-api/openapi.json @@ -1,7 +1,7 @@ { "swagger" : "2.0", "info" : { - "version" : "v1.0.0-rc0", + "version" : "v1.0.0-rc1", "title" : "Aries Cloud Agent" }, "tags" : [ { From 00446e953e216f99c6a2491a5b1516de59654faa Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Fri, 4 Nov 2022 14:21:00 -0700 Subject: [PATCH 562/872] Add this PR to the Changelog Signed-off-by: Stephen Curran --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a646cac3e..3e90d82bd2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -102,6 +102,7 @@ v1.0.0 release. - Release management pull requests - Release 1.0.0-rc0 [\#1904](https://github.com/hyperledger/aries-cloudagent-python/pull/1904) ([swcurran](https://github.com/swcurran)) - Add 0.7.5 patch Changelog entry to main branch Changelog [\#1996](https://github.com/hyperledger/aries-cloudagent-python/pull/1996) ([swcurran](https://github.com/swcurran)) + - Release 1.0.0-rc1 [\#2005](https://github.com/hyperledger/aries-cloudagent-python/pull/2005) ([swcurran](https://github.com/swcurran)) # 0.7.5 From 8124da70a20def3292de438b951c2e8b0da71ea7 Mon Sep 17 00:00:00 2001 From: Nao Nishijima Date: Mon, 7 Nov 2022 22:57:48 +0900 Subject: [PATCH 563/872] Fix typos in alice-local.sh & faber-local.sh There are typos in alice-local.sh & faber-local.sh to run pip install. Signed-off-by: Nao Nishijima --- demo/alice-local.sh | 2 +- demo/faber-local.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/demo/alice-local.sh b/demo/alice-local.sh index babc85b491..b1639fc90a 100755 --- a/demo/alice-local.sh +++ b/demo/alice-local.sh @@ -1,7 +1,7 @@ #!/bin/bash # this runs the Faber example as a local instace of instance of aca-py # you need to run a local von-network (in the von-network directory run "./manage start --logs") -# ... and you need to install the local aca-py python libraries locally ("pip install -r ../requriements.txt -r ../requirements.indy.txt -r ../requirements.bbs.txt") +# ... and you need to install the local aca-py python libraries locally ("pip install -r ../requirements.txt -r ../requirements.indy.txt -r ../requirements.bbs.txt") # the following will auto-respond on connection and credential requests, but not proof requests PYTHONPATH=.. ../bin/aca-py start \ diff --git a/demo/faber-local.sh b/demo/faber-local.sh index 7471c31b67..4cfe6edae4 100755 --- a/demo/faber-local.sh +++ b/demo/faber-local.sh @@ -1,7 +1,7 @@ #!/bin/bash # this runs the Faber example as a local instace of instance of aca-py # you need to run a local von-network (in the von-network directory run "./manage start --logs") -# ... and you need to install the local aca-py python libraries locally ("pip install -r ../requriements.txt -r ../requirements.indy.txt -r ../requirements.bbs.txt") +# ... and you need to install the local aca-py python libraries locally ("pip install -r ../requirements.txt -r ../requirements.indy.txt -r ../requirements.bbs.txt") # the following will auto-respond on connection and credential requests, but not proof requests PYTHONPATH=.. ../bin/aca-py start \ From 3e0fc04067bee6e2891147c5eb8547d9afe841f6 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Mon, 7 Nov 2022 12:39:01 -0800 Subject: [PATCH 564/872] Added debugging Signed-off-by: Ian Costanzo --- aries_cloudagent/protocols/routing/v1_0/manager.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/aries_cloudagent/protocols/routing/v1_0/manager.py b/aries_cloudagent/protocols/routing/v1_0/manager.py index 44c1b7b7c6..008b13af3a 100644 --- a/aries_cloudagent/protocols/routing/v1_0/manager.py +++ b/aries_cloudagent/protocols/routing/v1_0/manager.py @@ -1,5 +1,6 @@ """Routing manager classes for tracking and inspecting routing records.""" +import logging from typing import Coroutine, Sequence from ....core.error import BaseError @@ -16,6 +17,9 @@ from .models.route_updated import RouteUpdated +LOGGER = logging.getLogger(__name__) + + class RoutingManagerError(BaseError): """Generic routing error.""" @@ -55,15 +59,19 @@ async def get_recipient(self, recip_verkey: str) -> RouteRecord: raise RoutingManagerError("Must pass non-empty recip_verkey") try: + LOGGER.error(">>> fetching routing record for verkey: " + recip_verkey) async with self._profile.session() as session: record = await RouteRecord.retrieve_by_recipient_key( session, recip_verkey ) + LOGGER.error(">>> FOUND routing record for verkey: " + recip_verkey) except StorageDuplicateError: + LOGGER.error(">>> DUPLICATE routing record for verkey: " + recip_verkey) raise RouteNotFoundError( f"More than one route record found with recipient key: {recip_verkey}" ) except StorageNotFoundError: + LOGGER.error(">>> NOT FOUND routing record for verkey: " + recip_verkey) raise RouteNotFoundError( f"No route found with recipient key: {recip_verkey}" ) @@ -136,6 +144,7 @@ async def create_route_record( ) if not recipient_key: raise RoutingManagerError("Missing recipient_key") + LOGGER.error(">>> creating routing record for verkey: " + recipient_key) route = RouteRecord( connection_id=client_connection_id, wallet_id=internal_wallet_id, @@ -143,6 +152,7 @@ async def create_route_record( ) async with self._profile.session() as session: await route.save(session, reason="Created new route") + LOGGER.error(">>> CREATED routing record for verkey: " + recipient_key) return route async def update_routes( From 1128f041820133a7378dcd0adbae94037569ec75 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Mon, 7 Nov 2022 13:41:33 -0800 Subject: [PATCH 565/872] Debugging Signed-off-by: Ian Costanzo --- .../protocols/routing/v1_0/manager.py | 42 +++++++++++-------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/aries_cloudagent/protocols/routing/v1_0/manager.py b/aries_cloudagent/protocols/routing/v1_0/manager.py index 008b13af3a..5ac5656dd2 100644 --- a/aries_cloudagent/protocols/routing/v1_0/manager.py +++ b/aries_cloudagent/protocols/routing/v1_0/manager.py @@ -1,5 +1,6 @@ """Routing manager classes for tracking and inspecting routing records.""" +import asyncio import logging from typing import Coroutine, Sequence @@ -58,25 +59,30 @@ async def get_recipient(self, recip_verkey: str) -> RouteRecord: if not recip_verkey: raise RoutingManagerError("Must pass non-empty recip_verkey") - try: - LOGGER.error(">>> fetching routing record for verkey: " + recip_verkey) - async with self._profile.session() as session: - record = await RouteRecord.retrieve_by_recipient_key( - session, recip_verkey + pause = True + record = None + while not record: + try: + LOGGER.error(">>> fetching routing record for verkey: " + recip_verkey) + async with self._profile.session() as session: + record = await RouteRecord.retrieve_by_recipient_key( + session, recip_verkey + ) + LOGGER.error(">>> FOUND routing record for verkey: " + recip_verkey) + return record + except StorageDuplicateError: + LOGGER.error(">>> DUPLICATE routing record for verkey: " + recip_verkey) + raise RouteNotFoundError( + f"More than one route record found with recipient key: {recip_verkey}" ) - LOGGER.error(">>> FOUND routing record for verkey: " + recip_verkey) - except StorageDuplicateError: - LOGGER.error(">>> DUPLICATE routing record for verkey: " + recip_verkey) - raise RouteNotFoundError( - f"More than one route record found with recipient key: {recip_verkey}" - ) - except StorageNotFoundError: - LOGGER.error(">>> NOT FOUND routing record for verkey: " + recip_verkey) - raise RouteNotFoundError( - f"No route found with recipient key: {recip_verkey}" - ) - - return record + except StorageNotFoundError: + LOGGER.error(">>> NOT FOUND routing record for verkey: " + recip_verkey) + if not pause: + raise RouteNotFoundError( + f"No route found with recipient key: {recip_verkey}" + ) + await asyncio.wait(500) + pause = False async def get_routes( self, client_connection_id: str = None, tag_filter: dict = None From 215c9cca85afb1012718f12ce985dfa9d8f14004 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Mon, 7 Nov 2022 13:59:34 -0800 Subject: [PATCH 566/872] Debugging Signed-off-by: Ian Costanzo --- aries_cloudagent/protocols/routing/v1_0/manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aries_cloudagent/protocols/routing/v1_0/manager.py b/aries_cloudagent/protocols/routing/v1_0/manager.py index 5ac5656dd2..babed988de 100644 --- a/aries_cloudagent/protocols/routing/v1_0/manager.py +++ b/aries_cloudagent/protocols/routing/v1_0/manager.py @@ -81,7 +81,7 @@ async def get_recipient(self, recip_verkey: str) -> RouteRecord: raise RouteNotFoundError( f"No route found with recipient key: {recip_verkey}" ) - await asyncio.wait(500) + await asyncio.sleep(0.5) pause = False async def get_routes( From f9bc5f85b27c340e567c4a2e7f761a44fffefc99 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Mon, 7 Nov 2022 14:21:40 -0800 Subject: [PATCH 567/872] Debugging Signed-off-by: Ian Costanzo --- .../protocols/routing/v1_0/manager.py | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/aries_cloudagent/protocols/routing/v1_0/manager.py b/aries_cloudagent/protocols/routing/v1_0/manager.py index babed988de..33408d0e7c 100644 --- a/aries_cloudagent/protocols/routing/v1_0/manager.py +++ b/aries_cloudagent/protocols/routing/v1_0/manager.py @@ -20,6 +20,9 @@ LOGGER = logging.getLogger(__name__) +RECIP_ROUTE_PAUSE = 0.1 +RECIP_ROUTE_RETRY = 5 + class RoutingManagerError(BaseError): """Generic routing error.""" @@ -59,30 +62,30 @@ async def get_recipient(self, recip_verkey: str) -> RouteRecord: if not recip_verkey: raise RoutingManagerError("Must pass non-empty recip_verkey") - pause = True + i = 0 record = None while not record: try: - LOGGER.error(">>> fetching routing record for verkey: " + recip_verkey) + LOGGER.info(">>> fetching routing record for verkey: " + recip_verkey) async with self._profile.session() as session: record = await RouteRecord.retrieve_by_recipient_key( session, recip_verkey ) - LOGGER.error(">>> FOUND routing record for verkey: " + recip_verkey) + LOGGER.info(">>> FOUND routing record for verkey: " + recip_verkey) return record except StorageDuplicateError: - LOGGER.error(">>> DUPLICATE routing record for verkey: " + recip_verkey) + LOGGER.info(">>> DUPLICATE routing record for verkey: " + recip_verkey) raise RouteNotFoundError( f"More than one route record found with recipient key: {recip_verkey}" ) except StorageNotFoundError: - LOGGER.error(">>> NOT FOUND routing record for verkey: " + recip_verkey) - if not pause: + LOGGER.info(">>> NOT FOUND routing record for verkey: " + recip_verkey) + i += 1 + if i > RECIP_ROUTE_RETRY: raise RouteNotFoundError( f"No route found with recipient key: {recip_verkey}" ) - await asyncio.sleep(0.5) - pause = False + await asyncio.sleep(RECIP_ROUTE_PAUSE) async def get_routes( self, client_connection_id: str = None, tag_filter: dict = None @@ -150,7 +153,7 @@ async def create_route_record( ) if not recipient_key: raise RoutingManagerError("Missing recipient_key") - LOGGER.error(">>> creating routing record for verkey: " + recipient_key) + LOGGER.info(">>> creating routing record for verkey: " + recipient_key) route = RouteRecord( connection_id=client_connection_id, wallet_id=internal_wallet_id, @@ -158,7 +161,7 @@ async def create_route_record( ) async with self._profile.session() as session: await route.save(session, reason="Created new route") - LOGGER.error(">>> CREATED routing record for verkey: " + recipient_key) + LOGGER.info(">>> CREATED routing record for verkey: " + recipient_key) return route async def update_routes( From 0e0ccdd8795c21210ace4a5db76aeddb8efd2828 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Mon, 7 Nov 2022 15:05:45 -0800 Subject: [PATCH 568/872] Debugging Signed-off-by: Ian Costanzo --- aries_cloudagent/protocols/routing/v1_0/manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aries_cloudagent/protocols/routing/v1_0/manager.py b/aries_cloudagent/protocols/routing/v1_0/manager.py index 33408d0e7c..d200a30b59 100644 --- a/aries_cloudagent/protocols/routing/v1_0/manager.py +++ b/aries_cloudagent/protocols/routing/v1_0/manager.py @@ -21,7 +21,7 @@ LOGGER = logging.getLogger(__name__) RECIP_ROUTE_PAUSE = 0.1 -RECIP_ROUTE_RETRY = 5 +RECIP_ROUTE_RETRY = 10 class RoutingManagerError(BaseError): From abb2e4aaa15a9d0431e537ca77331ec0ada9b240 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Tue, 8 Nov 2022 10:47:36 -0500 Subject: [PATCH 569/872] fix: mock wait process complete in tests (#1) Signed-off-by: Daniel Bluhm --- aries_cloudagent/transport/inbound/tests/test_http_transport.py | 1 + 1 file changed, 1 insertion(+) diff --git a/aries_cloudagent/transport/inbound/tests/test_http_transport.py b/aries_cloudagent/transport/inbound/tests/test_http_transport.py index c561ba7827..fd74775572 100644 --- a/aries_cloudagent/transport/inbound/tests/test_http_transport.py +++ b/aries_cloudagent/transport/inbound/tests/test_http_transport.py @@ -62,6 +62,7 @@ def receive_message( message: InboundMessage, can_respond: bool = False, ): + message.wait_processing_complete = async_mock.CoroutineMock() self.message_results.append((message.payload, message.receipt, can_respond)) if self.result_event: self.result_event.set() From 4614a3f103ccbb85405cb6513f1ce8e6904cf792 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Tue, 8 Nov 2022 13:18:46 -0800 Subject: [PATCH 570/872] Added a bit about manually creating a revoc reg tails file Signed-off-by: Ian Costanzo --- .../CredentialRevocation.md | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/docs/GettingStartedAriesDev/CredentialRevocation.md b/docs/GettingStartedAriesDev/CredentialRevocation.md index 8674a54e57..79eaad141c 100644 --- a/docs/GettingStartedAriesDev/CredentialRevocation.md +++ b/docs/GettingStartedAriesDev/CredentialRevocation.md @@ -151,3 +151,33 @@ further customize notification handling. If the argument `--monitor-revocation-notification` is used on startup, a webhook with the topic `revocation-notification` and a payload containing the thread ID and comment is emitted to registered webhook urls. + +## Manually Creating Revocation Registries + +The process for creating revocation registries is completely automated - when you create a Credential Definition with revocation enabled, a revocation registry is automatically created (in fact 2 registries are created), and when a registry fills up, a new one is automatically created. + +However the Aca-Py admin api supports endpoints to explicitely create a new revocation registry, if you desire. + +There are several endpoints that must be called, and they must be called in this order: + +1. Create revoc registry `POST /revocation/create-registry` + + - you need to provide the credential definition id and the size of the registry + +2. Fix the tails file URI `PATCH /revocation/registry/{rev_reg_id}` + + - here you need to provide the full URI that will be written to the ledger, for example: + +``` +{ + "tails_public_uri": "http://host.docker.internal:6543/VDKEEMMSRTEqK4m7iiq5ZL:4:VDKEEMMSRTEqK4m7iiq5ZL:3:CL:8:faber.agent.degree_schema:CL_ACCUM:3cb5c439-928c-483c-a9a8-629c307e6b2d" +} +``` + +3. Post the revoc def to the ledger `POST /revocation/registry/{rev_reg_id}/definition` + + - if you are an author (i.e. have a DID with restricted ledger write access) then this transaction may need to go through an endorser + +4. Write the tails file `PUT /revocation/registry/{rev_reg_id}/tails-file` + + - the tails server will check that the registry definition is already written to the ledger From 54e6464abd4d4846ff6e59e34b97b98017439e9e Mon Sep 17 00:00:00 2001 From: Roman Reinert Date: Wed, 9 Nov 2022 14:15:51 +0000 Subject: [PATCH 571/872] fix claim format designation in presentation submission Signed-off-by: Roman Reinert --- aries_cloudagent/protocols/present_proof/dif/pres_exch.py | 2 +- .../protocols/present_proof/dif/pres_exch_handler.py | 2 +- .../protocols/present_proof/dif/tests/test_pres_exch.py | 2 +- .../present_proof/v2_0/formats/dif/tests/test_handler.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/aries_cloudagent/protocols/present_proof/dif/pres_exch.py b/aries_cloudagent/protocols/present_proof/dif/pres_exch.py index 3155d28860..5b9cdfe1de 100644 --- a/aries_cloudagent/protocols/present_proof/dif/pres_exch.py +++ b/aries_cloudagent/protocols/present_proof/dif/pres_exch.py @@ -837,7 +837,7 @@ class Meta: fmt = fields.Str( description="Format", required=False, - default="ldp_vp", + default="ldp_vc", data_key="format", ) path = fields.Str( diff --git a/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py b/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py index 813dbe287d..3713fc444a 100644 --- a/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py +++ b/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py @@ -1356,7 +1356,7 @@ async def merge( if f"{cred_id}-{cred_id}" not in dict_of_descriptors: descriptor_map = InputDescriptorMapping( id=desc_id, - fmt="ldp_vp", + fmt="ldp_vc", path=(f"$.verifiableCredential[{dict_of_creds[cred_id]}]"), ) descriptors.append(descriptor_map) diff --git a/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch.py b/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch.py index 2709024263..b8515188cd 100644 --- a/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch.py +++ b/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch.py @@ -363,7 +363,7 @@ def test_verifiable_presentation_wrapper(self): "descriptor_map": [ { "id": "citizenship_input_1", - "format": "ldp_vp", + "format": "ldp_vc", "path": "$.verifiableCredential[0]", } ], diff --git a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py index 85d4a9b8a2..42ad3618ac 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py @@ -199,7 +199,7 @@ "descriptor_map": [ { "id": "citizenship_input_1", - "format": "ldp_vp", + "format": "ldp_vc", "path": "$.verifiableCredential[0]", } ], From a74431f9d934f313e3a35635cd765eeae444013b Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Tue, 22 Nov 2022 11:18:56 -0800 Subject: [PATCH 572/872] Multi-ledger issues WIP Signed-off-by: Ian Costanzo --- aries_cloudagent/admin/server.py | 1 + demo/multi_ledger_config.yml | 8 ++++++-- demo/runners/agent_container.py | 4 +++- demo/runners/support/agent.py | 9 ++++++++- 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/aries_cloudagent/admin/server.py b/aries_cloudagent/admin/server.py index 6d84de327d..c7ab79334d 100644 --- a/aries_cloudagent/admin/server.py +++ b/aries_cloudagent/admin/server.py @@ -370,6 +370,7 @@ async def check_multitenant_authorization(request: web.Request, handler): and not is_server_path and not is_unprotected_path(path) and not base_limited_access_path + and not (request.method == "OPTIONS") # CORS fix ): raise web.HTTPUnauthorized() diff --git a/demo/multi_ledger_config.yml b/demo/multi_ledger_config.yml index 3290f50d5e..88694188c9 100644 --- a/demo/multi_ledger_config.yml +++ b/demo/multi_ledger_config.yml @@ -1,6 +1,10 @@ -- id: bcorvinTest +- id: local is_production: true - genesis_url: 'http://test.bcovrin.vonx.io/genesis' + is_write: true + genesis_url: 'http://$LEDGER_HOST:9000/genesis' +#- id: bcorvinTest +# is_production: true +# genesis_url: 'http://dev.bcovrin.vonx.io/genesis' - id: greenlightTest is_production: true genesis_url: 'http://dev.greenlight.bcovrin.vonx.io/genesis' diff --git a/demo/runners/agent_container.py b/demo/runners/agent_container.py index a7ee7745ab..87b9ac203c 100644 --- a/demo/runners/agent_container.py +++ b/demo/runners/agent_container.py @@ -1274,9 +1274,11 @@ async def create_agent_with_args(args, ident: str = None): ) multi_ledger_config_path = None + genesis = None if "multi_ledger" in args and args.multi_ledger: multi_ledger_config_path = "./demo/multi_ledger_config.yml" - genesis = await default_genesis_txns() + else: + genesis = await default_genesis_txns() if not genesis and not multi_ledger_config_path: print("Error retrieving ledger genesis transactions") sys.exit(1) diff --git a/demo/runners/support/agent.py b/demo/runners/support/agent.py index 8398df1279..fb923a3028 100644 --- a/demo/runners/support/agent.py +++ b/demo/runners/support/agent.py @@ -213,6 +213,7 @@ def __init__( self.agency_wallet_did = self.did self.agency_wallet_key = self.wallet_key + self.multi_write_ledger_url = None if self.genesis_txn_list: updated_config_list = [] with open(self.genesis_txn_list, "r") as stream: @@ -225,6 +226,8 @@ def __init__( "$LEDGER_HOST", str(self.external_host) ) updated_config_list.append(config) + if "is_write" in config and config["is_write"]: + self.multi_write_ledger_url = config["genesis_url"].replace("/genesis", "") with open(self.genesis_txn_list, "w") as file: documents = yaml.dump(updated_config_list, file) @@ -479,7 +482,10 @@ async def register_did( # if registering a did for issuing indy credentials, publish the did on the ledger self.log(f"Registering {self.ident} ...") if not ledger_url: - ledger_url = LEDGER_URL + if self.multi_write_ledger_url: + ledger_url = self.multi_write_ledger_url + else: + ledger_url = LEDGER_URL if not ledger_url: ledger_url = f"http://{self.external_host}:9000" data = {"alias": alias or self.ident} @@ -501,6 +507,7 @@ async def register_did( await asyncio.sleep(3.0) nym_info = data else: + log_msg("using ledger: " + ledger_url + "/register") resp = await self.client_session.post( ledger_url + "/register", json=data ) From 6a627a2bfc1d2d4e74b2a28e127401a811578552 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Tue, 22 Nov 2022 15:53:33 -0800 Subject: [PATCH 573/872] Use write_ledger info globally in multi-ledger mode Signed-off-by: Ian Costanzo --- aries_cloudagent/config/argparse.py | 26 ++++++++++++++++++++------ aries_cloudagent/core/conductor.py | 4 ++-- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index 6822f72c2f..260dc9a70b 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -872,31 +872,45 @@ def get_settings(self, args: Namespace) -> dict: if args.no_ledger: settings["ledger.disabled"] = True else: - configured = False + single_configured = False + multi_configured = False if args.genesis_url: settings["ledger.genesis_url"] = args.genesis_url - configured = True + single_configured = True elif args.genesis_file: settings["ledger.genesis_file"] = args.genesis_file - configured = True + single_configured = True elif args.genesis_transactions: settings["ledger.genesis_transactions"] = args.genesis_transactions - configured = True + single_configured = True if args.genesis_transactions_list: with open(args.genesis_transactions_list, "r") as stream: txn_config_list = yaml.safe_load(stream) ledger_config_list = [] for txn_config in txn_config_list: ledger_config_list.append(txn_config) + if "is_write" in txn_config and txn_config["is_write"]: + if "genesis_url" in txn_config: + settings["ledger.genesis_url"] = txn_config["genesis_url"] + elif "genesis_file" in txn_config: + settings["ledger.genesis_file"] = txn_config["genesis_file"] + elif "genesis_transactions" in txn_config: + settings["ledger.genesis_transactions"] = txn_config["genesis_transactions"] + else: + raise ArgsParseError("No genesis information provided for write ledger") settings["ledger.ledger_config_list"] = ledger_config_list - configured = True - if not configured: + multi_configured = True + if not (single_configured or multi_configured): raise ArgsParseError( "One of --genesis-url --genesis-file, --genesis-transactions " "or --genesis-transactions-list must be specified (unless " "--no-ledger is specified to explicitly configure aca-py to" " run with no ledger)." ) + if single_configured and multi_configured: + raise ArgsParseError( + "Cannot configure both single- and multi-ledger." + ) if args.ledger_pool_name: settings["ledger.pool_name"] = args.ledger_pool_name if args.ledger_keepalive: diff --git a/aries_cloudagent/core/conductor.py b/aries_cloudagent/core/conductor.py index c24862a4e9..edf881f84f 100644 --- a/aries_cloudagent/core/conductor.py +++ b/aries_cloudagent/core/conductor.py @@ -144,7 +144,7 @@ async def setup(self): self.root_profile.BACKEND_NAME == "askar" and ledger.BACKEND_NAME == "indy-vdr" ): - context.injector.bind_instance(BaseLedger, ledger) + # context.injector.bind_instance(BaseLedger, ledger) context.injector.bind_provider( IndyVerifier, ClassProvider( @@ -156,7 +156,7 @@ async def setup(self): self.root_profile.BACKEND_NAME == "indy" and ledger.BACKEND_NAME == "indy" ): - context.injector.bind_instance(BaseLedger, ledger) + # context.injector.bind_instance(BaseLedger, ledger) context.injector.bind_provider( IndyVerifier, ClassProvider( From 5f1c539d6b01a54fe5f85a84c8c713fe4a20ae05 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Tue, 22 Nov 2022 16:10:19 -0800 Subject: [PATCH 574/872] Formatting Signed-off-by: Ian Costanzo --- aries_cloudagent/config/argparse.py | 20 +++++++++++++------- aries_cloudagent/core/conductor.py | 2 +- demo/runners/support/agent.py | 4 +++- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index 260dc9a70b..678028dcb7 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -891,13 +891,21 @@ def get_settings(self, args: Namespace) -> dict: ledger_config_list.append(txn_config) if "is_write" in txn_config and txn_config["is_write"]: if "genesis_url" in txn_config: - settings["ledger.genesis_url"] = txn_config["genesis_url"] + settings["ledger.genesis_url"] = txn_config[ + "genesis_url" + ] elif "genesis_file" in txn_config: - settings["ledger.genesis_file"] = txn_config["genesis_file"] + settings["ledger.genesis_file"] = txn_config[ + "genesis_file" + ] elif "genesis_transactions" in txn_config: - settings["ledger.genesis_transactions"] = txn_config["genesis_transactions"] + settings["ledger.genesis_transactions"] = txn_config[ + "genesis_transactions" + ] else: - raise ArgsParseError("No genesis information provided for write ledger") + raise ArgsParseError( + "No genesis information provided for write ledger" + ) settings["ledger.ledger_config_list"] = ledger_config_list multi_configured = True if not (single_configured or multi_configured): @@ -908,9 +916,7 @@ def get_settings(self, args: Namespace) -> dict: " run with no ledger)." ) if single_configured and multi_configured: - raise ArgsParseError( - "Cannot configure both single- and multi-ledger." - ) + raise ArgsParseError("Cannot configure both single- and multi-ledger.") if args.ledger_pool_name: settings["ledger.pool_name"] = args.ledger_pool_name if args.ledger_keepalive: diff --git a/aries_cloudagent/core/conductor.py b/aries_cloudagent/core/conductor.py index edf881f84f..d65133981b 100644 --- a/aries_cloudagent/core/conductor.py +++ b/aries_cloudagent/core/conductor.py @@ -28,7 +28,7 @@ from ..config.wallet import wallet_config from ..core.profile import Profile from ..indy.verifier import IndyVerifier -from ..ledger.base import BaseLedger +# from ..ledger.base import BaseLedger from ..ledger.error import LedgerConfigError, LedgerTransactionError from ..ledger.multiple_ledger.base_manager import ( BaseMultipleLedgerManager, diff --git a/demo/runners/support/agent.py b/demo/runners/support/agent.py index fb923a3028..1ec42565dc 100644 --- a/demo/runners/support/agent.py +++ b/demo/runners/support/agent.py @@ -227,7 +227,9 @@ def __init__( ) updated_config_list.append(config) if "is_write" in config and config["is_write"]: - self.multi_write_ledger_url = config["genesis_url"].replace("/genesis", "") + self.multi_write_ledger_url = config["genesis_url"].replace( + "/genesis", "" + ) with open(self.genesis_txn_list, "w") as file: documents = yaml.dump(updated_config_list, file) From 26875d4c1f96f94eee3897a16dbdf10650b33ec1 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Tue, 22 Nov 2022 17:06:57 -0800 Subject: [PATCH 575/872] Add multi/multi integration test Signed-off-by: Ian Costanzo --- demo/features/0586-sign-transaction.feature | 1 + demo/multi_ledger_config.yml | 10 +++++----- docker/Dockerfile.bdd | 1 + 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/demo/features/0586-sign-transaction.feature b/demo/features/0586-sign-transaction.feature index 1a66ef69ab..261800926d 100644 --- a/demo/features/0586-sign-transaction.feature +++ b/demo/features/0586-sign-transaction.feature @@ -25,6 +25,7 @@ Feature: RFC 0586 Aries sign (endorse) transactions functions | --mediation | --mediation | driverslicense | | --multitenant | --multitenant | driverslicense | | --mediation --multitenant | --mediation --multitenant | driverslicense | + | --multitenant --multi-ledger | --multitenant --multi-ledger | driverslicense | @T001.1-RFC0586 @GHA diff --git a/demo/multi_ledger_config.yml b/demo/multi_ledger_config.yml index 88694188c9..c3d19f1cc9 100644 --- a/demo/multi_ledger_config.yml +++ b/demo/multi_ledger_config.yml @@ -1,10 +1,10 @@ -- id: local +#- id: local +# is_production: true +# genesis_url: 'http://$LEDGER_HOST:9000/genesis' +- id: bcorvinTest is_production: true is_write: true - genesis_url: 'http://$LEDGER_HOST:9000/genesis' -#- id: bcorvinTest -# is_production: true -# genesis_url: 'http://dev.bcovrin.vonx.io/genesis' + genesis_url: 'http://dev.bcovrin.vonx.io/genesis' - id: greenlightTest is_production: true genesis_url: 'http://dev.greenlight.bcovrin.vonx.io/genesis' diff --git a/docker/Dockerfile.bdd b/docker/Dockerfile.bdd index cacf38b6aa..f821d813ec 100644 --- a/docker/Dockerfile.bdd +++ b/docker/Dockerfile.bdd @@ -4,5 +4,6 @@ FROM faber-alice-demo RUN pip3 install --no-cache-dir -r demo/requirements.behave.txt WORKDIR ./demo +ADD demo ./demo RUN chmod a+w . ENTRYPOINT ["behave"] From 3e8802c53539aae8954f58ceaa8e6c67339f0cec Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Tue, 22 Nov 2022 17:19:23 -0800 Subject: [PATCH 576/872] Fix multi-leg config Signed-off-by: Ian Costanzo --- demo/multi_ledger_config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/demo/multi_ledger_config.yml b/demo/multi_ledger_config.yml index c3d19f1cc9..b4b07d1c64 100644 --- a/demo/multi_ledger_config.yml +++ b/demo/multi_ledger_config.yml @@ -4,7 +4,7 @@ - id: bcorvinTest is_production: true is_write: true - genesis_url: 'http://dev.bcovrin.vonx.io/genesis' + genesis_url: 'http://test.bcovrin.vonx.io/genesis' - id: greenlightTest is_production: true - genesis_url: 'http://dev.greenlight.bcovrin.vonx.io/genesis' + genesis_url: 'http://test.greenlight.bcovrin.vonx.io/genesis' From 24c9fb3cd5eeb1cc3f24fdd7d820b0f36cb1aa33 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Tue, 22 Nov 2022 17:20:14 -0800 Subject: [PATCH 577/872] Fix multi-leg config Signed-off-by: Ian Costanzo --- demo/multi_ledger_config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/multi_ledger_config.yml b/demo/multi_ledger_config.yml index b4b07d1c64..2c423b8d51 100644 --- a/demo/multi_ledger_config.yml +++ b/demo/multi_ledger_config.yml @@ -7,4 +7,4 @@ genesis_url: 'http://test.bcovrin.vonx.io/genesis' - id: greenlightTest is_production: true - genesis_url: 'http://test.greenlight.bcovrin.vonx.io/genesis' + genesis_url: 'http://dev.greenlight.bcovrin.vonx.io/genesis' From 80049e7e8ffac75b63b69970cb5c4e4c1031bc46 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Wed, 23 Nov 2022 06:27:38 -0800 Subject: [PATCH 578/872] Formatting Signed-off-by: Ian Costanzo --- aries_cloudagent/core/conductor.py | 1 + 1 file changed, 1 insertion(+) diff --git a/aries_cloudagent/core/conductor.py b/aries_cloudagent/core/conductor.py index d65133981b..9f03e8baf1 100644 --- a/aries_cloudagent/core/conductor.py +++ b/aries_cloudagent/core/conductor.py @@ -28,6 +28,7 @@ from ..config.wallet import wallet_config from ..core.profile import Profile from ..indy.verifier import IndyVerifier + # from ..ledger.base import BaseLedger from ..ledger.error import LedgerConfigError, LedgerTransactionError from ..ledger.multiple_ledger.base_manager import ( From f9867fdda5bc2662e999b9a32d54876a50cff9fb Mon Sep 17 00:00:00 2001 From: shaangill025 Date: Wed, 23 Nov 2022 10:50:37 -0800 Subject: [PATCH 579/872] fix for ledger.pool_name Signed-off-by: shaangill025 --- aries_cloudagent/config/argparse.py | 41 ++++++++++++++++++++++------- aries_cloudagent/core/conductor.py | 3 --- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index 6822f72c2f..b372bb56ce 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -75,10 +75,8 @@ def create_argument_parser(*, prog: str = None): def load_argument_groups(parser: ArgumentParser, *groups: Type[ArgumentGroup]): """Log a set of argument groups into a parser. - Returns: A callable to convert loaded arguments into a settings dictionary - """ group_inst = [] for group in groups: @@ -872,32 +870,56 @@ def get_settings(self, args: Namespace) -> dict: if args.no_ledger: settings["ledger.disabled"] = True else: - configured = False + single_configured = False + multi_configured = False + update_pool_name = False if args.genesis_url: settings["ledger.genesis_url"] = args.genesis_url - configured = True + single_configured = True elif args.genesis_file: settings["ledger.genesis_file"] = args.genesis_file - configured = True + single_configured = True elif args.genesis_transactions: settings["ledger.genesis_transactions"] = args.genesis_transactions - configured = True + single_configured = True if args.genesis_transactions_list: with open(args.genesis_transactions_list, "r") as stream: txn_config_list = yaml.safe_load(stream) ledger_config_list = [] for txn_config in txn_config_list: ledger_config_list.append(txn_config) + if "is_write" in txn_config and txn_config["is_write"]: + if "genesis_url" in txn_config: + settings["ledger.genesis_url"] = txn_config[ + "genesis_url" + ] + elif "genesis_file" in txn_config: + settings["ledger.genesis_file"] = txn_config[ + "genesis_file" + ] + elif "genesis_transactions" in txn_config: + settings["ledger.genesis_transactions"] = txn_config[ + "genesis_transactions" + ] + else: + raise ArgsParseError( + "No genesis information provided for write ledger" + ) + if "id" in txn_config: + settings["ledger.pool_name"] = txn_config["id"] + update_pool_name = True settings["ledger.ledger_config_list"] = ledger_config_list - configured = True - if not configured: + multi_configured = True + if not (single_configured or multi_configured): raise ArgsParseError( "One of --genesis-url --genesis-file, --genesis-transactions " "or --genesis-transactions-list must be specified (unless " "--no-ledger is specified to explicitly configure aca-py to" " run with no ledger)." ) - if args.ledger_pool_name: + if single_configured and multi_configured: + raise ArgsParseError("Cannot configure both single- and multi-ledger.") + if args.ledger_pool_name and not update_pool_name: settings["ledger.pool_name"] = args.ledger_pool_name if args.ledger_keepalive: settings["ledger.keepalive"] = args.ledger_keepalive @@ -1327,7 +1349,6 @@ def get_settings(self, args: Namespace): class MediationInviteGroup(ArgumentGroup): """ Mediation invitation settings. - These can be provided at provision- and start-time. """ diff --git a/aries_cloudagent/core/conductor.py b/aries_cloudagent/core/conductor.py index c24862a4e9..eb79c819a2 100644 --- a/aries_cloudagent/core/conductor.py +++ b/aries_cloudagent/core/conductor.py @@ -28,7 +28,6 @@ from ..config.wallet import wallet_config from ..core.profile import Profile from ..indy.verifier import IndyVerifier -from ..ledger.base import BaseLedger from ..ledger.error import LedgerConfigError, LedgerTransactionError from ..ledger.multiple_ledger.base_manager import ( BaseMultipleLedgerManager, @@ -144,7 +143,6 @@ async def setup(self): self.root_profile.BACKEND_NAME == "askar" and ledger.BACKEND_NAME == "indy-vdr" ): - context.injector.bind_instance(BaseLedger, ledger) context.injector.bind_provider( IndyVerifier, ClassProvider( @@ -156,7 +154,6 @@ async def setup(self): self.root_profile.BACKEND_NAME == "indy" and ledger.BACKEND_NAME == "indy" ): - context.injector.bind_instance(BaseLedger, ledger) context.injector.bind_provider( IndyVerifier, ClassProvider( From 53b413b451aa12b3b18ea81a577fa1e9e035ba00 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Wed, 23 Nov 2022 13:30:38 -0800 Subject: [PATCH 580/872] Comment formatting: Signed-off-by: Ian Costanzo --- aries_cloudagent/config/argparse.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index b372bb56ce..d19bff39c6 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -74,9 +74,12 @@ def create_argument_parser(*, prog: str = None): def load_argument_groups(parser: ArgumentParser, *groups: Type[ArgumentGroup]): - """Log a set of argument groups into a parser. + """ + Log a set of argument groups into a parser. + Returns: A callable to convert loaded arguments into a settings dictionary + """ group_inst = [] for group in groups: @@ -1349,6 +1352,7 @@ def get_settings(self, args: Namespace): class MediationInviteGroup(ArgumentGroup): """ Mediation invitation settings. + These can be provided at provision- and start-time. """ From 724aceb529ee1dfce437fbfe54dc371fcb0b1fd4 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Wed, 23 Nov 2022 13:58:40 -0800 Subject: [PATCH 581/872] Integration test for the multi/multi scenario Signed-off-by: Ian Costanzo --- demo/features/taa-txn-author-acceptance.feature | 2 ++ demo/multi_ledger_config_bdd.yml | 11 +++++++++++ docker/Dockerfile.bdd | 2 +- 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 demo/multi_ledger_config_bdd.yml diff --git a/demo/features/taa-txn-author-acceptance.feature b/demo/features/taa-txn-author-acceptance.feature index 55d5790227..ac8274c1ec 100644 --- a/demo/features/taa-txn-author-acceptance.feature +++ b/demo/features/taa-txn-author-acceptance.feature @@ -17,6 +17,8 @@ Feature: TAA Transaction Author Agreement related tests | --taa-accept | driverslicense | | --taa-accept --multitenant | driverslicense | | --taa-accept --revocation | driverslicense | + | --taa-accept --multi-ledger | driverslicense | + | --taa-accept --multitenant --multi-ledger | driverslicense | @T001a-TAA @taa_required Scenario Outline: accept the ledger TAA and write to the ledger via endorser diff --git a/demo/multi_ledger_config_bdd.yml b/demo/multi_ledger_config_bdd.yml new file mode 100644 index 0000000000..14f7919fe1 --- /dev/null +++ b/demo/multi_ledger_config_bdd.yml @@ -0,0 +1,11 @@ +- id: local + is_production: true + is_write: true + genesis_url: 'http://$LEDGER_HOST:9000/genesis' +- id: bcorvinTest + is_production: true +# is_write: true + genesis_url: 'http://test.bcovrin.vonx.io/genesis' +- id: greenlightTest + is_production: true + genesis_url: 'http://dev.greenlight.bcovrin.vonx.io/genesis' diff --git a/docker/Dockerfile.bdd b/docker/Dockerfile.bdd index f821d813ec..d2e6c2098a 100644 --- a/docker/Dockerfile.bdd +++ b/docker/Dockerfile.bdd @@ -4,6 +4,6 @@ FROM faber-alice-demo RUN pip3 install --no-cache-dir -r demo/requirements.behave.txt WORKDIR ./demo -ADD demo ./demo +ADD demo/multi_ledger_config_bdd.yml ./demo/multi_ledger_config.yml RUN chmod a+w . ENTRYPOINT ["behave"] From a1eb27f7dbd2bf4f664be29c4d8181ed087ef3ba Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Thu, 24 Nov 2022 13:16:12 -0800 Subject: [PATCH 582/872] Dock updates for json demo Signed-off-by: Ian Costanzo --- demo/AliceWantsAJsonCredential.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/demo/AliceWantsAJsonCredential.md b/demo/AliceWantsAJsonCredential.md index 8f37bbbc47..e18a117abc 100644 --- a/demo/AliceWantsAJsonCredential.md +++ b/demo/AliceWantsAJsonCredential.md @@ -29,10 +29,14 @@ Open up a second shell (so you have 2 shells open in the `demo` directory) and i Note that you start the `faber` agent with AIP2.0 options. (When you specify `--cred-type json-ld` faber will set aip to `20` automatically, so the `--aip` option is not strictly required.) +Also note that the above will only work with the `/issue-credential-2.0/create-offer` endpoint. If you want to use the `/issue-credential-2.0/send` endpoint - which automates each step of the credential exchange - you will need to include the `--no-auto` option when starting each of the alice and faber agents (since the alice and faber controllers *also* automatically respond to each step in the credential exchange). + (Alternately you can run run Alice and Faber agents locally, see the `./faber-local.sh` and `./alice-local.sh` scripts in the `demo` directory.) Copy the "invitation" json text from the Faber shell and paste into the Alice shell to establish a connection between the two agents. +(If you are running with `--no-auto` you will also need to call the `/connections/accept-invitation` endpoint in alice's admin api swagger page.) + Now open up two browser windows to the [Faber](http://localhost:8021/api/doc) and [Alice](http://localhost:8031/api/doc) admin api swagger pages. Using the Faber admin api, you have to create a DID with the appropriate: @@ -78,7 +82,9 @@ Congradulations, you are now ready to start issuing JSON-LD credentials! - You have created a (non-public) DID for Faber to use to sign/issue the credentials - you will need to copy the DID that you created above into the examples below (as `issuer`). - You have created a (non-public) DID for Alice to use as her `credentialSubject.id` - this is required for Alice to sign the proof (the `credentialSubject.id` is not required, but then the provided presentation can't be verified). -To issue a credential, use the `/issue-credential-2.0/send` (or `/issue-credential-2.0/create-offer`) endpoint, you can test with this example payload (just replace the "connection_id", "issuer" key, "credentialSubject.id" and "proofType" with appropriate values: +To issue a credential, use the `/issue-credential-2.0/send` (or `/issue-credential-2.0/create-offer`) endpoint. As mentioned above, if you are using the `/send` endpoint, you need to make sure to include `--no-auto` when starting both of the agents. + +You can test with this example payload (just replace the "connection_id", "issuer" key, "credentialSubject.id" and "proofType" with appropriate values: ``` { @@ -174,6 +180,8 @@ To see the issued credential, call the `/credentials/w3c` endpoint on Alice's ad } ``` +If you *don't* see the credential in your wallet, look up the credential exchange record (in alice's admin api - `/issue-credential-2.0/records`) and check the state. If the state is `credential-received`, then the credential has been received but not stored, in this case just call the `/store` endpoint for this credential exchange. + ## Building More Realistic JSON-LD Credentials From 821030237158f44baf88c053a8ba819fb5205529 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Thu, 24 Nov 2022 15:32:11 -0800 Subject: [PATCH 583/872] Wordsmithing Signed-off-by: Ian Costanzo --- demo/AliceWantsAJsonCredential.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/AliceWantsAJsonCredential.md b/demo/AliceWantsAJsonCredential.md index e18a117abc..82c83844da 100644 --- a/demo/AliceWantsAJsonCredential.md +++ b/demo/AliceWantsAJsonCredential.md @@ -82,7 +82,7 @@ Congradulations, you are now ready to start issuing JSON-LD credentials! - You have created a (non-public) DID for Faber to use to sign/issue the credentials - you will need to copy the DID that you created above into the examples below (as `issuer`). - You have created a (non-public) DID for Alice to use as her `credentialSubject.id` - this is required for Alice to sign the proof (the `credentialSubject.id` is not required, but then the provided presentation can't be verified). -To issue a credential, use the `/issue-credential-2.0/send` (or `/issue-credential-2.0/create-offer`) endpoint. As mentioned above, if you are using the `/send` endpoint, you need to make sure to include `--no-auto` when starting both of the agents. +To issue a credential, use the `/issue-credential-2.0/create-offer` endpoint. (You can also use the `/issue-credential-2.0/send`) endpoint, if, as mentioned above, you have included the `--no-auto` when starting both of the agents.) You can test with this example payload (just replace the "connection_id", "issuer" key, "credentialSubject.id" and "proofType" with appropriate values: From 05cf6c19ad891aacecf488876c2bacc449d1dd88 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Fri, 25 Nov 2022 10:18:06 -0800 Subject: [PATCH 584/872] Special handling for the write ledger Signed-off-by: Ian Costanzo --- aries_cloudagent/core/conductor.py | 3 ++ .../ledger/multiple_ledger/indy_manager.py | 10 ++++-- .../multiple_ledger/manager_provider.py | 33 ++++++++++--------- 3 files changed, 28 insertions(+), 18 deletions(-) diff --git a/aries_cloudagent/core/conductor.py b/aries_cloudagent/core/conductor.py index 9dfe8f8ab2..8b753732a9 100644 --- a/aries_cloudagent/core/conductor.py +++ b/aries_cloudagent/core/conductor.py @@ -28,6 +28,7 @@ from ..config.wallet import wallet_config from ..core.profile import Profile from ..indy.verifier import IndyVerifier +# from ..ledger.base import BaseLedger from ..ledger.error import LedgerConfigError, LedgerTransactionError from ..ledger.multiple_ledger.base_manager import ( @@ -144,6 +145,7 @@ async def setup(self): self.root_profile.BACKEND_NAME == "askar" and ledger.BACKEND_NAME == "indy-vdr" ): + # context.injector.bind_instance(BaseLedger, ledger) context.injector.bind_provider( IndyVerifier, ClassProvider( @@ -155,6 +157,7 @@ async def setup(self): self.root_profile.BACKEND_NAME == "indy" and ledger.BACKEND_NAME == "indy" ): + # context.injector.bind_instance(BaseLedger, ledger) context.injector.bind_provider( IndyVerifier, ClassProvider( diff --git a/aries_cloudagent/ledger/multiple_ledger/indy_manager.py b/aries_cloudagent/ledger/multiple_ledger/indy_manager.py index 121c745cb5..44257dfdfa 100644 --- a/aries_cloudagent/ledger/multiple_ledger/indy_manager.py +++ b/aries_cloudagent/ledger/multiple_ledger/indy_manager.py @@ -53,7 +53,11 @@ def __init__( async def get_write_ledger(self) -> Optional[Tuple[str, IndySdkLedger]]: """Return the write IndySdkLedger instance.""" - return self.write_ledger_info + # return self.write_ledger_info + if self.write_ledger_info: + return (self.write_ledger_info[0], self.profile.inject_or(BaseLedger)) + else: + return None async def get_prod_ledgers(self) -> Mapping: """Return production ledgers mapping.""" @@ -83,7 +87,9 @@ async def _get_ledger_by_did( """ try: indy_sdk_ledger = None - if ledger_id in self.production_ledgers: + if self.write_ledger_info and ledger_id == self.write_ledger_info[0]: + indy_sdk_ledger = self.get_write_ledger() + elif ledger_id in self.production_ledgers: indy_sdk_ledger = self.production_ledgers.get(ledger_id) else: indy_sdk_ledger = self.non_production_ledgers.get(ledger_id) diff --git a/aries_cloudagent/ledger/multiple_ledger/manager_provider.py b/aries_cloudagent/ledger/multiple_ledger/manager_provider.py index cfe6cc0a17..788fa2ebfe 100644 --- a/aries_cloudagent/ledger/multiple_ledger/manager_provider.py +++ b/aries_cloudagent/ledger/multiple_ledger/manager_provider.py @@ -84,24 +84,25 @@ def provide(self, settings: BaseSettings, injector: BaseInjector): pool_name = config.get("pool_name") ledger_is_production = config.get("is_production") ledger_is_write = config.get("is_write") - ledger_pool = pool_class( - pool_name, - keepalive=keepalive, - cache=cache, - genesis_transactions=genesis_transactions, - read_only=read_only, - socks_proxy=socks_proxy, - ) - ledger_instance = ledger_class( - pool=ledger_pool, - profile=self.root_profile, - ) if ledger_is_write: - write_ledger_info = (ledger_id, ledger_instance) - if ledger_is_production: - indy_sdk_production_ledgers[ledger_id] = ledger_instance + write_ledger_info = (ledger_id, None) else: - indy_sdk_non_production_ledgers[ledger_id] = ledger_instance + ledger_pool = pool_class( + pool_name, + keepalive=keepalive, + cache=cache, + genesis_transactions=genesis_transactions, + read_only=read_only, + socks_proxy=socks_proxy, + ) + ledger_instance = ledger_class( + pool=ledger_pool, + profile=self.root_profile, + ) + if ledger_is_production: + indy_sdk_production_ledgers[ledger_id] = ledger_instance + else: + indy_sdk_non_production_ledgers[ledger_id] = ledger_instance if settings.get_value("ledger.genesis_transactions"): ledger_instance = self.root_profile.inject_or(BaseLedger) ledger_id = "startup::" + ledger_instance.pool.name From 5a2baa59f62c66ec94af3b0a1d4c7c3c8aa60450 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Fri, 25 Nov 2022 13:34:14 -0800 Subject: [PATCH 585/872] Ledger files Signed-off-by: Ian Costanzo --- aries_cloudagent/core/conductor.py | 3 --- aries_cloudagent/ledger/multiple_ledger/indy_manager.py | 4 +++- aries_cloudagent/ledger/multiple_ledger/manager_provider.py | 4 +++- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/aries_cloudagent/core/conductor.py b/aries_cloudagent/core/conductor.py index 8b753732a9..9dfe8f8ab2 100644 --- a/aries_cloudagent/core/conductor.py +++ b/aries_cloudagent/core/conductor.py @@ -28,7 +28,6 @@ from ..config.wallet import wallet_config from ..core.profile import Profile from ..indy.verifier import IndyVerifier -# from ..ledger.base import BaseLedger from ..ledger.error import LedgerConfigError, LedgerTransactionError from ..ledger.multiple_ledger.base_manager import ( @@ -145,7 +144,6 @@ async def setup(self): self.root_profile.BACKEND_NAME == "askar" and ledger.BACKEND_NAME == "indy-vdr" ): - # context.injector.bind_instance(BaseLedger, ledger) context.injector.bind_provider( IndyVerifier, ClassProvider( @@ -157,7 +155,6 @@ async def setup(self): self.root_profile.BACKEND_NAME == "indy" and ledger.BACKEND_NAME == "indy" ): - # context.injector.bind_instance(BaseLedger, ledger) context.injector.bind_provider( IndyVerifier, ClassProvider( diff --git a/aries_cloudagent/ledger/multiple_ledger/indy_manager.py b/aries_cloudagent/ledger/multiple_ledger/indy_manager.py index 44257dfdfa..9016d12ec1 100644 --- a/aries_cloudagent/ledger/multiple_ledger/indy_manager.py +++ b/aries_cloudagent/ledger/multiple_ledger/indy_manager.py @@ -140,7 +140,9 @@ async def lookup_did_in_configured_ledgers( cache_key = f"did_ledger_id_resolver::{did}" if bool(cache_did and self.cache and await self.cache.get(cache_key)): cached_ledger_id = await self.cache.get(cache_key) - if cached_ledger_id in self.production_ledgers: + if self.write_ledger_info and cached_ledger_id == self.write_ledger_info[0]: + return self.get_write_ledger() + elif cached_ledger_id in self.production_ledgers: return (cached_ledger_id, self.production_ledgers.get(cached_ledger_id)) elif cached_ledger_id in self.non_production_ledgers: return ( diff --git a/aries_cloudagent/ledger/multiple_ledger/manager_provider.py b/aries_cloudagent/ledger/multiple_ledger/manager_provider.py index 788fa2ebfe..7a40c2530f 100644 --- a/aries_cloudagent/ledger/multiple_ledger/manager_provider.py +++ b/aries_cloudagent/ledger/multiple_ledger/manager_provider.py @@ -102,7 +102,9 @@ def provide(self, settings: BaseSettings, injector: BaseInjector): if ledger_is_production: indy_sdk_production_ledgers[ledger_id] = ledger_instance else: - indy_sdk_non_production_ledgers[ledger_id] = ledger_instance + indy_sdk_non_production_ledgers[ + ledger_id + ] = ledger_instance if settings.get_value("ledger.genesis_transactions"): ledger_instance = self.root_profile.inject_or(BaseLedger) ledger_id = "startup::" + ledger_instance.pool.name From a2e7f9e3e5fd8b7fbea58b4dcd7d6f15ef103957 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Fri, 25 Nov 2022 14:44:00 -0800 Subject: [PATCH 586/872] fix: Correct typo in model -- required spelled incorrectly Signed-off-by: Stephen Curran Signed-off-by: Stephen Curran Signed-off-by: Stephen Curran --- aries_cloudagent/indy/models/cred_request.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aries_cloudagent/indy/models/cred_request.py b/aries_cloudagent/indy/models/cred_request.py index 5a022bebc0..92a80e14d6 100644 --- a/aries_cloudagent/indy/models/cred_request.py +++ b/aries_cloudagent/indy/models/cred_request.py @@ -44,7 +44,7 @@ class Meta: unknown = EXCLUDE prover_did = fields.Str( - requred=True, + required=True, description="Prover DID", **INDY_DID, ) From e89cac743aaf0b3eac0df1787ababdf052cd2bc3 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Mon, 28 Nov 2022 08:49:52 -0800 Subject: [PATCH 587/872] Add missing reference Signed-off-by: Ian Costanzo --- aries_cloudagent/ledger/multiple_ledger/indy_manager.py | 1 + 1 file changed, 1 insertion(+) diff --git a/aries_cloudagent/ledger/multiple_ledger/indy_manager.py b/aries_cloudagent/ledger/multiple_ledger/indy_manager.py index 9016d12ec1..1b47d89866 100644 --- a/aries_cloudagent/ledger/multiple_ledger/indy_manager.py +++ b/aries_cloudagent/ledger/multiple_ledger/indy_manager.py @@ -9,6 +9,7 @@ from ...cache.base import BaseCache from ...core.profile import Profile +from ...ledger.base import BaseLedger from ...ledger.error import LedgerError from ...wallet.crypto import did_is_self_certified From 8699b1dac866325cf437b6cc4e7d6bb3af2f7345 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Mon, 28 Nov 2022 10:10:37 -0800 Subject: [PATCH 588/872] Test for write ledger Signed-off-by: Ian Costanzo --- aries_cloudagent/ledger/multiple_ledger/indy_manager.py | 4 +++- .../ledger/multiple_ledger/tests/test_indy_manager.py | 8 +++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/aries_cloudagent/ledger/multiple_ledger/indy_manager.py b/aries_cloudagent/ledger/multiple_ledger/indy_manager.py index 1b47d89866..e8f86e8577 100644 --- a/aries_cloudagent/ledger/multiple_ledger/indy_manager.py +++ b/aries_cloudagent/ledger/multiple_ledger/indy_manager.py @@ -89,7 +89,9 @@ async def _get_ledger_by_did( try: indy_sdk_ledger = None if self.write_ledger_info and ledger_id == self.write_ledger_info[0]: - indy_sdk_ledger = self.get_write_ledger() + indy_sdk_ledger = await self.get_write_ledger() + if indy_sdk_ledger: + indy_sdk_ledger = indy_sdk_ledger[1] elif ledger_id in self.production_ledgers: indy_sdk_ledger = self.production_ledgers.get(ledger_id) else: diff --git a/aries_cloudagent/ledger/multiple_ledger/tests/test_indy_manager.py b/aries_cloudagent/ledger/multiple_ledger/tests/test_indy_manager.py index 5dac56e7ed..de629b2d78 100644 --- a/aries_cloudagent/ledger/multiple_ledger/tests/test_indy_manager.py +++ b/aries_cloudagent/ledger/multiple_ledger/tests/test_indy_manager.py @@ -11,6 +11,7 @@ from ....cache.base import BaseCache from ....cache.in_memory import InMemoryCache from ....core.in_memory import InMemoryProfile +from ....ledger.base import BaseLedger from ....messaging.responder import BaseResponder from ...error import LedgerError @@ -36,6 +37,7 @@ async def setUp(self): IndySdkLedgerPool("test_prod_1", checked=True), self.profile ) test_write_ledger = ("test_prod_1", test_prod_ledger) + self.context.injector.bind_instance(BaseLedger, test_prod_ledger) self.production_ledger["test_prod_1"] = test_prod_ledger self.production_ledger["test_prod_2"] = IndySdkLedger( IndySdkLedgerPool("test_prod_2", checked=True), self.profile @@ -385,13 +387,13 @@ async def test_lookup_did_in_configured_ledgers_prod_not_cached( async def test_lookup_did_in_configured_ledgers_cached_prod_ledger(self): cache = InMemoryCache() - await cache.set("did_ledger_id_resolver::Av63wJYM7xYR4AiygYq4c3", "test_prod_1") + await cache.set("did_ledger_id_resolver::Av63wJYM7xYR4AiygYq4c3", "test_prod_2") self.profile.context.injector.bind_instance(BaseCache, cache) (ledger_id, ledger_inst,) = await self.manager.lookup_did_in_configured_ledgers( "Av63wJYM7xYR4AiygYq4c3", cache_did=True ) - assert ledger_id == "test_prod_1" - assert ledger_inst.pool.name == "test_prod_1" + assert ledger_id == "test_prod_2" + assert ledger_inst.pool.name == "test_prod_2" async def test_lookup_did_in_configured_ledgers_cached_non_prod_ledger(self): cache = InMemoryCache() From 93cbd2a22ccb5ca9b34735ef8744f4ab6a7bd756 Mon Sep 17 00:00:00 2001 From: Micah Peltier Date: Mon, 28 Nov 2022 11:55:48 -0700 Subject: [PATCH 589/872] fix: save metadata on OOB invitation with public DID Signed-off-by: Micah Peltier --- aries_cloudagent/protocols/out_of_band/v1_0/manager.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py index 30e51e1e99..7737891d3a 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py @@ -258,6 +258,12 @@ async def create_invitation( async with self.profile.session() as session: await conn_rec.save(session, reason="Created new invitation") await conn_rec.attach_invitation(session, invi_msg) + + await conn_rec.attach_invitation(session, invi_msg) + + if metadata: + for key, value in metadata.items(): + await conn_rec.metadata_set(session, key, value) else: our_service = ServiceDecorator( recipient_keys=[our_recipient_key], From 0eb63e0d7f41775eed52796c22f534f0a1f36733 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Fri, 2 Dec 2022 11:45:36 -0500 Subject: [PATCH 590/872] fix: remove no longer needed files Signed-off-by: Daniel Bluhm --- docker/Dockerfile.test-indy | 26 ------------------- docker/Makefile | 51 ------------------------------------- 2 files changed, 77 deletions(-) delete mode 100644 docker/Dockerfile.test-indy delete mode 100644 docker/Makefile diff --git a/docker/Dockerfile.test-indy b/docker/Dockerfile.test-indy deleted file mode 100644 index 6b38d885e6..0000000000 --- a/docker/Dockerfile.test-indy +++ /dev/null @@ -1,26 +0,0 @@ -ARG python_version=3.6.13 -ARG indy_version=1.16.0 -ARG base_image=ghcr.io/hyperledger/indy-python:py${python_version}-${indy_version} -FROM ${base_image} - -USER indy - -RUN mkdir src test-reports - -WORKDIR /home/indy/src - -RUN mkdir -p test-reports && chown -R indy:indy test-reports && chmod -R ug+rw test-reports - -ADD requirements*.txt ./ - -USER root -RUN pip3 install --no-cache-dir \ - -r requirements.txt \ - -r requirements.askar.txt \ - -r requirements.bbs.txt \ - -r requirements.dev.txt - -ADD --chown=indy:root . . -USER indy - -ENTRYPOINT ["/bin/bash", "-c", "pytest \"$@\"", "--"] diff --git a/docker/Makefile b/docker/Makefile deleted file mode 100644 index c5ecf7c398..0000000000 --- a/docker/Makefile +++ /dev/null @@ -1,51 +0,0 @@ -# A simple makefile purely to demonstrate building the new docker images - -CONTAINER_RUNTIME ?= docker -IMAGE_NAME=ghcr.io/hyperledger/aries-cloudagent-python -PYTHON_VERSION=3.6.13 -PYTHON_VERSION_MAJ_MIN=3.6 -RUST_VERSION=1.46 -ACAPY_VERSION=0.7.4 -ACAPY_REQS=[askar,bbs] -INDY_VERSION=1.16.0 -INDY_SDK_URL=https://codeload.github.com/hyperledger/indy-sdk/tar.gz/refs/tags/v$(INDY_VERSION) -INDY_IMAGE_NAME=ghcr.io/hyperledger/indy-python - -all: indy-python indy standard - -indy-python: - $(CONTAINER_RUNTIME) build -t $(INDY_IMAGE_NAME):latest \ - --build-arg python_version=$(PYTHON_VERSION) \ - --build-arg rust_version=$(RUST_VERSION) \ - --build-arg indy_version=$(INDY_VERSION) \ - --build-arg indy_sdk_url=$(INDY_SDK_URL) \ - -f Dockerfile.indy-base . - $(CONTAINER_RUNTIME) tag $(INDY_IMAGE_NAME):latest \ - $(INDY_IMAGE_NAME):py$(PYTHON_VERSION)-$(INDY_VERSION) - $(CONTAINER_RUNTIME) tag $(INDY_IMAGE_NAME):latest \ - $(INDY_IMAGE_NAME):py$(PYTHON_VERSION_MAJ_MIN)-$(INDY_VERSION) - -indy: - $(CONTAINER_RUNTIME) build -t $(IMAGE_NAME):indy-latest \ - --build-arg python_version=$(PYTHON_VERSION) \ - --build-arg indy_version=$(INDY_VERSION) \ - --build-arg acapy_version=$(ACAPY_VERSION) \ - --build-arg acapy_reqs=$(ACAPY_REQS) \ - -f Dockerfile.indy . - $(CONTAINER_RUNTIME) tag $(IMAGE_NAME):indy-latest \ - $(IMAGE_NAME):py$(PYTHON_VERSION)-indy-$(INDY_VERSION)-$(ACAPY_VERSION) - $(CONTAINER_RUNTIME) tag $(IMAGE_NAME):indy-latest \ - $(IMAGE_NAME):py$(PYTHON_VERSION_MAJ_MIN)-indy-$(INDY_VERSION)-$(ACAPY_VERSION) - -standard: - $(CONTAINER_RUNTIME) build -t $(IMAGE_NAME):latest \ - --build-arg python_version=$(PYTHON_VERSION) \ - --build-arg acapy_version=$(ACAPY_VERSION) \ - --build-arg acapy_reqs=$(ACAPY_REQS) \ - -f Dockerfile . - $(CONTAINER_RUNTIME) tag $(IMAGE_NAME):latest \ - $(IMAGE_NAME):py$(PYTHON_VERSION)-$(ACAPY_VERSION) - $(CONTAINER_RUNTIME) tag $(IMAGE_NAME):latest \ - $(IMAGE_NAME):py$(PYTHON_VERSION_MAJ_MIN)-$(ACAPY_VERSION) - -.PHONY: all indy-python indy standard From a90e568572bf5346ac177d483f931b699a198cb7 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Fri, 2 Dec 2022 11:48:57 -0500 Subject: [PATCH 591/872] fix: paths in integrationtests worflow Signed-off-by: Daniel Bluhm --- .github/workflows/integrationtests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/integrationtests.yml b/.github/workflows/integrationtests.yml index 404f7c562a..454e579ebc 100644 --- a/.github/workflows/integrationtests.yml +++ b/.github/workflows/integrationtests.yml @@ -17,9 +17,9 @@ jobs: with: path: acapy #- name: run-von-network - # uses: ./acapy/actions/run-von-network + # uses: ./acapy/.github/actions/run-von-network #- name: run-indy-tails-server - # uses: ./acapy/actions/run-indy-tails-server + # uses: ./acapy/.github/actions/run-indy-tails-server - name: run-integration-tests uses: ./acapy/.github/actions/run-integration-tests # to run with a specific set of tests include the following parameter: From 9baf15c888c9b80e44d17dc96b83bbb79d06457f Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Fri, 2 Dec 2022 12:15:07 -0500 Subject: [PATCH 592/872] fix: run python 3.6 on ubuntu-20.04 Signed-off-by: Daniel Bluhm --- .github/workflows/nightly-tests.yml | 14 ++++++++++++-- .github/workflows/pr-tests.yml | 2 ++ .github/workflows/tests-indy.yml | 5 ++++- .github/workflows/tests.yml | 5 ++++- 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/.github/workflows/nightly-tests.yml b/.github/workflows/nightly-tests.yml index 72e51140fb..e9b6d5d77a 100644 --- a/.github/workflows/nightly-tests.yml +++ b/.github/workflows/nightly-tests.yml @@ -11,19 +11,29 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"] + os: ["ubuntu-latest"] + python-version: ["3.7", "3.8", "3.9", "3.10"] + include: + - os: "ubuntu-20.04" + python-version: "3.6" uses: ./.github/workflows/tests.yml with: python-version: ${{ matrix.python-version }} + os: ${{ matrix.os }} tests-indy: name: Tests (Indy) strategy: fail-fast: false matrix: - python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"] + os: ["ubuntu-latest"] + python-version: ["3.7", "3.8", "3.9", "3.10"] + include: + - os: "ubuntu-20.04" + python-version: "3.6" uses: ./.github/workflows/tests-indy.yml with: python-version: ${{ matrix.python-version }} + os: ${{ matrix.os }} indy-version: "1.16.0" diff --git a/.github/workflows/pr-tests.yml b/.github/workflows/pr-tests.yml index 228ec7dd7f..851ea9cf35 100644 --- a/.github/workflows/pr-tests.yml +++ b/.github/workflows/pr-tests.yml @@ -9,6 +9,7 @@ jobs: uses: ./.github/workflows/tests.yml with: python-version: "3.6" + os: "ubuntu-20.04" tests-indy: name: Tests (Indy) @@ -16,3 +17,4 @@ jobs: with: python-version: "3.6" indy-version: "1.16.0" + os: "ubuntu-20.04" diff --git a/.github/workflows/tests-indy.yml b/.github/workflows/tests-indy.yml index 3037354fd4..a893acf5b5 100644 --- a/.github/workflows/tests-indy.yml +++ b/.github/workflows/tests-indy.yml @@ -9,11 +9,14 @@ on: indy-version: required: true type: string + os: + required: true + type: string jobs: tests: name: Test Python ${{ inputs.python-version }} on Indy ${{ inputs.indy-version }} - runs-on: ubuntu-latest + runs-on: ${{ inputs.os }} steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 086f026172..e098b1dd3c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -6,11 +6,14 @@ on: python-version: required: true type: string + os: + required: true + type: string jobs: tests: name: Test Python ${{ inputs.python-version }} - runs-on: ubuntu-latest + runs-on: ${{ inputs.os }} steps: - uses: actions/checkout@v3 - name: Set up Python ${{ inputs.python-version }} From 024b0bef7fb0749394ba1eb362b1c717f3ebb315 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Fri, 2 Dec 2022 12:17:22 -0500 Subject: [PATCH 593/872] fix: use repo owner instead of actor as username This averts issues that occur in the hyperledger org to publish values to ghcr.io Signed-off-by: Daniel Bluhm --- .github/workflows/publish-indy.yml | 2 +- .github/workflows/publish.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish-indy.yml b/.github/workflows/publish-indy.yml index dd7c23cdc7..6fda9c50c5 100644 --- a/.github/workflows/publish-indy.yml +++ b/.github/workflows/publish-indy.yml @@ -52,7 +52,7 @@ jobs: uses: docker/login-action@v1 with: registry: ghcr.io - username: ${{ github.actor }} + username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - name: Setup Image Metadata (manual) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 9ecfc8df35..c56d4675f2 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -45,7 +45,7 @@ jobs: uses: docker/login-action@v1 with: registry: ghcr.io - username: ${{ github.actor }} + username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - name: Setup Image Metadata (manual) From a613242e85361750ac1bd3eb071af2bcf5e2b0b3 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Fri, 2 Dec 2022 12:36:47 -0500 Subject: [PATCH 594/872] fix: attempt to fix tagged images Signed-off-by: Daniel Bluhm --- .github/workflows/publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index c56d4675f2..8c4dfc56a7 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -56,7 +56,7 @@ jobs: images: | ghcr.io/${{ steps.info.outputs.repo-owner }}/aries-cloudagent-python tags: | - type=raw,value=${{ inputs.tag }} + type=raw,value=py${{ matrix.python-version }}-${{ inputs.tag }} - name: Setup Image Metadata (release) if: github.event_name == 'release' From 564a9af261c085670021f3913aceea7efcf96bb4 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Fri, 2 Dec 2022 12:43:36 -0500 Subject: [PATCH 595/872] fix: manually tagged indy image has python-version Signed-off-by: Daniel Bluhm --- .github/workflows/publish-indy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-indy.yml b/.github/workflows/publish-indy.yml index 6fda9c50c5..084380e386 100644 --- a/.github/workflows/publish-indy.yml +++ b/.github/workflows/publish-indy.yml @@ -63,7 +63,7 @@ jobs: images: | ghcr.io/${{ steps.info.outputs.repo-owner }}/aries-cloudagent-python tags: | - type=raw,value=${{ inputs.tag }} + type=raw,value=py${{ matrix.python-version }}-${{ inputs.tag }} - name: Setup Image Metadata (release) if: github.event_name == 'release' From dd7310fd5a67412f95ee506d86c39fc85ba693ca Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Fri, 2 Dec 2022 12:52:19 -0500 Subject: [PATCH 596/872] fix: clarifications in docker images readme Signed-off-by: Daniel Bluhm --- ContainerImagesAndGithubActions.md | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/ContainerImagesAndGithubActions.md b/ContainerImagesAndGithubActions.md index ec4a2f32db..25bfca9dc8 100644 --- a/ContainerImagesAndGithubActions.md +++ b/ContainerImagesAndGithubActions.md @@ -57,7 +57,7 @@ py3.8-indy-A.B.C-X.Y.Z | Indy | py3.8-indy-1.16.0-0.7.4 | Standard image v py3.9-indy-A.B.C-X.Y.Z | Indy | py3.9-indy-1.16.0-0.7.4 | Standard image variant built on Python 3.9 for ACA-Py version X.Y.Z and Indy SDK Version A.B.C | py3.10-indy-A.B.C-X.Y.Z | Indy | py3.10-indy-1.16.0-0.7.4 | Standard image variant built on Python 3.10 for ACA-Py version X.Y.Z and Indy SDK Version A.B.C | -### Key Image Differences +### Image Comparison There are several key differences that should be noted between the two image variants and between the BC Gov ACA-Py images. @@ -67,18 +67,15 @@ variants and between the BC Gov ACA-Py images. - Does **NOT** include `libindy` - Default user is `aries` - Uses container's system python environment rather than `pyenv` - - Askar and Indy Shared libraries are installed through pip from - pre-compiled binaries included in the python wrappers. + - Askar and Indy Shared libraries are installed as dependencies of ACA-Py through pip from pre-compiled binaries included in the python wrappers - Built from repo contents - Indy Image - Based on slim variant of Debian - - Based on `indy-python` + - Built from multi-stage build step (`indy-base` in the Dockerfile) which includes Indy dependencies; this could be replaced with an explicit `indy-python` image from the Indy SDK repo - Includes `libindy` but does **NOT** include the Indy CLI - Default user is `indy` - - Based on `indy-python` - Uses container's system python environment rather than `pyenv` - - Askar and Indy Shared libraries are installed through pip from - pre-compiled binaries included in the python wrappers + - Askar and Indy Shared libraries are installed as dependencies of ACA-Py through pip from pre-compiled binaries included in the python wrappers - Built from repo contents - Includes Indy postgres storage plugin - `bcgovimages/aries-cloudagent` From e9d07cf65120ff125f798cd27d457a73e9638631 Mon Sep 17 00:00:00 2001 From: Char Howland Date: Mon, 5 Dec 2022 18:40:33 -0700 Subject: [PATCH 597/872] fix: black code formatter Signed-off-by: Char Howland --- aries_cloudagent/protocols/issue_credential/v1_0/routes.py | 6 ++++-- aries_cloudagent/protocols/issue_credential/v2_0/routes.py | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/routes.py b/aries_cloudagent/protocols/issue_credential/v1_0/routes.py index 90853c598b..3cba79cb88 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/routes.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/routes.py @@ -382,8 +382,10 @@ async def credential_exchange_retrieve(request: web.BaseRequest): @docs( tags=["issue-credential v1.0"], - summary=("Create a credential record without " - "sending (generally for use with Out-Of-Band)"), + summary=( + "Create a credential record without " + "sending (generally for use with Out-Of-Band)" + ), ) @request_schema(V10CredentialCreateSchema()) @response_schema(V10CredentialExchangeSchema(), 200, description="") diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/routes.py b/aries_cloudagent/protocols/issue_credential/v2_0/routes.py index f35745f7ab..e3a066d339 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/routes.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/routes.py @@ -521,8 +521,10 @@ async def credential_exchange_retrieve(request: web.BaseRequest): @docs( tags=["issue-credential v2.0"], - summary=("Create a credential record without " - "sending (generally for use with Out-Of-Band)"), + summary=( + "Create a credential record without " + "sending (generally for use with Out-Of-Band)" + ), ) @request_schema(V20IssueCredSchemaCore()) @response_schema(V20CredExRecordSchema(), 200, description="") From e976f08351b2152cc1793fa6a2954fdce88aca57 Mon Sep 17 00:00:00 2001 From: Jean-Francois Blier <101359928+jfblier-amplitude@users.noreply.github.com> Date: Tue, 13 Dec 2022 06:06:22 -0500 Subject: [PATCH 598/872] #2041 - Issue JSON-LD has invalid Admin API documentation Signed-off-by: Jean-Francois Blier <101359928+jfblier-amplitude@users.noreply.github.com> --- demo/AliceWantsAJsonCredential.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/demo/AliceWantsAJsonCredential.md b/demo/AliceWantsAJsonCredential.md index 82c83844da..9626d18f21 100644 --- a/demo/AliceWantsAJsonCredential.md +++ b/demo/AliceWantsAJsonCredential.md @@ -35,7 +35,7 @@ Also note that the above will only work with the `/issue-credential-2.0/create-o Copy the "invitation" json text from the Faber shell and paste into the Alice shell to establish a connection between the two agents. -(If you are running with `--no-auto` you will also need to call the `/connections/accept-invitation` endpoint in alice's admin api swagger page.) +(If you are running with `--no-auto` you will also need to call the `/connections/{conn_id}/accept-invitation` endpoint in alice's admin api swagger page.) Now open up two browser windows to the [Faber](http://localhost:8021/api/doc) and [Alice](http://localhost:8031/api/doc) admin api swagger pages. @@ -82,7 +82,7 @@ Congradulations, you are now ready to start issuing JSON-LD credentials! - You have created a (non-public) DID for Faber to use to sign/issue the credentials - you will need to copy the DID that you created above into the examples below (as `issuer`). - You have created a (non-public) DID for Alice to use as her `credentialSubject.id` - this is required for Alice to sign the proof (the `credentialSubject.id` is not required, but then the provided presentation can't be verified). -To issue a credential, use the `/issue-credential-2.0/create-offer` endpoint. (You can also use the `/issue-credential-2.0/send`) endpoint, if, as mentioned above, you have included the `--no-auto` when starting both of the agents.) +To issue a credential, use the `/issue-credential-2.0/send-offer` endpoint. (You can also use the `/issue-credential-2.0/send`) endpoint, if, as mentioned above, you have included the `--no-auto` when starting both of the agents.) You can test with this example payload (just replace the "connection_id", "issuer" key, "credentialSubject.id" and "proofType" with appropriate values: @@ -336,7 +336,7 @@ In Alice's swagger page, submit the `/credentials/records/w3c` endpoint to see t ### Request Presentation Example -To request a proof, submit the following (with appropriate `connection_id`) to Faber's `/request-presentation-2.0/request-proof` endpoint: +To request a proof, submit the following (with appropriate `connection_id`) to Faber's `/present-proof-2.0/send-request` endpoint: ``` { @@ -408,7 +408,7 @@ To request a proof, submit the following (with appropriate `connection_id`) to F Note that the `is_holder` property can be used by Faber to verify that the holder of credential is the same as the subject of the attribute (`familyName`). Later on, the received presentation will be signed and verifiable only if `is_holder` with ` "directive": "required"` is included in the presentation request. -There are several ways that Alice can respond with a presentation. The simplest will just tell aca-py to put the presentation together and send it to Faber - submit the following to Alice's `/request-presentation-2.0/{pres_ex_id}/send-presentation`: +There are several ways that Alice can respond with a presentation. The simplest will just tell aca-py to put the presentation together and send it to Faber - submit the following to Alice's `/present-proof-2.0/records/{pres_ex_id}/send-presentation`: ``` { From 135767125631ff8f689394f546d1bb9d55dbe5c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dobros=C5=82aw=20=C5=BBybort?= Date: Wed, 23 Nov 2022 14:40:35 +0100 Subject: [PATCH 599/872] Add ability to set docker container name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Dobrosław Żybort --- scripts/run_docker | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/scripts/run_docker b/scripts/run_docker index 31703b7555..28b939be4c 100755 --- a/scripts/run_docker +++ b/scripts/run_docker @@ -27,13 +27,13 @@ for arg in "$@"; do echo "Backing up database before running aca-py upgrade is highly recommended. Do you wish to proceed" select yn in "Yes" "No"; do case $yn in - Yes) break ;; - No) exit ;; + Yes) break ;; + No) exit ;; esac done fi done -ACAPY_NETWORK_NAME="${NETWORK_NAME}" + if [ -n "${ENABLE_PTVSD}" ]; then ARGS="${ARGS} -e ENABLE_PTVSD=\"${ENABLE_PTVSD}\" -p $PTVSD_PORT:$PTVSD_PORT" fi @@ -48,13 +48,28 @@ if [ "$OSTYPE" == "msys" ]; then CONTAINER_RUNTIME="winpty docker" fi -RAND_NAME=$(env LC_ALL=C tr -dc 'a-zA-Z0-9' < /dev/urandom | fold -w 16 | head -n 1) -if [ -z "$NETWORK_NAME" ]; then - echo "No Docker network specified." - $CONTAINER_RUNTIME run --rm -ti --name "aries-cloudagent-runner_${RAND_NAME}" \ - $ARGS aries-cloudagent-run "$@" +ACAPY_CONTAINER_NAME="${CONTAINER_NAME}" +if [ -n "${CONTAINER_NAME}" ]; then + ARGS="${ARGS} --name ${CONTAINER_NAME}" +else + RAND_NAME=$(env LC_ALL=C tr -dc 'a-zA-Z0-9' Date: Thu, 8 Dec 2022 16:37:13 +0100 Subject: [PATCH 600/872] Address comments to run_docker MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Dobrosław Żybort --- scripts/run_docker | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/scripts/run_docker b/scripts/run_docker index 28b939be4c..b201c71c7e 100755 --- a/scripts/run_docker +++ b/scripts/run_docker @@ -61,11 +61,13 @@ echo "Container name: ${ACAPY_CONTAINER_NAME}" ACAPY_NETWORK_NAME="${NETWORK_NAME}" # Create new Docker network if it does not exist. -if [ -n "${NETWORK_NAME}" ] && [ -z "$(docker network ls --filter name=^"${NETWORK_NAME}"$ --format="{{ .Name }}")" ]; then - docker network create "${NETWORK_NAME}" -fi if [ -n "${NETWORK_NAME}" ]; then - echo "Docker network specified: ${ACAPY_NETWORK_NAME}" + if [ -z "$("$CONTAINER_RUNTIME" network ls --filter name=^"${NETWORK_NAME}"$ --format="{{ .Name }}")" ]; then + echo "Creating new Docker network: ${ACAPY_NETWORK_NAME}" + "$CONTAINER_RUNTIME" network create "${NETWORK_NAME}" + else + echo "Attaching to existing Docker network: ${ACAPY_NETWORK_NAME}" + fi ARGS="${ARGS} --network ${NETWORK_NAME}" else echo "No Docker network specified." From b4763c6164edd6e3eacd08b994e7bcad623859b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dobros=C5=82aw=20=C5=BBybort?= Date: Thu, 8 Dec 2022 16:38:46 +0100 Subject: [PATCH 601/872] Update run_docker MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Dobrosław Żybort --- scripts/run_docker | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/run_docker b/scripts/run_docker index b201c71c7e..af6e854ab5 100755 --- a/scripts/run_docker +++ b/scripts/run_docker @@ -60,8 +60,8 @@ echo "" echo "Container name: ${ACAPY_CONTAINER_NAME}" ACAPY_NETWORK_NAME="${NETWORK_NAME}" -# Create new Docker network if it does not exist. if [ -n "${NETWORK_NAME}" ]; then + # Create new Docker network if it does not exist. if [ -z "$("$CONTAINER_RUNTIME" network ls --filter name=^"${NETWORK_NAME}"$ --format="{{ .Name }}")" ]; then echo "Creating new Docker network: ${ACAPY_NETWORK_NAME}" "$CONTAINER_RUNTIME" network create "${NETWORK_NAME}" From b3421d8d583564c068901f16d80474a6b544ce8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dobros=C5=82aw=20=C5=BBybort?= Date: Tue, 13 Dec 2022 16:47:42 +0100 Subject: [PATCH 602/872] Avoid mixing many env variable names for one functionality MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Dobrosław Żybort --- scripts/run_docker | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/scripts/run_docker b/scripts/run_docker index af6e854ab5..3dbfcd3f3b 100755 --- a/scripts/run_docker +++ b/scripts/run_docker @@ -48,25 +48,23 @@ if [ "$OSTYPE" == "msys" ]; then CONTAINER_RUNTIME="winpty docker" fi -ACAPY_CONTAINER_NAME="${CONTAINER_NAME}" if [ -n "${CONTAINER_NAME}" ]; then ARGS="${ARGS} --name ${CONTAINER_NAME}" else RAND_NAME=$(env LC_ALL=C tr -dc 'a-zA-Z0-9' Date: Wed, 14 Dec 2022 10:36:19 -0700 Subject: [PATCH 603/872] fix: DIDMethod refs in tests Signed-off-by: Micah Peltier --- .../protocols/connections/v1_0/tests/test_manager.py | 8 ++++---- .../protocols/didexchange/v1_0/tests/test_manager.py | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py index 6b0b6171f3..9d651c7f87 100644 --- a/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py @@ -658,8 +658,8 @@ async def test_receive_request_public_did_conn_invite(self): recipient_did=self.test_did, recipient_did_public=True ) await session.wallet.create_local_did( - method=DIDMethod.SOV, - key_type=KeyType.ED25519, + method=SOV, + key_type=ED25519, seed=None, did=self.test_did, ) @@ -699,8 +699,8 @@ async def test_receive_request_public_did_unsolicited(self): recipient_did=self.test_did, recipient_did_public=True ) await session.wallet.create_local_did( - method=DIDMethod.SOV, - key_type=KeyType.ED25519, + method=SOV, + key_type=ED25519, seed=None, did=self.test_did, ) diff --git a/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py index f912290daa..05641b86a4 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py @@ -988,8 +988,8 @@ async def test_receive_request_implicit_public_did_not_enabled(self): await mediation_record.save(session) await session.wallet.create_local_did( - method=DIDMethod.SOV, - key_type=KeyType.ED25519, + method=SOV, + key_type=ED25519, seed=None, did=TestConfig.test_did, ) @@ -1051,8 +1051,8 @@ async def test_receive_request_implicit_public_did(self): await mediation_record.save(session) await session.wallet.create_local_did( - method=DIDMethod.SOV, - key_type=KeyType.ED25519, + method=SOV, + key_type=ED25519, seed=None, did=TestConfig.test_did, ) From c78dd829b2ea2624fd033e1334a2ab78223329c4 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Sun, 18 Dec 2022 15:51:11 -0800 Subject: [PATCH 604/872] Code formatting Signed-off-by: Ian Costanzo --- aries_cloudagent/protocols/issue_credential/v1_0/routes.py | 6 ++++-- aries_cloudagent/protocols/issue_credential/v2_0/routes.py | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/routes.py b/aries_cloudagent/protocols/issue_credential/v1_0/routes.py index 90853c598b..3cba79cb88 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/routes.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/routes.py @@ -382,8 +382,10 @@ async def credential_exchange_retrieve(request: web.BaseRequest): @docs( tags=["issue-credential v1.0"], - summary=("Create a credential record without " - "sending (generally for use with Out-Of-Band)"), + summary=( + "Create a credential record without " + "sending (generally for use with Out-Of-Band)" + ), ) @request_schema(V10CredentialCreateSchema()) @response_schema(V10CredentialExchangeSchema(), 200, description="") diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/routes.py b/aries_cloudagent/protocols/issue_credential/v2_0/routes.py index f35745f7ab..e3a066d339 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/routes.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/routes.py @@ -521,8 +521,10 @@ async def credential_exchange_retrieve(request: web.BaseRequest): @docs( tags=["issue-credential v2.0"], - summary=("Create a credential record without " - "sending (generally for use with Out-Of-Band)"), + summary=( + "Create a credential record without " + "sending (generally for use with Out-Of-Band)" + ), ) @request_schema(V20IssueCredSchemaCore()) @response_schema(V20CredExRecordSchema(), 200, description="") From 5d974d6a857f37c87b23359eb588f49a9576a566 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Humbert?= Date: Mon, 19 Dec 2022 08:56:41 +0100 Subject: [PATCH 605/872] fix: indy dependency version format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Clément Humbert --- requirements.indy.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.indy.txt b/requirements.indy.txt index a364cfe7dd..352d0b22bc 100644 --- a/requirements.indy.txt +++ b/requirements.indy.txt @@ -1 +1 @@ -python3-indy>=1.11.1<2 +python3-indy>=1.11.1,<2 From 47e97bcb1715fa192433de12075da56aa6289f5f Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Mon, 19 Dec 2022 21:06:49 -0500 Subject: [PATCH 606/872] fix: metadata action in publish workflows Signed-off-by: Daniel Bluhm --- .github/workflows/publish-indy.yml | 6 +++--- .github/workflows/publish.yml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/publish-indy.yml b/.github/workflows/publish-indy.yml index 084380e386..8761807d22 100644 --- a/.github/workflows/publish-indy.yml +++ b/.github/workflows/publish-indy.yml @@ -58,17 +58,17 @@ jobs: - name: Setup Image Metadata (manual) if: github.event_name == 'workflow_dispatch' id: dispatch-meta - uses: docker/metadata-action@v3 + uses: docker/metadata-action@v4.1.1 with: images: | ghcr.io/${{ steps.info.outputs.repo-owner }}/aries-cloudagent-python tags: | - type=raw,value=py${{ matrix.python-version }}-${{ inputs.tag }} + type=raw,value=py${{ matrix.python-version }}-indy-${{ inputs.tag }} - name: Setup Image Metadata (release) if: github.event_name == 'release' id: meta - uses: docker/metadata-action@v3 + uses: docker/metadata-action@v4.1.1 with: images: | ghcr.io/${{ steps.info.outputs.repo-owner }}/aries-cloudagent-python diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 8c4dfc56a7..b270b55a8a 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -51,7 +51,7 @@ jobs: - name: Setup Image Metadata (manual) if: github.event_name == 'workflow_dispatch' id: dispatch-meta - uses: docker/metadata-action@v3 + uses: docker/metadata-action@v4.1.1 with: images: | ghcr.io/${{ steps.info.outputs.repo-owner }}/aries-cloudagent-python @@ -61,7 +61,7 @@ jobs: - name: Setup Image Metadata (release) if: github.event_name == 'release' id: meta - uses: docker/metadata-action@v3 + uses: docker/metadata-action@v4.1.1 with: images: | ghcr.io/${{ steps.info.outputs.repo-owner }}/aries-cloudagent-python From 7492fa8e93660df3b5bde576458b7647910b69db Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Tue, 20 Dec 2022 11:17:19 -0800 Subject: [PATCH 607/872] Added some additional integration test cases Signed-off-by: Ian Costanzo --- demo/features/0586-sign-transaction.feature | 2 ++ 1 file changed, 2 insertions(+) diff --git a/demo/features/0586-sign-transaction.feature b/demo/features/0586-sign-transaction.feature index 261800926d..5069f2974e 100644 --- a/demo/features/0586-sign-transaction.feature +++ b/demo/features/0586-sign-transaction.feature @@ -26,6 +26,7 @@ Feature: RFC 0586 Aries sign (endorse) transactions functions | --multitenant | --multitenant | driverslicense | | --mediation --multitenant | --mediation --multitenant | driverslicense | | --multitenant --multi-ledger | --multitenant --multi-ledger | driverslicense | + | --multitenant --multi-ledger --revocation | --multitenant --multi-ledger --revocation | driverslicense | @T001.1-RFC0586 @GHA @@ -94,6 +95,7 @@ Feature: RFC 0586 Aries sign (endorse) transactions functions | --revocation --public-did --mediation | --revocation --mediation | driverslicense | Data_DL_NormalizedValues | | --revocation --public-did --multitenant | --revocation --multitenant | driverslicense | Data_DL_NormalizedValues | | --revocation --public-did --mediation --multitenant | --revocation --mediation --multitenant | driverslicense | Data_DL_NormalizedValues | + | --multitenant --multi-ledger --revocation --public-did | --multitenant --multi-ledger --revocation | driverslicense | Data_DL_NormalizedValues | @T002.1-RFC0586 @GHA Scenario Outline: endorse a schema and cred def transaction, write to the ledger, issue and revoke a credential, manually invoking each endorsement endpoint From 614fec0dd7276ba3669f9f6bfb4f096805e3d376 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Tue, 20 Dec 2022 16:14:31 -0800 Subject: [PATCH 608/872] Additional integration tests for revocation scenarios Signed-off-by: Ian Costanzo --- demo/bdd_support/agent_backchannel_client.py | 2 + demo/features/0454-present-proof.feature | 26 ++++++++++++- ...ge_over_19_v2_with_health_id_no_revoc.json | 24 ++++++++++++ ...n_DL_age_over_19_v2_with_health_id_r2.json | 24 ++++++++++++ ...ge_over_19_v2_with_health_id_no_revoc.json | 38 +++++++++++++++++++ ...t_DL_age_over_19_v2_with_health_id_r2.json | 38 +++++++++++++++++++ demo/features/steps/0454-present-proof.py | 14 +++++++ demo/runners/agent_container.py | 6 ++- 8 files changed, 168 insertions(+), 4 deletions(-) create mode 100644 demo/features/data/presentation_DL_age_over_19_v2_with_health_id_no_revoc.json create mode 100644 demo/features/data/presentation_DL_age_over_19_v2_with_health_id_r2.json create mode 100644 demo/features/data/proof_request_DL_age_over_19_v2_with_health_id_no_revoc.json create mode 100644 demo/features/data/proof_request_DL_age_over_19_v2_with_health_id_r2.json diff --git a/demo/bdd_support/agent_backchannel_client.py b/demo/bdd_support/agent_backchannel_client.py index 9adb2d1d9c..668defd807 100644 --- a/demo/bdd_support/agent_backchannel_client.py +++ b/demo/bdd_support/agent_backchannel_client.py @@ -130,10 +130,12 @@ def aries_container_receive_credential( def aries_container_request_proof( the_container: AgentContainer, proof_request: dict, + explicit_revoc_required: bool = False, ): return run_coroutine( the_container.request_proof, proof_request, + explicit_revoc_required=explicit_revoc_required, ) diff --git a/demo/features/0454-present-proof.feature b/demo/features/0454-present-proof.feature index 9abece8cd1..209abf950b 100644 --- a/demo/features/0454-present-proof.feature +++ b/demo/features/0454-present-proof.feature @@ -98,7 +98,7 @@ Feature: RFC 0454 Aries agent present proof | Acme | --revocation --public-did --multitenant | --multitenant | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | @T003-RFC0454.1 @GHA - Scenario Outline: Present Proof for multiple credentials where the one is revocable and one isn't + Scenario Outline: Present Proof for multiple credentials where the one is revocable and one isn't, neither credential is revoked Given we have "4" agents | name | role | capabilities | | Acme1 | issuer1 | | @@ -118,7 +118,7 @@ Feature: RFC 0454 Aries agent present proof | Acme1 | --revocation --public-did | Acme2 | --public-did | | driverslicense_v2 | Data_DL_MaxValues | health_id | Data_DL_MaxValues | DL_age_over_19_v2_with_health_id | @T003-RFC0454.2 @GHA - Scenario Outline: Present Proof for multiple credentials where the one is revocable and one isn't, and the revocable credential is revoked + Scenario Outline: Present Proof for multiple credentials where the one is revocable and one isn't, and the revocable credential is revoked, and the proof checks for revocation and fails Given we have "4" agents | name | role | capabilities | | Acme1 | issuer1 | | @@ -137,3 +137,25 @@ Feature: RFC 0454 Aries agent present proof Examples: | issuer1 | Acme1_capabilities | issuer2 | Acme2_capabilities | Bob_cap | Schema_name_1 | Credential_data_1 | Schema_name_2 | Credential_data_2 | Proof_request | | Acme1 | --revocation --public-did | Acme2 | --public-did | | driverslicense_v2 | Data_DL_MaxValues | health_id | Data_DL_MaxValues | DL_age_over_19_v2_with_health_id | + | Acme1 | --revocation --public-did | Acme2 | --public-did | | driverslicense_v2 | Data_DL_MaxValues | health_id | Data_DL_MaxValues | DL_age_over_19_v2_with_health_id_r2 | + + @T003-RFC0454.3 @GHA + Scenario Outline: Present Proof for multiple credentials where the one is revocable and one isn't, and the revocable credential is revoked, and the proof doesn't check for revocation and passes + Given we have "4" agents + | name | role | capabilities | + | Acme1 | issuer1 | | + | Acme2 | issuer2 | | + | Faber | verifier | | + | Bob | prover | | + And "" and "Bob" have an existing connection + And "Bob" has an issued credential from "" + And "" revokes the credential + And "" and "Bob" have an existing connection + And "Bob" has an issued credential from "" + And "Faber" and "Bob" have an existing connection + When "Faber" sends a request with explicit revocation status for proof presentation to "Bob" + Then "Faber" has the proof verified + + Examples: + | issuer1 | Acme1_capabilities | issuer2 | Acme2_capabilities | Bob_cap | Schema_name_1 | Credential_data_1 | Schema_name_2 | Credential_data_2 | Proof_request | + | Acme1 | --revocation --public-did | Acme2 | --public-did | | driverslicense_v2 | Data_DL_MaxValues | health_id | Data_DL_MaxValues | DL_age_over_19_v2_with_health_id_no_revoc | diff --git a/demo/features/data/presentation_DL_age_over_19_v2_with_health_id_no_revoc.json b/demo/features/data/presentation_DL_age_over_19_v2_with_health_id_no_revoc.json new file mode 100644 index 0000000000..af35bfdaee --- /dev/null +++ b/demo/features/data/presentation_DL_age_over_19_v2_with_health_id_no_revoc.json @@ -0,0 +1,24 @@ +{ + "presentation": { + "comment": "This is a comment for the send presentation.", + "requested_attributes": { + "address_attrs": { + "cred_type_name": "Schema_DriversLicense_v2", + "revealed": true, + "cred_id": "replace_me" + }, + "health_attrs": { + "cred_type_name": "Schema_Health_ID", + "revealed": true, + "cred_id": "replace_me" + } + }, + "requested_predicates": { + "age": { + "cred_type_name": "Schema_DriversLicense_v2", + "cred_id": "replace me" + } + }, + "self_attested_attributes": {} + } +} \ No newline at end of file diff --git a/demo/features/data/presentation_DL_age_over_19_v2_with_health_id_r2.json b/demo/features/data/presentation_DL_age_over_19_v2_with_health_id_r2.json new file mode 100644 index 0000000000..af35bfdaee --- /dev/null +++ b/demo/features/data/presentation_DL_age_over_19_v2_with_health_id_r2.json @@ -0,0 +1,24 @@ +{ + "presentation": { + "comment": "This is a comment for the send presentation.", + "requested_attributes": { + "address_attrs": { + "cred_type_name": "Schema_DriversLicense_v2", + "revealed": true, + "cred_id": "replace_me" + }, + "health_attrs": { + "cred_type_name": "Schema_Health_ID", + "revealed": true, + "cred_id": "replace_me" + } + }, + "requested_predicates": { + "age": { + "cred_type_name": "Schema_DriversLicense_v2", + "cred_id": "replace me" + } + }, + "self_attested_attributes": {} + } +} \ No newline at end of file diff --git a/demo/features/data/proof_request_DL_age_over_19_v2_with_health_id_no_revoc.json b/demo/features/data/proof_request_DL_age_over_19_v2_with_health_id_no_revoc.json new file mode 100644 index 0000000000..e52e1e245a --- /dev/null +++ b/demo/features/data/proof_request_DL_age_over_19_v2_with_health_id_no_revoc.json @@ -0,0 +1,38 @@ +{ + "presentation_proposal": { + "requested_attributes": { + "address_attrs": { + "name": "address", + "restrictions": [ + { + "schema_name": "Schema_DriversLicense_v2", + "schema_version": "1.0.1" + } + ] + }, + "health_attrs": { + "name": "health_id_num", + "restrictions": [ + { + "schema_name": "Schema_Health_ID", + "schema_version": "1.0.0" + } + ] + } + }, + "requested_predicates": { + "age": { + "name": "age", + "p_type": ">", + "p_value": 19, + "restrictions": [ + { + "schema_name": "Schema_DriversLicense_v2", + "schema_version": "1.0.1" + } + ] + } + }, + "version": "0.1.0" + } +} \ No newline at end of file diff --git a/demo/features/data/proof_request_DL_age_over_19_v2_with_health_id_r2.json b/demo/features/data/proof_request_DL_age_over_19_v2_with_health_id_r2.json new file mode 100644 index 0000000000..e52e1e245a --- /dev/null +++ b/demo/features/data/proof_request_DL_age_over_19_v2_with_health_id_r2.json @@ -0,0 +1,38 @@ +{ + "presentation_proposal": { + "requested_attributes": { + "address_attrs": { + "name": "address", + "restrictions": [ + { + "schema_name": "Schema_DriversLicense_v2", + "schema_version": "1.0.1" + } + ] + }, + "health_attrs": { + "name": "health_id_num", + "restrictions": [ + { + "schema_name": "Schema_Health_ID", + "schema_version": "1.0.0" + } + ] + } + }, + "requested_predicates": { + "age": { + "name": "age", + "p_type": ">", + "p_value": 19, + "restrictions": [ + { + "schema_name": "Schema_DriversLicense_v2", + "schema_version": "1.0.1" + } + ] + } + }, + "version": "0.1.0" + } +} \ No newline at end of file diff --git a/demo/features/steps/0454-present-proof.py b/demo/features/steps/0454-present-proof.py index 9b8b9cb8d6..36cfedaa67 100644 --- a/demo/features/steps/0454-present-proof.py +++ b/demo/features/steps/0454-present-proof.py @@ -43,6 +43,20 @@ def step_impl(context, verifier, request_for_proof, prover): context.proof_exchange = proof_exchange +@when( + '"{verifier}" sends a request with explicit revocation status for proof presentation {request_for_proof} to "{prover}"' +) +def step_impl(context, verifier, request_for_proof, prover): + agent = context.active_agents[verifier] + + proof_request_info = read_proof_req_data(request_for_proof) + + proof_exchange = aries_container_request_proof(agent["agent"], proof_request_info, explicit_revoc_required=True) + + context.proof_request = proof_request_info + context.proof_exchange = proof_exchange + + @then('"{verifier}" has the proof verified') def step_impl(context, verifier): agent = context.active_agents[verifier] diff --git a/demo/runners/agent_container.py b/demo/runners/agent_container.py index 87da5528da..ca07b192a3 100644 --- a/demo/runners/agent_container.py +++ b/demo/runners/agent_container.py @@ -928,7 +928,7 @@ async def receive_credential( return matched - async def request_proof(self, proof_request): + async def request_proof(self, proof_request, explicit_revoc_required: bool=False): log_status("#20 Request proof of degree from alice") if self.cred_type == CRED_FORMAT_INDY: @@ -963,7 +963,7 @@ async def request_proof(self, proof_request): ] = non_revoked non_revoked_supplied = True - if not non_revoked_supplied: + if not non_revoked_supplied and not explicit_revoc_required: # else just make it global indy_proof_request["non_revoked"] = non_revoked @@ -1010,6 +1010,8 @@ async def verify_proof(self, proof_request): print("No proof received") return None + # log_status(f">>> last proof received: {self.agent.last_proof_received}") + if self.cred_type == CRED_FORMAT_INDY: # return verified status return self.agent.last_proof_received["verified"] From edbd459180fcbd10a67e32943601a0672462339b Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Thu, 22 Dec 2022 11:40:18 -0500 Subject: [PATCH 609/872] ci: add gha for pr-tests Signed-off-by: Daniel Bluhm --- .github/workflows/pr-tests.yml | 20 +++ .github/workflows/tests-indy.yml | 58 +++++++ .github/workflows/tests.yml | 35 +++++ docker/Dockerfile.indy | 260 +++++++++++++++++++++++++++++++ 4 files changed, 373 insertions(+) create mode 100644 .github/workflows/pr-tests.yml create mode 100644 .github/workflows/tests-indy.yml create mode 100644 .github/workflows/tests.yml create mode 100644 docker/Dockerfile.indy diff --git a/.github/workflows/pr-tests.yml b/.github/workflows/pr-tests.yml new file mode 100644 index 0000000000..851ea9cf35 --- /dev/null +++ b/.github/workflows/pr-tests.yml @@ -0,0 +1,20 @@ +name: PR Tests + +on: + pull_request: + +jobs: + tests: + name: Tests + uses: ./.github/workflows/tests.yml + with: + python-version: "3.6" + os: "ubuntu-20.04" + + tests-indy: + name: Tests (Indy) + uses: ./.github/workflows/tests-indy.yml + with: + python-version: "3.6" + indy-version: "1.16.0" + os: "ubuntu-20.04" diff --git a/.github/workflows/tests-indy.yml b/.github/workflows/tests-indy.yml new file mode 100644 index 0000000000..a893acf5b5 --- /dev/null +++ b/.github/workflows/tests-indy.yml @@ -0,0 +1,58 @@ +name: Tests (Indy) + +on: + workflow_call: + inputs: + python-version: + required: true + type: string + indy-version: + required: true + type: string + os: + required: true + type: string + +jobs: + tests: + name: Test Python ${{ inputs.python-version }} on Indy ${{ inputs.indy-version }} + runs-on: ${{ inputs.os }} + steps: + - uses: actions/checkout@v3 + + - name: Cache image layers + uses: actions/cache@v3 + with: + path: /tmp/.buildx-cache-test + key: ${{ runner.os }}-buildx-test-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx-test- + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + + - name: Build test image + uses: docker/build-push-action@v3 + with: + load: true + context: . + file: docker/Dockerfile.indy + target: acapy-test + tags: acapy-test:latest + build-args: | + python_version=${{ inputs.python-version }} + indy_version=${{ inputs.indy-version }} + cache-from: type=local,src=/tmp/.buildx-cache-test + cache-to: type=local,dest=/tmp/.buildx-cache-test-new,mode=max + + # Temp fix + # https://github.com/docker/build-push-action/issues/252 + # https://github.com/moby/buildkit/issues/1896 + - name: Move cache + run: | + rm -rf /tmp/.buildx-cache-test + mv /tmp/.buildx-cache-test-new /tmp/.buildx-cache-test + + - name: Run pytest + run: | + docker run --rm acapy-test:latest diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000000..919d1e4f21 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,35 @@ +name: Tests + +on: + workflow_call: + inputs: + python-version: + required: true + type: string + os: + required: true + type: string + +jobs: + tests: + name: Test Python ${{ inputs.python-version }} + runs-on: ${{ inputs.os }} + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ inputs.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ inputs.python-version }} + cache: 'pip' + cache-dependency-path: 'requirements*.txt' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip3 install --no-cache-dir \ + -r requirements.txt \ + -r requirements.askar.txt \ + -r requirements.bbs.txt \ + -r requirements.dev.txt + - name: Tests + run: | + pytest diff --git a/docker/Dockerfile.indy b/docker/Dockerfile.indy new file mode 100644 index 0000000000..1072998985 --- /dev/null +++ b/docker/Dockerfile.indy @@ -0,0 +1,260 @@ +ARG python_version=3.6.13 +ARG rust_version=1.46 + +# This image could be replaced with an "indy" image from another repo, +# such as the indy-sdk +FROM rust:${rust_version}-slim-buster as indy-builder + +ARG user=indy +ENV HOME="/home/$user" +WORKDIR $HOME +RUN mkdir -p .local/bin .local/etc .local/lib + +# Install environment +RUN apt-get update -y && \ + apt-get install -y --no-install-recommends \ + automake \ + build-essential \ + ca-certificates \ + cmake \ + curl \ + git \ + libbz2-dev \ + libffi-dev \ + libgmp-dev \ + liblzma-dev \ + libncurses5-dev \ + libncursesw5-dev \ + libsecp256k1-dev \ + libsodium-dev \ + libsqlite3-dev \ + libssl-dev \ + libtool \ + libzmq3-dev \ + pkg-config \ + zlib1g-dev && \ + rm -rf /var/lib/apt/lists/* + +# set to --release for smaller, optimized library +ARG indy_build_flags=--release + +ARG indy_version=1.16.0 +ARG indy_sdk_url=https://codeload.github.com/hyperledger/indy-sdk/tar.gz/refs/tags/v${indy_version} + +# make local libs and binaries accessible +ENV PATH="$HOME/.local/bin:$PATH" +ENV LIBRARY_PATH="$HOME/.local/lib:$LIBRARY_PATH" + +# Download and extract indy-sdk +RUN mkdir indy-sdk && \ + curl "${indy_sdk_url}" | tar -xz -C indy-sdk + +# Build and install indy-sdk +WORKDIR $HOME/indy-sdk +RUN cd indy-sdk*/libindy && \ + cargo build ${indy_build_flags} && \ + cp target/*/libindy.so "$HOME/.local/lib" && \ + cargo clean + +# Package python3-indy +RUN tar czvf ../python3-indy.tgz -C indy-sdk*/wrappers/python . + +# grab the latest sdk code for the postgres plug-in +WORKDIR $HOME +ARG indy_postgres_url=${indy_sdk_url} +RUN mkdir indy-postgres && \ + curl "${indy_postgres_url}" | tar -xz -C indy-postgres + +# Build and install postgres_storage plugin +WORKDIR $HOME/indy-postgres +RUN cd indy-sdk*/experimental/plugins/postgres_storage && \ + cargo build ${indy_build_flags} && \ + cp target/*/libindystrgpostgres.so "$HOME/.local/lib" && \ + cargo clean + +# Clean up SDK +WORKDIR $HOME +RUN rm -rf indy-sdk indy-postgres + + +# Indy Base Image +# This image could be replaced with an "indy-python" image from another repo, +# such as the indy-sdk +FROM python:${python_version}-slim-buster as indy-base + +ARG uid=1001 +ARG user=indy +ARG indy_version + +ENV HOME="/home/$user" \ + APP_ROOT="$HOME" \ + LC_ALL=C.UTF-8 \ + LANG=C.UTF-8 \ + PIP_NO_CACHE_DIR=off \ + PYTHONUNBUFFERED=1 \ + PYTHONIOENCODING=UTF-8 \ + RUST_LOG=warning \ + SHELL=/bin/bash \ + SUMMARY="indy-python base image" \ + DESCRIPTION="aries-cloudagent provides a base image for running Hyperledger Aries agents in Docker. \ + This image provides all the necessary dependencies to use the indy-sdk in python. Based on Debian Buster." + +LABEL summary="$SUMMARY" \ + description="$DESCRIPTION" \ + io.k8s.description="$DESCRIPTION" \ + io.k8s.display-name="indy-python $indy_version" \ + name="indy-python" \ + version="$indy_version" \ + maintainer="" + +# Add indy user +RUN useradd -U -ms /bin/bash -u $uid $user + +# Install environment +RUN apt-get update -y && \ + apt-get install -y --no-install-recommends \ + apt-transport-https \ + ca-certificates \ + bzip2 \ + curl \ + git \ + less \ + libffi6 \ + libgmp10 \ + liblzma5 \ + libncurses5 \ + libncursesw5 \ + libsecp256k1-0 \ + libzmq5 \ + net-tools \ + openssl \ + sqlite3 \ + vim-tiny \ + zlib1g && \ + rm -rf /var/lib/apt/lists/* /usr/share/doc/* + +WORKDIR $HOME + +# Copy build results +COPY --from=indy-builder --chown=$user:$user $HOME . + +RUN mkdir -p $HOME/.local/bin + +# Add local binaries and aliases to path +ENV PATH="$HOME/.local/bin:$PATH" + +# Make libraries resolvable by python +ENV LD_LIBRARY_PATH="$HOME/.local/lib:$LD_LIBRARY_PATH" +RUN echo "$HOME/.local/lib" > /etc/ld.so.conf.d/local.conf && ldconfig + +# Install python3-indy +RUN pip install --no-cache-dir python3-indy.tgz && rm python3-indy.tgz + +# - In order to drop the root user, we have to make some directories writable +# to the root group as OpenShift default security model is to run the container +# under random UID. +RUN usermod -a -G 0 $user + +# Create standard directories to allow volume mounting and set permissions +# Note: PIP_NO_CACHE_DIR environment variable should be cleared to allow caching +RUN mkdir -p \ + $HOME/.cache/pip/http \ + $HOME/.indy-cli/networks \ + $HOME/.indy_client/wallet \ + $HOME/.indy_client/pool \ + $HOME/.indy_client/ledger-cache \ + $HOME/ledger/sandbox/data \ + $HOME/log + +# The root group needs access the directories under $HOME/.indy_client for the container to function in OpenShift. +# Also ensure the permissions on the python 'site-packages' folder are set correctly. +RUN chown -R $user:root $HOME/.indy_client \ + && chmod -R ug+rw $HOME/log $HOME/ledger $HOME/.cache $HOME/.indy_client + +USER $user + +CMD ["bash"] + + +# ACA-Py Test +# Used to run ACA-Py unit tests with Indy +FROM indy-base as acapy-test + +USER indy + +RUN mkdir src test-reports + +WORKDIR /home/indy/src + +RUN mkdir -p test-reports && chown -R indy:indy test-reports && chmod -R ug+rw test-reports + +ADD requirements*.txt ./ + +USER root +RUN pip3 install --no-cache-dir \ + -r requirements.txt \ + -r requirements.askar.txt \ + -r requirements.bbs.txt \ + -r requirements.dev.txt + +ADD --chown=indy:root . . +USER indy + +ENTRYPOINT ["/bin/bash", "-c", "pytest \"$@\"", "--"] + +# ACA-Py Builder +# Build ACA-Py wheel using setuptools +FROM python:${python_version}-slim-buster AS acapy-builder + +WORKDIR /src + +ADD . . + +RUN pip install setuptools wheel +RUN python setup.py sdist bdist_wheel + + +# ACA-Py Indy +# Install wheel from builder and commit final image +FROM indy-base AS main + +ARG uid=1001 +ARG user=indy +ARG acapy_version +ARG acapy_reqs=[askar,bbs] + +ENV HOME="/home/$user" \ + APP_ROOT="$HOME" \ + LC_ALL=C.UTF-8 \ + LANG=C.UTF-8 \ + PIP_NO_CACHE_DIR=off \ + PYTHONUNBUFFERED=1 \ + PYTHONIOENCODING=UTF-8 \ + RUST_LOG=warning \ + SHELL=/bin/bash \ + SUMMARY="aries-cloudagent image" \ + DESCRIPTION="aries-cloudagent provides a base image for running Hyperledger Aries agents in Docker. \ + This image layers the python implementation of aries-cloudagent $acapy_version. \ + This image includes indy-sdk and supporting libraries." + +LABEL summary="$SUMMARY" \ + description="$DESCRIPTION" \ + io.k8s.description="$DESCRIPTION" \ + io.k8s.display-name="aries-cloudagent $acapy_version" \ + name="aries-cloudagent" \ + version="$acapy_version" \ + maintainer="" + +# Create standard directories to allow volume mounting and set permissions +# Note: PIP_NO_CACHE_DIR environment variable should be cleared to allow caching +RUN mkdir -p $HOME/.aries_cloudagent + +# The root group needs access the directories under $HOME/.indy_client for the container to function in OpenShift. +# Also ensure the permissions on the python 'site-packages' folder are set correctly. +RUN chmod -R ug+rw $HOME/.aries_cloudagent + +COPY --from=acapy-builder /src/dist/aries_cloudagent*.whl . + +RUN pip install --no-cache-dir --find-links=. aries_cloudagent${acapy_reqs} && rm aries_cloudagent*.whl + +ENTRYPOINT ["aca-py"] From 5550e0dc4c973161f352a674c414a229976b2489 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Thu, 22 Dec 2022 11:04:47 -0800 Subject: [PATCH 610/872] Additional proof request/revocation tests Signed-off-by: Ian Costanzo --- demo/features/0454-present-proof.feature | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/demo/features/0454-present-proof.feature b/demo/features/0454-present-proof.feature index 209abf950b..6d5ec1ed84 100644 --- a/demo/features/0454-present-proof.feature +++ b/demo/features/0454-present-proof.feature @@ -117,6 +117,26 @@ Feature: RFC 0454 Aries agent present proof | issuer1 | Acme1_capabilities | issuer2 | Acme2_capabilities | Bob_cap | Schema_name_1 | Credential_data_1 | Schema_name_2 | Credential_data_2 | Proof_request | | Acme1 | --revocation --public-did | Acme2 | --public-did | | driverslicense_v2 | Data_DL_MaxValues | health_id | Data_DL_MaxValues | DL_age_over_19_v2_with_health_id | + @T003-RFC0454.1f + Scenario Outline: Present Proof for multiple credentials where the one is revocable and one isn't, neither credential is revoked, fails due to requesting request-level revocation + Given we have "4" agents + | name | role | capabilities | + | Acme1 | issuer1 | | + | Acme2 | issuer2 | | + | Faber | verifier | | + | Bob | prover | | + And "" and "Bob" have an existing connection + And "Bob" has an issued credential from "" + And "" and "Bob" have an existing connection + And "Bob" has an issued credential from "" + And "Faber" and "Bob" have an existing connection + When "Faber" sends a request for proof presentation to "Bob" + Then "Faber" has the proof verification fail + + Examples: + | issuer1 | Acme1_capabilities | issuer2 | Acme2_capabilities | Bob_cap | Schema_name_1 | Credential_data_1 | Schema_name_2 | Credential_data_2 | Proof_request | + | Acme1 | --revocation --public-did | Acme2 | --public-did | | driverslicense_v2 | Data_DL_MaxValues | health_id | Data_DL_MaxValues | DL_age_over_19_v2_with_health_id_r2 | + @T003-RFC0454.2 @GHA Scenario Outline: Present Proof for multiple credentials where the one is revocable and one isn't, and the revocable credential is revoked, and the proof checks for revocation and fails Given we have "4" agents From a276a76f8c2fa576d10d391c27440c298270fe45 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Thu, 22 Dec 2022 11:09:47 -0800 Subject: [PATCH 611/872] Code formatting Signed-off-by: Ian Costanzo --- demo/features/steps/0454-present-proof.py | 4 +++- demo/runners/agent_container.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/demo/features/steps/0454-present-proof.py b/demo/features/steps/0454-present-proof.py index 36cfedaa67..816682ab2a 100644 --- a/demo/features/steps/0454-present-proof.py +++ b/demo/features/steps/0454-present-proof.py @@ -51,7 +51,9 @@ def step_impl(context, verifier, request_for_proof, prover): proof_request_info = read_proof_req_data(request_for_proof) - proof_exchange = aries_container_request_proof(agent["agent"], proof_request_info, explicit_revoc_required=True) + proof_exchange = aries_container_request_proof( + agent["agent"], proof_request_info, explicit_revoc_required=True + ) context.proof_request = proof_request_info context.proof_exchange = proof_exchange diff --git a/demo/runners/agent_container.py b/demo/runners/agent_container.py index ca07b192a3..c18c472ebc 100644 --- a/demo/runners/agent_container.py +++ b/demo/runners/agent_container.py @@ -928,7 +928,7 @@ async def receive_credential( return matched - async def request_proof(self, proof_request, explicit_revoc_required: bool=False): + async def request_proof(self, proof_request, explicit_revoc_required: bool = False): log_status("#20 Request proof of degree from alice") if self.cred_type == CRED_FORMAT_INDY: From eb204c67c8853d467fb84079469c0b5f6a182358 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dobros=C5=82aw=20=C5=BBybort?= Date: Thu, 15 Dec 2022 13:09:11 +0100 Subject: [PATCH 612/872] Add missing --mediator-connections-invite cmd arg info to docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Dobrosław Żybort --- Mediation.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Mediation.md b/Mediation.md index 425996e16e..c301a39e77 100644 --- a/Mediation.md +++ b/Mediation.md @@ -15,6 +15,7 @@ * `--open-mediation` - Instructs mediators to automatically grant all incoming mediation requests. * `--mediator-invitation` - Receive invitation, send mediation request and set as default mediator. +* `--mediator-connections-invite` - Connect to mediator through a connection invitation. If not specified, connect using an OOB invitation. * `--default-mediator-id` - Set pre-existing mediator as default mediator. * `--clear-default-mediator` - Clear the stored default mediator. @@ -72,4 +73,4 @@ See [Aries RFC 0211: Coordinate Mediation Protocol](https://github.com/hyperledg ## Using a Mediator After establishing a connection with a mediator also having mediation granted, you can use that mediator id for future did_comm connections. - When creating, receiving or accepting a invitation intended to be Mediated, you provide `mediation_id` with the desired mediator id. if using a single mediator for all future connections, You can set a default mediation id. If no mediation_id is provided the default mediation id will be used instead. \ No newline at end of file + When creating, receiving or accepting a invitation intended to be Mediated, you provide `mediation_id` with the desired mediator id. if using a single mediator for all future connections, You can set a default mediation id. If no mediation_id is provided the default mediation id will be used instead. From 621042d9496462566cd5cfcde31029920d927632 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Thu, 22 Dec 2022 15:52:10 -0500 Subject: [PATCH 613/872] ci: test additional versions of python nightly Signed-off-by: Daniel Bluhm --- .github/workflows/nightly-tests.yml | 39 +++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .github/workflows/nightly-tests.yml diff --git a/.github/workflows/nightly-tests.yml b/.github/workflows/nightly-tests.yml new file mode 100644 index 0000000000..e9b6d5d77a --- /dev/null +++ b/.github/workflows/nightly-tests.yml @@ -0,0 +1,39 @@ +name: Nightly Tests + +on: + schedule: + - cron: '0 0 * * *' + workflow_dispatch: + +jobs: + tests: + name: Tests + strategy: + fail-fast: false + matrix: + os: ["ubuntu-latest"] + python-version: ["3.7", "3.8", "3.9", "3.10"] + include: + - os: "ubuntu-20.04" + python-version: "3.6" + uses: ./.github/workflows/tests.yml + with: + python-version: ${{ matrix.python-version }} + os: ${{ matrix.os }} + + tests-indy: + name: Tests (Indy) + strategy: + fail-fast: false + matrix: + os: ["ubuntu-latest"] + python-version: ["3.7", "3.8", "3.9", "3.10"] + include: + - os: "ubuntu-20.04" + python-version: "3.6" + + uses: ./.github/workflows/tests-indy.yml + with: + python-version: ${{ matrix.python-version }} + os: ${{ matrix.os }} + indy-version: "1.16.0" From 0e1f06fe63cb14bb99848041d819ea447c01b4dd Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Thu, 22 Dec 2022 14:54:41 -0800 Subject: [PATCH 614/872] do not reject invitation with unknown handshake protocol(s) Signed-off-by: Andrew Whitehead --- .../out_of_band/v1_0/messages/invitation.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/messages/invitation.py b/aries_cloudagent/protocols/out_of_band/v1_0/messages/invitation.py index e461201975..e51da83d60 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/messages/invitation.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/messages/invitation.py @@ -221,9 +221,6 @@ class Meta: fields.Str( description="Handshake protocol", example=DIDCommPrefix.qualify_current(HSProto.RFC23.name), - validate=lambda hsp: ( - DIDCommPrefix.unqualify(hsp) in [p.name for p in HSProto] - ), ), required=False, ) @@ -275,14 +272,11 @@ def validate_fields(self, data, **kwargs): ValidationError: If any of the fields do not validate """ handshake_protocols = data.get("handshake_protocols") - requests_attach = data.get("requests_attach") - if not ( - (handshake_protocols and len(handshake_protocols) > 0) - or (requests_attach and len(requests_attach) > 0) - ): + requests_attach = data.get("requests~attach") + if not handshake_protocols and not requests_attach: raise ValidationError( "Model must include non-empty " - "handshake_protocols or requests_attach or both" + "handshake_protocols or requests~attach or both" ) # services = data.get("services") From 50312e56279a82c4dcf22c29f5d8d3e8ca29daf5 Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Thu, 22 Dec 2022 15:12:30 -0800 Subject: [PATCH 615/872] fix service entry validation Signed-off-by: Andrew Whitehead --- aries_cloudagent/messaging/valid.py | 49 ++++++------------- .../v1_0/models/connection_detail.py | 2 +- .../out_of_band/v1_0/messages/invitation.py | 12 +++-- .../v1_0/messages/tests/test_invitation.py | 5 +- 4 files changed, 26 insertions(+), 42 deletions(-) diff --git a/aries_cloudagent/messaging/valid.py b/aries_cloudagent/messaging/valid.py index 6253c6b304..5af2a2d7be 100644 --- a/aries_cloudagent/messaging/valid.py +++ b/aries_cloudagent/messaging/valid.py @@ -23,60 +23,41 @@ class StrOrDictField(Field): """URI or Dict field for Marshmallow.""" - def _serialize(self, value, attr, obj, **kwargs): - return value - def _deserialize(self, value, attr, data, **kwargs): - if isinstance(value, (str, dict)): - return value - else: + if not isinstance(value, (str, dict)): raise ValidationError("Field should be str or dict") + return super()._deserialize(value, attr, data, **kwargs) class StrOrNumberField(Field): """String or Number field for Marshmallow.""" - def _serialize(self, value, attr, obj, **kwargs): - return value - def _deserialize(self, value, attr, data, **kwargs): - if isinstance(value, (str, float, int)): - return value - else: + if not isinstance(value, (str, float, int)): raise ValidationError("Field should be str or int or float") + return super()._deserialize(value, attr, data, **kwargs) class DictOrDictListField(Field): """Dict or Dict List field for Marshmallow.""" - def _serialize(self, value, attr, obj, **kwargs): - return value - def _deserialize(self, value, attr, data, **kwargs): - # dict - if isinstance(value, dict): - return value - # list of dicts - elif isinstance(value, list) and all(isinstance(item, dict) for item in value): - return value - else: - raise ValidationError("Field should be dict or list of dicts") + if not isinstance(value, dict): + if not isinstance(value, list) or not all( + isinstance(item, dict) for item in value + ): + raise ValidationError("Field should be dict or list of dicts") + return super()._deserialize(value, attr, data, **kwargs) class UriOrDictField(StrOrDictField): """URI or Dict field for Marshmallow.""" - def __init__(self, *args, **kwargs): - """Initialize new UriOrDictField instance.""" - super().__init__(*args, **kwargs) - - # Insert validation into self.validators so that multiple errors can be stored. - self.validators.insert(0, self._uri_validator) - - def _uri_validator(self, value): - # Check if URI when + def _deserialize(self, value, attr, data, **kwargs): if isinstance(value, str): - return Uri()(value) + # Check regex + Uri()(value) + return super()._deserialize(value, attr, data, **kwargs) class IntEpoch(Range): @@ -775,7 +756,7 @@ def __call__(self, value): except ValidationError: raise ValidationError( f"credential subject id {value[0]} must be URI" - ) + ) from None return value diff --git a/aries_cloudagent/protocols/connections/v1_0/models/connection_detail.py b/aries_cloudagent/protocols/connections/v1_0/models/connection_detail.py index cdbac51cd7..96a64abed1 100644 --- a/aries_cloudagent/protocols/connections/v1_0/models/connection_detail.py +++ b/aries_cloudagent/protocols/connections/v1_0/models/connection_detail.py @@ -23,7 +23,7 @@ def _serialize(self, value, attr, obj, **kwargs): """ return value.serialize() - def _deserialize(self, value, attr, data, **kwargs): + def _deserialize(self, value, attr=None, data=None, **kwargs): """ Deserialize a value into a DIDDoc. diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/messages/invitation.py b/aries_cloudagent/protocols/out_of_band/v1_0/messages/invitation.py index e51da83d60..be3c0b80a8 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/messages/invitation.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/messages/invitation.py @@ -96,13 +96,17 @@ def _serialize(self, value, attr, obj, **kwargs): def _deserialize(self, value, attr, data, **kwargs): if isinstance(value, dict): return Service.deserialize(value) + elif isinstance(value, Service): + return value elif isinstance(value, str): - if bool(DIDValidation.PATTERN.match(value)): - return value - else: + if not DIDValidation.PATTERN.match(value): raise ValidationError( "Service item must be a valid decentralized identifier (DID)" ) + return value + raise ValidationError( + "Service item must be a valid decentralized identifier (DID) or object" + ) class InvitationMessage(AgentMessage): @@ -272,7 +276,7 @@ def validate_fields(self, data, **kwargs): ValidationError: If any of the fields do not validate """ handshake_protocols = data.get("handshake_protocols") - requests_attach = data.get("requests~attach") + requests_attach = data.get("requests_attach") if not handshake_protocols and not requests_attach: raise ValidationError( "Model must include non-empty " diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/messages/tests/test_invitation.py b/aries_cloudagent/protocols/out_of_band/v1_0/messages/tests/test_invitation.py index 801b17680e..004c7d5ca5 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/messages/tests/test_invitation.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/messages/tests/test_invitation.py @@ -139,9 +139,8 @@ def test_invalid_invi_wrong_type_services(self): "services": [123], } - invi_schema = InvitationMessageSchema() - with pytest.raises(test_module.ValidationError): - invi_schema.validate_fields(obj_x) + errs = InvitationMessageSchema().validate(obj_x) + assert errs and "services" in errs def test_assign_msg_type_version_to_model_inst(self): test_msg = InvitationMessage() From 224b93eac7d19982bce82536370474fcf798fbd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Humbert?= Date: Thu, 22 Dec 2022 16:25:19 +0100 Subject: [PATCH 616/872] feat: enable creation of DIDs for all registered methods MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To leverage the recent addition of the `DIDMethods` registry: * Relax condition on did format for the `POST /wallet/did` endpoint * Validate new DIDs parameters using `DIDMethods` in Askar and InMemory profiles validate new DIDs parameters Signed-off-by: Clément Humbert --- aries_cloudagent/core/tests/test_conductor.py | 3 +- .../ledger/tests/test_indy_vdr.py | 2 +- .../messaging/jsonld/tests/test_routes.py | 3 +- .../connections/v1_0/tests/test_manager.py | 3 +- .../tests/test_mediation_request_handler.py | 2 + .../v1_0/tests/test_mediation_manager.py | 5 +- .../v1_0/tests/test_routes.py | 2 + .../handlers/tests/test_complete_handler.py | 2 + .../handlers/tests/test_invitation_handler.py | 2 + .../handlers/tests/test_request_handler.py | 3 +- .../handlers/tests/test_response_handler.py | 3 +- .../v1_0/messages/tests/test_request.py | 10 ++- .../v1_0/messages/tests/test_response.py | 12 ++- .../didexchange/v1_0/tests/test_manager.py | 3 +- .../v1_0/tests/test_manager.py | 3 +- .../dif/tests/test_pres_exch_handler.py | 4 +- .../transport/tests/test_pack_format.py | 3 +- aries_cloudagent/wallet/askar.py | 31 +++---- aries_cloudagent/wallet/did_method.py | 35 +++++++- .../wallet/did_parameters_validation.py | 65 +++++++++++++++ aries_cloudagent/wallet/in_memory.py | 26 ++---- aries_cloudagent/wallet/routes.py | 26 ++++-- .../tests/test_did_parameters_validation.py | 83 +++++++++++++++++++ .../wallet/tests/test_in_memory_wallet.py | 1 + .../wallet/tests/test_indy_wallet.py | 5 +- aries_cloudagent/wallet/tests/test_routes.py | 46 +++++++++- 26 files changed, 312 insertions(+), 71 deletions(-) create mode 100644 aries_cloudagent/wallet/did_parameters_validation.py create mode 100644 aries_cloudagent/wallet/tests/test_did_parameters_validation.py diff --git a/aries_cloudagent/core/tests/test_conductor.py b/aries_cloudagent/core/tests/test_conductor.py index c105a66307..d37f10161e 100644 --- a/aries_cloudagent/core/tests/test_conductor.py +++ b/aries_cloudagent/core/tests/test_conductor.py @@ -36,7 +36,7 @@ from ...utils.stats import Collector from ...version import __version__ from ...wallet.base import BaseWallet -from ...wallet.did_method import SOV +from ...wallet.did_method import SOV, DIDMethods from ...wallet.key_type import ED25519 from .. import conductor as test_module @@ -87,6 +87,7 @@ async def build_context(self) -> InjectionContext: context.injector.bind_instance(ProfileManager, InMemoryProfileManager()) context.injector.bind_instance(ProtocolRegistry, ProtocolRegistry()) context.injector.bind_instance(BaseWireFormat, self.wire_format) + context.injector.bind_instance(DIDMethods, DIDMethods()) context.injector.bind_instance(DIDResolver, DIDResolver([])) context.injector.bind_instance(EventBus, MockEventBus()) return context diff --git a/aries_cloudagent/ledger/tests/test_indy_vdr.py b/aries_cloudagent/ledger/tests/test_indy_vdr.py index 5f820aeeab..6a575a5737 100644 --- a/aries_cloudagent/ledger/tests/test_indy_vdr.py +++ b/aries_cloudagent/ledger/tests/test_indy_vdr.py @@ -28,7 +28,7 @@ @pytest.fixture() def ledger(): - profile = InMemoryProfile.test_profile() + profile = InMemoryProfile.test_profile(bind={DIDMethods: DIDMethods()}) ledger = IndyVdrLedger(IndyVdrLedgerPool("test-ledger"), profile) async def open(): diff --git a/aries_cloudagent/messaging/jsonld/tests/test_routes.py b/aries_cloudagent/messaging/jsonld/tests/test_routes.py index ebddf2b9f3..cc9d35c724 100644 --- a/aries_cloudagent/messaging/jsonld/tests/test_routes.py +++ b/aries_cloudagent/messaging/jsonld/tests/test_routes.py @@ -14,7 +14,7 @@ from ....resolver.did_resolver import DIDResolver from ....vc.ld_proofs.document_loader import DocumentLoader from ....wallet.base import BaseWallet -from ....wallet.did_method import SOV +from ....wallet.did_method import SOV, DIDMethods from ....wallet.error import WalletError from ....wallet.key_type import ED25519 from ..error import ( @@ -274,6 +274,7 @@ async def setUp(self): self.context.profile.context.injector.bind_instance( DocumentLoader, custom_document_loader ) + self.context.profile.context.injector.bind_instance(DIDMethods, DIDMethods()) self.did_info = await (await self.context.session()).wallet.create_local_did( SOV, ED25519 ) diff --git a/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py index be52b0aefb..c906d12a44 100644 --- a/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py @@ -23,7 +23,7 @@ from .....storage.error import StorageNotFoundError from .....transport.inbound.receipt import MessageReceipt from .....wallet.base import DIDInfo -from .....wallet.did_method import SOV +from .....wallet.did_method import SOV, DIDMethods from .....wallet.error import WalletNotFoundError from .....wallet.in_memory import InMemoryWallet from .....wallet.key_type import ED25519 @@ -93,6 +93,7 @@ async def setUp(self): BaseCache: InMemoryCache(), OobMessageProcessor: self.oob_mock, RouteManager: self.route_manager, + DIDMethods: DIDMethods(), }, ) self.context = self.profile.context diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/tests/test_mediation_request_handler.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/tests/test_mediation_request_handler.py index 61d2b4d449..d3c342ec07 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/tests/test_mediation_request_handler.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/tests/test_mediation_request_handler.py @@ -13,6 +13,7 @@ from ...models.mediation_record import MediationRecord from ..mediation_request_handler import MediationRequestHandler +from ......wallet.did_method import DIDMethods TEST_CONN_ID = "conn-id" TEST_VERKEY = "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx" @@ -24,6 +25,7 @@ class TestMediationRequestHandler(AsyncTestCase): async def setUp(self): """setup dependencies of messaging""" self.context = RequestContext.test_context() + self.context.profile.context.injector.bind_instance(DIDMethods, DIDMethods()) self.session = await self.context.session() self.context.message = MediationRequest() self.context.connection_ready = True diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py index 97bc2f80f7..7e93eaa841 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py @@ -26,6 +26,7 @@ from ..messages.mediate_grant import MediationGrant from ..messages.mediate_request import MediationRequest from ..models.mediation_record import MediationRecord +from .....wallet.did_method import DIDMethods TEST_CONN_ID = "conn-id" TEST_THREAD_ID = "thread-id" @@ -42,7 +43,9 @@ def profile() -> Iterable[Profile]: """Fixture for profile used in tests.""" # pylint: disable=W0621 - yield InMemoryProfile.test_profile(bind={EventBus: MockEventBus()}) + yield InMemoryProfile.test_profile( + bind={EventBus: MockEventBus(), DIDMethods: DIDMethods()} + ) @pytest.fixture diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_routes.py index 4cbd017a45..ababf0ea16 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_routes.py @@ -6,11 +6,13 @@ from .....storage.error import StorageError, StorageNotFoundError from ..models.mediation_record import MediationRecord from ..route_manager import RouteManager +from .....wallet.did_method import DIDMethods class TestCoordinateMediationRoutes(AsyncTestCase): def setUp(self): self.profile = InMemoryProfile.test_profile() + self.profile.context.injector.bind_instance(DIDMethods, DIDMethods()) self.context = AdminRequestContext.test_context(profile=self.profile) self.outbound_message_router = async_mock.CoroutineMock() self.request_dict = { diff --git a/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_complete_handler.py b/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_complete_handler.py index bbeba7c27f..19372e515c 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_complete_handler.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_complete_handler.py @@ -10,11 +10,13 @@ from ...messages.problem_report_reason import ProblemReportReason from .. import complete_handler as test_module +from ......wallet.did_method import DIDMethods @pytest.fixture() def request_context() -> RequestContext: ctx = RequestContext.test_context() + ctx.injector.bind_instance(DIDMethods, DIDMethods()) ctx.message_receipt = MessageReceipt() yield ctx diff --git a/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_invitation_handler.py b/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_invitation_handler.py index 95a728310c..279d905863 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_invitation_handler.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_invitation_handler.py @@ -9,11 +9,13 @@ from ...handlers.invitation_handler import InvitationHandler from ...messages.problem_report_reason import ProblemReportReason +from ......wallet.did_method import DIDMethods @pytest.fixture() def request_context() -> RequestContext: ctx = RequestContext.test_context() + ctx.injector.bind_instance(DIDMethods, DIDMethods()) ctx.message_receipt = MessageReceipt() yield ctx diff --git a/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_request_handler.py b/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_request_handler.py index 8f8c4381b1..6c7a5892b8 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_request_handler.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_request_handler.py @@ -9,7 +9,7 @@ Service, ) from ......core.in_memory import InMemoryProfile -from ......wallet.did_method import SOV +from ......wallet.did_method import SOV, DIDMethods from ......wallet.key_type import ED25519 from ......messaging.decorators.attach_decorator import AttachDecorator from ......messaging.request_context import RequestContext @@ -76,6 +76,7 @@ async def setUp(self): "debug.auto_accept_requests_public": True, } ) + self.session.profile.context.injector.bind_instance(DIDMethods, DIDMethods()) self.conn_rec = conn_record.ConnRecord( my_did="55GkHamhTU1ZbTbV2ab9DE", diff --git a/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_response_handler.py b/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_response_handler.py index ff9750ca14..ba81955e36 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_response_handler.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/handlers/tests/test_response_handler.py @@ -13,7 +13,7 @@ from ......messaging.request_context import RequestContext from ......messaging.responder import MockResponder from ......transport.inbound.receipt import MessageReceipt -from ......wallet.did_method import SOV +from ......wallet.did_method import SOV, DIDMethods from ......wallet.key_type import ED25519 from .....problem_report.v1_0.message import ProblemReport @@ -63,6 +63,7 @@ async def setUp(self): self.ctx = RequestContext.test_context() self.ctx.message_receipt = MessageReceipt() + self.ctx.profile.context.injector.bind_instance(DIDMethods, DIDMethods()) wallet = (await self.ctx.session()).wallet self.did_info = await wallet.create_local_did( method=SOV, diff --git a/aries_cloudagent/protocols/didexchange/v1_0/messages/tests/test_request.py b/aries_cloudagent/protocols/didexchange/v1_0/messages/tests/test_request.py index 37569858e6..dc4c8b189e 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/messages/tests/test_request.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/messages/tests/test_request.py @@ -5,7 +5,7 @@ from ......connections.models.diddoc import DIDDoc, PublicKey, PublicKeyType, Service from ......core.in_memory import InMemoryProfile from ......messaging.decorators.attach_decorator import AttachDecorator -from ......wallet.did_method import SOV +from ......wallet.did_method import SOV, DIDMethods from ......wallet.key_type import ED25519 from .....didcomm_prefix import DIDCommPrefix from ...message_types import DIDX_REQUEST @@ -49,7 +49,9 @@ def make_did_doc(self): class TestDIDXRequest(AsyncTestCase, TestConfig): async def setUp(self): - self.wallet = InMemoryProfile.test_session().wallet + self.session = InMemoryProfile.test_session() + self.session.profile.context.injector.bind_instance(DIDMethods, DIDMethods()) + self.wallet = self.session.wallet self.did_info = await self.wallet.create_local_did( method=SOV, key_type=ED25519, @@ -106,7 +108,9 @@ class TestDIDXRequestSchema(AsyncTestCase, TestConfig): """Test request schema.""" async def setUp(self): - self.wallet = InMemoryProfile.test_session().wallet + self.session = InMemoryProfile.test_session() + self.session.profile.context.injector.bind_instance(DIDMethods, DIDMethods()) + self.wallet = self.session.wallet self.did_info = await self.wallet.create_local_did( method=SOV, key_type=ED25519, diff --git a/aries_cloudagent/protocols/didexchange/v1_0/messages/tests/test_response.py b/aries_cloudagent/protocols/didexchange/v1_0/messages/tests/test_response.py index 59657ef2dc..3be60ca51e 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/messages/tests/test_response.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/messages/tests/test_response.py @@ -5,7 +5,7 @@ from ......connections.models.diddoc import DIDDoc, PublicKey, PublicKeyType, Service from ......core.in_memory import InMemoryProfile from ......messaging.decorators.attach_decorator import AttachDecorator -from ......wallet.did_method import SOV +from ......wallet.did_method import SOV, DIDMethods from ......wallet.key_type import ED25519 from .....didcomm_prefix import DIDCommPrefix from ...message_types import DIDX_RESPONSE @@ -48,7 +48,10 @@ def make_did_doc(self): class TestDIDXResponse(AsyncTestCase, TestConfig): async def setUp(self): - self.wallet = InMemoryProfile.test_session().wallet + self.session = InMemoryProfile.test_session() + self.session.profile.context.injector.bind_instance(DIDMethods, DIDMethods()) + self.wallet = self.session.wallet + self.did_info = await self.wallet.create_local_did( method=SOV, key_type=ED25519, @@ -102,7 +105,10 @@ class TestDIDXResponseSchema(AsyncTestCase, TestConfig): """Test response schema.""" async def setUp(self): - self.wallet = InMemoryProfile.test_session().wallet + self.session = InMemoryProfile.test_session() + self.session.profile.context.injector.bind_instance(DIDMethods, DIDMethods()) + self.wallet = self.session.wallet + self.did_info = await self.wallet.create_local_did( method=SOV, key_type=ED25519, diff --git a/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py index 18f87d6b30..69de49fcfd 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py @@ -24,7 +24,7 @@ from .....storage.error import StorageNotFoundError from .....transport.inbound.receipt import MessageReceipt from .....wallet.did_info import DIDInfo -from .....wallet.did_method import SOV +from .....wallet.did_method import SOV, DIDMethods from .....wallet.error import WalletError from .....wallet.in_memory import InMemoryWallet from .....wallet.key_type import ED25519 @@ -102,6 +102,7 @@ async def setUp(self): BaseCache: InMemoryCache(), OobMessageProcessor: self.oob_mock, RouteManager: self.route_manager, + DIDMethods: DIDMethods(), }, ) self.context = self.profile.context diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_manager.py index 1ce20b7ca5..f4b2719237 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_manager.py @@ -12,7 +12,7 @@ from .....ledger.base import BaseLedger from .....storage.error import StorageNotFoundError from .....wallet.base import BaseWallet -from .....wallet.did_method import SOV +from .....wallet.did_method import SOV, DIDMethods from .....wallet.key_type import ED25519 from ..manager import TransactionManager, TransactionManagerError from ..models.transaction_record import TransactionRecord @@ -112,6 +112,7 @@ async def setUp(self): self.profile = self.context.profile injector = self.profile.context.injector injector.bind_instance(BaseLedger, self.ledger) + injector.bind_instance(DIDMethods, DIDMethods()) async with self.profile.session() as session: self.wallet: BaseWallet = session.inject_or(BaseWallet) diff --git a/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py b/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py index 72525e2fe8..87597359cc 100644 --- a/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py +++ b/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py @@ -15,7 +15,7 @@ from .....storage.vc_holder.vc_record import VCRecord from .....wallet.base import BaseWallet, DIDInfo from .....wallet.crypto import KeyType -from .....wallet.did_method import SOV, KEY +from .....wallet.did_method import SOV, KEY, DIDMethods from .....wallet.error import WalletNotFoundError from .....vc.ld_proofs import ( BbsBlsSignature2020, @@ -69,7 +69,7 @@ def event_loop(request): @pytest.fixture(scope="class") def profile(): - profile = InMemoryProfile.test_profile() + profile = InMemoryProfile.test_profile(bind={DIDMethods: DIDMethods()}) context = profile.context context.injector.bind_instance(DIDResolver, DIDResolver([])) context.injector.bind_instance(DocumentLoader, custom_document_loader) diff --git a/aries_cloudagent/transport/tests/test_pack_format.py b/aries_cloudagent/transport/tests/test_pack_format.py index 02a7712e1c..764e4a54bf 100644 --- a/aries_cloudagent/transport/tests/test_pack_format.py +++ b/aries_cloudagent/transport/tests/test_pack_format.py @@ -8,7 +8,7 @@ from ...protocols.didcomm_prefix import DIDCommPrefix from ...protocols.routing.v1_0.message_types import FORWARD from ...wallet.base import BaseWallet -from ...wallet.did_method import SOV +from ...wallet.did_method import SOV, DIDMethods from ...wallet.error import WalletError from ...wallet.key_type import ED25519 from .. import pack_format as test_module @@ -33,6 +33,7 @@ class TestPackWireFormat(AsyncTestCase): def setUp(self): self.session = InMemoryProfile.test_session() + self.session.profile.context.injector.bind_instance(DIDMethods, DIDMethods()) self.wallet = self.session.inject(BaseWallet) async def test_errors(self): diff --git a/aries_cloudagent/wallet/askar.py b/aries_cloudagent/wallet/askar.py index 5d84df9f26..2a0093d276 100644 --- a/aries_cloudagent/wallet/askar.py +++ b/aries_cloudagent/wallet/askar.py @@ -15,9 +15,9 @@ SeedMethod, ) +from .did_parameters_validation import DIDParametersValidation from ..askar.didcomm.v1 import pack_message, unpack_message from ..askar.profile import AskarProfileSession -from ..did.did_key import DIDKey from ..ledger.base import BaseLedger from ..ledger.endpoint_type import EndpointType from ..ledger.error import LedgerConfigError @@ -30,7 +30,7 @@ validate_seed, verify_signed_message, ) -from .did_method import SOV, KEY, DIDMethod, DIDMethods +from .did_method import SOV, DIDMethod, DIDMethods from .error import WalletError, WalletDuplicateError, WalletNotFoundError from .key_type import BLS12381G2, ED25519, KeyType, KeyTypes from .util import b58_to_bytes, bytes_to_b58 @@ -171,29 +171,23 @@ async def create_local_did( WalletError: If there is another backend error """ - - # validate key_type - if not method.supports_key_type(key_type): - raise WalletError( - f"Invalid key type {key_type.key_type}" - f" for DID method {method.method_name}" - ) - - if method == KEY and did: - raise WalletError("Not allowed to set DID for DID method 'key'") + did_validation = DIDParametersValidation( + self._session.context.inject(DIDMethods) + ) + did_validation.validate_key_type(method, key_type) if not metadata: metadata = {} - if method not in [SOV, KEY]: - raise WalletError( - f"Unsupported DID method for askar storage: {method.method_name}" - ) try: keypair = _create_keypair(key_type, seed) verkey_bytes = keypair.get_public_bytes() verkey = bytes_to_b58(verkey_bytes) + did = did_validation.validate_or_derive_did( + method, key_type, verkey_bytes, did + ) + try: await self._session.handle.insert_key( verkey, keypair, metadata=json.dumps(metadata) @@ -205,11 +199,6 @@ async def create_local_did( else: raise WalletError("Error inserting key") from err - if method == KEY: - did = DIDKey.from_public_key(verkey_bytes, key_type).did - elif not did: - did = bytes_to_b58(verkey_bytes[:16]) - item = await self._session.handle.fetch(CATEGORY_DID, did, for_update=True) if item: did_info = item.value_json diff --git a/aries_cloudagent/wallet/did_method.py b/aries_cloudagent/wallet/did_method.py index af971ea019..cbe1361b39 100644 --- a/aries_cloudagent/wallet/did_method.py +++ b/aries_cloudagent/wallet/did_method.py @@ -1,19 +1,35 @@ """did method.py contains registry for did methods.""" +from enum import Enum from typing import Dict, List, Mapping, Optional from .error import BaseError from .key_type import BLS12381G2, ED25519, KeyType +class HolderDefinedDid(Enum): + """Define if a holder can specify its own did for a given method.""" + + NO = "no" # holder CANNOT provide a DID + ALLOWED = "allowed" # holder CAN provide a DID + REQUIRED = "required" # holder MUST provide a DID + + class DIDMethod: """Class to represent a did method.""" - def __init__(self, name: str, key_types: List[KeyType], rotation: bool = False): + def __init__( + self, + name: str, + key_types: List[KeyType], + rotation: bool = False, + holder_defined_did: HolderDefinedDid = HolderDefinedDid.NO, + ): """Construct did method class.""" self._method_name: str = name self._supported_key_types: List[KeyType] = key_types self._supports_rotation: bool = rotation + self._holder_defined_did: HolderDefinedDid = holder_defined_did @property def method_name(self): @@ -34,8 +50,21 @@ def supports_key_type(self, key_type: KeyType) -> bool: """Check whether the current method supports the key type.""" return key_type in self.supported_key_types + def holder_defined_did(self) -> HolderDefinedDid: + """Return the did derivation policy. -SOV = DIDMethod(name="sov", key_types=[ED25519], rotation=True) + eg: did:key DIDs are derived from the verkey -> HolderDefinedDid.NO + eg: did:web DIDs cannot be derived from key material -> HolderDefinedDid.REQUIRED + """ + return self._holder_defined_did + + +SOV = DIDMethod( + name="sov", + key_types=[ED25519], + rotation=True, + holder_defined_did=HolderDefinedDid.ALLOWED, +) KEY = DIDMethod( name="key", key_types=[ED25519, BLS12381G2], @@ -55,7 +84,7 @@ def __init__(self) -> None: def registered(self, method: str) -> bool: """Check for a supported method.""" - return method in list(self._registry.items()) + return method in self._registry.keys() def register(self, method: DIDMethod): """Register a new did method.""" diff --git a/aries_cloudagent/wallet/did_parameters_validation.py b/aries_cloudagent/wallet/did_parameters_validation.py new file mode 100644 index 0000000000..04572c77bf --- /dev/null +++ b/aries_cloudagent/wallet/did_parameters_validation.py @@ -0,0 +1,65 @@ +"""Tooling to validate DID creation parameters.""" + +from typing import Optional + +from aries_cloudagent.did.did_key import DIDKey +from aries_cloudagent.wallet.did_method import ( + DIDMethods, + DIDMethod, + HolderDefinedDid, + KEY, + SOV, +) +from aries_cloudagent.wallet.error import WalletError +from aries_cloudagent.wallet.key_type import KeyType +from aries_cloudagent.wallet.util import bytes_to_b58 + + +class DIDParametersValidation: + """A utility class to check compatibility of provided DID creation parameters.""" + + def __init__(self, did_methods: DIDMethods): + """:param did_methods: DID method registry relevant for the validation.""" + self.did_methods = did_methods + + @staticmethod + def validate_key_type(method: DIDMethod, key_type: KeyType): + """Validate compatibility of the DID method with the desired key type.""" + # validate key_type + if not method.supports_key_type(key_type): + raise WalletError( + f"Invalid key type {key_type.key_type}" + f" for DID method {method.method_name}" + ) + + def validate_or_derive_did( + self, + method: DIDMethod, + key_type: KeyType, + verkey: bytes, + did: Optional[str], + ) -> str: + """ + Validate compatibility of the provided did (if any) with the given DID method. + + If no DID was provided, automatically derive one for methods that support it. + """ + if method.holder_defined_did() == HolderDefinedDid.NO and did: + raise WalletError( + f"Not allowed to set DID for DID method '{method.method_name}'" + ) + elif method.holder_defined_did() == HolderDefinedDid.REQUIRED and not did: + raise WalletError(f"Providing a DID is required {method.method_name}") + elif not self.did_methods.registered(method.method_name): + raise WalletError( + f"Unsupported DID method for current storage: {method.method_name}" + ) + + # We need some did method specific handling. If more did methods + # are added it is probably better create a did method specific handler + elif method == KEY: + return DIDKey.from_public_key(verkey, key_type).did + elif method == SOV: + return bytes_to_b58(verkey[:16]) if not did else did + + return did diff --git a/aries_cloudagent/wallet/in_memory.py b/aries_cloudagent/wallet/in_memory.py index f9bb03a37f..5023e8d6c0 100644 --- a/aries_cloudagent/wallet/in_memory.py +++ b/aries_cloudagent/wallet/in_memory.py @@ -3,8 +3,8 @@ import asyncio from typing import List, Sequence, Tuple, Union +from .did_parameters_validation import DIDParametersValidation from ..core.in_memory import InMemoryProfile -from ..did.did_key import DIDKey from .base import BaseWallet from .crypto import ( @@ -17,7 +17,7 @@ ) from .did_info import KeyInfo, DIDInfo from .did_posture import DIDPosture -from .did_method import SOV, KEY, DIDMethod, DIDMethods +from .did_method import SOV, DIDMethod, DIDMethods from .error import WalletError, WalletDuplicateError, WalletNotFoundError from .key_type import KeyType from .util import b58_to_bytes, bytes_to_b58, random_seed @@ -212,27 +212,15 @@ async def create_local_did( """ seed = validate_seed(seed) or random_seed() - # validate key_type - if not method.supports_key_type(key_type): - raise WalletError( - f"Invalid key type {key_type.key_type} for method {method.method_name}" - ) + did_methods: DIDMethods = self.profile.context.inject(DIDMethods) + did_validation = DIDParametersValidation(did_methods) + + did_validation.validate_key_type(method, key_type) verkey, secret = create_keypair(key_type, seed) verkey_enc = bytes_to_b58(verkey) - # We need some did method specific handling. If more did methods - # are added it is probably better create a did method specific handler - if method == KEY: - if did: - raise WalletError("Not allowed to set DID for DID method 'key'") - - did = DIDKey.from_public_key(verkey, key_type).did - elif method == SOV: - if not did: - did = bytes_to_b58(verkey[:16]) - else: - raise WalletError(f"Unsupported DID method: {method.method_name}") + did = did_validation.validate_or_derive_did(method, key_type, verkey, did) if ( did in self.profile.local_dids diff --git a/aries_cloudagent/wallet/routes.py b/aries_cloudagent/wallet/routes.py index 1c038957dc..1fb4998934 100644 --- a/aries_cloudagent/wallet/routes.py +++ b/aries_cloudagent/wallet/routes.py @@ -25,6 +25,7 @@ INDY_DID, INDY_OR_KEY_DID, INDY_RAW_PUBLIC_KEY, + GENERIC_DID, ) from ..protocols.coordinate_mediation.v1_0.route_manager import RouteManager from ..protocols.endorse_transaction.v1_0.manager import ( @@ -38,7 +39,7 @@ from ..storage.error import StorageError, StorageNotFoundError from .base import BaseWallet from .did_info import DIDInfo -from .did_method import SOV, KEY, DIDMethod, DIDMethods +from .did_method import SOV, KEY, DIDMethod, DIDMethods, HolderDefinedDid from .did_posture import DIDPosture from .error import WalletError, WalletNotFoundError from .key_type import BLS12381G2, ED25519, KeyTypes @@ -119,7 +120,7 @@ class DIDEndpointSchema(OpenAPISchema): class DIDListQueryStringSchema(OpenAPISchema): """Parameters and validators for DID list request query string.""" - did = fields.Str(description="DID of interest", required=False, **INDY_OR_KEY_DID) + did = fields.Str(description="DID of interest", required=False, **GENERIC_DID) verkey = fields.Str( description="Verification key of interest", required=False, @@ -163,6 +164,8 @@ class DIDCreateOptionsSchema(OpenAPISchema): validate=validate.OneOf([ED25519.key_type, BLS12381G2.key_type]), ) + did = fields.Str(required=False, **GENERIC_DID) + class DIDCreateSchema(OpenAPISchema): """Parameters and validators for create DID endpoint.""" @@ -171,7 +174,6 @@ class DIDCreateSchema(OpenAPISchema): required=False, default=SOV.method_name, example=SOV.method_name, - validate=validate.OneOf([KEY.method_name, SOV.method_name]), ) options = fields.Nested( @@ -374,14 +376,26 @@ async def wallet_create_did(request: web.BaseRequest): f" support key type {key_type.key_type}" ) ) + + did = body.get("did") + if method.holder_defined_did() == HolderDefinedDid.NO and did: + raise web.HTTPForbidden( + reason=( + f"method {method.method_name} does not" + f" support user-defined DIDs" + ) + ) + elif method.holder_defined_did() == HolderDefinedDid.REQUIRED and not did: + raise web.HTTPBadRequest( + reason=f"method {method.method_name} requires a user-defined DIDs" + ) + wallet = session.inject_or(BaseWallet) if not wallet: raise web.HTTPForbidden(reason="No wallet available") try: info = await wallet.create_local_did( - method=method, - key_type=key_type, - seed=seed, + method=method, key_type=key_type, seed=seed, did=did ) except WalletError as err: diff --git a/aries_cloudagent/wallet/tests/test_did_parameters_validation.py b/aries_cloudagent/wallet/tests/test_did_parameters_validation.py new file mode 100644 index 0000000000..73565f827f --- /dev/null +++ b/aries_cloudagent/wallet/tests/test_did_parameters_validation.py @@ -0,0 +1,83 @@ +import pytest + +from aries_cloudagent.wallet.did_method import DIDMethods, DIDMethod, HolderDefinedDid +from aries_cloudagent.wallet.did_parameters_validation import DIDParametersValidation +from aries_cloudagent.wallet.error import WalletError +from aries_cloudagent.wallet.key_type import ED25519, BLS12381G1 + + +@pytest.fixture +def did_methods_registry(): + return DIDMethods() + + +def test_validate_key_type_uses_didmethod_when_validating_key_type( + did_methods_registry, +): + # given + ed_method = DIDMethod("ed-method", [ED25519]) + did_methods_registry.register(ed_method) + did_validation = DIDParametersValidation(did_methods_registry) + + # when - then + assert did_validation.validate_key_type(ed_method, ED25519) is None + with pytest.raises(WalletError): + did_validation.validate_key_type(ed_method, BLS12381G1) + + +def test_validate_key_type_raises_exception_when_validating_unknown_did_method( + did_methods_registry, +): + # given + unknown_method = DIDMethod("unknown", []) + did_validation = DIDParametersValidation(did_methods_registry) + + # when - then + with pytest.raises(WalletError): + did_validation.validate_key_type(unknown_method, ED25519) + + +def test_set_did_raises_error_when_did_is_provided_and_method_doesnt_allow( + did_methods_registry, +): + # given + ed_method = DIDMethod( + "derived-did", [ED25519], holder_defined_did=HolderDefinedDid.NO + ) + did_methods_registry.register(ed_method) + did_validation = DIDParametersValidation(did_methods_registry) + + # when - then + with pytest.raises(WalletError): + did_validation.validate_or_derive_did( + ed_method, ED25519, b"verkey", "did:edward:self-defined" + ) + + +def test_validate_or_derive_did_raises_error_when_no_did_is_provided_and_method_requires_one( + did_methods_registry, +): + # given + ed_method = DIDMethod( + "self-defined-did", [ED25519], holder_defined_did=HolderDefinedDid.REQUIRED + ) + did_methods_registry.register(ed_method) + did_validation = DIDParametersValidation(did_methods_registry) + + # when - then + with pytest.raises(WalletError): + did_validation.validate_or_derive_did(ed_method, ED25519, b"verkey", did=None) + + +def test_validate_or_derive_did_raises_exception_when_validating_unknown_did_method( + did_methods_registry, +): + # given + unknown_method = DIDMethod("unknown", []) + did_validation = DIDParametersValidation(did_methods_registry) + + # when - then + with pytest.raises(WalletError): + did_validation.validate_or_derive_did( + unknown_method, ED25519, b"verkey", did=None + ) diff --git a/aries_cloudagent/wallet/tests/test_in_memory_wallet.py b/aries_cloudagent/wallet/tests/test_in_memory_wallet.py index ecb3c97263..e9d81ffa27 100644 --- a/aries_cloudagent/wallet/tests/test_in_memory_wallet.py +++ b/aries_cloudagent/wallet/tests/test_in_memory_wallet.py @@ -13,6 +13,7 @@ @pytest.fixture() async def wallet(): profile = InMemoryProfile.test_profile() + profile.context.injector.bind_instance(DIDMethods, DIDMethods()) wallet = InMemoryWallet(profile) yield wallet diff --git a/aries_cloudagent/wallet/tests/test_indy_wallet.py b/aries_cloudagent/wallet/tests/test_indy_wallet.py index 8dfe821e58..47ae72cdec 100644 --- a/aries_cloudagent/wallet/tests/test_indy_wallet.py +++ b/aries_cloudagent/wallet/tests/test_indy_wallet.py @@ -17,7 +17,7 @@ from ...indy.sdk.wallet_setup import IndyWalletConfig from ...ledger.endpoint_type import EndpointType from ...ledger.indy import IndySdkLedgerPool -from ...wallet.did_method import SOV +from ...wallet.did_method import SOV, DIDMethods from ...wallet.key_type import ED25519 from .. import indy as test_module from ..base import BaseWallet @@ -28,7 +28,7 @@ @pytest.fixture() async def in_memory_wallet(): - profile = InMemoryProfile.test_profile() + profile = InMemoryProfile.test_profile(bind={DIDMethods: DIDMethods()}) wallet = InMemoryWallet(profile) yield wallet @@ -38,6 +38,7 @@ async def wallet(): key = await IndySdkWallet.generate_wallet_key() context = InjectionContext() context.injector.bind_instance(IndySdkLedgerPool, IndySdkLedgerPool("name")) + context.injector.bind_instance(DIDMethods, DIDMethods()) with async_mock.patch.object(IndySdkProfile, "_make_finalizer"): profile = cast( IndySdkProfile, diff --git a/aries_cloudagent/wallet/tests/test_routes.py b/aries_cloudagent/wallet/tests/test_routes.py index 75abc4d483..f04e6431ef 100644 --- a/aries_cloudagent/wallet/tests/test_routes.py +++ b/aries_cloudagent/wallet/tests/test_routes.py @@ -1,4 +1,5 @@ import mock as async_mock +import pytest from aiohttp.web import HTTPForbidden from async_case import IsolatedAsyncioTestCase @@ -6,7 +7,7 @@ from ...core.in_memory import InMemoryProfile from ...ledger.base import BaseLedger from ...protocols.coordinate_mediation.v1_0.route_manager import RouteManager -from ...wallet.did_method import SOV, DIDMethods +from ...wallet.did_method import SOV, DIDMethods, DIDMethod, HolderDefinedDid from ...wallet.key_type import ED25519, KeyTypes from .. import routes as test_module from ..base import BaseWallet @@ -38,7 +39,8 @@ def setUp(self): self.test_verkey = "verkey" self.test_posted_did = "posted-did" self.test_posted_verkey = "posted-verkey" - self.context.injector.bind_instance(DIDMethods, DIDMethods()) + self.did_methods = DIDMethods() + self.context.injector.bind_instance(DIDMethods, self.did_methods) async def test_missing_wallet(self): self.session_inject[BaseWallet] = None @@ -134,6 +136,46 @@ async def test_create_did_unsupported_key_type(self): with self.assertRaises(test_module.web.HTTPForbidden): await test_module.wallet_create_did(self.request) + async def test_create_did_method_requires_user_defined_did(self): + # given + did_custom = DIDMethod( + name="custom", + key_types=[ED25519], + rotation=True, + holder_defined_did=HolderDefinedDid.REQUIRED, + ) + self.did_methods.register(did_custom) + + self.request.json = async_mock.AsyncMock( + return_value={"method": "custom", "options": {"key_type": "ed25519"}} + ) + + # when - then + with self.assertRaises(test_module.web.HTTPBadRequest): + await test_module.wallet_create_did(self.request) + + async def test_create_did_method_doesnt_support_user_defined_did(self): + did_custom = DIDMethod( + name="custom", + key_types=[ED25519], + rotation=True, + holder_defined_did=HolderDefinedDid.NO, + ) + self.did_methods.register(did_custom) + + # when + self.request.json = async_mock.AsyncMock( + return_value={ + "method": "custom", + "did": "did:custom:aCustomUserDefinedDID", + "options": {"key_type": ED25519.key_type}, + } + ) + + # then + with self.assertRaises(test_module.web.HTTPForbidden): + await test_module.wallet_create_did(self.request) + async def test_create_did_x(self): self.wallet.create_local_did.side_effect = test_module.WalletError() with self.assertRaises(test_module.web.HTTPBadRequest): From cab68e136a59ddb2635b38a6e6b8c6bf43220dd0 Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Wed, 4 Jan 2023 12:14:25 -0800 Subject: [PATCH 617/872] update github actions dependencies (for node16 support) Signed-off-by: Andrew Whitehead --- .github/workflows/blackformat.yml | 6 +++-- .github/workflows/codeql.yml | 9 +++++--- .github/workflows/integrationtests.yml | 2 +- .github/workflows/pythonpublish.yml | 32 +++++++++++++------------- 4 files changed, 27 insertions(+), 22 deletions(-) diff --git a/.github/workflows/blackformat.yml b/.github/workflows/blackformat.yml index a9ae5dfdee..1885ba40c4 100644 --- a/.github/workflows/blackformat.yml +++ b/.github/workflows/blackformat.yml @@ -10,7 +10,9 @@ jobs: name: lint runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: "3.9" - name: Black Code Formatter Check uses: psf/black@stable diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 9d5ee6a7ea..e6f15917a0 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -12,15 +12,18 @@ jobs: runs-on: ubuntu-latest if: (github.event_name == 'pull_request' && github.repository == 'hyperledger/aries-cloudagent-python') || (github.event_name != 'pull_request') + permissions: + security-events: write + steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v2 with: languages: python - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/integrationtests.yml b/.github/workflows/integrationtests.yml index db62b14a34..2d68ee315f 100644 --- a/.github/workflows/integrationtests.yml +++ b/.github/workflows/integrationtests.yml @@ -13,7 +13,7 @@ jobs: if: (github.event_name == 'pull_request' && github.repository == 'hyperledger/aries-cloudagent-python') || (github.event_name != 'pull_request') steps: - name: checkout-acapy - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: path: acapy #- name: run-von-network diff --git a/.github/workflows/pythonpublish.yml b/.github/workflows/pythonpublish.yml index 21f2f01de1..b42e56685b 100644 --- a/.github/workflows/pythonpublish.yml +++ b/.github/workflows/pythonpublish.yml @@ -8,19 +8,19 @@ jobs: deploy: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 - - name: Set up Python - uses: actions/setup-python@v1 - with: - python-version: '3.x' - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install setuptools wheel twine - - name: Build and publish - env: - TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} - TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} - run: | - python setup.py sdist bdist_wheel - twine upload dist/* + - uses: actions/checkout@v3 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: "3.x" + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install setuptools wheel twine + - name: Build and publish + env: + TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} + TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} + run: | + python setup.py sdist bdist_wheel + twine upload dist/* From 25ca92aff9b0043137fe3d1cf2e0a4787817a945 Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Mon, 9 Jan 2023 09:18:38 -0800 Subject: [PATCH 618/872] update setup-buildx-action Signed-off-by: Andrew Whitehead --- .github/workflows/tests-indy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests-indy.yml b/.github/workflows/tests-indy.yml index a893acf5b5..7e69e76b30 100644 --- a/.github/workflows/tests-indy.yml +++ b/.github/workflows/tests-indy.yml @@ -29,7 +29,7 @@ jobs: ${{ runner.os }}-buildx-test- - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v2 - name: Build test image uses: docker/build-push-action@v3 From 97b44f67e7723bdd12247c02358c79b2c9496e13 Mon Sep 17 00:00:00 2001 From: Roman Reinert Date: Mon, 9 Jan 2023 16:25:36 +0000 Subject: [PATCH 619/872] define state 'deleted' in BaseRecord Signed-off-by: Roman Reinert --- aries_cloudagent/messaging/models/base_record.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/messaging/models/base_record.py b/aries_cloudagent/messaging/models/base_record.py index da1f7914f1..ec0bcd0189 100644 --- a/aries_cloudagent/messaging/models/base_record.py +++ b/aries_cloudagent/messaging/models/base_record.py @@ -81,6 +81,7 @@ class Meta: EVENT_NAMESPACE: str = "acapy::record" LOG_STATE_FLAG = None TAG_NAMES = {"state"} + STATE_DELETED = "deleted" def __init__( self, @@ -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) From c54963181b5b0bb6708f7d09c8c8ac3a3b2e8773 Mon Sep 17 00:00:00 2001 From: Roman Reinert Date: Tue, 10 Jan 2023 10:30:47 +0000 Subject: [PATCH 620/872] add function to get attributes by prefix Signed-off-by: Roman Reinert --- .../messaging/models/base_record.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/aries_cloudagent/messaging/models/base_record.py b/aries_cloudagent/messaging/models/base_record.py index ec0bcd0189..c696cf6771 100644 --- a/aries_cloudagent/messaging/models/base_record.py +++ b/aries_cloudagent/messaging/models/base_record.py @@ -498,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.""" From 4c6d7caef6bc9ecee309dc3a36e158a8f710a33e Mon Sep 17 00:00:00 2001 From: Roman Reinert Date: Tue, 10 Jan 2023 11:44:41 +0000 Subject: [PATCH 621/872] fix attribute validation in conn- cred_ex- and pres_ex records Signed-off-by: Roman Reinert --- .../connections/models/conn_record.py | 18 +++--------------- .../v2_0/models/cred_ex_record.py | 18 +++--------------- .../present_proof/v2_0/models/pres_exchange.py | 18 +++--------------- 3 files changed, 9 insertions(+), 45 deletions(-) diff --git a/aries_cloudagent/connections/models/conn_record.py b/aries_cloudagent/connections/models/conn_record.py index eb0342970a..ca9e21b07f 100644 --- a/aries_cloudagent/connections/models/conn_record.py +++ b/aries_cloudagent/connections/models/conn_record.py @@ -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, ) @@ -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( @@ -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( diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/models/cred_ex_record.py b/aries_cloudagent/protocols/issue_credential/v2_0/models/cred_ex_record.py index fbbc0adaf5..900310f524 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/models/cred_ex_record.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/models/cred_ex_record.py @@ -296,11 +296,7 @@ class Meta: description="Issue-credential exchange initiator: self or external", example=V20CredExRecord.INITIATOR_SELF, validate=validate.OneOf( - [ - getattr(V20CredExRecord, m) - for m in vars(V20CredExRecord) - if m.startswith("INITIATOR_") - ] + V20CredExRecord.get_attributes_by_prefix("INITIATOR_", walk_mro=False) ), ) role = fields.Str( @@ -308,11 +304,7 @@ class Meta: description="Issue-credential exchange role: holder or issuer", example=V20CredExRecord.ROLE_ISSUER, validate=validate.OneOf( - [ - getattr(V20CredExRecord, m) - for m in vars(V20CredExRecord) - if m.startswith("ROLE_") - ] + V20CredExRecord.get_attributes_by_prefix("ROLE_", walk_mro=False) ), ) state = fields.Str( @@ -320,11 +312,7 @@ class Meta: description="Issue-credential exchange state", example=V20CredExRecord.STATE_DONE, validate=validate.OneOf( - [ - getattr(V20CredExRecord, m) - for m in vars(V20CredExRecord) - if m.startswith("STATE_") - ] + V20CredExRecord.get_attributes_by_prefix("STATE_", walk_mro=True) ), ) cred_preview = fields.Nested( diff --git a/aries_cloudagent/protocols/present_proof/v2_0/models/pres_exchange.py b/aries_cloudagent/protocols/present_proof/v2_0/models/pres_exchange.py index cc314aa9db..4b5e22a10b 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/models/pres_exchange.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/models/pres_exchange.py @@ -244,11 +244,7 @@ class Meta: description="Present-proof exchange initiator: self or external", example=V20PresExRecord.INITIATOR_SELF, validate=validate.OneOf( - [ - getattr(V20PresExRecord, m) - for m in vars(V20PresExRecord) - if m.startswith("INITIATOR_") - ] + V20PresExRecord.get_attributes_by_prefix("INITIATOR_", walk_mro=False) ), ) role = fields.Str( @@ -256,22 +252,14 @@ class Meta: description="Present-proof exchange role: prover or verifier", example=V20PresExRecord.ROLE_PROVER, validate=validate.OneOf( - [ - getattr(V20PresExRecord, m) - for m in vars(V20PresExRecord) - if m.startswith("ROLE_") - ] + V20PresExRecord.get_attributes_by_prefix("ROLE_", walk_mro=False) ), ) state = fields.Str( required=False, description="Present-proof exchange state", validate=validate.OneOf( - [ - getattr(V20PresExRecord, m) - for m in vars(V20PresExRecord) - if m.startswith("STATE_") - ] + V20PresExRecord.get_attributes_by_prefix("STATE_", walk_mro=True) ), ) pres_proposal = fields.Nested( From a6c36d02ee1f68b7fabdc11c3e5ba561b6138c85 Mon Sep 17 00:00:00 2001 From: Roman Reinert Date: Tue, 10 Jan 2023 11:45:07 +0000 Subject: [PATCH 622/872] add validation to oob record Signed-off-by: Roman Reinert --- .../protocols/out_of_band/v1_0/models/oob_record.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/models/oob_record.py b/aries_cloudagent/protocols/out_of_band/v1_0/models/oob_record.py index 69f668335b..e550d9eb54 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/models/oob_record.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/models/oob_record.py @@ -3,7 +3,7 @@ import json from typing import Any, Mapping, Optional, Union -from marshmallow import fields +from marshmallow import fields, validate from .....connections.models.conn_record import ConnRecord from .....core.profile import ProfileSession @@ -248,6 +248,9 @@ class Meta: required=True, description="Out of band message exchange state", example=OobRecord.STATE_AWAIT_RESPONSE, + validate=validate.OneOf( + OobRecord.get_attributes_by_prefix("STATE_", walk_mro=True) + ), ) invi_msg_id = fields.Str( required=True, @@ -287,4 +290,7 @@ class Meta: description="OOB Role", required=False, example=OobRecord.ROLE_RECEIVER, + validate=validate.OneOf( + OobRecord.get_attributes_by_prefix("ROLE_", walk_mro=False) + ), ) From aae6d83d14c6eb0449b64c1b218947e738fd2f44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Humbert?= Date: Fri, 13 Jan 2023 07:55:57 +0100 Subject: [PATCH 623/872] fix: create-did, get did from options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Get holder-defined DID from options instead of requests' root. Signed-off-by: Clément Humbert --- aries_cloudagent/wallet/routes.py | 2 +- aries_cloudagent/wallet/tests/test_routes.py | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/aries_cloudagent/wallet/routes.py b/aries_cloudagent/wallet/routes.py index 1fb4998934..912d8e9930 100644 --- a/aries_cloudagent/wallet/routes.py +++ b/aries_cloudagent/wallet/routes.py @@ -377,7 +377,7 @@ async def wallet_create_did(request: web.BaseRequest): ) ) - did = body.get("did") + did = body.get("options", {}).get("did") if method.holder_defined_did() == HolderDefinedDid.NO and did: raise web.HTTPForbidden( reason=( diff --git a/aries_cloudagent/wallet/tests/test_routes.py b/aries_cloudagent/wallet/tests/test_routes.py index f04e6431ef..03c1dc57cb 100644 --- a/aries_cloudagent/wallet/tests/test_routes.py +++ b/aries_cloudagent/wallet/tests/test_routes.py @@ -167,8 +167,10 @@ async def test_create_did_method_doesnt_support_user_defined_did(self): self.request.json = async_mock.AsyncMock( return_value={ "method": "custom", - "did": "did:custom:aCustomUserDefinedDID", - "options": {"key_type": ED25519.key_type}, + "options": { + "key_type": ED25519.key_type, + "did": "did:custom:aCustomUserDefinedDID", + }, } ) From 275f3057e2de510bbdcf4d3790903b1b1b204430 Mon Sep 17 00:00:00 2001 From: Wade Barnes Date: Thu, 12 Jan 2023 06:56:13 -0800 Subject: [PATCH 624/872] fix: Publish ACA-Py Image workflows Signed-off-by: Wade Barnes --- .github/workflows/publish-indy.yml | 47 ++++++++++++------------------ .github/workflows/publish.yml | 40 +++++++++---------------- 2 files changed, 32 insertions(+), 55 deletions(-) diff --git a/.github/workflows/publish-indy.yml b/.github/workflows/publish-indy.yml index 8761807d22..31dbc2b9a1 100644 --- a/.github/workflows/publish-indy.yml +++ b/.github/workflows/publish-indy.yml @@ -1,33 +1,32 @@ -name: Publish ACA-Py (Indy) +name: Publish ACA-Py Image (Indy) +run-name: Publish ACA-Py ${{ inputs.tag || github.event.release.tag_name }} Image (Indy ${{ inputs.indy_version || '1.16.0' }}) on: release: types: [released] + workflow_dispatch: inputs: indy_version: description: 'Indy SDK Version' - required: false + required: true + default: 1.16.0 type: string tag: description: 'Image tag' required: true type: string - version: - description: "Version label in image" - required: true - type: string env: - INDY_VERSION: 1.16.0 + INDY_VERSION: ${{ inputs.indy_version || '1.16.0' }} jobs: publish-image: strategy: fail-fast: false matrix: - python-version: ['3.7', '3.8', '3.9', '3.10'] + python-version: ['3.6', '3.9'] - name: Publish (Indy) + name: Publish ACA-Py Image (Indy) runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -35,7 +34,7 @@ jobs: - name: Gather image info id: info run: | - echo "::set-output name=repo-owner::${GITHUB_REPOSITORY_OWNER,,}" + echo "repo-owner=${GITHUB_REPOSITORY_OWNER,,}" >> $GITHUB_OUTPUT - name: Cache Docker layers uses: actions/cache@v3 @@ -46,34 +45,23 @@ jobs: ${{ runner.os }}-buildx- - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v2 - name: Log in to the GitHub Container Registry - uses: docker/login-action@v1 + uses: docker/login-action@v2 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Setup Image Metadata (manual) - if: github.event_name == 'workflow_dispatch' - id: dispatch-meta - uses: docker/metadata-action@v4.1.1 - with: - images: | - ghcr.io/${{ steps.info.outputs.repo-owner }}/aries-cloudagent-python - tags: | - type=raw,value=py${{ matrix.python-version }}-indy-${{ inputs.tag }} - - - name: Setup Image Metadata (release) - if: github.event_name == 'release' + - name: Setup Image Metadata id: meta - uses: docker/metadata-action@v4.1.1 + uses: docker/metadata-action@v4 with: images: | ghcr.io/${{ steps.info.outputs.repo-owner }}/aries-cloudagent-python tags: | - type=semver,pattern=py${{ matrix.python-version }}-indy-{{ inputs.indy_version || env.INDY_VERSION }}-{{version}} + type=raw,value=py${{ matrix.python-version }}-indy-${{ env.INDY_VERSION }}-${{ inputs.tag || github.event.release.tag_name }} - name: Build and Push Image to ghcr.io uses: docker/build-push-action@v3 @@ -81,12 +69,13 @@ jobs: push: true context: . file: docker/Dockerfile.indy - tags: ${{ steps.dispatch-meta.outputs.tags || steps.meta.outputs.tags }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} target: main build-args: | python_version=${{ matrix.python-version }} - indy_version=${{ inputs.indy_version || env.INDY_VERSION }} - acapy_version=${{ inputs.version || github.event.release.tag_name }} + indy_version=${{ env.INDY_VERSION }} + acapy_version=${{ inputs.tag || github.event.release.tag_name }} cache-from: type=local,src=/tmp/.buildx-cache cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index b270b55a8a..abbd88ccc2 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,26 +1,24 @@ -name: Publish ACA-Py +name: Publish ACA-Py Image +run-name: Publish ACA-Py ${{ inputs.tag || github.event.release.tag_name }} Image on: release: types: [released] + workflow_dispatch: inputs: tag: description: 'Image tag' required: true type: string - version: - description: "Version label in image" - required: true - type: string jobs: publish-image: strategy: fail-fast: false matrix: - python-version: ['3.7', '3.8', '3.9', '3.10'] + python-version: ['3.6', '3.9'] - name: Publish + name: Publish ACA-Py Image runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -28,7 +26,7 @@ jobs: - name: Gather image info id: info run: | - echo "::set-output name=repo-owner::${GITHUB_REPOSITORY_OWNER,,}" + echo "repo-owner=${GITHUB_REPOSITORY_OWNER,,}" >> $GITHUB_OUTPUT - name: Cache Docker layers uses: actions/cache@v3 @@ -39,34 +37,23 @@ jobs: ${{ runner.os }}-buildx- - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v2 - name: Log in to the GitHub Container Registry - uses: docker/login-action@v1 + uses: docker/login-action@v2 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Setup Image Metadata (manual) - if: github.event_name == 'workflow_dispatch' - id: dispatch-meta - uses: docker/metadata-action@v4.1.1 - with: - images: | - ghcr.io/${{ steps.info.outputs.repo-owner }}/aries-cloudagent-python - tags: | - type=raw,value=py${{ matrix.python-version }}-${{ inputs.tag }} - - - name: Setup Image Metadata (release) - if: github.event_name == 'release' + - name: Setup Image Metadata id: meta - uses: docker/metadata-action@v4.1.1 + uses: docker/metadata-action@v4 with: images: | ghcr.io/${{ steps.info.outputs.repo-owner }}/aries-cloudagent-python tags: | - type=semver,pattern=py${{ matrix.python-version }}-{{version}} + type=raw,value=py${{ matrix.python-version }}-${{ inputs.tag || github.event.release.tag_name }} - name: Build and Push Image to ghcr.io uses: docker/build-push-action@v3 @@ -74,11 +61,12 @@ jobs: push: true context: . file: docker/Dockerfile - tags: ${{ steps.dispatch-meta.outputs.tags || steps.meta.outputs.tags }} + tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} + target: main build-args: | python_version=${{ matrix.python-version }} - acapy_version=${{ inputs.version || github.event.release.tag_name }} + acapy_version=${{ inputs.tag || github.event.release.tag_name }} cache-from: type=local,src=/tmp/.buildx-cache cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max From c29d72526a16af9576c728e884908b181a050d9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Humbert?= Date: Mon, 16 Jan 2023 09:44:19 +0100 Subject: [PATCH 625/872] chore: add documentation for DIDMethods MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Augment existing swagger documentation for the `POST /wallet/did/create` route * Add `DIDMethods.md` documentation to introduce the concept of the `DIDMethods` registry and its use to register new methods. Signed-off-by: Clément Humbert --- DIDMethods.md | 45 +++++++++++++++++++++++++++++++ aries_cloudagent/wallet/routes.py | 13 +++++++-- 2 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 DIDMethods.md diff --git a/DIDMethods.md b/DIDMethods.md new file mode 100644 index 0000000000..179103aba8 --- /dev/null +++ b/DIDMethods.md @@ -0,0 +1,45 @@ +# DID methods in ACA-Py +Decentralized Identifiers, or DIDs, are URIs that point to documents that describe cryptographic primitives and protocols used in decentralized identity management. +DIDs include methods that describe where and how documents can be retrieved. +DID methods support specific types of keys and may or may not require the holder to specify the DID itself. + +ACA-Py provides a `DIDMethods` registry holding all the DID methods supported for storage in a wallet + +> :warning: Askar and InMemory are the only wallets supporting this registry. + +## Registering a DID method +By default, ACA-Py supports `did:key` and `did:sov`. +Plugins can register DID additional methods to make them available to holders. +Here's a snippet adding support for `did:web` to the registry from a plugin `setup` method. + +```python= +WEB = DIDMethod( + name="web", + key_types=[ED25519, BLS12381G2], + rotation=True, + holder_defined_did=HolderDefinedDid.REQUIRED # did:web is not derived from key material but from a user-provided respository name +) + +async def setup(context: InjectionContext): + methods = context.inject(DIDMethods) + methods.register(WEB) +``` + +## Creating a DID + +`POST /wallet/did/create` can be provided with parameters for any registered DID method. Here's a follow-up to the +`did:web` method example: + +```json= +{ + "method": "web", + "options": { + "did": "did:web:doma.in", + "key_type": "ed25519" + } +} +``` + +## Resolving DIDs + +For specifics on how DIDs are resolved in ACA-Py, see: [DID Resolution](DIDResolution.md). diff --git a/aries_cloudagent/wallet/routes.py b/aries_cloudagent/wallet/routes.py index 912d8e9930..3cd204f66b 100644 --- a/aries_cloudagent/wallet/routes.py +++ b/aries_cloudagent/wallet/routes.py @@ -161,10 +161,17 @@ class DIDCreateOptionsSchema(OpenAPISchema): key_type = fields.Str( required=True, example=ED25519.key_type, + description="Key type to use for the DID keypair. " + + "Validated with the chosen DID method's supported key types.", validate=validate.OneOf([ED25519.key_type, BLS12381G2.key_type]), ) - did = fields.Str(required=False, **GENERIC_DID) + did = fields.Str( + required=False, + description="Specify final value of the did (including did:: prefix)" + + "if the method supports or requires so.", + **GENERIC_DID, + ) class DIDCreateSchema(OpenAPISchema): @@ -174,12 +181,14 @@ class DIDCreateSchema(OpenAPISchema): required=False, default=SOV.method_name, example=SOV.method_name, + description="Method for the requested DID." + + "Supported methods are 'key', 'sov', and any other registered method.", ) options = fields.Nested( DIDCreateOptionsSchema, required=False, - description="To define a key type for a did:key", + description="To define a key type and/or a did depending on chosen DID method.", ) seed = fields.Str( From 1bdf7268a72ddbf522bdb47e4b8a9ee65a4e75e7 Mon Sep 17 00:00:00 2001 From: John Kent Date: Fri, 13 Jan 2023 17:38:12 -0500 Subject: [PATCH 626/872] feat: issue #2068 Allow HEAD requests to default route Signed-off-by: John Kent --- .pre-commit-config.yaml | 2 +- aries_cloudagent/admin/server.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b4a0f811b8..6c0600e70f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -12,7 +12,7 @@ repos: hooks: - id: black stages: [commit] - - repo: https://gitlab.com/pycqa/flake8 + - repo: https://github.com/pycqa/flake8.git rev: 3.9.0 hooks: - id: flake8 diff --git a/aries_cloudagent/admin/server.py b/aries_cloudagent/admin/server.py index c7ab79334d..846405a024 100644 --- a/aries_cloudagent/admin/server.py +++ b/aries_cloudagent/admin/server.py @@ -433,7 +433,7 @@ async def setup_context(request: web.Request, handler): ) server_routes = [ - web.get("/", self.redirect_handler, allow_head=False), + web.get("/", self.redirect_handler, allow_head=True), web.get("/plugins", self.plugins_handler, allow_head=False), web.get("/status", self.status_handler, allow_head=False), web.get("/status/config", self.config_handler, allow_head=False), From 2102fb1253d9e56d38550b37b6550a64ae679f96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emiliano=20Su=C3=B1=C3=A9?= Date: Wed, 18 Jan 2023 09:23:46 -0800 Subject: [PATCH 627/872] Enable multi-arch builds for gha publish workflows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Emiliano Suñé Signed-off-by: Wade Barnes --- .github/workflows/publish-indy.yml | 11 ++++++++++- .github/workflows/publish.yml | 4 +++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish-indy.yml b/.github/workflows/publish-indy.yml index 31dbc2b9a1..874851d5c9 100644 --- a/.github/workflows/publish-indy.yml +++ b/.github/workflows/publish-indy.yml @@ -16,6 +16,10 @@ on: required: true type: string +# Note: +# - ACA-Py with Indy SDK image builds do not include support for the linux/arm64 platform. +# - See notes below for details. + env: INDY_VERSION: ${{ inputs.indy_version || '1.16.0' }} @@ -29,7 +33,8 @@ jobs: name: Publish ACA-Py Image (Indy) runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - name: Checkout Code + uses: actions/checkout@v3 - name: Gather image info id: info @@ -78,6 +83,10 @@ jobs: acapy_version=${{ inputs.tag || github.event.release.tag_name }} cache-from: type=local,src=/tmp/.buildx-cache cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max + # Images do not include support for the linux/arm64 platform due to a known issue compiling the postgres plugin + # - https://github.com/hyperledger/indy-sdk/issues/2445 + # There is a pending PR to fix this issue here; https://github.com/hyperledger/indy-sdk/pull/2453 + platforms: linux/amd64,linux/386 # Temp fix # https://github.com/docker/build-push-action/issues/252 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index abbd88ccc2..6ee9378c61 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -21,7 +21,8 @@ jobs: name: Publish ACA-Py Image runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - name: Checkout Code + uses: actions/checkout@v3 - name: Gather image info id: info @@ -69,6 +70,7 @@ jobs: acapy_version=${{ inputs.tag || github.event.release.tag_name }} cache-from: type=local,src=/tmp/.buildx-cache cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max + platforms: linux/amd64,linux/arm64,linux/386 # Temp fix # https://github.com/docker/build-push-action/issues/252 From 72185aa8394844b4c6387c203ce4767cc735034f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emiliano=20Su=C3=B1=C3=A9?= Date: Wed, 18 Jan 2023 09:24:37 -0800 Subject: [PATCH 628/872] Tweak dockerfiles to install build dependencies and remove them when done MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Emiliano Suñé --- docker/Dockerfile | 4 ++++ docker/Dockerfile.indy | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/docker/Dockerfile b/docker/Dockerfile index 955479f307..fa7ec1e957 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -44,6 +44,7 @@ RUN apt-get update -y && \ apt-get install -y --no-install-recommends \ apt-transport-https \ ca-certificates \ + build-essential \ bzip2 \ curl \ git \ @@ -88,6 +89,9 @@ COPY --from=build /src/dist/aries_cloudagent*.whl . RUN pip install --no-cache-dir --find-links=. aries_cloudagent${acapy_reqs} && rm aries_cloudagent*.whl +# Clean-up unneccessary build dependencies and reduce final image size +RUN apt-get purge -y --auto-remove build-essential + USER $user ENTRYPOINT ["aca-py"] diff --git a/docker/Dockerfile.indy b/docker/Dockerfile.indy index 1072998985..19f7f86a64 100644 --- a/docker/Dockerfile.indy +++ b/docker/Dockerfile.indy @@ -115,6 +115,7 @@ RUN apt-get update -y && \ apt-get install -y --no-install-recommends \ apt-transport-https \ ca-certificates \ + build-essential \ bzip2 \ curl \ git \ @@ -257,4 +258,7 @@ COPY --from=acapy-builder /src/dist/aries_cloudagent*.whl . RUN pip install --no-cache-dir --find-links=. aries_cloudagent${acapy_reqs} && rm aries_cloudagent*.whl +# Clean-up unneccessary build dependencies and reduce final image size +# RUN apt-get purge -y --auto-remove build-essential + ENTRYPOINT ["aca-py"] From 21d8328eecf2b63e35b7ea48a68d12820799da7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Humbert?= Date: Mon, 23 Jan 2023 16:22:33 +0100 Subject: [PATCH 629/872] fix: create local DID return schema MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As a follow-up to the changes in the creation endpoint, relax return schema to include any did method. Signed-off-by: Clément Humbert --- aries_cloudagent/wallet/routes.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/aries_cloudagent/wallet/routes.py b/aries_cloudagent/wallet/routes.py index 3cd204f66b..462192d03e 100644 --- a/aries_cloudagent/wallet/routes.py +++ b/aries_cloudagent/wallet/routes.py @@ -23,7 +23,6 @@ ENDPOINT, ENDPOINT_TYPE, INDY_DID, - INDY_OR_KEY_DID, INDY_RAW_PUBLIC_KEY, GENERIC_DID, ) @@ -55,7 +54,7 @@ class WalletModuleResponseSchema(OpenAPISchema): class DIDSchema(OpenAPISchema): """Result schema for a DID.""" - did = fields.Str(description="DID of interest", **INDY_OR_KEY_DID) + did = fields.Str(description="DID of interest", **GENERIC_DID) verkey = fields.Str(description="Public verification key", **INDY_RAW_PUBLIC_KEY) posture = fields.Str( description=( @@ -66,11 +65,7 @@ class DIDSchema(OpenAPISchema): **DID_POSTURE, ) method = fields.Str( - description="Did method associated with the DID", - example=SOV.method_name, - validate=validate.OneOf( - [method.method_name for method in [SOV, KEY]] - ), # TODO: support more methods + description="Did method associated with the DID", example=SOV.method_name ) key_type = fields.Str( description="Key type associated with the DID", From 73862d9e915b4de1bd5cf7b770875275c32c4bda Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Tue, 24 Jan 2023 09:27:57 -0800 Subject: [PATCH 630/872] Multitenancy demo Signed-off-by: Ian Costanzo --- demo/docker-agent/Dockerfile.acapy | 4 --- demo/multi-demo/Dockerfile.acapy | 10 +++++++ demo/multi-demo/README.md | 40 +++++++++++++++++++++++++ demo/multi-demo/docker-compose.yml | 47 ++++++++++++++++++++++++++++++ demo/multi-demo/ngrok-wait.sh | 47 ++++++++++++++++++++++++++++++ 5 files changed, 144 insertions(+), 4 deletions(-) create mode 100644 demo/multi-demo/Dockerfile.acapy create mode 100644 demo/multi-demo/README.md create mode 100644 demo/multi-demo/docker-compose.yml create mode 100755 demo/multi-demo/ngrok-wait.sh diff --git a/demo/docker-agent/Dockerfile.acapy b/demo/docker-agent/Dockerfile.acapy index aa9e5593de..a8eee30ae0 100644 --- a/demo/docker-agent/Dockerfile.acapy +++ b/demo/docker-agent/Dockerfile.acapy @@ -8,7 +8,3 @@ COPY ngrok-wait.sh ngrok-wait.sh RUN chmod +x ./ngrok-wait.sh USER $user - -# temporary until this PR gets merged/released -RUN pip uninstall -y aries-cloudagent -RUN pip install aries-cloudagent[indy,bbs,askar]@git+https://github.com/ianco/aries-cloudagent-python@endorser-write-did diff --git a/demo/multi-demo/Dockerfile.acapy b/demo/multi-demo/Dockerfile.acapy new file mode 100644 index 0000000000..a8eee30ae0 --- /dev/null +++ b/demo/multi-demo/Dockerfile.acapy @@ -0,0 +1,10 @@ +FROM bcgovimages/aries-cloudagent:py36-1.16-1_1.0.0-rc0 + +USER root + +ADD https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64 ./jq +RUN chmod +x ./jq +COPY ngrok-wait.sh ngrok-wait.sh +RUN chmod +x ./ngrok-wait.sh + +USER $user diff --git a/demo/multi-demo/README.md b/demo/multi-demo/README.md new file mode 100644 index 0000000000..208a8f7996 --- /dev/null +++ b/demo/multi-demo/README.md @@ -0,0 +1,40 @@ +# Running an Aca-Py Agent in Multitenant Mode + +This directory contains scripts to run an aca-py agent in multitenancy mode. + +## Running the Agent + +The docker-compose script runs ngrok to expose the agent's port publicly, and stores wallet data in a postgres database. + +To run the agent in this repo, open a command shell in this directory and run: + +- to build the containers: + +```bash +docker-compose build +``` + +- to run the agent: + +```bash +docker-compose up +``` + +You can connect to the [agent's api service here](http://localhost:8010). + +Note that all the configuration settings are hard-coded in the docker-compose file and ngrok-wait.sh script, so if you change any configs you need to rebuild the docker images. + +- to shut down the agent: + +```bash +docker-compose stop +docker-compose rm -f +``` + +This will leave the agent's wallet data, so if you restart the agent it will maintain any created data. + +- to remove the agent's wallet: + +```bash +docker volume rm docker-agent_wallet-db-data +``` diff --git a/demo/multi-demo/docker-compose.yml b/demo/multi-demo/docker-compose.yml new file mode 100644 index 0000000000..be9fb0683c --- /dev/null +++ b/demo/multi-demo/docker-compose.yml @@ -0,0 +1,47 @@ +# Sample docker-compose to start a local aca-py multitenancy agent +# To start aca-py and the postgres database, just run `docker-compose up` +# To shut down the services run `docker-compose rm` - this will retain the postgres database, so you can change aca-py startup parameters +# and restart the docker containers without losing your wallet data +# If you want to delete your wallet data just run `docker volume ls -q | xargs docker volume rm` +version: "3" +services: + ngrok-agent: + image: wernight/ngrok + ports: + - 4067:4040 + command: ngrok http multi-agent:8001 --log stdout + + multi-agent: + build: + context: . + dockerfile: Dockerfile.acapy + environment: + - NGROK_NAME=ngrok-agent + ports: + - 8010:8010 + - 8001:8001 + depends_on: + - wallet-db + entrypoint: /bin/bash + command: [ + "-c", + "sleep 5; \ + ./ngrok-wait.sh" + ] + volumes: + - ./ngrok-wait.sh:/home/indy/ngrok-wait.sh + + wallet-db: + image: vcr-postgresql + environment: + - POSTGRESQL_USER=DB_USER + - POSTGRESQL_PASSWORD=DB_PASSWORD + - POSTGRESQL_DATABASE=DB_USER + - POSTGRESQL_ADMIN_PASSWORD=mysecretpassword + ports: + - 5433:5432 + volumes: + - wallet-db-data:/var/lib/pgsql/data + +volumes: + wallet-db-data: diff --git a/demo/multi-demo/ngrok-wait.sh b/demo/multi-demo/ngrok-wait.sh new file mode 100755 index 0000000000..df0bc08d6e --- /dev/null +++ b/demo/multi-demo/ngrok-wait.sh @@ -0,0 +1,47 @@ +#!/bin/bash + +# based on code developed by Sovrin: https://github.com/hyperledger/aries-acapy-plugin-toolbox + +echo "using ngrok end point [$NGROK_NAME]" + +NGROK_ENDPOINT=null +while [ -z "$NGROK_ENDPOINT" ] || [ "$NGROK_ENDPOINT" = "null" ] +do + echo "Fetching end point from ngrok service" + NGROK_ENDPOINT=$(curl --silent $NGROK_NAME:4040/api/tunnels | ./jq -r '.tunnels[] | select(.proto=="https") | .public_url') + + if [ -z "$NGROK_ENDPOINT" ] || [ "$NGROK_ENDPOINT" = "null" ]; then + echo "ngrok not ready, sleeping 5 seconds...." + sleep 5 + fi +done + +export ACAPY_ENDPOINT=$NGROK_ENDPOINT + +echo "Starting aca-py agent with endpoint [$ACAPY_ENDPOINT]" + +# ... if you want to echo the aca-py startup command ... +set -x + +exec aca-py start \ + --auto-provision \ + --inbound-transport http '0.0.0.0' 8001 \ + --outbound-transport http \ + --genesis-url "http://test.bcovrin.vonx.io/genesis" \ + --endpoint "${ACAPY_ENDPOINT}" \ + --auto-ping-connection \ + --monitor-ping \ + --public-invites \ + --wallet-type "askar" \ + --wallet-name "test_multi" \ + --wallet-key "secret_key" \ + --wallet-storage-type "postgres_storage" \ + --wallet-storage-config "{\"url\":\"wallet-db:5432\",\"max_connections\":5,\"scheme\":\"MultiWalletSingleTable\"}" \ + --wallet-storage-creds "{\"account\":\"DB_USER\",\"password\":\"DB_PASSWORD\",\"admin_account\":\"postgres\",\"admin_password\":\"mysecretpassword\"}" \ + --admin '0.0.0.0' 8010 \ + --label "test_multi" \ + --admin-insecure-mode \ + --multitenant \ + --multitenant-admin \ + --jwt-secret "very_secret_secret" \ + --log-level "error" From 34a924db92024bed94fb7b16e97f0a3277c87b0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dobros=C5=82aw=20=C5=BBybort?= Date: Wed, 25 Jan 2023 16:21:02 +0100 Subject: [PATCH 631/872] Allow using YAML configuration file with run_docker MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Dobrosław Żybort --- scripts/run_docker | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/scripts/run_docker b/scripts/run_docker index 3dbfcd3f3b..13b65ee2c6 100755 --- a/scripts/run_docker +++ b/scripts/run_docker @@ -70,6 +70,23 @@ if [ -n "${NETWORK_NAME}" ]; then else echo "No Docker network specified." fi -echo "" -$CONTAINER_RUNTIME run --rm -ti $ARGS aries-cloudagent-run "$@" +if [ -n "${ARG_FILE}" ]; then + if [ -f "${ARG_FILE}" ]; then + ARG_FILE_IN_DOCKER="/home/indy/arg-file.yml" + ARGS="${ARGS} -v ${ARG_FILE}:${ARG_FILE_IN_DOCKER}" + echo "" + echo "Using acapy config file: ${ARG_FILE}" + else + echo "Config file not found: ${ARG_FILE}" || exit 1 + fi +fi + +ACAPY_ARGUMENTS=("$@") +if [ -n "${ARG_FILE_IN_DOCKER}" ]; then + ACAPY_ARGUMENTS=("${ACAPY_ARGUMENTS[@]}" "--arg-file" "${ARG_FILE_IN_DOCKER}") +fi + +echo "" +# shellcheck disable=SC2086,SC2090 +$CONTAINER_RUNTIME run --rm -ti $ARGS aries-cloudagent-run "${ACAPY_ARGUMENTS[@]}" From b2f0cb7e360b94e666148543fe14e0a67cce9556 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Humbert?= Date: Thu, 26 Jan 2023 11:44:17 +0100 Subject: [PATCH 632/872] feat: universal resolver - configurable authentication MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add `--universal-resolver-bearer-token` to allow authentication against privately run universal resolvers BREAKING CHANGE: * Configured url should now include the `/1.0` suffix. The default url reflects that changes Signed-off-by: Clément Humbert --- aries_cloudagent/config/argparse.py | 17 ++++++ .../resolver/default/tests/test_universal.py | 18 +++--- .../resolver/default/universal.py | 60 +++++++++++-------- 3 files changed, 61 insertions(+), 34 deletions(-) diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index a00fffa8b2..3ed950a6f5 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -643,6 +643,14 @@ def add_arguments(self, parser: ArgumentParser): "resolver instance." ), ) + parser.add_argument( + "--universal-resolver-bearer-token", + type=str, + nargs="?", + metavar="", + env_var="ACAPY_UNIVERSAL_RESOLVER_BEARER_TOKEN", + help="Bearer token if universal resolver instance requires authentication.", + ), def get_settings(self, args: Namespace) -> dict: """Extract general settings.""" @@ -688,12 +696,21 @@ def get_settings(self, args: Namespace) -> dict: "--universal-resolver-regex cannot be used without --universal-resolver" ) + if args.universal_resolver_bearer_token and not args.universal_resolver: + raise ArgsParseError( + "--universal-resolver-bearer-token " + + "cannot be used without --universal-resolver" + ) + if args.universal_resolver: settings["resolver.universal"] = args.universal_resolver if args.universal_resolver_regex: settings["resolver.universal.supported"] = args.universal_resolver_regex + if args.universal_resolver_bearer_token: + settings["resolver.universal.token"] = args.universal_resolver_bearer_token + return settings diff --git a/aries_cloudagent/resolver/default/tests/test_universal.py b/aries_cloudagent/resolver/default/tests/test_universal.py index 381e9194e3..8d9df9638d 100644 --- a/aries_cloudagent/resolver/default/tests/test_universal.py +++ b/aries_cloudagent/resolver/default/tests/test_universal.py @@ -54,7 +54,7 @@ class MockClientSession: def __init__(self, response: MockResponse = None): self.response = response - def __call__(self): + def __call__(self, headers): return self async def __aenter__(self): @@ -101,7 +101,7 @@ async def test_resolve_not_found(profile, resolver, mock_client_session): @pytest.mark.asyncio -async def test_resolve_unexpeceted_status(profile, resolver, mock_client_session): +async def test_resolve_unexpected_status(profile, resolver, mock_client_session): mock_client_session.response = MockResponse( 500, "Server failed to complete request" ) @@ -112,21 +112,21 @@ async def test_resolve_unexpeceted_status(profile, resolver, mock_client_session @pytest.mark.asyncio async def test_fetch_resolver_props(mock_client_session: MockClientSession): mock_client_session.response = MockResponse(200, {"test": "json"}) - assert await test_module._fetch_resolver_props("test") == {"test": "json"} + assert await UniversalResolver()._fetch_resolver_props() == {"test": "json"} mock_client_session.response = MockResponse(404, "Not found") with pytest.raises(ResolverError): - await test_module._fetch_resolver_props("test") + await UniversalResolver()._fetch_resolver_props() @pytest.mark.asyncio async def test_get_supported_did_regex(): props = {"example": {"http": {"pattern": "match a test string"}}} with async_mock.patch.object( - test_module, + UniversalResolver, "_fetch_resolver_props", async_mock.CoroutineMock(return_value=props), ): - pattern = await test_module._get_supported_did_regex("test") + pattern = await UniversalResolver()._get_supported_did_regex() assert pattern.fullmatch("match a test string") @@ -169,7 +169,7 @@ async def test_setup_endpoint_set(resolver: UniversalResolver): context = async_mock.MagicMock() context.settings = settings with async_mock.patch.object( - test_module, + UniversalResolver, "_get_supported_did_regex", async_mock.CoroutineMock(return_value="pattern"), ): @@ -189,7 +189,7 @@ async def test_setup_endpoint_default(resolver: UniversalResolver): context = async_mock.MagicMock() context.settings = settings with async_mock.patch.object( - test_module, + UniversalResolver, "_get_supported_did_regex", async_mock.CoroutineMock(return_value="pattern"), ): @@ -205,7 +205,7 @@ async def test_setup_endpoint_unset(resolver: UniversalResolver): context = async_mock.MagicMock() context.settings = settings with async_mock.patch.object( - test_module, + UniversalResolver, "_get_supported_did_regex", async_mock.CoroutineMock(return_value="pattern"), ): diff --git a/aries_cloudagent/resolver/default/universal.py b/aries_cloudagent/resolver/default/universal.py index 85ca9e2dba..2efee46009 100644 --- a/aries_cloudagent/resolver/default/universal.py +++ b/aries_cloudagent/resolver/default/universal.py @@ -11,25 +11,7 @@ from ..base import BaseDIDResolver, DIDNotFound, ResolverError, ResolverType LOGGER = logging.getLogger(__name__) -DEFAULT_ENDPOINT = "https://dev.uniresolver.io" - - -async def _fetch_resolver_props(endpoint: str) -> dict: - """Retrieve universal resolver properties.""" - async with aiohttp.ClientSession() as session: - async with session.get(f"{endpoint}/1.0/properties/") as resp: - if resp.status >= 200 and resp.status < 400: - return await resp.json() - raise ResolverError( - "Failed to retrieve resolver properties: " + await resp.text() - ) - - -async def _get_supported_did_regex(endpoint: str) -> Pattern: - props = await _fetch_resolver_props(endpoint) - return _compile_supported_did_regex( - driver["http"]["pattern"] for driver in props.values() - ) +DEFAULT_ENDPOINT = "https://dev.uniresolver.io/1.0" def _compile_supported_did_regex(patterns: Iterable[Union[str, Pattern]]): @@ -54,25 +36,37 @@ def __init__( *, endpoint: Optional[str] = None, supported_did_regex: Optional[Pattern] = None, + bearer_token: Optional[str] = None, ): """Initialize UniversalResolver.""" super().__init__(ResolverType.NON_NATIVE) self._endpoint = endpoint self._supported_did_regex = supported_did_regex + self.__default_headers = ( + {"Authorization": f"Bearer {bearer_token}"} if bearer_token else {} + ) + async def setup(self, context: InjectionContext): - """Preform setup, populate supported method list, configuration.""" + """Perform setup, populate supported method list, configuration.""" + + # configure endpoint endpoint = context.settings.get_str("resolver.universal") if endpoint == "DEFAULT" or not endpoint: endpoint = DEFAULT_ENDPOINT + self._endpoint = endpoint + + # configure authorization + token = context.settings.get_str("resolver.universal.token") + self.__default_headers = {"Authorization": f"Bearer {token}"} if token else {} + # configure supported methods supported = context.settings.get("resolver.universal.supported") if supported is None: - supported_did_regex = await _get_supported_did_regex(endpoint) + supported_did_regex = await self._get_supported_did_regex() else: supported_did_regex = _compile_supported_did_regex(supported) - self._endpoint = endpoint self._supported_did_regex = supported_did_regex @property @@ -91,8 +85,8 @@ async def _resolve( ) -> dict: """Resolve DID through remote universal resolver.""" - async with aiohttp.ClientSession() as session: - async with session.get(f"{self._endpoint}/1.0/identifiers/{did}") as resp: + async with aiohttp.ClientSession(headers=self.__default_headers) as session: + async with session.get(f"{self._endpoint}/identifiers/{did}") as resp: if resp.status == 200: doc = await resp.json() did_doc = doc["didDocument"] @@ -103,5 +97,21 @@ async def _resolve( text = await resp.text() raise ResolverError( - f"Unexecpted status from universal resolver ({resp.status}): {text}" + f"Unexpected status from universal resolver ({resp.status}): {text}" + ) + + async def _fetch_resolver_props(self) -> dict: + """Retrieve universal resolver properties.""" + async with aiohttp.ClientSession(headers=self.__default_headers) as session: + async with session.get(f"{self._endpoint}/properties/") as resp: + if 200 <= resp.status < 400: + return await resp.json() + raise ResolverError( + "Failed to retrieve resolver properties: " + await resp.text() ) + + async def _get_supported_did_regex(self) -> Pattern: + props = await self._fetch_resolver_props() + return _compile_supported_did_regex( + driver["http"]["pattern"] for driver in props.values() + ) From 835da7a5909226f1a21350117d7a9709f121e75d Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Thu, 26 Jan 2023 17:39:27 -0500 Subject: [PATCH 633/872] fix: respect auto verify flag in pres proof v2 Signed-off-by: Daniel Bluhm --- .../protocols/present_proof/v2_0/handlers/pres_handler.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/protocols/present_proof/v2_0/handlers/pres_handler.py b/aries_cloudagent/protocols/present_proof/v2_0/handlers/pres_handler.py index 22c1e59dd5..487ba886d5 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/handlers/pres_handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/handlers/pres_handler.py @@ -64,7 +64,11 @@ async def handle(self, context: RequestContext, responder: BaseResponder): ) # Automatically move to next state if flag is set - if pres_ex_record and pres_ex_record.auto_verify: + if ( + pres_ex_record + and pres_ex_record.auto_verify + or context.settings.get("debug.auto_verify_presentation") + ): try: await pres_manager.verify_pres(pres_ex_record) except (BaseModelError, LedgerError, StorageError) as err: From 5578c0d0ae6fb056602bf3c192b68fc524b271db Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Thu, 26 Jan 2023 17:51:32 -0500 Subject: [PATCH 634/872] fix: pres proof v2 use dispatcher responder if available Signed-off-by: Daniel Bluhm --- .../present_proof/v2_0/handlers/pres_handler.py | 2 +- .../protocols/present_proof/v2_0/manager.py | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/aries_cloudagent/protocols/present_proof/v2_0/handlers/pres_handler.py b/aries_cloudagent/protocols/present_proof/v2_0/handlers/pres_handler.py index 487ba886d5..493cfad5c1 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/handlers/pres_handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/handlers/pres_handler.py @@ -70,7 +70,7 @@ async def handle(self, context: RequestContext, responder: BaseResponder): or context.settings.get("debug.auto_verify_presentation") ): try: - await pres_manager.verify_pres(pres_ex_record) + await pres_manager.verify_pres(pres_ex_record, responder) except (BaseModelError, LedgerError, StorageError) as err: self._logger.exception(err) if pres_ex_record: diff --git a/aries_cloudagent/protocols/present_proof/v2_0/manager.py b/aries_cloudagent/protocols/present_proof/v2_0/manager.py index ef542f0c29..c15809a1ce 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/manager.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/manager.py @@ -370,7 +370,9 @@ async def receive_pres( return pres_ex_record - async def verify_pres(self, pres_ex_record: V20PresExRecord): + async def verify_pres( + self, pres_ex_record: V20PresExRecord, responder: Optional[BaseResponder] = None + ): """ Verify a presentation. @@ -402,11 +404,13 @@ async def verify_pres(self, pres_ex_record: V20PresExRecord): await pres_ex_record.save(session, reason="verify v2.0 presentation") if pres_request_msg.will_confirm: - await self.send_pres_ack(pres_ex_record) + await self.send_pres_ack(pres_ex_record, responder) return pres_ex_record - async def send_pres_ack(self, pres_ex_record: V20PresExRecord): + async def send_pres_ack( + self, pres_ex_record: V20PresExRecord, responder: Optional[BaseResponder] = None + ): """ Send acknowledgement of presentation receipt. @@ -414,7 +418,7 @@ async def send_pres_ack(self, pres_ex_record: V20PresExRecord): pres_ex_record: presentation exchange record with thread id """ - responder = self._profile.inject_or(BaseResponder) + responder = responder or self._profile.inject_or(BaseResponder) if responder: pres_ack_message = V20PresAck(verification_result=pres_ex_record.verified) From 154133a19db7405c79f9de1874576386c0fd6bc8 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Thu, 26 Jan 2023 17:56:29 -0500 Subject: [PATCH 635/872] fix: respect auto verify in pres v1 Signed-off-by: Daniel Bluhm --- .../v1_0/handlers/presentation_handler.py | 8 ++++++-- .../protocols/present_proof/v1_0/manager.py | 12 ++++++++---- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_handler.py b/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_handler.py index 8eb0daeef4..e30eb65306 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_handler.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_handler.py @@ -64,10 +64,14 @@ async def handle(self, context: RequestContext, responder: BaseResponder): ) # Automatically move to next state if flag is set - if presentation_exchange_record and presentation_exchange_record.auto_verify: + if ( + presentation_exchange_record + and presentation_exchange_record.auto_verify + or context.settings.get("debug.auto_verify_presentation") + ): try: await presentation_manager.verify_presentation( - presentation_exchange_record + presentation_exchange_record, responder ) except (BaseModelError, LedgerError, StorageError) as err: self._logger.exception(err) diff --git a/aries_cloudagent/protocols/present_proof/v1_0/manager.py b/aries_cloudagent/protocols/present_proof/v1_0/manager.py index 2f3af46da5..0f0ce6da8f 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/manager.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/manager.py @@ -393,7 +393,9 @@ async def receive_presentation( return presentation_exchange_record async def verify_presentation( - self, presentation_exchange_record: V10PresentationExchange + self, + presentation_exchange_record: V10PresentationExchange, + responder: Optional[BaseResponder] = None, ): """ Verify a presentation. @@ -436,11 +438,13 @@ async def verify_presentation( session, reason="verify presentation" ) - await self.send_presentation_ack(presentation_exchange_record) + await self.send_presentation_ack(presentation_exchange_record, responder) return presentation_exchange_record async def send_presentation_ack( - self, presentation_exchange_record: V10PresentationExchange + self, + presentation_exchange_record: V10PresentationExchange, + responder: Optional[BaseResponder] = None, ): """ Send acknowledgement of presentation receipt. @@ -449,7 +453,7 @@ async def send_presentation_ack( presentation_exchange_record: presentation exchange record with thread id """ - responder = self._profile.inject_or(BaseResponder) + responder = responder or self._profile.inject_or(BaseResponder) if not presentation_exchange_record.connection_id: # Find associated oob record. If this presentation exchange is created From 7a2fd4ef888b0507a3ac3271daa628a783c15a15 Mon Sep 17 00:00:00 2001 From: Kim Ebert Date: Sun, 29 Jan 2023 17:03:44 -0700 Subject: [PATCH 636/872] fix: fix connection timing bug Signed-off-by: Kim Ebert --- aries_cloudagent/protocols/connections/v1_0/manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aries_cloudagent/protocols/connections/v1_0/manager.py b/aries_cloudagent/protocols/connections/v1_0/manager.py index cd0d5f6033..2c3085058d 100644 --- a/aries_cloudagent/protocols/connections/v1_0/manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/manager.py @@ -541,7 +541,7 @@ async def receive_request( new_connection = ConnRecord( invitation_key=connection_key, my_did=my_info.did, - state=ConnRecord.State.INVITATION.rfc160, + state=ConnRecord.State.REQUEST.rfc160, accept=connection.accept, their_role=connection.their_role, connection_protocol=CONN_PROTO, From c4472aa911702dcc2342605bd38e6ddf808aa99f Mon Sep 17 00:00:00 2001 From: Kim Ebert Date: Sun, 29 Jan 2023 17:03:44 -0700 Subject: [PATCH 637/872] fix: fix connection timing bug Signed-off-by: Kim Ebert --- aries_cloudagent/protocols/connections/v1_0/manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aries_cloudagent/protocols/connections/v1_0/manager.py b/aries_cloudagent/protocols/connections/v1_0/manager.py index 28b673a061..8d69a62821 100644 --- a/aries_cloudagent/protocols/connections/v1_0/manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/manager.py @@ -479,7 +479,7 @@ async def receive_request( new_connection = ConnRecord( invitation_key=connection_key, my_did=my_info.did, - state=ConnRecord.State.INVITATION.rfc160, + state=ConnRecord.State.REQUEST.rfc160, accept=connection.accept, their_role=connection.their_role, connection_protocol=CONN_PROTO, From 6e2bb470ad8307a6cb301092a72ef1ad7fb3ba52 Mon Sep 17 00:00:00 2001 From: pradeepp88 Date: Tue, 31 Jan 2023 16:09:30 -0500 Subject: [PATCH 638/872] Updating base images from slim-buster to slim-bullseye Signed-off-by: pradeepp88 --- docker/Dockerfile | 6 +++--- docker/Dockerfile.indy | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index fa7ec1e957..166e7b159e 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,5 +1,5 @@ ARG python_version=3.6.13 -FROM python:${python_version}-slim-buster AS build +FROM python:${python_version}-slim-bullseye AS build WORKDIR /src @@ -8,7 +8,7 @@ ADD . . RUN pip install setuptools wheel RUN python setup.py sdist bdist_wheel -FROM python:${python_version}-slim-buster AS main +FROM python:${python_version}-slim-bullseye AS main ARG uid=1001 ARG user=aries @@ -49,7 +49,7 @@ RUN apt-get update -y && \ curl \ git \ less \ - libffi6 \ + libffi-dev \ libgmp10 \ liblzma5 \ libncurses5 \ diff --git a/docker/Dockerfile.indy b/docker/Dockerfile.indy index 19f7f86a64..93649df4ef 100644 --- a/docker/Dockerfile.indy +++ b/docker/Dockerfile.indy @@ -3,7 +3,7 @@ ARG rust_version=1.46 # This image could be replaced with an "indy" image from another repo, # such as the indy-sdk -FROM rust:${rust_version}-slim-buster as indy-builder +FROM rust:${rust_version}-slim as indy-builder ARG user=indy ENV HOME="/home/$user" @@ -80,7 +80,7 @@ RUN rm -rf indy-sdk indy-postgres # Indy Base Image # This image could be replaced with an "indy-python" image from another repo, # such as the indy-sdk -FROM python:${python_version}-slim-buster as indy-base +FROM python:${python_version}-slim-bullseye as indy-base ARG uid=1001 ARG user=indy @@ -97,7 +97,7 @@ ENV HOME="/home/$user" \ SHELL=/bin/bash \ SUMMARY="indy-python base image" \ DESCRIPTION="aries-cloudagent provides a base image for running Hyperledger Aries agents in Docker. \ - This image provides all the necessary dependencies to use the indy-sdk in python. Based on Debian Buster." + This image provides all the necessary dependencies to use the indy-sdk in python. Based on Debian bullseye." LABEL summary="$SUMMARY" \ description="$DESCRIPTION" \ @@ -120,7 +120,7 @@ RUN apt-get update -y && \ curl \ git \ less \ - libffi6 \ + libffi-dev \ libgmp10 \ liblzma5 \ libncurses5 \ @@ -205,7 +205,7 @@ ENTRYPOINT ["/bin/bash", "-c", "pytest \"$@\"", "--"] # ACA-Py Builder # Build ACA-Py wheel using setuptools -FROM python:${python_version}-slim-buster AS acapy-builder +FROM python:${python_version}-slim-bullseye AS acapy-builder WORKDIR /src From 22c376299572827e3a0f26d4c739b027f7d8a121 Mon Sep 17 00:00:00 2001 From: "ram.challa" Date: Wed, 1 Feb 2023 10:24:17 -0500 Subject: [PATCH 639/872] Delete Tails second commit Signed-off-by: ram.challa --- aries_cloudagent/revocation/routes.py | 190 +++++++++++++------------- 1 file changed, 96 insertions(+), 94 deletions(-) diff --git a/aries_cloudagent/revocation/routes.py b/aries_cloudagent/revocation/routes.py index 3cba351d03..3be671b567 100644 --- a/aries_cloudagent/revocation/routes.py +++ b/aries_cloudagent/revocation/routes.py @@ -2,6 +2,8 @@ import json import logging +import os +import shutil from asyncio import shield import re @@ -99,9 +101,7 @@ class TxnOrRevRegResultSchema(OpenAPISchema): """Result schema for credential definition send request.""" sent = fields.Nested( - RevRegResultSchema(), - required=False, - definition="Content sent", + RevRegResultSchema(), required=False, definition="Content sent" ) txn = fields.Nested( TransactionRecordSchema(), @@ -130,9 +130,7 @@ def validate_fields(self, data, **kwargs): ) rev_reg_id = fields.Str( - description="Revocation registry identifier", - required=False, - **INDY_REV_REG_ID, + description="Revocation registry identifier", required=False, **INDY_REV_REG_ID ) cred_rev_id = fields.Str( description="Credential revocation identifier", @@ -140,9 +138,27 @@ def validate_fields(self, data, **kwargs): **INDY_CRED_REV_ID, ) cred_ex_id = fields.Str( - description="Credential exchange identifier", + description="Credential exchange identifier", required=False, **UUID4 + ) + + +class RevRegId(OpenAPISchema): + """Parameters and validators for delete tails file request.""" + + @validates_schema + def validate_fields(self, data, **kwargs): + """Validate schema fields - must have (rr-id and cr-id) xor cx-id.""" + + rev_reg_id = data.get("rev_reg_id") + cred_def_id = data.get("cred_def_id") + + rev_reg_id = fields.Str( + description="Revocation registry identifier", required=False, **INDY_REV_REG_ID + ) + cred_def_id = fields.Str( + description="Credential definition identifier", required=False, - **UUID4, + **INDY_CRED_DEF_ID, ) @@ -175,8 +191,7 @@ def validate_fields(self, data, **kwargs): required=False, ) notify = fields.Boolean( - description="Send a notification to the credential recipient", - required=False, + description="Send a notification to the credential recipient", required=False ) notify_version = fields.String( description="Specify which version of the revocation notification should be sent", @@ -223,9 +238,7 @@ class TxnOrPublishRevocationsResultSchema(OpenAPISchema): """Result schema for credential definition send request.""" sent = fields.Nested( - PublishRevocationsSchema(), - required=False, - definition="Content sent", + PublishRevocationsSchema(), required=False, definition="Content sent" ) txn = fields.Nested( TransactionRecordSchema(), @@ -267,9 +280,7 @@ class CredRevRecordDetailsResultSchema(OpenAPISchema): class CredRevIndyRecordsResultSchema(OpenAPISchema): """Result schema for revoc reg delta.""" - rev_reg_delta = fields.Dict( - description="Indy revocation registry delta", - ) + rev_reg_delta = fields.Dict(description="Indy revocation registry delta") class RevRegIssuedResultSchema(OpenAPISchema): @@ -286,22 +297,19 @@ class RevRegUpdateRequestMatchInfoSchema(OpenAPISchema): """Path parameters and validators for request taking rev reg id.""" apply_ledger_update = fields.Bool( - description="Apply updated accumulator transaction to ledger", - required=True, + description="Apply updated accumulator transaction to ledger", required=True ) class RevRegWalletUpdatedResultSchema(OpenAPISchema): """Number of wallet revocation entries status updated.""" - rev_reg_delta = fields.Dict( - description="Indy revocation registry delta", - ) + rev_reg_delta = fields.Dict(description="Indy revocation registry delta") accum_calculated = fields.Dict( - description="Calculated accumulator for phantom revocations", + description="Calculated accumulator for phantom revocations" ) accum_fixed = fields.Dict( - description="Applied ledger transaction to fix revocations", + description="Applied ledger transaction to fix revocations" ) @@ -367,9 +375,7 @@ class RevRegIdMatchInfoSchema(OpenAPISchema): """Path parameters and validators for request taking rev reg id.""" rev_reg_id = fields.Str( - description="Revocation Registry identifier", - required=True, - **INDY_REV_REG_ID, + description="Revocation Registry identifier", required=True, **INDY_REV_REG_ID ) @@ -387,8 +393,7 @@ class CreateRevRegTxnForEndorserOptionSchema(OpenAPISchema): """Class for user to input whether to create a transaction for endorser or not.""" create_transaction_for_endorser = fields.Boolean( - description="Create Transaction For Endorser's signature", - required=False, + description="Create Transaction For Endorser's signature", required=False ) @@ -400,10 +405,7 @@ class RevRegConnIdMatchInfoSchema(OpenAPISchema): ) -@docs( - tags=["revocation"], - summary="Revoke an issued credential", -) +@docs(tags=["revocation"], summary="Revoke an issued credential") @request_schema(RevokeRequestSchema()) @response_schema(RevocationModuleResponseSchema(), description="") async def revoke(request: web.BaseRequest): @@ -475,9 +477,7 @@ async def publish_revocations(request: web.BaseRequest): rev_manager = RevocationManager(context.profile) try: - rev_reg_resp = await rev_manager.publish_pending_revocations( - rrid2crid, - ) + rev_reg_resp = await rev_manager.publish_pending_revocations(rrid2crid) except (RevocationError, StorageError, IndyIssuerError, LedgerError) as err: raise web.HTTPBadRequest(reason=err.roll_up) from err @@ -548,9 +548,7 @@ async def create_rev_reg(request: web.BaseRequest): try: revoc = IndyRevocation(profile) issuer_rev_reg_rec = await revoc.init_issuer_registry( - credential_definition_id, - max_cred_num=max_cred_num, - notify=False, + credential_definition_id, max_cred_num=max_cred_num, notify=False ) except RevocationNotSupportedError as e: raise web.HTTPBadRequest(reason=e.message) from e @@ -600,10 +598,7 @@ async def rev_regs_created(request: web.BaseRequest): ) -@docs( - tags=["revocation"], - summary="Get revocation registry by revocation registry id", -) +@docs(tags=["revocation"], summary="Get revocation registry by revocation registry id") @match_info_schema(RevRegIdMatchInfoSchema()) @response_schema(RevRegResultSchema(), 200, description="") async def get_rev_reg(request: web.BaseRequest): @@ -698,10 +693,7 @@ async def get_rev_reg_issued(request: web.BaseRequest): return web.json_response(results) -@docs( - tags=["revocation"], - summary="Get details of revoked credentials from ledger", -) +@docs(tags=["revocation"], summary="Get details of revoked credentials from ledger") @match_info_schema(RevRegIdMatchInfoSchema()) @response_schema(CredRevIndyRecordsResultSchema(), 200, description="") async def get_rev_reg_indy_recs(request: web.BaseRequest): @@ -722,11 +714,7 @@ async def get_rev_reg_indy_recs(request: web.BaseRequest): revoc = IndyRevocation(context.profile) rev_reg_delta = await revoc.get_issuer_rev_reg_delta(rev_reg_id) - return web.json_response( - { - "rev_reg_delta": rev_reg_delta, - } - ) + return web.json_response({"rev_reg_delta": rev_reg_delta}) @docs( @@ -817,10 +805,7 @@ async def update_rev_reg_revoked_state(request: web.BaseRequest): ) -@docs( - tags=["revocation"], - summary="Get credential revocation status", -) +@docs(tags=["revocation"], summary="Get credential revocation status") @querystring_schema(CredRevRecordQueryStringSchema()) @response_schema(CredRevRecordResultSchema(), 200, description="") async def get_cred_rev_record(request: web.BaseRequest): @@ -917,10 +902,7 @@ async def get_tails_file(request: web.BaseRequest) -> web.FileResponse: return web.FileResponse(path=rev_reg.tails_local_path, status=200) -@docs( - tags=["revocation"], - summary="Upload local tails file to server", -) +@docs(tags=["revocation"], summary="Upload local tails file to server") @match_info_schema(RevRegIdMatchInfoSchema()) @response_schema(RevocationModuleResponseSchema(), description="") async def upload_tails_file(request: web.BaseRequest): @@ -951,10 +933,7 @@ async def upload_tails_file(request: web.BaseRequest): return web.json_response({}) -@docs( - tags=["revocation"], - summary="Send revocation registry definition to ledger", -) +@docs(tags=["revocation"], summary="Send revocation registry definition to ledger") @match_info_schema(RevRegIdMatchInfoSchema()) @querystring_schema(CreateRevRegTxnForEndorserOptionSchema()) @querystring_schema(RevRegConnIdMatchInfoSchema()) @@ -1024,9 +1003,7 @@ async def send_rev_reg_def(request: web.BaseRequest): rev_reg = await revoc.get_issuer_rev_reg_record(rev_reg_id) rev_reg_resp = await rev_reg.send_def( - profile, - write_ledger=write_ledger, - endorser_did=endorser_did, + profile, write_ledger=write_ledger, endorser_did=endorser_did ) LOGGER.debug("published rev reg definition: %s", rev_reg_id) except StorageNotFoundError as err: @@ -1066,10 +1043,7 @@ async def send_rev_reg_def(request: web.BaseRequest): return web.json_response({"txn": transaction.serialize()}) -@docs( - tags=["revocation"], - summary="Send revocation registry entry to ledger", -) +@docs(tags=["revocation"], summary="Send revocation registry entry to ledger") @match_info_schema(RevRegIdMatchInfoSchema()) @querystring_schema(CreateRevRegTxnForEndorserOptionSchema()) @querystring_schema(RevRegConnIdMatchInfoSchema()) @@ -1137,9 +1111,7 @@ async def send_rev_reg_entry(request: web.BaseRequest): revoc = IndyRevocation(profile) rev_reg = await revoc.get_issuer_rev_reg_record(rev_reg_id) rev_entry_resp = await rev_reg.send_entry( - profile, - write_ledger=write_ledger, - endorser_did=endorser_did, + profile, write_ledger=write_ledger, endorser_did=endorser_did ) LOGGER.debug("published registry entry: %s", rev_reg_id) @@ -1155,8 +1127,7 @@ async def send_rev_reg_entry(request: web.BaseRequest): transaction_mgr = TransactionManager(profile) try: transaction = await transaction_mgr.create_record( - messages_attach=rev_entry_resp["result"], - connection_id=connection_id, + messages_attach=rev_entry_resp["result"], connection_id=connection_id ) except StorageError as err: raise web.HTTPBadRequest(reason=err.roll_up) from err @@ -1297,9 +1268,7 @@ async def generate(rr_record: IssuerRevRegRecord) -> dict: public_uri = tails_base_url.rstrip("/") + f"/{registry_record.revoc_reg_id}" await rr_record.set_tails_file_public_uri(profile, public_uri) rev_reg_resp = await rr_record.send_def( - profile, - write_ledger=write_ledger, - endorser_did=endorser_did, + profile, write_ledger=write_ledger, endorser_did=endorser_did ) if write_ledger: # Upload the tails file @@ -1336,8 +1305,7 @@ async def generate(rr_record: IssuerRevRegRecord) -> dict: responder = profile.inject_or(BaseResponder) if responder: await responder.send( - revo_transaction_request, - connection_id=connection.connection_id, + revo_transaction_request, connection_id=connection.connection_id ) else: LOGGER.warning( @@ -1384,9 +1352,7 @@ async def on_revocation_entry_event(profile: Profile, event: Event): async with profile.session() as session: registry_record = await IssuerRevRegRecord.retrieve_by_id(session, record_id) rev_entry_resp = await registry_record.send_entry( - profile, - write_ledger=write_ledger, - endorser_did=endorser_did, + profile, write_ledger=write_ledger, endorser_did=endorser_did ) if not write_ledger: @@ -1418,8 +1384,7 @@ async def on_revocation_entry_event(profile: Profile, event: Event): responder = profile.inject_or(BaseResponder) if responder: await responder.send( - revo_transaction_request, - connection_id=connection.connection_id, + revo_transaction_request, connection_id=connection.connection_id ) else: LOGGER.warning( @@ -1467,6 +1432,49 @@ async def on_revocation_registry_endorsed_event(profile: Profile, event: Event): ) +@querystring_schema(RevRegId()) +@docs(tags=["revocation"], summary="Delete the tail files") +async def delete_tails(request: web.BaseRequest) -> json: + context: AdminRequestContext = request["context"] + rev_reg_id = request.query.get("rev_reg_id") + cred_def_id = request.query.get("cred_def_id") + revoc = IndyRevocation(context.profile) + session = revoc._profile.session() + if rev_reg_id: + rev_reg = await revoc.get_issuer_rev_reg_record(rev_reg_id) + tails_path = rev_reg.tails_local_path + main_dir_rev = os.path.dirname(tails_path) + try: + shutil.rmtree(main_dir_rev) + return web.json_response({"message": "All files deleted successfully"}) + except Exception as e: + return web.json_response({"message": str(e)}) + elif cred_def_id: + async with session: + cred_reg = sorted( + await IssuerRevRegRecord.query_by_cred_def_id( + session, cred_def_id, IssuerRevRegRecord.STATE_GENERATED + ) + )[0] + tails_path = cred_reg.tails_local_path + main_dir_rev = os.path.dirname(tails_path) + main_dir_cred = os.path.dirname(main_dir_rev) + filenames = os.listdir(main_dir_cred) + try: + flag = 0 + for i in filenames: + if re.search(cred_def_id, i): + shutil.rmtree(main_dir_cred + "/" + i) + flag = 1 + if flag: + return web.json_response({"message": "All files deleted successfully"}) + else: + return web.json_response({"message": "No such file or directory"}) + + except Exception as e: + return web.json_response({"message": str(e)}) + + async def register(app: web.Application): """Register routes.""" app.add_routes( @@ -1474,16 +1482,13 @@ async def register(app: web.Application): web.post("/revocation/revoke", revoke), web.post("/revocation/publish-revocations", publish_revocations), web.post( - "/revocation/clear-pending-revocations", - clear_pending_revocations, + "/revocation/clear-pending-revocations", clear_pending_revocations ), web.get( "/revocation/credential-record", get_cred_rev_record, allow_head=False ), web.get( - "/revocation/registries/created", - rev_regs_created, - allow_head=False, + "/revocation/registries/created", rev_regs_created, allow_head=False ), web.get("/revocation/registry/{rev_reg_id}", get_rev_reg, allow_head=False), web.get( @@ -1516,10 +1521,7 @@ async def register(app: web.Application): get_tails_file, allow_head=False, ), - web.patch( - "/revocation/registry/{rev_reg_id}/set-state", - set_rev_reg_state, - ), + web.patch("/revocation/registry/{rev_reg_id}/set-state", set_rev_reg_state), web.put( "/revocation/registry/{rev_reg_id}/fix-revocation-entry-state", update_rev_reg_revoked_state, From ea1ec8a52fcb5dcf61356fddec26b28bd7525e5e Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Thu, 2 Feb 2023 08:09:23 -0800 Subject: [PATCH 640/872] black 23.1.0 formatter changes Signed-off-by: Shaanjot Gill --- aries_cloudagent/admin/server.py | 2 +- aries_cloudagent/askar/didcomm/v2.py | 6 +- aries_cloudagent/config/tests/test_wallet.py | 1 - aries_cloudagent/connections/base_manager.py | 2 +- .../connections/models/diddoc/diddoc.py | 1 - .../models/diddoc/tests/test_diddoc.py | 1 - aries_cloudagent/core/conductor.py | 8 +-- .../core/in_memory/didcomm/tests/test_1pu.py | 3 - .../core/in_memory/didcomm/tests/test_ecdh.py | 6 +- aries_cloudagent/core/tests/test_conductor.py | 2 - .../core/tests/test_oob_processor.py | 9 --- .../did/tests/test_did_key_bls12381g1g2.py | 1 + aries_cloudagent/indy/models/pres_preview.py | 2 +- aries_cloudagent/indy/models/xform.py | 4 +- aries_cloudagent/indy/sdk/holder.py | 12 ++-- aries_cloudagent/indy/verifier.py | 16 ++--- aries_cloudagent/ledger/indy.py | 3 + .../ledger/merkel_validation/trie.py | 2 +- .../tests/test_indy_manager.py | 10 ++- .../tests/test_indy_vdr_manager.py | 10 ++- aries_cloudagent/ledger/tests/test_indy.py | 8 --- aries_cloudagent/ledger/tests/test_routes.py | 1 - .../tests/test_routes.py | 1 - .../decorators/tests/test_decorator_set.py | 5 -- .../tests/test_localization_decorator.py | 1 - .../decorators/tests/test_thread_decorator.py | 3 - .../decorators/tests/test_trace_decorator.py | 4 -- aries_cloudagent/messaging/jsonld/routes.py | 2 +- .../messaging/schemas/tests/test_routes.py | 1 - aries_cloudagent/multitenant/base.py | 2 +- aries_cloudagent/multitenant/route_manager.py | 2 +- .../multitenant/tests/test_base.py | 1 - .../actionmenu/v1_0/tests/test_routes.py | 10 --- .../actionmenu/v1_0/tests/test_service.py | 8 +-- .../basicmessage/v1_0/tests/test_routes.py | 3 - .../tests/test_connection_invitation.py | 1 - .../tests/test_connection_response.py | 1 - .../connections/v1_0/tests/test_routes.py | 2 - .../didexchange/v1_0/tests/test_manager.py | 1 - .../didexchange/v1_0/tests/test_routes.py | 1 - .../v1_0/messages/tests/test_disclose.py | 1 - .../v1_0/messages/tests/test_query.py | 1 - .../v2_0/messages/tests/test_queries.py | 1 - .../v1_0/tests/test_manager.py | 1 - .../v1_0/tests/test_routes.py | 2 - .../issue_credential/v1_0/manager.py | 72 +++++++++++-------- .../v1_0/tests/test_manager.py | 1 - .../v1_0/tests/test_routes.py | 16 ----- .../formats/ld_proof/tests/test_handler.py | 1 - .../issue_credential/v2_0/manager.py | 58 +++++++-------- .../v2_0/tests/test_manager.py | 9 --- .../v2_0/tests/test_routes.py | 14 ---- .../protocols/out_of_band/v1_0/manager.py | 1 - .../out_of_band/v1_0/tests/test_manager.py | 3 - .../present_proof/indy/pres_exch_handler.py | 2 +- .../test_presentation_request_handler.py | 9 --- .../protocols/present_proof/v1_0/manager.py | 12 ++-- .../present_proof/v1_0/tests/test_routes.py | 38 ---------- .../tests/test_pres_request_handler.py | 10 --- .../protocols/present_proof/v2_0/manager.py | 10 ++- .../protocols/present_proof/v2_0/routes.py | 3 +- .../v1_0/messages/tests/test_forward.py | 1 - .../revocation/tests/test_routes.py | 3 - aries_cloudagent/transport/pack_format.py | 1 - aries_cloudagent/vc/vc_ld/tests/test_vc_ld.py | 1 - aries_cloudagent/wallet/bbs.py | 2 +- aries_cloudagent/wallet/indy.py | 4 +- aries_cloudagent/wallet/tests/test_routes.py | 1 - demo/runners/agent_container.py | 5 +- demo/runners/performance.py | 1 - docs/conf.py | 1 + 71 files changed, 139 insertions(+), 305 deletions(-) diff --git a/aries_cloudagent/admin/server.py b/aries_cloudagent/admin/server.py index 846405a024..def29588eb 100644 --- a/aries_cloudagent/admin/server.py +++ b/aries_cloudagent/admin/server.py @@ -491,7 +491,7 @@ async def start(self) -> None: def sort_dict(raw: dict) -> dict: """Order (JSON, string keys) dict asciibetically by key, recursively.""" - for (k, v) in raw.items(): + for k, v in raw.items(): if isinstance(v, dict): raw[k] = sort_dict(v) return dict(sorted([item for item in raw.items()], key=lambda x: x[0])) diff --git a/aries_cloudagent/askar/didcomm/v2.py b/aries_cloudagent/askar/didcomm/v2.py index c74b5cd9e7..7d89a3ec9c 100644 --- a/aries_cloudagent/askar/didcomm/v2.py +++ b/aries_cloudagent/askar/didcomm/v2.py @@ -33,7 +33,7 @@ def ecdh_es_encrypt(to_verkeys: Mapping[str, Key], message: bytes) -> bytes: except AskarError: raise DidcommEnvelopeError("Error creating content encryption key") - for (kid, recip_key) in to_verkeys.items(): + for kid, recip_key in to_verkeys.items(): try: epk = Key.generate(recip_key.algorithm, ephemeral=True) except AskarError: @@ -145,7 +145,7 @@ def ecdh_1pu_encrypt( apu = b64url(sender_kid) apv = [] - for (kid, recip_key) in to_verkeys.items(): + for kid, recip_key in to_verkeys.items(): if agree_alg: if agree_alg != recip_key.algorithm: raise DidcommEnvelopeError("Recipient key types must be consistent") @@ -173,7 +173,7 @@ def ecdh_1pu_encrypt( raise DidcommEnvelopeError("Error encrypting message payload") wrapper.set_payload(payload.ciphertext, payload.nonce, payload.tag) - for (kid, recip_key) in to_verkeys.items(): + for kid, recip_key in to_verkeys.items(): enc_key = ecdh.Ecdh1PU(alg_id, apu, apv).sender_wrap_key( wrap_alg, epk, sender_key, recip_key, cek, cc_tag=payload.tag ) diff --git a/aries_cloudagent/config/tests/test_wallet.py b/aries_cloudagent/config/tests/test_wallet.py index 9d514bc735..3ff2e5a8b8 100644 --- a/aries_cloudagent/config/tests/test_wallet.py +++ b/aries_cloudagent/config/tests/test_wallet.py @@ -187,7 +187,6 @@ async def test_wallet_config_bad_seed_x(self): ) as mock_seed_to_did, async_mock.patch.object( test_module, "add_or_update_version_to_storage", async_mock.CoroutineMock() ): - with self.assertRaises(test_module.ConfigError): await test_module.wallet_config(self.context, provision=True) diff --git a/aries_cloudagent/connections/base_manager.py b/aries_cloudagent/connections/base_manager.py index 53172b9966..815843b4c0 100644 --- a/aries_cloudagent/connections/base_manager.py +++ b/aries_cloudagent/connections/base_manager.py @@ -150,7 +150,7 @@ async def create_did_document( routing_keys = [*routing_keys, *mediator_routing_keys] svc_endpoints = [mediation_record.endpoint] - for (endpoint_index, svc_endpoint) in enumerate(svc_endpoints or []): + for endpoint_index, svc_endpoint in enumerate(svc_endpoints or []): endpoint_ident = "indy" if endpoint_index == 0 else f"indy{endpoint_index}" service = Service( did_info.did, diff --git a/aries_cloudagent/connections/models/diddoc/diddoc.py b/aries_cloudagent/connections/models/diddoc/diddoc.py index d1b8bcb0cc..ea47866487 100644 --- a/aries_cloudagent/connections/models/diddoc/diddoc.py +++ b/aries_cloudagent/connections/models/diddoc/diddoc.py @@ -176,7 +176,6 @@ def add_service_pubkeys( rv = [] for tag in [tags] if isinstance(tags, str) else list(tags): - for svc_key in service.get(tag, {}): canon_key = canon_ref(self.did, svc_key) pubkey = None diff --git a/aries_cloudagent/connections/models/diddoc/tests/test_diddoc.py b/aries_cloudagent/connections/models/diddoc/tests/test_diddoc.py index be002ddc27..141a0e6051 100644 --- a/aries_cloudagent/connections/models/diddoc/tests/test_diddoc.py +++ b/aries_cloudagent/connections/models/diddoc/tests/test_diddoc.py @@ -26,7 +26,6 @@ class TestDIDDoc(AsyncTestCase): async def test_basic(self): - # One authn key by reference dd_in = { "@context": "https://w3id.org/did/v1", diff --git a/aries_cloudagent/core/conductor.py b/aries_cloudagent/core/conductor.py index 9dfe8f8ab2..1f09dbc13a 100644 --- a/aries_cloudagent/core/conductor.py +++ b/aries_cloudagent/core/conductor.py @@ -455,11 +455,9 @@ async def start(self) -> None: auto_accept=True, ) async with self.root_profile.session() as session: - await ( - MediationInviteStore( - session.context.inject(BaseStorage) - ).mark_default_invite_as_used() - ) + await MediationInviteStore( + session.context.inject(BaseStorage) + ).mark_default_invite_as_used() await record.metadata_set( session, MediationManager.SEND_REQ_AFTER_CONNECTION, True diff --git a/aries_cloudagent/core/in_memory/didcomm/tests/test_1pu.py b/aries_cloudagent/core/in_memory/didcomm/tests/test_1pu.py index f12bdc25d0..e57b7bddda 100644 --- a/aries_cloudagent/core/in_memory/didcomm/tests/test_1pu.py +++ b/aries_cloudagent/core/in_memory/didcomm/tests/test_1pu.py @@ -6,7 +6,6 @@ def test_1pu_hex_example(): - # Previously randomly generated 3 sets of keys aliceSecretKey = "23832cbef38641b8754a35f1f79bbcbc248e09ac93b01c2eaf12474f2ac406b6" alicePublicKey = "04fd4ca9eb7954a03517ac8249e6070aa3112e582f596b10f0d45d757b56d5dc0395a7d207d06503a4d6ad6e2ad3a1fd8cc233c072c0dc0f32213deb712c32cbdf" @@ -41,7 +40,6 @@ def test_1pu_hex_example(): # Example key exchange in https://tools.ietf.org/id/draft-madden-jose-ecdh-1pu-03.html#rfc.appendix.A def test_1pu_appendix_example(): - # Convert the three JWK keys into hex encoded byte format # Alice Key @@ -106,7 +104,6 @@ def test_1pu_appendix_example(): def main(): - test_1pu_hex_example() test_1pu_appendix_example() diff --git a/aries_cloudagent/core/in_memory/didcomm/tests/test_ecdh.py b/aries_cloudagent/core/in_memory/didcomm/tests/test_ecdh.py index 16f15754d9..943456c7b8 100644 --- a/aries_cloudagent/core/in_memory/didcomm/tests/test_ecdh.py +++ b/aries_cloudagent/core/in_memory/didcomm/tests/test_ecdh.py @@ -2,9 +2,9 @@ from ..derive_ecdh import * + # Generate the same shared secret from imported generated keys def test_ecdh_derive_shared_secret(): - # Import keys for two participating users aliceSecretKey = "23832cbef38641b8754a35f1f79bbcbc248e09ac93b01c2eaf12474f2ac406b6" alicePublicKey = "04fd4ca9eb7954a03517ac8249e6070aa3112e582f596b10f0d45d757b56d5dc0395a7d207d06503a4d6ad6e2ad3a1fd8cc233c072c0dc0f32213deb712c32cbdf" @@ -23,7 +23,6 @@ def test_ecdh_derive_shared_secret(): # Generate the same shared secret from random keys def test_ecdh_derive_shared_secret_random(): - # Generate random keys for the two participating users aliceSecretKey = SigningKey.generate(curve=NIST256p) alice = ECDH(curve=NIST256p) @@ -46,7 +45,6 @@ def test_ecdh_derive_shared_secret_random(): # Test the entire key generation flow, DeriveECDHSecret() into ConcatKDF() def test_ecdh_generate_key(): - aliceSecretKey = "23832cbef38641b8754a35f1f79bbcbc248e09ac93b01c2eaf12474f2ac406b6" alicePublicKey = "04fd4ca9eb7954a03517ac8249e6070aa3112e582f596b10f0d45d757b56d5dc0395a7d207d06503a4d6ad6e2ad3a1fd8cc233c072c0dc0f32213deb712c32cbdf" @@ -78,7 +76,6 @@ def test_ecdh_generate_key(): # Test the entire key generation flow, derive_shared_secret() into concat_kdf() def test_ecdh_generate_key_random(): - aliceSecretKey = SigningKey.generate(curve=NIST256p) alice = ECDH(curve=NIST256p) alice.load_private_key(aliceSecretKey) @@ -113,7 +110,6 @@ def test_ecdh_generate_key_random(): def main(): - test_ecdh_derive_shared_secret() test_ecdh_derive_shared_secret_random() test_ecdh_generate_key() diff --git a/aries_cloudagent/core/tests/test_conductor.py b/aries_cloudagent/core/tests/test_conductor.py index d37f10161e..d4e73b4a8f 100644 --- a/aries_cloudagent/core/tests/test_conductor.py +++ b/aries_cloudagent/core/tests/test_conductor.py @@ -222,7 +222,6 @@ async def test_stats(self): ) as mock_outbound_mgr, async_mock.patch.object( test_module, "LoggingConfigurator", autospec=True ) as mock_logger: - mock_inbound_mgr.return_value.sessions = ["dummy"] mock_outbound_mgr.return_value.outbound_buffer = [ async_mock.MagicMock(state=QueuedOutboundMessage.STATE_ENCODE), @@ -263,7 +262,6 @@ async def test_inbound_message_handler(self): with async_mock.patch.object( conductor.dispatcher, "queue_message", autospec=True ) as mock_dispatch_q: - message_body = "{}" receipt = MessageReceipt(direct_response_mode="snail mail") message = InboundMessage(message_body, receipt) diff --git a/aries_cloudagent/core/tests/test_oob_processor.py b/aries_cloudagent/core/tests/test_oob_processor.py index 5197daedbd..7c73a946ad 100644 --- a/aries_cloudagent/core/tests/test_oob_processor.py +++ b/aries_cloudagent/core/tests/test_oob_processor.py @@ -59,7 +59,6 @@ async def test_clean_finished_oob_record_no_multi_use_no_request_attach(self): "retrieve_by_tag_filter", async_mock.CoroutineMock(return_value=mock_oob), ) as mock_retrieve_oob: - await self.oob_processor.clean_finished_oob_record( self.profile, test_message ) @@ -88,7 +87,6 @@ async def test_clean_finished_oob_record_multi_use(self): "retrieve_by_tag_filter", async_mock.CoroutineMock(return_value=mock_oob), ) as mock_retrieve_oob: - await self.oob_processor.clean_finished_oob_record( self.profile, test_message ) @@ -143,7 +141,6 @@ async def test_find_oob_target_for_outbound_message(self): "retrieve_by_tag_filter", async_mock.CoroutineMock(return_value=mock_oob), ) as mock_retrieve_oob: - target = await self.oob_processor.find_oob_target_for_outbound_message( self.profile, outbound ) @@ -182,7 +179,6 @@ async def test_find_oob_target_for_outbound_message_oob_not_found(self): "retrieve_by_tag_filter", async_mock.CoroutineMock(side_effect=(StorageNotFoundError(),)), ) as mock_retrieve_oob: - target = await self.oob_processor.find_oob_target_for_outbound_message( self.profile, outbound ) @@ -217,7 +213,6 @@ async def test_find_oob_target_for_outbound_message_update_service_thread(self): "retrieve_by_tag_filter", async_mock.CoroutineMock(return_value=mock_oob), ): - message = json.dumps({}) outbound = OutboundMessage(reply_thread_id="the-thid", payload=message) await self.oob_processor.find_oob_target_for_outbound_message( @@ -525,7 +520,6 @@ async def test_find_oob_record_for_inbound_message_attach_thread_id_not_in_list( async def test_find_oob_record_for_inbound_message_not_attach_thread_id_matching( self, ): - with async_mock.patch.object( OobRecord, "retrieve_by_tag_filter", @@ -639,7 +633,6 @@ async def test_find_oob_record_for_inbound_message_their_service_set_on_oob_reco async def test_find_oob_record_for_inbound_message_session_emit_delete( self, ): - with async_mock.patch.object( OobRecord, "retrieve_by_tag_filter", @@ -664,7 +657,6 @@ async def test_find_oob_record_for_inbound_message_session_emit_delete( async def test_find_oob_record_for_inbound_message_session_connectionless_save( self, ): - self.oob_record.connection_id = None with async_mock.patch.object( @@ -751,7 +743,6 @@ async def test_handle_message_connectionless(self): self.inbound_message_router.assert_called_once_with(self.profile, ANY, False) async def test_handle_message_unsupported_message_type(self): - with self.assertRaises(OobMessageProcessorError) as err: await self.oob_processor.handle_message( self.profile, [{"@type": "unsupported"}], async_mock.MagicMock() diff --git a/aries_cloudagent/did/tests/test_did_key_bls12381g1g2.py b/aries_cloudagent/did/tests/test_did_key_bls12381g1g2.py index f5ed77e64d..5877bed1c6 100644 --- a/aries_cloudagent/did/tests/test_did_key_bls12381g1g2.py +++ b/aries_cloudagent/did/tests/test_did_key_bls12381g1g2.py @@ -26,6 +26,7 @@ [b"\xee\x01", b58_to_bytes(TEST_BLS12381G1G2_BASE58_KEY)] ) + # The tests here are a bit quirky because g1g2 is a concatenation of g1 and g2 public key bytes # but it works with the already existing did key implementation. class TestDIDKey(TestCase): diff --git a/aries_cloudagent/indy/models/pres_preview.py b/aries_cloudagent/indy/models/pres_preview.py index 5f73978f33..0edb1cdb06 100644 --- a/aries_cloudagent/indy/models/pres_preview.py +++ b/aries_cloudagent/indy/models/pres_preview.py @@ -403,7 +403,7 @@ def non_revoc(cred_def_id: str) -> IndyNonRevocationInterval: }, } - for (reft, attr_spec) in attr_specs_names.items(): + for reft, attr_spec in attr_specs_names.items(): proof_req["requested_attributes"][ "{}_{}_uuid".format( len(proof_req["requested_attributes"]), canon(attr_spec["names"][0]) diff --git a/aries_cloudagent/indy/models/xform.py b/aries_cloudagent/indy/models/xform.py index 9e7d5e7601..afcb635fe2 100644 --- a/aries_cloudagent/indy/models/xform.py +++ b/aries_cloudagent/indy/models/xform.py @@ -30,7 +30,7 @@ async def indy_proof_req_preview2indy_requested_creds( "requested_predicates": {}, } - for (referent, req_item) in indy_proof_req["requested_attributes"].items(): + for referent, req_item in indy_proof_req["requested_attributes"].items(): credentials = await holder.get_credentials_for_presentation_request_by_referent( presentation_request=indy_proof_req, referents=(referent,), @@ -116,7 +116,7 @@ def indy_proof_req2non_revoc_intervals(indy_proof_req: dict): """Return non-revocation intervals by requested item referent in proof request.""" non_revoc_intervals = {} for req_item_type in ("requested_attributes", "requested_predicates"): - for (reft, req_item) in indy_proof_req[req_item_type].items(): + for reft, req_item in indy_proof_req[req_item_type].items(): interval = req_item.get( "non_revoked", indy_proof_req.get("non_revoked"), diff --git a/aries_cloudagent/indy/sdk/holder.py b/aries_cloudagent/indy/sdk/holder.py index 48cb2abac0..efb1a4f3ba 100644 --- a/aries_cloudagent/indy/sdk/holder.py +++ b/aries_cloudagent/indy/sdk/holder.py @@ -226,11 +226,13 @@ async def fetch(reft, limit): with IndyErrorHandler( "Error when constructing wallet credential query", IndyHolderError ): - search_handle = await ( - indy.anoncreds.prover_search_credentials_for_proof_req( - self.wallet.handle, - json.dumps(presentation_request), - json.dumps(extra_query), + search_handle = ( + await ( + indy.anoncreds.prover_search_credentials_for_proof_req( + self.wallet.handle, + json.dumps(presentation_request), + json.dumps(extra_query), + ) ) ) diff --git a/aries_cloudagent/indy/verifier.py b/aries_cloudagent/indy/verifier.py index f61ca829f2..9a83ce5b8f 100644 --- a/aries_cloudagent/indy/verifier.py +++ b/aries_cloudagent/indy/verifier.py @@ -59,12 +59,12 @@ def non_revoc_intervals(self, pres_req: dict, pres: dict, cred_defs: dict) -> li """ msgs = [] - for (req_proof_key, pres_key) in { + for req_proof_key, pres_key in { "revealed_attrs": "requested_attributes", "revealed_attr_groups": "requested_attributes", "predicates": "requested_predicates", }.items(): - for (uuid, spec) in pres["requested_proof"].get(req_proof_key, {}).items(): + for uuid, spec in pres["requested_proof"].get(req_proof_key, {}).items(): if ( "revocation" not in cred_defs[ @@ -132,7 +132,7 @@ async def check_timestamps( LOGGER.debug(f">>> got non-revoc intervals: {non_revoc_intervals}") # timestamp for irrevocable credential cred_defs = [] - for (index, ident) in enumerate(pres["identifiers"]): + for index, ident in enumerate(pres["identifiers"]): LOGGER.debug(f">>> got (index, ident): ({index},{ident})") cred_def_id = ident["cred_def_id"] multitenant_mgr = profile.inject_or(BaseMultitenantManager) @@ -184,7 +184,7 @@ async def check_timestamps( revealed_groups = pres["requested_proof"].get("revealed_attr_groups", {}) self_attested = pres["requested_proof"].get("self_attested_attrs", {}) preds = pres["requested_proof"].get("predicates", {}) - for (uuid, req_attr) in pres_req["requested_attributes"].items(): + for uuid, req_attr in pres_req["requested_attributes"].items(): if "name" in req_attr: if uuid in revealed_attrs: index = revealed_attrs[uuid]["sub_proof_index"] @@ -258,7 +258,7 @@ async def check_timestamps( f"{non_revoc_intervals[uuid]}" ) - for (uuid, req_pred) in pres_req["requested_predicates"].items(): + for uuid, req_pred in pres_req["requested_predicates"].items(): pred_spec = preds.get(uuid) if pred_spec is None or "sub_proof_index" not in pred_spec: raise ValueError( @@ -314,7 +314,7 @@ async def pre_verify(self, pres_req: dict, pres: dict) -> list: if "proof" not in pres: raise ValueError("Presentation missing 'proof'") - for (uuid, req_pred) in pres_req["requested_predicates"].items(): + for uuid, req_pred in pres_req["requested_predicates"].items(): try: canon_attr = canon(req_pred["name"]) matched = False @@ -340,7 +340,7 @@ async def pre_verify(self, pres_req: dict, pres: dict) -> list: unrevealed_attrs = pres["requested_proof"].get("unrevealed_attrs", {}) revealed_groups = pres["requested_proof"].get("revealed_attr_groups", {}) self_attested = pres["requested_proof"].get("self_attested_attrs", {}) - for (uuid, req_attr) in pres_req["requested_attributes"].items(): + for uuid, req_attr in pres_req["requested_attributes"].items(): if "name" in req_attr: if uuid in revealed_attrs: pres_req_attr_spec = {req_attr["name"]: revealed_attrs[uuid]} @@ -375,7 +375,7 @@ async def pre_verify(self, pres_req: dict, pres: dict) -> list: f"Request attribute missing 'name' and 'names': '{uuid}'" ) - for (attr, spec) in pres_req_attr_spec.items(): + for attr, spec in pres_req_attr_spec.items(): try: primary_enco = pres["proof"]["proofs"][spec["sub_proof_index"]][ "primary_proof" diff --git a/aries_cloudagent/ledger/indy.py b/aries_cloudagent/ledger/indy.py index de3e8d40fe..a127f8c028 100644 --- a/aries_cloudagent/ledger/indy.py +++ b/aries_cloudagent/ledger/indy.py @@ -389,6 +389,8 @@ async def _submit( if taa_accept: acceptance = await self.get_latest_txn_author_acceptance() if acceptance: + # flake8 and black 23.1.0 check collision fix + # fmt: off request_json = await ( indy.ledger.append_txn_author_agreement_acceptance_to_request( request_json, @@ -399,6 +401,7 @@ async def _submit( acceptance["time"], ) ) + # fmt: on if write_ledger: submit_op = indy.ledger.sign_and_submit_request( self.pool.handle, diff --git a/aries_cloudagent/ledger/merkel_validation/trie.py b/aries_cloudagent/ledger/merkel_validation/trie.py index 2c7acedf98..08eb958140 100644 --- a/aries_cloudagent/ledger/merkel_validation/trie.py +++ b/aries_cloudagent/ledger/merkel_validation/trie.py @@ -79,7 +79,7 @@ async def verify_spv_proof(expected_value, proof_nodes, serialized=True): json.loads(rlp_decode(decoded_node[1])[0].decode("utf-8")) ) == expected_value: return True - except (DecodingError): + except DecodingError: continue return False except Exception: diff --git a/aries_cloudagent/ledger/multiple_ledger/tests/test_indy_manager.py b/aries_cloudagent/ledger/multiple_ledger/tests/test_indy_manager.py index de629b2d78..572992a313 100644 --- a/aries_cloudagent/ledger/multiple_ledger/tests/test_indy_manager.py +++ b/aries_cloudagent/ledger/multiple_ledger/tests/test_indy_manager.py @@ -389,7 +389,10 @@ async def test_lookup_did_in_configured_ledgers_cached_prod_ledger(self): cache = InMemoryCache() await cache.set("did_ledger_id_resolver::Av63wJYM7xYR4AiygYq4c3", "test_prod_2") self.profile.context.injector.bind_instance(BaseCache, cache) - (ledger_id, ledger_inst,) = await self.manager.lookup_did_in_configured_ledgers( + ( + ledger_id, + ledger_inst, + ) = await self.manager.lookup_did_in_configured_ledgers( "Av63wJYM7xYR4AiygYq4c3", cache_did=True ) assert ledger_id == "test_prod_2" @@ -401,7 +404,10 @@ async def test_lookup_did_in_configured_ledgers_cached_non_prod_ledger(self): "did_ledger_id_resolver::Av63wJYM7xYR4AiygYq4c3", "test_non_prod_2", None ) self.profile.context.injector.bind_instance(BaseCache, cache) - (ledger_id, ledger_inst,) = await self.manager.lookup_did_in_configured_ledgers( + ( + ledger_id, + ledger_inst, + ) = await self.manager.lookup_did_in_configured_ledgers( "Av63wJYM7xYR4AiygYq4c3", cache_did=True ) assert ledger_id == "test_non_prod_2" diff --git a/aries_cloudagent/ledger/multiple_ledger/tests/test_indy_vdr_manager.py b/aries_cloudagent/ledger/multiple_ledger/tests/test_indy_vdr_manager.py index a09fb13983..86aa27a3d5 100644 --- a/aries_cloudagent/ledger/multiple_ledger/tests/test_indy_vdr_manager.py +++ b/aries_cloudagent/ledger/multiple_ledger/tests/test_indy_vdr_manager.py @@ -443,7 +443,10 @@ async def test_lookup_did_in_configured_ledgers_cached_prod_ledger(self): cache = InMemoryCache() await cache.set("did_ledger_id_resolver::Av63wJYM7xYR4AiygYq4c3", "test_prod_1") self.profile.context.injector.bind_instance(BaseCache, cache) - (ledger_id, ledger_inst,) = await self.manager.lookup_did_in_configured_ledgers( + ( + ledger_id, + ledger_inst, + ) = await self.manager.lookup_did_in_configured_ledgers( "Av63wJYM7xYR4AiygYq4c3", cache_did=True ) assert ledger_id == "test_prod_1" @@ -455,7 +458,10 @@ async def test_lookup_did_in_configured_ledgers_cached_non_prod_ledger(self): "did_ledger_id_resolver::Av63wJYM7xYR4AiygYq4c3", "test_non_prod_2", None ) self.profile.context.injector.bind_instance(BaseCache, cache) - (ledger_id, ledger_inst,) = await self.manager.lookup_did_in_configured_ledgers( + ( + ledger_id, + ledger_inst, + ) = await self.manager.lookup_did_in_configured_ledgers( "Av63wJYM7xYR4AiygYq4c3", cache_did=True ) assert ledger_id == "test_non_prod_2" diff --git a/aries_cloudagent/ledger/tests/test_indy.py b/aries_cloudagent/ledger/tests/test_indy.py index 0b36b67452..fe9c8216ee 100644 --- a/aries_cloudagent/ledger/tests/test_indy.py +++ b/aries_cloudagent/ledger/tests/test_indy.py @@ -337,7 +337,6 @@ async def test_submit_signed_taa_accept( mock_create_config, mock_set_proto, ): - mock_append_taa.return_value = "{}" mock_sign_submit.return_value = '{"op": "REPLY"}' @@ -387,7 +386,6 @@ async def test_submit_unsigned( mock_create_config, mock_set_proto, ): - mock_did = async_mock.MagicMock() future = asyncio.Future() @@ -419,7 +417,6 @@ async def test_submit_unsigned_ledger_transaction_error( mock_create_config, mock_set_proto, ): - mock_did = async_mock.MagicMock() future = asyncio.Future() @@ -454,7 +451,6 @@ async def test_submit_rejected( mock_create_config, mock_set_proto, ): - mock_did = async_mock.MagicMock() future = asyncio.Future() @@ -690,7 +686,6 @@ async def test_send_schema_ledger_transaction_error_already_exists( mock_create_config, mock_set_proto, ): - mock_wallet = async_mock.MagicMock() self.session.context.injector.bind_provider(BaseWallet, mock_wallet) mock_is_ledger_read_only.return_value = False @@ -736,7 +731,6 @@ async def test_send_schema_ledger_read_only( mock_create_config, mock_set_proto, ): - mock_wallet = async_mock.MagicMock() self.session.context.injector.bind_provider(BaseWallet, mock_wallet) @@ -778,7 +772,6 @@ async def test_send_schema_issuer_error( mock_create_config, mock_set_proto, ): - mock_wallet = async_mock.MagicMock() self.session.context.injector.bind_provider(BaseWallet, mock_wallet) mock_is_ledger_read_only.return_value = False @@ -823,7 +816,6 @@ async def test_send_schema_ledger_transaction_error( mock_create_config, mock_set_proto, ): - mock_wallet = async_mock.MagicMock() self.session.context.injector.bind_provider(BaseWallet, mock_wallet) diff --git a/aries_cloudagent/ledger/tests/test_routes.py b/aries_cloudagent/ledger/tests/test_routes.py index 95bce0d497..14853fc6bb 100644 --- a/aries_cloudagent/ledger/tests/test_routes.py +++ b/aries_cloudagent/ledger/tests/test_routes.py @@ -418,7 +418,6 @@ async def test_register_nym_create_transaction_for_endorser_storage_x(self): ) as mock_conn_rec_retrieve, async_mock.patch.object( test_module, "TransactionManager", async_mock.MagicMock() ) as mock_txn_mgr: - mock_txn_mgr.return_value = async_mock.MagicMock( create_record=async_mock.AsyncMock( side_effect=test_module.StorageError() diff --git a/aries_cloudagent/messaging/credential_definitions/tests/test_routes.py b/aries_cloudagent/messaging/credential_definitions/tests/test_routes.py index 24b5a41bda..3a0cccb1b2 100644 --- a/aries_cloudagent/messaging/credential_definitions/tests/test_routes.py +++ b/aries_cloudagent/messaging/credential_definitions/tests/test_routes.py @@ -158,7 +158,6 @@ async def test_send_credential_definition_create_transaction_for_endorser_storag ) as mock_conn_rec_retrieve, async_mock.patch.object( test_module, "TransactionManager", async_mock.MagicMock() ) as mock_txn_mgr: - mock_conn_rec_retrieve.return_value = async_mock.MagicMock( metadata_get=async_mock.CoroutineMock( return_value={ diff --git a/aries_cloudagent/messaging/decorators/tests/test_decorator_set.py b/aries_cloudagent/messaging/decorators/tests/test_decorator_set.py index bed13f62c7..96e2c034af 100644 --- a/aries_cloudagent/messaging/decorators/tests/test_decorator_set.py +++ b/aries_cloudagent/messaging/decorators/tests/test_decorator_set.py @@ -33,7 +33,6 @@ def test_deco_set(self): assert all(k in deco_set.models for k in DEFAULT_MODELS) def test_extract(self): - decor_value = {} message = {"~decorator": decor_value, "one": "TWO"} @@ -47,7 +46,6 @@ def test_extract(self): assert remain == {"one": "TWO"} def test_dict(self): - decors = BaseDecoratorSet() decors["test"] = "TEST" assert decors["test"] == "TEST" @@ -55,7 +53,6 @@ def test_dict(self): assert result == {"~test": "TEST"} def test_decorator_model(self): - decor_value = {} message = {"~test": {"value": "TEST"}} @@ -70,7 +67,6 @@ def test_decorator_model(self): assert result == message def test_field_decorator(self): - decor_value = {} message = {"test~decorator": decor_value, "one": "TWO"} @@ -86,7 +82,6 @@ def test_field_decorator(self): assert "test~decorator" in decors.to_dict() def test_skip_decorator(self): - decor_value = {} message = {"handled~decorator": decor_value, "one": "TWO"} diff --git a/aries_cloudagent/messaging/decorators/tests/test_localization_decorator.py b/aries_cloudagent/messaging/decorators/tests/test_localization_decorator.py index 93e7e32d4b..6140f4e1f4 100644 --- a/aries_cloudagent/messaging/decorators/tests/test_localization_decorator.py +++ b/aries_cloudagent/messaging/decorators/tests/test_localization_decorator.py @@ -4,7 +4,6 @@ class TestThreadDecorator(TestCase): - LOCALE = "en-ca" LOCALIZABLE = ["a", "b"] CATALOGS = ["http://192.168.56.111/my-project/catalog.json"] diff --git a/aries_cloudagent/messaging/decorators/tests/test_thread_decorator.py b/aries_cloudagent/messaging/decorators/tests/test_thread_decorator.py index 898930233f..8ad7be4878 100644 --- a/aries_cloudagent/messaging/decorators/tests/test_thread_decorator.py +++ b/aries_cloudagent/messaging/decorators/tests/test_thread_decorator.py @@ -4,14 +4,12 @@ class TestThreadDecorator(TestCase): - thread_id = "tid-001" parent_id = "tid-000" sender_order = 1 received_orders = {"did": 2} def test_init(self): - decorator = ThreadDecorator( thid=self.thread_id, pthid=self.parent_id, @@ -24,7 +22,6 @@ def test_init(self): assert decorator.received_orders == self.received_orders def test_serialize_load(self): - decorator = ThreadDecorator( thid=self.thread_id, pthid=self.parent_id, diff --git a/aries_cloudagent/messaging/decorators/tests/test_trace_decorator.py b/aries_cloudagent/messaging/decorators/tests/test_trace_decorator.py index 2d2613fe96..b67da74477 100644 --- a/aries_cloudagent/messaging/decorators/tests/test_trace_decorator.py +++ b/aries_cloudagent/messaging/decorators/tests/test_trace_decorator.py @@ -4,7 +4,6 @@ class TestTraceDecorator(TestCase): - target_api = "http://example.com/api/trace/" full_thread_api = False target_msg = TRACE_MESSAGE_TARGET @@ -20,7 +19,6 @@ class TestTraceDecorator(TestCase): outcome = "OK ..." def test_init_api(self): - decorator = TraceDecorator( target=self.target_api, full_thread=self.full_thread_api, @@ -29,7 +27,6 @@ def test_init_api(self): assert decorator.full_thread == self.full_thread_api def test_init_message(self): - x_msg_id = self.msg_id x_thread_id = self.thread_id x_trace_report = TraceReport( @@ -64,7 +61,6 @@ def test_init_message(self): assert trace_report.outcome == self.outcome def test_serialize_load(self): - x_msg_id = self.msg_id x_thread_id = self.thread_id x_trace_report = TraceReport( diff --git a/aries_cloudagent/messaging/jsonld/routes.py b/aries_cloudagent/messaging/jsonld/routes.py index eb3b7b48c2..f89db9752a 100644 --- a/aries_cloudagent/messaging/jsonld/routes.py +++ b/aries_cloudagent/messaging/jsonld/routes.py @@ -84,7 +84,7 @@ async def sign(request: web.BaseRequest): session, doc.get("credential"), doc.get("options"), body.get("verkey") ) response["signed_doc"] = doc_with_proof - except (BaseJSONLDMessagingError) as err: + except BaseJSONLDMessagingError as err: response["error"] = str(err) except (WalletError, InjectionError): raise web.HTTPForbidden(reason="No wallet available") diff --git a/aries_cloudagent/messaging/schemas/tests/test_routes.py b/aries_cloudagent/messaging/schemas/tests/test_routes.py index 91a826d628..f5347a6b2b 100644 --- a/aries_cloudagent/messaging/schemas/tests/test_routes.py +++ b/aries_cloudagent/messaging/schemas/tests/test_routes.py @@ -157,7 +157,6 @@ async def test_send_schema_create_transaction_for_endorser_storage_x(self): ) as mock_conn_rec_retrieve, async_mock.patch.object( test_module, "TransactionManager", async_mock.MagicMock() ) as mock_txn_mgr: - mock_txn_mgr.return_value = async_mock.MagicMock( create_record=async_mock.CoroutineMock( side_effect=test_module.StorageError() diff --git a/aries_cloudagent/multitenant/base.py b/aries_cloudagent/multitenant/base.py index ae5f5c60bd..9e242d3351 100644 --- a/aries_cloudagent/multitenant/base.py +++ b/aries_cloudagent/multitenant/base.py @@ -378,7 +378,7 @@ async def _get_wallet_by_key(self, recipient_key: str) -> Optional[WalletRecord] ) return wallet - except (RouteNotFoundError): + except RouteNotFoundError: pass async def get_profile_for_key( diff --git a/aries_cloudagent/multitenant/route_manager.py b/aries_cloudagent/multitenant/route_manager.py index ff8f1d37e7..d430aa0c51 100644 --- a/aries_cloudagent/multitenant/route_manager.py +++ b/aries_cloudagent/multitenant/route_manager.py @@ -66,7 +66,7 @@ async def _route_for_key( # If no error is thrown, it means there is already a record return None - except (StorageNotFoundError): + except StorageNotFoundError: pass await routing_mgr.create_route_record( diff --git a/aries_cloudagent/multitenant/tests/test_base.py b/aries_cloudagent/multitenant/tests/test_base.py index a58cbc9375..c302b44813 100644 --- a/aries_cloudagent/multitenant/tests/test_base.py +++ b/aries_cloudagent/multitenant/tests/test_base.py @@ -209,7 +209,6 @@ async def test_create_wallet_fails_if_wallet_name_exists(self): ) async def test_create_wallet_saves_wallet_record_creates_profile(self): - mock_route_manager = async_mock.MagicMock() mock_route_manager.route_public_did = async_mock.CoroutineMock() self.context.injector.bind_instance(RouteManager, mock_route_manager) diff --git a/aries_cloudagent/protocols/actionmenu/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/actionmenu/v1_0/tests/test_routes.py index 48148e5ff9..f766d9a77f 100644 --- a/aries_cloudagent/protocols/actionmenu/v1_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/actionmenu/v1_0/tests/test_routes.py @@ -78,7 +78,6 @@ async def test_actionmenu_perform(self): ) as mock_perform, async_mock.patch.object( test_module.web, "json_response" ) as mock_response: - mock_conn_record.retrieve_by_id = async_mock.CoroutineMock() res = await test_module.actionmenu_perform(self.request) @@ -97,7 +96,6 @@ async def test_actionmenu_perform_no_conn_record(self): ) as mock_conn_record, async_mock.patch.object( test_module, "Perform", autospec=True ) as mock_perform: - # Emulate storage not found (bad connection id) mock_conn_record.retrieve_by_id = async_mock.CoroutineMock( side_effect=StorageNotFoundError @@ -115,7 +113,6 @@ async def test_actionmenu_perform_conn_not_ready(self): ) as mock_conn_record, async_mock.patch.object( test_module, "Perform", autospec=True ) as mock_perform: - # Emulate connection not ready mock_conn_record.retrieve_by_id = async_mock.CoroutineMock() mock_conn_record.retrieve_by_id.return_value.is_ready = False @@ -134,7 +131,6 @@ async def test_actionmenu_request(self): ) as menu_request, async_mock.patch.object( test_module.web, "json_response" ) as mock_response: - mock_conn_record.retrieve_by_id = async_mock.CoroutineMock() res = await test_module.actionmenu_request(self.request) @@ -153,7 +149,6 @@ async def test_actionmenu_request_no_conn_record(self): ) as mock_conn_record, async_mock.patch.object( test_module, "Perform", autospec=True ) as mock_perform: - # Emulate storage not found (bad connection id) mock_conn_record.retrieve_by_id = async_mock.CoroutineMock( side_effect=StorageNotFoundError @@ -171,7 +166,6 @@ async def test_actionmenu_request_conn_not_ready(self): ) as mock_conn_record, async_mock.patch.object( test_module, "Perform", autospec=True ) as mock_perform: - # Emulate connection not ready mock_conn_record.retrieve_by_id = async_mock.CoroutineMock() mock_conn_record.retrieve_by_id.return_value.is_ready = False @@ -190,7 +184,6 @@ async def test_actionmenu_send(self): ) as mock_menu, async_mock.patch.object( test_module.web, "json_response" ) as mock_response: - mock_conn_record.retrieve_by_id = async_mock.CoroutineMock() mock_menu.deserialize = async_mock.MagicMock() @@ -210,7 +203,6 @@ async def test_actionmenu_send_deserialize_x(self): ) as mock_conn_record, async_mock.patch.object( test_module, "Menu", autospec=True ) as mock_menu: - mock_conn_record.retrieve_by_id = async_mock.CoroutineMock() mock_menu.deserialize = async_mock.MagicMock( side_effect=test_module.BaseModelError("cannot deserialize") @@ -228,7 +220,6 @@ async def test_actionmenu_send_no_conn_record(self): ) as mock_conn_record, async_mock.patch.object( test_module, "Menu", autospec=True ) as mock_menu: - mock_menu.deserialize = async_mock.MagicMock() # Emulate storage not found (bad connection id) @@ -248,7 +239,6 @@ async def test_actionmenu_send_conn_not_ready(self): ) as mock_conn_record, async_mock.patch.object( test_module, "Menu", autospec=True ) as mock_menu: - mock_menu.deserialize = async_mock.MagicMock() # Emulate connection not ready diff --git a/aries_cloudagent/protocols/actionmenu/v1_0/tests/test_service.py b/aries_cloudagent/protocols/actionmenu/v1_0/tests/test_service.py index 8a94c08361..4573997115 100644 --- a/aries_cloudagent/protocols/actionmenu/v1_0/tests/test_service.py +++ b/aries_cloudagent/protocols/actionmenu/v1_0/tests/test_service.py @@ -17,8 +17,8 @@ async def test_get_active_menu(self): mock_event_bus = MockEventBus() self.context.profile.context.injector.bind_instance(EventBus, mock_event_bus) - self.menu_service = await ( - test_module.DriverMenuService.service_handler()(self.context) + self.menu_service = await test_module.DriverMenuService.service_handler()( + self.context ) connection = async_mock.MagicMock() @@ -41,8 +41,8 @@ async def test_perform_menu_action(self): mock_event_bus = MockEventBus() self.context.profile.context.injector.bind_instance(EventBus, mock_event_bus) - self.menu_service = await ( - test_module.DriverMenuService.service_handler()(self.context) + self.menu_service = await test_module.DriverMenuService.service_handler()( + self.context ) action_name = "action" diff --git a/aries_cloudagent/protocols/basicmessage/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/basicmessage/v1_0/tests/test_routes.py index 6265cbd762..ca730021b0 100644 --- a/aries_cloudagent/protocols/basicmessage/v1_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/basicmessage/v1_0/tests/test_routes.py @@ -34,7 +34,6 @@ async def test_connections_send_message(self): ) as mock_basic_message, async_mock.patch.object( test_module.web, "json_response" ) as mock_response: - mock_connection_record.retrieve_by_id = async_mock.CoroutineMock() res = await test_module.connections_send_message(self.request) @@ -50,7 +49,6 @@ async def test_connections_send_message_no_conn_record(self): ) as mock_connection_record, async_mock.patch.object( test_module, "BasicMessage", autospec=True ) as mock_basic_message: - # Emulate storage not found (bad connection id) mock_connection_record.retrieve_by_id = async_mock.CoroutineMock( side_effect=StorageNotFoundError @@ -68,7 +66,6 @@ async def test_connections_send_message_not_ready(self): ) as mock_connection_record, async_mock.patch.object( test_module, "BasicMessage", autospec=True ) as mock_basic_message: - # Emulate connection not ready mock_connection_record.retrieve_by_id = async_mock.CoroutineMock() mock_connection_record.retrieve_by_id.return_value.is_ready = False diff --git a/aries_cloudagent/protocols/connections/v1_0/messages/tests/test_connection_invitation.py b/aries_cloudagent/protocols/connections/v1_0/messages/tests/test_connection_invitation.py index 58d38b5917..b35bf95e1a 100644 --- a/aries_cloudagent/protocols/connections/v1_0/messages/tests/test_connection_invitation.py +++ b/aries_cloudagent/protocols/connections/v1_0/messages/tests/test_connection_invitation.py @@ -84,7 +84,6 @@ def test_from_no_url(self): class TestConnectionInvitationSchema(TestCase): - connection_invitation = ConnectionInvitation( label="label", did="did:sov:QmWbsNYhMrjHiqZDTUTEJs" ) diff --git a/aries_cloudagent/protocols/connections/v1_0/messages/tests/test_connection_response.py b/aries_cloudagent/protocols/connections/v1_0/messages/tests/test_connection_response.py index fd47e9c896..0fadf94ff4 100644 --- a/aries_cloudagent/protocols/connections/v1_0/messages/tests/test_connection_response.py +++ b/aries_cloudagent/protocols/connections/v1_0/messages/tests/test_connection_response.py @@ -19,7 +19,6 @@ class TestConfig: - test_seed = "testseed000000000000000000000001" test_did = "55GkHamhTU1ZbTbV2ab9DE" test_verkey = "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx" diff --git a/aries_cloudagent/protocols/connections/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/connections/v1_0/tests/test_routes.py index 5886e5aed9..274ef8b6e6 100644 --- a/aries_cloudagent/protocols/connections/v1_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/connections/v1_0/tests/test_routes.py @@ -360,7 +360,6 @@ async def test_connections_create_invitation(self): ) as mock_conn_mgr, async_mock.patch.object( test_module.web, "json_response" ) as mock_response: - mock_conn_mgr.return_value.create_invitation = async_mock.CoroutineMock( return_value=( async_mock.MagicMock( # connection record @@ -543,7 +542,6 @@ async def test_connections_accept_invitation(self): ) as mock_conn_mgr, async_mock.patch.object( test_module.web, "json_response" ) as mock_response: - mock_conn_rec_retrieve_by_id.return_value = mock_conn_rec mock_conn_mgr.return_value.create_request = async_mock.CoroutineMock() diff --git a/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py index 30ae59db04..f5ddf31b4d 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py @@ -41,7 +41,6 @@ class TestConfig: - test_seed = "testseed000000000000000000000001" test_did = "55GkHamhTU1ZbTbV2ab9DE" test_verkey = "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx" diff --git a/aries_cloudagent/protocols/didexchange/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/didexchange/v1_0/tests/test_routes.py index 44584c5d1e..8de2345b31 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/tests/test_routes.py @@ -43,7 +43,6 @@ async def test_didx_accept_invitation(self): ) as mock_didx_mgr, async_mock.patch.object( test_module.web, "json_response" ) as mock_response: - mock_conn_rec_class.retrieve_by_id.return_value = mock_conn_rec mock_didx_mgr.return_value.create_request = async_mock.CoroutineMock() diff --git a/aries_cloudagent/protocols/discovery/v1_0/messages/tests/test_disclose.py b/aries_cloudagent/protocols/discovery/v1_0/messages/tests/test_disclose.py index 658f25bc67..84e02cf12e 100644 --- a/aries_cloudagent/protocols/discovery/v1_0/messages/tests/test_disclose.py +++ b/aries_cloudagent/protocols/discovery/v1_0/messages/tests/test_disclose.py @@ -42,7 +42,6 @@ def test_serialize(self, mock_disclose_schema_dump): class TestDiscloseSchema(TestCase): - disclose = Disclose(protocols=[]) def test_make_model(self): diff --git a/aries_cloudagent/protocols/discovery/v1_0/messages/tests/test_query.py b/aries_cloudagent/protocols/discovery/v1_0/messages/tests/test_query.py index 114c02f80c..18af3b6ba7 100644 --- a/aries_cloudagent/protocols/discovery/v1_0/messages/tests/test_query.py +++ b/aries_cloudagent/protocols/discovery/v1_0/messages/tests/test_query.py @@ -39,7 +39,6 @@ def test_serialize(self, mock_query_schema_dump): class TestQuerySchema(TestCase): - query = Query(query="*", comment="comment") def test_make_model(self): diff --git a/aries_cloudagent/protocols/discovery/v2_0/messages/tests/test_queries.py b/aries_cloudagent/protocols/discovery/v2_0/messages/tests/test_queries.py index e8957dc912..01dfa793f3 100644 --- a/aries_cloudagent/protocols/discovery/v2_0/messages/tests/test_queries.py +++ b/aries_cloudagent/protocols/discovery/v2_0/messages/tests/test_queries.py @@ -60,7 +60,6 @@ def test_serialize(self, mock_queries_schema_dump): class TestQuerySchema(TestCase): - test_queries = [ QueryItem(feature_type="protocol", match=TEST_QUERY_PROTOCOL), QueryItem(feature_type="goal-code", match=TEST_QUERY_GOAL_CODE), diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_manager.py index f4b2719237..df10ffe4ee 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_manager.py @@ -496,7 +496,6 @@ async def test_complete_transaction(self): ) as save_record, async_mock.patch.object( ConnRecord, "retrieve_by_id" ) as mock_conn_rec_retrieve: - mock_conn_rec_retrieve.return_value = async_mock.MagicMock( metadata_get=async_mock.CoroutineMock( return_value={ diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_routes.py index 611d1245ce..4d6e3b0897 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_routes.py @@ -1553,7 +1553,6 @@ async def test_transaction_write_schema_txn(self): ) as mock_txn_mgr, async_mock.patch.object( test_module.web, "json_response" ) as mock_response: - mock_txn_mgr.return_value.complete_transaction = async_mock.CoroutineMock() mock_txn_mgr.return_value.complete_transaction.return_value = ( @@ -1600,7 +1599,6 @@ async def test_transaction_write_wrong_state_x(self): with async_mock.patch.object( TransactionRecord, "retrieve_by_id", async_mock.CoroutineMock() ) as mock_txn_rec_retrieve: - mock_txn_rec_retrieve.return_value = async_mock.MagicMock( serialize=async_mock.MagicMock(return_value={"...": "..."}), state=TransactionRecord.STATE_TRANSACTION_CREATED, diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/manager.py b/aries_cloudagent/protocols/issue_credential/v1_0/manager.py index 90d1a15217..1bca37dea8 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/manager.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/manager.py @@ -356,13 +356,15 @@ async def receive_offer( # Get credential exchange record (holder sent proposal first) # or create it (issuer sent offer first) try: - cred_ex_record = await ( - V10CredentialExchange.retrieve_by_connection_and_thread( - txn, - connection_id, - message._thread_id, - role=V10CredentialExchange.ROLE_HOLDER, - for_update=True, + cred_ex_record = ( + await ( + V10CredentialExchange.retrieve_by_connection_and_thread( + txn, + connection_id, + message._thread_id, + role=V10CredentialExchange.ROLE_HOLDER, + for_update=True, + ) ) ) except StorageNotFoundError: # issuer sent this offer free of any proposal @@ -532,13 +534,15 @@ async def receive_request( async with self._profile.transaction() as txn: try: - cred_ex_record = await ( - V10CredentialExchange.retrieve_by_connection_and_thread( - txn, - connection_id, - message._thread_id, - role=V10CredentialExchange.ROLE_ISSUER, - for_update=True, + cred_ex_record = ( + await ( + V10CredentialExchange.retrieve_by_connection_and_thread( + txn, + connection_id, + message._thread_id, + role=V10CredentialExchange.ROLE_ISSUER, + for_update=True, + ) ) ) except StorageNotFoundError: @@ -741,13 +745,15 @@ async def receive_credential( async with self._profile.transaction() as txn: try: - cred_ex_record = await ( - V10CredentialExchange.retrieve_by_connection_and_thread( - txn, - connection_id, - message._thread_id, - role=V10CredentialExchange.ROLE_HOLDER, - for_update=True, + cred_ex_record = ( + await ( + V10CredentialExchange.retrieve_by_connection_and_thread( + txn, + connection_id, + message._thread_id, + role=V10CredentialExchange.ROLE_HOLDER, + for_update=True, + ) ) ) except StorageNotFoundError: @@ -947,13 +953,15 @@ async def receive_credential_ack( """ async with self._profile.transaction() as txn: try: - cred_ex_record = await ( - V10CredentialExchange.retrieve_by_connection_and_thread( - txn, - connection_id, - message._thread_id, - role=V10CredentialExchange.ROLE_ISSUER, - for_update=True, + cred_ex_record = ( + await ( + V10CredentialExchange.retrieve_by_connection_and_thread( + txn, + connection_id, + message._thread_id, + role=V10CredentialExchange.ROLE_ISSUER, + for_update=True, + ) ) ) except StorageNotFoundError: @@ -987,9 +995,11 @@ async def receive_problem_report( """ async with self._profile.transaction() as txn: try: - cred_ex_record = await ( - V10CredentialExchange.retrieve_by_connection_and_thread( - txn, connection_id, message._thread_id, for_update=True + cred_ex_record = ( + await ( + V10CredentialExchange.retrieve_by_connection_and_thread( + txn, connection_id, message._thread_id, for_update=True + ) ) ) except StorageNotFoundError: diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py index 94ec08f0f6..838f5abdf4 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py @@ -1357,7 +1357,6 @@ async def test_store_credential(self): ) as save_ex, async_mock.patch.object( V10CredentialExchange, "delete_record", autospec=True ) as delete_ex: - mock_rev_reg.from_definition = async_mock.MagicMock( return_value=async_mock.MagicMock( get_or_fetch_local_tails_path=async_mock.CoroutineMock() diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_routes.py index 1afc367d20..129f7eceb1 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_routes.py @@ -253,7 +253,6 @@ async def test_credential_exchange_send_no_conn_record(self): ) as mock_conn_rec, async_mock.patch.object( test_module, "CredentialManager", autospec=True ) as mock_credential_manager: - # Emulate storage not found (bad connection id) mock_conn_rec.retrieve_by_id = async_mock.CoroutineMock( side_effect=test_module.StorageNotFoundError() @@ -280,7 +279,6 @@ async def test_credential_exchange_send_not_ready(self): ) as mock_conn_rec, async_mock.patch.object( test_module, "CredentialManager", autospec=True ) as mock_credential_manager: - # Emulate connection not ready mock_conn_rec.retrieve_by_id.return_value.is_ready = False @@ -343,7 +341,6 @@ async def test_credential_exchange_send_proposal(self): ) as mock_credential_manager, async_mock.patch.object( test_module.web, "json_response" ) as mock_response: - mock_cred_ex_record = async_mock.MagicMock() mock_credential_manager.return_value.create_proposal.return_value = ( mock_cred_ex_record @@ -367,7 +364,6 @@ async def test_credential_exchange_send_proposal_no_conn_record(self): ) as mock_credential_manager, async_mock.patch.object( test_module.CredentialPreview, "deserialize", autospec=True ) as mock_preview_deserialize: - # Emulate storage not found (bad connection id) mock_conn_rec.retrieve_by_id = async_mock.CoroutineMock( side_effect=test_module.StorageNotFoundError() @@ -405,7 +401,6 @@ async def test_credential_exchange_send_proposal_not_ready(self): ) as mock_credential_manager, async_mock.patch.object( test_module.CredentialPreview, "deserialize", autospec=True ) as mock_preview_deserialize: - # Emulate connection not ready mock_conn_rec.retrieve_by_id = async_mock.CoroutineMock() mock_conn_rec.retrieve_by_id.return_value.is_ready = False @@ -595,7 +590,6 @@ async def test_credential_exchange_send_free_offer(self): ) as mock_credential_manager, async_mock.patch.object( test_module.web, "json_response" ) as mock_response: - mock_credential_manager.return_value.create_offer = ( async_mock.CoroutineMock() ) @@ -647,7 +641,6 @@ async def test_credential_exchange_send_free_offer_no_conn_record(self): ) as mock_conn_rec, async_mock.patch.object( test_module, "CredentialManager", autospec=True ) as mock_credential_manager: - # Emulate storage not found (bad connection id) mock_conn_rec.retrieve_by_id = async_mock.CoroutineMock( side_effect=test_module.StorageNotFoundError() @@ -673,7 +666,6 @@ async def test_credential_exchange_send_free_offer_not_ready(self): ) as mock_conn_rec, async_mock.patch.object( test_module, "CredentialManager", autospec=True ) as mock_credential_manager: - # Emulate connection not ready mock_conn_rec.retrieve_by_id = async_mock.CoroutineMock() mock_conn_rec.retrieve_by_id.return_value.is_ready = False @@ -739,7 +731,6 @@ async def test_credential_exchange_send_bound_offer(self): ) as mock_cred_ex, async_mock.patch.object( test_module.web, "json_response" ) as mock_response: - mock_cred_ex.retrieve_by_id = async_mock.CoroutineMock() mock_cred_ex.retrieve_by_id.return_value.state = ( mock_cred_ex.STATE_PROPOSAL_RECEIVED @@ -878,7 +869,6 @@ async def test_credential_exchange_send_request(self): ) as mock_cred_ex, async_mock.patch.object( test_module.web, "json_response" ) as mock_response: - mock_cred_ex.retrieve_by_id = async_mock.CoroutineMock() mock_cred_ex.retrieve_by_id.return_value.state = ( mock_cred_ex.STATE_OFFER_RECEIVED @@ -912,7 +902,6 @@ async def test_credential_exchange_send_request_no_conn(self): ) as mock_cred_ex, async_mock.patch.object( test_module.web, "json_response" ) as mock_response: - mock_oob_rec.retrieve_by_tag_filter = async_mock.CoroutineMock( return_value=async_mock.MagicMock(our_recipient_key="our-recipient_key") ) @@ -1037,7 +1026,6 @@ async def test_credential_exchange_issue(self): ) as mock_cred_ex, async_mock.patch.object( test_module.web, "json_response" ) as mock_response: - mock_cred_ex.retrieve_by_id = async_mock.CoroutineMock() mock_cred_ex.retrieve_by_id.return_value.state = ( mock_cred_ex.STATE_REQUEST_RECEIVED @@ -1087,7 +1075,6 @@ async def test_credential_exchange_issue_no_conn_record(self): ) as mock_credential_manager, async_mock.patch.object( test_module, "V10CredentialExchange", autospec=True ) as mock_cred_ex_cls: - mock_cred_ex_rec.state = mock_cred_ex_cls.STATE_REQUEST_RECEIVED mock_cred_ex_cls.retrieve_by_id = async_mock.CoroutineMock( return_value=mock_cred_ex_rec @@ -1120,7 +1107,6 @@ async def test_credential_exchange_issue_not_ready(self): ) as mock_credential_manager, async_mock.patch.object( test_module, "V10CredentialExchange", autospec=True ) as mock_cred_ex: - mock_cred_ex.retrieve_by_id = async_mock.CoroutineMock() mock_cred_ex.retrieve_by_id.return_value.state = ( mock_cred_ex.STATE_REQUEST_RECEIVED @@ -1216,7 +1202,6 @@ async def test_credential_exchange_store(self): ) as mock_cred_ex, async_mock.patch.object( test_module.web, "json_response" ) as mock_response: - mock_cred_ex.retrieve_by_id = async_mock.CoroutineMock() mock_cred_ex.retrieve_by_id.return_value.state = ( mock_cred_ex.STATE_CREDENTIAL_RECEIVED @@ -1253,7 +1238,6 @@ async def test_credential_exchange_store_bad_cred_id_json(self): ) as mock_cred_ex, async_mock.patch.object( test_module.web, "json_response" ) as mock_response: - mock_cred_ex.retrieve_by_id = async_mock.CoroutineMock() mock_cred_ex.retrieve_by_id.return_value.state = ( mock_cred_ex.STATE_CREDENTIAL_RECEIVED diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/tests/test_handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/tests/test_handler.py index ea4565a6b6..f0e11070e6 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/tests/test_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/tests/test_handler.py @@ -274,7 +274,6 @@ async def test_get_suite_for_detail(self): "_did_info_for_did", async_mock.CoroutineMock(), ) as mock_did_info: - suite = await self.handler._get_suite_for_detail(detail) assert suite.signature_type == detail.options.proof_type diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/manager.py b/aries_cloudagent/protocols/issue_credential/v2_0/manager.py index f1d3f541c3..ad6f3f9313 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/manager.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/manager.py @@ -284,13 +284,11 @@ async def receive_offer( # or create it (issuer sent offer first) try: async with self._profile.session() as session: - cred_ex_record = await ( - V20CredExRecord.retrieve_by_conn_and_thread( - session, - connection_id, - cred_offer_message._thread_id, - role=V20CredExRecord.ROLE_HOLDER, - ) + cred_ex_record = await V20CredExRecord.retrieve_by_conn_and_thread( + session, + connection_id, + cred_offer_message._thread_id, + role=V20CredExRecord.ROLE_HOLDER, ) except StorageNotFoundError: # issuer sent this offer free of any proposal cred_ex_record = V20CredExRecord( @@ -420,13 +418,11 @@ async def receive_request( async with self._profile.session() as session: try: - cred_ex_record = await ( - V20CredExRecord.retrieve_by_conn_and_thread( - session, - connection_id, - cred_request_message._thread_id, - role=V20CredExRecord.ROLE_ISSUER, - ) + cred_ex_record = await V20CredExRecord.retrieve_by_conn_and_thread( + session, + connection_id, + cred_request_message._thread_id, + role=V20CredExRecord.ROLE_ISSUER, ) except StorageNotFoundError: # holder sent this request free of any offer @@ -553,13 +549,11 @@ async def receive_credential( # FIXME use transaction, fetch for_update async with self._profile.session() as session: - cred_ex_record = await ( - V20CredExRecord.retrieve_by_conn_and_thread( - session, - connection_id, - cred_issue_message._thread_id, - role=V20CredExRecord.ROLE_HOLDER, - ) + cred_ex_record = await V20CredExRecord.retrieve_by_conn_and_thread( + session, + connection_id, + cred_issue_message._thread_id, + role=V20CredExRecord.ROLE_HOLDER, ) cred_request_message = cred_ex_record.cred_request @@ -697,13 +691,11 @@ async def receive_credential_ack( """ # FIXME use transaction, fetch for_update async with self._profile.session() as session: - cred_ex_record = await ( - V20CredExRecord.retrieve_by_conn_and_thread( - session, - connection_id, - cred_ack_message._thread_id, - role=V20CredExRecord.ROLE_ISSUER, - ) + cred_ex_record = await V20CredExRecord.retrieve_by_conn_and_thread( + session, + connection_id, + cred_ack_message._thread_id, + role=V20CredExRecord.ROLE_ISSUER, ) cred_ex_record.state = V20CredExRecord.STATE_DONE @@ -740,12 +732,10 @@ async def receive_problem_report( """ # FIXME use transaction, fetch for_update async with self._profile.session() as session: - cred_ex_record = await ( - V20CredExRecord.retrieve_by_conn_and_thread( - session, - connection_id, - message._thread_id, - ) + cred_ex_record = await V20CredExRecord.retrieve_by_conn_and_thread( + session, + connection_id, + message._thread_id, ) cred_ex_record.state = V20CredExRecord.STATE_ABANDONED diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/tests/test_manager.py b/aries_cloudagent/protocols/issue_credential/v2_0/tests/test_manager.py index 583f52e9c5..55d654c5f1 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/tests/test_manager.py @@ -165,7 +165,6 @@ async def test_create_proposal(self): ) as mock_save, async_mock.patch.object( V20CredFormat.Format, "handler" ) as mock_handler: - mock_handler.return_value.create_proposal = async_mock.CoroutineMock( return_value=( V20CredFormat( @@ -209,7 +208,6 @@ async def test_create_proposal_no_preview(self): ) as mock_save, async_mock.patch.object( V20CredFormat.Format, "handler" ) as mock_handler: - mock_handler.return_value.create_proposal = async_mock.CoroutineMock( return_value=( V20CredFormat( @@ -261,7 +259,6 @@ async def test_receive_proposal(self): ) as mock_save, async_mock.patch.object( V20CredFormat.Format, "handler" ) as mock_handler: - mock_handler.return_value.receive_proposal = async_mock.CoroutineMock() cred_proposal = V20CredProposal( @@ -335,7 +332,6 @@ async def test_create_free_offer(self): ) as mock_save, async_mock.patch.object( V20CredFormat.Format, "handler" ) as mock_handler: - mock_handler.return_value.create_offer = async_mock.CoroutineMock( return_value=( V20CredFormat( @@ -627,7 +623,6 @@ async def test_create_bound_request(self): ) as mock_save, async_mock.patch.object( V20CredFormat.Format, "handler" ) as mock_handler: - mock_handler.return_value.create_request = async_mock.CoroutineMock( return_value=( V20CredFormat( @@ -715,7 +710,6 @@ async def test_create_free_request(self): ) as mock_save, async_mock.patch.object( V20CredFormat.Format, "handler" ) as mock_handler: - mock_handler.return_value.create_request = async_mock.CoroutineMock( return_value=( V20CredFormat( @@ -986,7 +980,6 @@ async def test_issue_credential(self): ) as mock_save, async_mock.patch.object( V20CredFormat.Format, "handler" ) as mock_handler: - mock_handler.return_value.issue_credential = async_mock.CoroutineMock( return_value=( V20CredFormat( @@ -1099,7 +1092,6 @@ async def test_receive_cred(self): ) as mock_retrieve, async_mock.patch.object( V20CredFormat.Format, "handler" ) as mock_handler: - mock_handler.return_value.receive_credential = async_mock.CoroutineMock() mock_retrieve.return_value = stored_cx_rec ret_cx_rec = await self.manager.receive_credential( @@ -1247,7 +1239,6 @@ async def test_store_credential(self): ) as mock_delete, async_mock.patch.object( V20CredFormat.Format, "handler" ) as mock_handler: - mock_handler.return_value.store_credential = async_mock.CoroutineMock() ret_cx_rec = await self.manager.store_credential( diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/tests/test_routes.py b/aries_cloudagent/protocols/issue_credential/v2_0/tests/test_routes.py index 7a689b66b4..845f21555c 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/tests/test_routes.py @@ -362,7 +362,6 @@ async def test_credential_exchange_send_request_no_conn_no_holder_did(self): ) as mock_cred_ex, async_mock.patch.object( test_module.web, "json_response" ) as mock_response: - mock_oob_rec.retrieve_by_tag_filter = async_mock.CoroutineMock( return_value=async_mock.MagicMock(our_recipient_key="our-recipient_key") ) @@ -407,7 +406,6 @@ async def test_credential_exchange_send_no_conn_record(self): ) as mock_conn_rec, async_mock.patch.object( test_module, "V20CredManager", autospec=True ) as mock_cred_mgr: - # Emulate storage not found (bad connection id) mock_conn_rec.retrieve_by_id = async_mock.CoroutineMock( side_effect=test_module.StorageNotFoundError() @@ -584,7 +582,6 @@ async def test_credential_exchange_send_proposal_not_ready(self): ) as mock_cred_mgr, async_mock.patch.object( test_module.V20CredPreview, "deserialize", autospec=True ) as mock_preview_deser: - # Emulate connection not ready mock_conn_rec.retrieve_by_id = async_mock.CoroutineMock() mock_conn_rec.retrieve_by_id.return_value.is_ready = False @@ -731,7 +728,6 @@ async def test_credential_exchange_send_free_offer_no_conn_record(self): ) as mock_conn_rec, async_mock.patch.object( test_module, "V20CredManager", autospec=True ) as mock_cred_mgr: - # Emulate storage not found (bad connection id) mock_conn_rec.retrieve_by_id = async_mock.CoroutineMock( side_effect=test_module.StorageNotFoundError() @@ -762,7 +758,6 @@ async def test_credential_exchange_send_free_offer_not_ready(self): ) as mock_conn_rec, async_mock.patch.object( test_module, "V20CredManager", autospec=True ) as mock_cred_mgr: - # Emulate connection not ready mock_conn_rec.retrieve_by_id = async_mock.CoroutineMock() mock_conn_rec.retrieve_by_id.return_value.is_ready = False @@ -986,7 +981,6 @@ async def test_credential_exchange_send_request(self): ) as mock_cx_rec_cls, async_mock.patch.object( test_module.web, "json_response" ) as mock_response: - mock_cx_rec_cls.retrieve_by_id = async_mock.CoroutineMock() mock_cx_rec_cls.retrieve_by_id.return_value.state = ( test_module.V20CredExRecord.STATE_OFFER_RECEIVED @@ -1096,7 +1090,6 @@ async def test_credential_exchange_send_free_request(self): ) as mock_cred_mgr, async_mock.patch.object( test_module.web, "json_response" ) as mock_response: - mock_cred_mgr.return_value.create_request = async_mock.CoroutineMock() mock_cx_rec = async_mock.MagicMock() @@ -1131,7 +1124,6 @@ async def test_credential_exchange_send_free_request_no_conn_record(self): ) as mock_conn_rec, async_mock.patch.object( test_module, "V20CredManager", autospec=True ) as mock_cred_mgr: - # Emulate storage not found (bad connection id) mock_conn_rec.retrieve_by_id = async_mock.CoroutineMock( side_effect=test_module.StorageNotFoundError() @@ -1158,7 +1150,6 @@ async def test_credential_exchange_send_free_request_not_ready(self): ) as mock_conn_rec, async_mock.patch.object( test_module, "V20CredManager", autospec=True ) as mock_cred_mgr: - # Emulate connection not ready mock_conn_rec.retrieve_by_id = async_mock.CoroutineMock() mock_conn_rec.retrieve_by_id.return_value.is_ready = False @@ -1186,7 +1177,6 @@ async def test_credential_exchange_send_free_request_x(self): ) as mock_cred_mgr, async_mock.patch.object( test_module.web, "json_response" ) as mock_response: - mock_cred_mgr.return_value.create_request = async_mock.CoroutineMock( side_effect=[ test_module.LedgerError(), @@ -1277,7 +1267,6 @@ async def test_credential_exchange_issue_no_conn_record(self): ) as mock_cred_mgr, async_mock.patch.object( test_module, "V20CredExRecord", autospec=True ) as mock_cx_rec_cls: - mock_cx_rec.state = mock_cx_rec_cls.STATE_REQUEST_RECEIVED mock_cx_rec_cls.retrieve_by_id = async_mock.CoroutineMock( return_value=mock_cx_rec @@ -1308,7 +1297,6 @@ async def test_credential_exchange_issue_not_ready(self): ) as mock_cred_mgr, async_mock.patch.object( test_module, "V20CredExRecord", autospec=True ) as mock_cx_rec: - mock_cx_rec.retrieve_by_id = async_mock.CoroutineMock() mock_cx_rec.retrieve_by_id.return_value.state = ( test_module.V20CredExRecord.STATE_REQUEST_RECEIVED @@ -1343,7 +1331,6 @@ async def test_credential_exchange_issue_rev_reg_full(self): ) as mock_cred_mgr, async_mock.patch.object( test_module, "V20CredExRecord", autospec=True ) as mock_cx_rec_cls: - mock_cx_rec.state = mock_cx_rec_cls.STATE_REQUEST_RECEIVED mock_cx_rec_cls.retrieve_by_id = async_mock.CoroutineMock( return_value=mock_cx_rec @@ -1464,7 +1451,6 @@ async def test_credential_exchange_store_bad_cred_id_json(self): ) as mock_ld_proof_get_detail_record, async_mock.patch.object( IndyCredFormatHandler, "get_detail_record", autospec=True ) as mock_indy_get_detail_record: - mock_cx_rec_cls.retrieve_by_id = async_mock.CoroutineMock() mock_cx_rec_cls.retrieve_by_id.return_value.state = ( test_module.V20CredExRecord.STATE_CREDENTIAL_RECEIVED diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py index b3c3f97311..58d46431f6 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py @@ -273,7 +273,6 @@ async def create_invitation( ).serialize() else: - if not my_endpoint: my_endpoint = self.profile.settings.get("default_endpoint") diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py index 07010c744c..4cbb29ab65 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py @@ -91,7 +91,6 @@ class TestConfig: - test_did = "55GkHamhTU1ZbTbV2ab9DE" test_verkey = "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx" test_endpoint = "http://localhost" @@ -712,7 +711,6 @@ async def test_create_invitation_public_x_multi_use(self): assert "Cannot create public invitation with" in str(context.exception) async def test_create_invitation_requests_attach_x_multi_use(self): - with self.assertRaises(OutOfBandManagerError) as context: await self.manager.create_invitation( public=False, @@ -1094,7 +1092,6 @@ async def test_receive_reuse_accepted_x(self): mock_retrieve_oob.side_effect = (StorageNotFoundError,) with self.assertRaises(test_module.OutOfBandManagerError) as err: - await self.manager.receive_reuse_accepted_message( reuse_msg_accepted, receipt, self.test_conn_rec ) diff --git a/aries_cloudagent/protocols/present_proof/indy/pres_exch_handler.py b/aries_cloudagent/protocols/present_proof/indy/pres_exch_handler.py index 51c6c264a9..4bff88cc89 100644 --- a/aries_cloudagent/protocols/present_proof/indy/pres_exch_handler.py +++ b/aries_cloudagent/protocols/present_proof/indy/pres_exch_handler.py @@ -194,7 +194,7 @@ async def return_presentation( f"Failed to create revocation state: {e.error_code}, {e.message}" ) raise e - for (referent, precis) in requested_referents.items(): + for referent, precis in requested_referents.items(): if "timestamp" not in precis: continue if referent in requested_credentials["requested_attributes"]: diff --git a/aries_cloudagent/protocols/present_proof/v1_0/handlers/tests/test_presentation_request_handler.py b/aries_cloudagent/protocols/present_proof/v1_0/handlers/tests/test_presentation_request_handler.py index add1efa8e9..83329461a4 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/handlers/tests/test_presentation_request_handler.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/handlers/tests/test_presentation_request_handler.py @@ -102,7 +102,6 @@ async def test_called(self): ) as mock_pres_mgr, async_mock.patch.object( test_module, "V10PresentationExchange", autospec=True ) as mock_pres_ex_cls: - mock_pres_ex_cls.retrieve_by_tag_filter = async_mock.CoroutineMock( return_value=px_rec_instance ) @@ -163,7 +162,6 @@ async def test_called_not_found(self): ) as mock_pres_mgr, async_mock.patch.object( test_module, "V10PresentationExchange", autospec=True ) as mock_pres_ex_cls: - mock_pres_ex_cls.retrieve_by_tag_filter = async_mock.CoroutineMock( side_effect=StorageNotFoundError ) @@ -335,7 +333,6 @@ async def test_called_auto_present_x(self): ) as mock_pres_mgr, async_mock.patch.object( test_module, "V10PresentationExchange", autospec=True ) as mock_pres_ex_cls: - mock_pres_ex_cls.return_value = mock_px_rec mock_pres_ex_cls.retrieve_by_tag_filter = async_mock.CoroutineMock( return_value=mock_px_rec @@ -415,7 +412,6 @@ async def test_called_auto_present_no_preview(self): ) as mock_pres_mgr, async_mock.patch.object( test_module, "V10PresentationExchange", autospec=True ) as mock_pres_ex_cls: - mock_pres_ex_cls.return_value = px_rec_instance mock_pres_ex_cls.retrieve_by_tag_filter = async_mock.CoroutineMock( return_value=px_rec_instance @@ -491,7 +487,6 @@ async def test_called_auto_present_pred_no_match(self): ) as mock_pres_mgr, async_mock.patch.object( test_module, "V10PresentationExchange", autospec=True ) as mock_pres_ex_cls: - mock_pres_ex_cls.return_value = px_rec_instance mock_pres_ex_cls.retrieve_by_tag_filter = async_mock.CoroutineMock( return_value=px_rec_instance @@ -565,7 +560,6 @@ async def test_called_auto_present_pred_single_match(self): ) as mock_pres_mgr, async_mock.patch.object( test_module, "V10PresentationExchange", autospec=True ) as mock_pres_ex_cls: - mock_pres_ex_cls.return_value = px_rec_instance mock_pres_ex_cls.retrieve_by_tag_filter = async_mock.CoroutineMock( return_value=px_rec_instance @@ -646,7 +640,6 @@ async def test_called_auto_present_pred_multi_match(self): ) as mock_pres_mgr, async_mock.patch.object( test_module, "V10PresentationExchange", autospec=True ) as mock_pres_ex_cls: - mock_pres_ex_cls.return_value = px_rec_instance mock_pres_ex_cls.retrieve_by_tag_filter = async_mock.CoroutineMock( return_value=px_rec_instance @@ -778,7 +771,6 @@ async def test_called_auto_present_multi_cred_match_reft(self): ) as mock_pres_mgr, async_mock.patch.object( test_module, "V10PresentationExchange", autospec=True ) as mock_pres_ex_cls: - mock_pres_ex_cls.return_value = px_rec_instance mock_pres_ex_cls.retrieve_by_tag_filter = async_mock.CoroutineMock( return_value=px_rec_instance @@ -892,7 +884,6 @@ async def test_called_auto_present_bait_and_switch(self): ) as mock_pres_mgr, async_mock.patch.object( test_module, "V10PresentationExchange", autospec=True ) as mock_pres_ex_cls: - mock_pres_ex_cls.return_value = px_rec_instance mock_pres_ex_cls.retrieve_by_tag_filter = async_mock.CoroutineMock( return_value=px_rec_instance diff --git a/aries_cloudagent/protocols/present_proof/v1_0/manager.py b/aries_cloudagent/protocols/present_proof/v1_0/manager.py index 2f3af46da5..7453506a45 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/manager.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/manager.py @@ -354,7 +354,7 @@ async def receive_presentation( presentation_preview = exchange_pres_proposal.presentation_proposal proof_req = presentation_exchange_record._presentation_request.ser - for (reft, attr_spec) in presentation["requested_proof"][ + for reft, attr_spec in presentation["requested_proof"][ "revealed_attrs" ].items(): name = proof_req["requested_attributes"][reft]["name"] @@ -540,12 +540,10 @@ async def receive_problem_report( """ # FIXME use transaction, fetch for_update async with self._profile.session() as session: - pres_ex_record = await ( - V10PresentationExchange.retrieve_by_tag_filter( - session, - {"thread_id": message._thread_id}, - {"connection_id": connection_id}, - ) + pres_ex_record = await V10PresentationExchange.retrieve_by_tag_filter( + session, + {"thread_id": message._thread_id}, + {"connection_id": connection_id}, ) pres_ex_record.state = V10PresentationExchange.STATE_ABANDONED diff --git a/aries_cloudagent/protocols/present_proof/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/present_proof/v1_0/tests/test_routes.py index 555c079b58..77e3ea1ca7 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/tests/test_routes.py @@ -67,7 +67,6 @@ async def test_presentation_exchange_list(self): ), autospec=True, ) as mock_presentation_exchange: - # Since we are mocking import importlib.reload(test_module) @@ -101,7 +100,6 @@ async def test_presentation_exchange_list_x(self): ), autospec=True, ) as mock_presentation_exchange: - # Since we are mocking import importlib.reload(test_module) @@ -122,7 +120,6 @@ async def test_presentation_exchange_credentials_list_not_found(self): ), autospec=True, ) as mock_presentation_exchange: - # Since we are mocking import importlib.reload(test_module) @@ -158,7 +155,6 @@ async def test_presentation_exchange_credentials_x(self): ), autospec=True, ) as mock_presentation_exchange: - # Since we are mocking import importlib.reload(test_module) @@ -191,7 +187,6 @@ async def test_presentation_exchange_credentials_list_single_referent(self): ), autospec=True, ) as mock_presentation_exchange: - # Since we are mocking import importlib.reload(test_module) @@ -229,7 +224,6 @@ async def test_presentation_exchange_credentials_list_multiple_referents(self): ), autospec=True, ) as mock_presentation_exchange: - # Since we are mocking import importlib.reload(test_module) @@ -253,7 +247,6 @@ async def test_presentation_exchange_retrieve(self): ), autospec=True, ) as mock_pres_ex: - # Since we are mocking import importlib.reload(test_module) @@ -280,7 +273,6 @@ async def test_presentation_exchange_retrieve_not_found(self): ), autospec=True, ) as mock_pres_ex: - # Since we are mocking import importlib.reload(test_module) @@ -307,7 +299,6 @@ async def test_presentation_exchange_retrieve_x(self): ), autospec=True, ) as mock_pres_ex: - # Since we are mocking import importlib.reload(test_module) @@ -334,7 +325,6 @@ async def test_presentation_exchange_send_proposal(self): "aries_cloudagent.indy.models.pres_preview.IndyPresPreview", autospec=True, ) as mock_preview: - # Since we are mocking import importlib.reload(test_module) @@ -360,7 +350,6 @@ async def test_presentation_exchange_send_proposal_no_conn_record(self): "aries_cloudagent.connections.models.conn_record.ConnRecord", autospec=True, ) as mock_connection_record: - # Since we are mocking import importlib.reload(test_module) @@ -388,7 +377,6 @@ async def test_presentation_exchange_send_proposal_not_ready(self): ), autospec=True, ) as mock_proposal: - # Since we are mocking import importlib.reload(test_module) @@ -411,7 +399,6 @@ async def test_presentation_exchange_send_proposal_x(self): "aries_cloudagent.indy.models.pres_preview.IndyPresPreview", autospec=True, ) as mock_preview: - # Since we are mocking import importlib.reload(test_module) @@ -456,7 +443,6 @@ async def test_presentation_exchange_create_request(self): "aries_cloudagent.indy.util.generate_pr_nonce", autospec=True, ) as mock_generate_nonce: - # Since we are mocking import importlib.reload(test_module) @@ -510,7 +496,6 @@ async def test_presentation_exchange_create_request_x(self): "aries_cloudagent.indy.util.generate_pr_nonce", autospec=True, ) as mock_generate_nonce: - # Since we are mocking import importlib.reload(test_module) @@ -562,7 +547,6 @@ async def test_presentation_exchange_send_free_request(self): ), autospec=True, ) as mock_presentation_exchange: - # Since we are mocking import importlib.reload(test_module) @@ -601,7 +585,6 @@ async def test_presentation_exchange_send_free_request_not_found(self): "aries_cloudagent.connections.models.conn_record.ConnRecord", autospec=True, ) as mock_connection_record: - # Since we are mocking import importlib.reload(test_module) @@ -620,7 +603,6 @@ async def test_presentation_exchange_send_free_request_not_ready(self): "aries_cloudagent.connections.models.conn_record.ConnRecord", autospec=True, ) as mock_connection_record: - # Since we are mocking import importlib.reload(test_module) @@ -664,7 +646,6 @@ async def test_presentation_exchange_send_free_request_x(self): ), autospec=True, ) as mock_presentation_exchange: - # Since we are mocking import importlib.reload(test_module) @@ -731,7 +712,6 @@ async def test_presentation_exchange_send_bound_request(self): "models.presentation_exchange.V10PresentationExchange", autospec=True, ) as mock_presentation_exchange: - # Since we are mocking import importlib.reload(test_module) @@ -793,7 +773,6 @@ async def test_presentation_exchange_send_bound_request_not_found(self): ), autospec=True, ) as mock_presentation_exchange: - # Since we are mocking import importlib.reload(test_module) @@ -838,7 +817,6 @@ async def test_presentation_exchange_send_bound_request_not_ready(self): ), autospec=True, ) as mock_presentation_exchange: - # Since we are mocking import importlib.reload(test_module) @@ -883,7 +861,6 @@ async def test_presentation_exchange_send_bound_request_bad_state(self): ), autospec=True, ) as mock_presentation_exchange: - # Since we are mocking import importlib.reload(test_module) @@ -923,7 +900,6 @@ async def test_presentation_exchange_send_bound_request_x(self): ), autospec=True, ) as mock_presentation_exchange: - # Since we are mocking import importlib.reload(test_module) @@ -998,7 +974,6 @@ async def test_presentation_exchange_send_presentation(self): ), autospec=True, ) as mock_presentation_exchange: - # Since we are mocking import importlib.reload(test_module) @@ -1075,7 +1050,6 @@ async def test_presentation_exchange_send_presentation_not_found(self): ), autospec=True, ) as mock_presentation_exchange: - # Since we are mocking import importlib.reload(test_module) @@ -1120,7 +1094,6 @@ async def test_presentation_exchange_send_presentation_not_ready(self): ), autospec=True, ) as mock_presentation_exchange: - # Since we are mocking import importlib.reload(test_module) @@ -1150,7 +1123,6 @@ async def test_presentation_exchange_send_presentation_bad_state(self): ), autospec=True, ) as mock_presentation_exchange: - # Since we are mocking import importlib.reload(test_module) @@ -1196,7 +1168,6 @@ async def test_presentation_exchange_send_presentation_x(self): ), autospec=True, ) as mock_presentation_exchange: - # Since we are mocking import importlib.reload(test_module) @@ -1251,7 +1222,6 @@ async def test_presentation_exchange_verify_presentation(self): ), autospec=True, ) as mock_presentation_exchange: - # Since we are mocking import importlib.reload(test_module) @@ -1311,7 +1281,6 @@ async def test_presentation_exchange_verify_presentation_bad_state(self): ), autospec=True, ) as mock_presentation_exchange: - # Since we are mocking import importlib.reload(test_module) @@ -1354,7 +1323,6 @@ async def test_presentation_exchange_verify_presentation_x(self): ), autospec=True, ) as mock_presentation_exchange: - # Since we are mocking import importlib.reload(test_module) @@ -1412,7 +1380,6 @@ async def test_presentation_exchange_problem_report(self): ) as mock_problem_report, async_mock.patch.object( test_module.web, "json_response" ) as mock_response: - # Since we are mocking import importlib.reload(test_module) @@ -1444,7 +1411,6 @@ async def test_presentation_exchange_problem_report_bad_pres_ex_id(self): ), autospec=True, ) as mock_pres_ex: - # Since we are mocking import importlib.reload(test_module) @@ -1474,7 +1440,6 @@ async def test_presentation_exchange_problem_report_x(self): ) as mock_problem_report, async_mock.patch.object( test_module.web, "json_response" ) as mock_response: - # Since we are mocking import importlib.reload(test_module) mock_pres_ex.retrieve_by_id = async_mock.CoroutineMock( @@ -1494,7 +1459,6 @@ async def test_presentation_exchange_remove(self): ), autospec=True, ) as mock_presentation_exchange: - # Since we are mocking import importlib.reload(test_module) @@ -1523,7 +1487,6 @@ async def test_presentation_exchange_remove_not_found(self): ), autospec=True, ) as mock_presentation_exchange: - # Since we are mocking import importlib.reload(test_module) @@ -1545,7 +1508,6 @@ async def test_presentation_exchange_remove_x(self): ), autospec=True, ) as mock_presentation_exchange: - # Since we are mocking import importlib.reload(test_module) diff --git a/aries_cloudagent/protocols/present_proof/v2_0/handlers/tests/test_pres_request_handler.py b/aries_cloudagent/protocols/present_proof/v2_0/handlers/tests/test_pres_request_handler.py index bd1e3d2174..c0e28fa043 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/handlers/tests/test_pres_request_handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/handlers/tests/test_pres_request_handler.py @@ -219,7 +219,6 @@ async def test_called(self): ) as mock_pres_mgr, async_mock.patch.object( test_module, "V20PresExRecord", autospec=True ) as mock_px_rec_cls: - mock_px_rec_cls.retrieve_by_tag_filter = async_mock.CoroutineMock( return_value=px_rec_instance ) @@ -279,7 +278,6 @@ async def test_called_not_found(self): ) as mock_pres_mgr, async_mock.patch.object( test_module, "V20PresExRecord", autospec=True ) as mock_px_rec_cls: - mock_px_rec_cls.retrieve_by_tag_filter = async_mock.CoroutineMock( side_effect=StorageNotFoundError ) @@ -349,7 +347,6 @@ async def test_called_auto_present_x(self): ) as mock_pres_mgr, async_mock.patch.object( test_module, "V20PresExRecord", autospec=True ) as mock_pres_ex_rec_cls: - mock_pres_ex_rec_cls.return_value = mock_px_rec mock_pres_ex_rec_cls.retrieve_by_tag_filter = async_mock.CoroutineMock( return_value=mock_px_rec @@ -419,7 +416,6 @@ async def test_called_auto_present_indy(self): ) as mock_pres_mgr, async_mock.patch.object( test_module, "V20PresExRecord", autospec=True ) as mock_pres_ex_rec_cls: - mock_pres_ex_rec_cls.return_value = mock_px_rec mock_pres_ex_rec_cls.retrieve_by_tag_filter = async_mock.CoroutineMock( return_value=mock_px_rec @@ -494,7 +490,6 @@ async def test_called_auto_present_dif(self): ) as mock_pres_mgr, async_mock.patch.object( test_module, "V20PresExRecord", autospec=True ) as mock_pres_ex_rec_cls: - mock_pres_ex_rec_cls.return_value = px_rec_instance mock_pres_ex_rec_cls.retrieve_by_tag_filter = async_mock.CoroutineMock( return_value=px_rec_instance @@ -565,7 +560,6 @@ async def test_called_auto_present_no_preview(self): ) as mock_pres_mgr, async_mock.patch.object( test_module, "V20PresExRecord", autospec=True ) as mock_pres_ex_rec_cls: - mock_pres_ex_rec_cls.return_value = px_rec_instance mock_pres_ex_rec_cls.retrieve_by_tag_filter = async_mock.CoroutineMock( return_value=px_rec_instance @@ -640,7 +634,6 @@ async def test_called_auto_present_pred_no_match(self): ) as mock_pres_mgr, async_mock.patch.object( test_module, "V20PresExRecord", autospec=True ) as mock_pres_ex_rec_cls: - mock_pres_ex_rec_cls.return_value = mock_px_rec mock_pres_ex_rec_cls.retrieve_by_tag_filter = async_mock.CoroutineMock( return_value=mock_px_rec @@ -705,7 +698,6 @@ async def test_called_auto_present_pred_single_match(self): ) as mock_pres_mgr, async_mock.patch.object( test_module, "V20PresExRecord", autospec=True ) as mock_pres_ex_rec_cls: - mock_pres_ex_rec_cls.return_value = px_rec_instance mock_pres_ex_rec_cls.retrieve_by_tag_filter = async_mock.CoroutineMock( return_value=px_rec_instance @@ -777,7 +769,6 @@ async def test_called_auto_present_pred_multi_match(self): ) as mock_pres_mgr, async_mock.patch.object( test_module, "V20PresExRecord", autospec=True ) as mock_pres_ex_rec_cls: - mock_pres_ex_rec_cls.return_value = px_rec_instance mock_pres_ex_rec_cls.retrieve_by_tag_filter = async_mock.CoroutineMock( return_value=px_rec_instance @@ -894,7 +885,6 @@ async def test_called_auto_present_multi_cred_match_reft(self): ) as mock_pres_mgr, async_mock.patch.object( test_module, "V20PresExRecord", autospec=True ) as mock_pres_ex_rec_cls: - mock_pres_ex_rec_cls.return_value = px_rec_instance mock_pres_ex_rec_cls.retrieve_by_tag_filter = async_mock.CoroutineMock( return_value=px_rec_instance diff --git a/aries_cloudagent/protocols/present_proof/v2_0/manager.py b/aries_cloudagent/protocols/present_proof/v2_0/manager.py index ef542f0c29..ea65e37c64 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/manager.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/manager.py @@ -472,12 +472,10 @@ async def receive_problem_report( """ # FIXME use transaction, fetch for_update async with self._profile.session() as session: - pres_ex_record = await ( - V20PresExRecord.retrieve_by_tag_filter( - session, - {"thread_id": message._thread_id}, - {"connection_id": connection_id}, - ) + pres_ex_record = await V20PresExRecord.retrieve_by_tag_filter( + session, + {"thread_id": message._thread_id}, + {"connection_id": connection_id}, ) pres_ex_record.state = V20PresExRecord.STATE_ABANDONED diff --git a/aries_cloudagent/protocols/present_proof/v2_0/routes.py b/aries_cloudagent/protocols/present_proof/v2_0/routes.py index 02d5b7fff3..68a55087ef 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/routes.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/routes.py @@ -331,7 +331,7 @@ async def _add_nonce(indy_proof_request: Mapping) -> Mapping: def _formats_attach(by_format: Mapping, msg_type: str, spec: str) -> Mapping: """Break out formats and proposals/requests/presentations for v2.0 messages.""" attach = [] - for (fmt_api, item_by_fmt) in by_format.items(): + for fmt_api, item_by_fmt in by_format.items(): if fmt_api == V20PresFormat.Format.INDY.api: attach.append( AttachDecorator.data_base64(mapping=item_by_fmt, ident=fmt_api) @@ -1090,7 +1090,6 @@ async def present_proof_send_presentation(request: web.BaseRequest): if pres_ex_record.connection_id: try: async with profile.session() as session: - conn_record = await ConnRecord.retrieve_by_id( session, pres_ex_record.connection_id ) diff --git a/aries_cloudagent/protocols/routing/v1_0/messages/tests/test_forward.py b/aries_cloudagent/protocols/routing/v1_0/messages/tests/test_forward.py index 4988e052b0..2dea1d988d 100644 --- a/aries_cloudagent/protocols/routing/v1_0/messages/tests/test_forward.py +++ b/aries_cloudagent/protocols/routing/v1_0/messages/tests/test_forward.py @@ -9,7 +9,6 @@ class TestForward(TestCase): - to = "to" msg = {"msg": "body"} diff --git a/aries_cloudagent/revocation/tests/test_routes.py b/aries_cloudagent/revocation/tests/test_routes.py index 34cb11d323..d651a442df 100644 --- a/aries_cloudagent/revocation/tests/test_routes.py +++ b/aries_cloudagent/revocation/tests/test_routes.py @@ -87,7 +87,6 @@ async def test_revoke(self): ) as mock_mgr, async_mock.patch.object( test_module.web, "json_response" ) as mock_response: - mock_mgr.return_value.revoke_credential = async_mock.CoroutineMock() await test_module.revoke(self.request) @@ -107,7 +106,6 @@ async def test_revoke_by_cred_ex_id(self): ) as mock_mgr, async_mock.patch.object( test_module.web, "json_response" ) as mock_response: - mock_mgr.return_value.revoke_credential = async_mock.CoroutineMock() await test_module.revoke(self.request) @@ -128,7 +126,6 @@ async def test_revoke_not_found(self): ) as mock_mgr, async_mock.patch.object( test_module.web, "json_response" ) as mock_response: - mock_mgr.return_value.revoke_credential = async_mock.CoroutineMock( side_effect=test_module.StorageNotFoundError() ) diff --git a/aries_cloudagent/transport/pack_format.py b/aries_cloudagent/transport/pack_format.py index f982748098..76ac70b87f 100644 --- a/aries_cloudagent/transport/pack_format.py +++ b/aries_cloudagent/transport/pack_format.py @@ -69,7 +69,6 @@ async def parse_message( # packed messages are detected by the absence of @type if "@type" not in message_dict: - try: unpack = self.unpack(session, message_body, receipt) message_json = await ( diff --git a/aries_cloudagent/vc/vc_ld/tests/test_vc_ld.py b/aries_cloudagent/vc/vc_ld/tests/test_vc_ld.py index 3c3643ec1a..c09f9345dc 100644 --- a/aries_cloudagent/vc/vc_ld/tests/test_vc_ld.py +++ b/aries_cloudagent/vc/vc_ld/tests/test_vc_ld.py @@ -259,7 +259,6 @@ async def test_verify_presentation_x_no_purpose_challenge(self): ) async def test_sign_presentation_x_no_purpose_challenge(self): - with self.assertRaises(LinkedDataProofException) as context: await sign_presentation( presentation=PRESENTATION_UNSIGNED, diff --git a/aries_cloudagent/wallet/bbs.py b/aries_cloudagent/wallet/bbs.py index fd80e24fb7..a204682a73 100644 --- a/aries_cloudagent/wallet/bbs.py +++ b/aries_cloudagent/wallet/bbs.py @@ -102,5 +102,5 @@ def create_bls12381g2_keypair(seed: bytes = None) -> Tuple[bytes, bytes]: try: key_pair = BlsKeyPair.generate_g2(seed) return key_pair.public_key, key_pair.secret_key - except (Exception) as error: + except Exception as error: raise BbsException("Unable to create keypair") from error diff --git a/aries_cloudagent/wallet/indy.py b/aries_cloudagent/wallet/indy.py index ea27f3132b..78356946bb 100644 --- a/aries_cloudagent/wallet/indy.py +++ b/aries_cloudagent/wallet/indy.py @@ -199,9 +199,9 @@ async def __get_keypair_signing_key(self, verkey: str) -> KeyInfo: metadata=key_pair["metadata"], key_type=key_types.from_key_type(key_pair["key_type"]) or BLS12381G2, ) - except (StorageNotFoundError): + except StorageNotFoundError: raise WalletNotFoundError(f"Unknown key: {verkey}") - except (StorageDuplicateError): + except StorageDuplicateError: raise WalletDuplicateError(f"Multiple keys exist for verkey: {verkey}") async def get_signing_key(self, verkey: str) -> KeyInfo: diff --git a/aries_cloudagent/wallet/tests/test_routes.py b/aries_cloudagent/wallet/tests/test_routes.py index 03c1dc57cb..051380c7f8 100644 --- a/aries_cloudagent/wallet/tests/test_routes.py +++ b/aries_cloudagent/wallet/tests/test_routes.py @@ -464,7 +464,6 @@ async def test_set_public_did_no_query_did(self): await test_module.wallet_set_public_did(self.request) async def test_set_public_did_no_ledger(self): - mock_route_manager = async_mock.MagicMock() mock_route_manager.mediation_record_if_id = async_mock.AsyncMock() mock_route_manager.__aenter__ = async_mock.AsyncMock( diff --git a/demo/runners/agent_container.py b/demo/runners/agent_container.py index c18c472ebc..bbcd11be40 100644 --- a/demo/runners/agent_container.py +++ b/demo/runners/agent_container.py @@ -622,7 +622,10 @@ async def create_schema_and_cred_def( random.randint(1, 101), ) ) - (_, cred_def_id,) = await self.register_schema_and_creddef( # schema id + ( + _, + cred_def_id, + ) = await self.register_schema_and_creddef( # schema id schema_name, version, schema_attrs, diff --git a/demo/runners/performance.py b/demo/runners/performance.py index b92125f3b2..02b6d3283e 100644 --- a/demo/runners/performance.py +++ b/demo/runners/performance.py @@ -275,7 +275,6 @@ async def main( wallet_type: str = None, arg_file: str = None, ): - if multi_ledger: genesis = None multi_ledger_config_path = "./demo/multi_ledger_config.yml" diff --git a/docs/conf.py b/docs/conf.py index d3c2d570da..6ed81e5982 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -242,6 +242,7 @@ # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = {"https://docs.python.org/": None} + # To supress cross-reference warnings # https://github.com/sphinx-doc/sphinx/issues/3866#issuecomment-768167824 class PatchedPythonDomain(PythonDomain): From b81a8312d792eb8a89299776e97d71dedb1baa92 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Thu, 2 Feb 2023 10:12:15 -0800 Subject: [PATCH 641/872] Cleanup demo postgres dependencies Signed-off-by: Ian Costanzo --- demo/docker-agent/docker-compose.yml | 8 +++----- demo/docker-agent/ngrok-wait.sh | 2 +- demo/docker/docker-compose.yml | 13 +++++++------ demo/multi-demo/docker-compose.yml | 8 +++----- demo/multi-demo/ngrok-wait.sh | 2 +- 5 files changed, 15 insertions(+), 18 deletions(-) diff --git a/demo/docker-agent/docker-compose.yml b/demo/docker-agent/docker-compose.yml index 8dd07c428f..35b7b2ac57 100644 --- a/demo/docker-agent/docker-compose.yml +++ b/demo/docker-agent/docker-compose.yml @@ -32,12 +32,10 @@ services: - ./ngrok-wait.sh:/home/indy/ngrok-wait.sh wallet-db: - image: vcr-postgresql + image: postgres:12 environment: - - POSTGRESQL_USER=DB_USER - - POSTGRESQL_PASSWORD=DB_PASSWORD - - POSTGRESQL_DATABASE=DB_USER - - POSTGRESQL_ADMIN_PASSWORD=mysecretpassword + - POSTGRES_USER=DB_USER + - POSTGRES_PASSWORD=DB_PASSWORD ports: - 5433:5432 volumes: diff --git a/demo/docker-agent/ngrok-wait.sh b/demo/docker-agent/ngrok-wait.sh index 70c50ce6f3..4c7ccde9db 100755 --- a/demo/docker-agent/ngrok-wait.sh +++ b/demo/docker-agent/ngrok-wait.sh @@ -37,7 +37,7 @@ exec aca-py start \ --wallet-key "secret_key" \ --wallet-storage-type "postgres_storage" \ --wallet-storage-config "{\"url\":\"wallet-db:5432\",\"max_connections\":5}" \ - --wallet-storage-creds "{\"account\":\"DB_USER\",\"password\":\"DB_PASSWORD\",\"admin_account\":\"postgres\",\"admin_password\":\"mysecretpassword\"}" \ + --wallet-storage-creds "{\"account\":\"DB_USER\",\"password\":\"DB_PASSWORD\",\"admin_account\":\"DB_USER\",\"admin_password\":\"DB_PASSWORD\"}" \ --admin '0.0.0.0' 8010 \ --label "test_author" \ --admin-insecure-mode \ diff --git a/demo/docker/docker-compose.yml b/demo/docker/docker-compose.yml index bcec777fd0..29facddf0f 100644 --- a/demo/docker/docker-compose.yml +++ b/demo/docker/docker-compose.yml @@ -3,6 +3,9 @@ # To shut down the services run `docker-compose rm` - this will retain the postgres database, so you can change aca-py startup parameters # and restart the docker containers without losing your wallet data # If you want to delete your wallet data just run `docker volume ls -q | xargs docker volume rm` + +# Note this requires von-network (https://github.com/bcgov/von-network) and indy-tails-server (https://github.com/bcgov/indy-tails-server) are already running + version: "3" services: vcr-agent: @@ -41,7 +44,7 @@ services: --wallet-key 'key' \ --wallet-storage-type 'postgres_storage' \ --wallet-storage-config '{\"url\":\"wallet-db:5432\",\"max_connections\":5}' \ - --wallet-storage-creds '{\"account\":\"DB_USER\",\"password\":\"DB_PASSWORD\",\"admin_account\":\"postgres\",\"admin_password\":\"mysecretpassword\"}' \ + --wallet-storage-creds '{\"account\":\"DB_USER\",\"password\":\"DB_PASSWORD\",\"admin_account\":\"DB_USER\",\"admin_password\":\"DB_PASSWORD\"}' \ --admin '0.0.0.0' 8010 \ --admin-insecure-mode \ --label 'tester_agent' \ @@ -58,12 +61,10 @@ services: # --genesis-transactions-list 'ledgers.yaml' \ wallet-db: - image: vcr-postgresql + image: postgres:12 environment: - - POSTGRESQL_USER=DB_USER - - POSTGRESQL_PASSWORD=DB_PASSWORD - - POSTGRESQL_DATABASE=DB_USER - - POSTGRESQL_ADMIN_PASSWORD=mysecretpassword + - POSTGRES_USER=DB_USER + - POSTGRES_PASSWORD=DB_PASSWORD ports: - 5433:5432 volumes: diff --git a/demo/multi-demo/docker-compose.yml b/demo/multi-demo/docker-compose.yml index be9fb0683c..80021ab211 100644 --- a/demo/multi-demo/docker-compose.yml +++ b/demo/multi-demo/docker-compose.yml @@ -32,12 +32,10 @@ services: - ./ngrok-wait.sh:/home/indy/ngrok-wait.sh wallet-db: - image: vcr-postgresql + image: postgres:12 environment: - - POSTGRESQL_USER=DB_USER - - POSTGRESQL_PASSWORD=DB_PASSWORD - - POSTGRESQL_DATABASE=DB_USER - - POSTGRESQL_ADMIN_PASSWORD=mysecretpassword + - POSTGRES_USER=DB_USER + - POSTGRES_PASSWORD=DB_PASSWORD ports: - 5433:5432 volumes: diff --git a/demo/multi-demo/ngrok-wait.sh b/demo/multi-demo/ngrok-wait.sh index df0bc08d6e..6800ec5cd1 100755 --- a/demo/multi-demo/ngrok-wait.sh +++ b/demo/multi-demo/ngrok-wait.sh @@ -37,7 +37,7 @@ exec aca-py start \ --wallet-key "secret_key" \ --wallet-storage-type "postgres_storage" \ --wallet-storage-config "{\"url\":\"wallet-db:5432\",\"max_connections\":5,\"scheme\":\"MultiWalletSingleTable\"}" \ - --wallet-storage-creds "{\"account\":\"DB_USER\",\"password\":\"DB_PASSWORD\",\"admin_account\":\"postgres\",\"admin_password\":\"mysecretpassword\"}" \ + --wallet-storage-creds "{\"account\":\"DB_USER\",\"password\":\"DB_PASSWORD\",\"admin_account\":\"DB_USER\",\"admin_password\":\"DB_PASSWORD\"}" \ --admin '0.0.0.0' 8010 \ --label "test_multi" \ --admin-insecure-mode \ From 7edd4170c9447b007e63e37a2e613a5c19cfba0d Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Thu, 2 Feb 2023 10:30:57 -0800 Subject: [PATCH 642/872] Fix docker command Signed-off-by: Ian Costanzo --- demo/multi-demo/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/multi-demo/README.md b/demo/multi-demo/README.md index 208a8f7996..90274cb259 100644 --- a/demo/multi-demo/README.md +++ b/demo/multi-demo/README.md @@ -36,5 +36,5 @@ This will leave the agent's wallet data, so if you restart the agent it will mai - to remove the agent's wallet: ```bash -docker volume rm docker-agent_wallet-db-data +docker volume rm multi-demo_wallet-db-data ``` From a6b91595499fefd06ff7003913bfa6ca646ecf01 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Thu, 2 Feb 2023 11:40:24 -0800 Subject: [PATCH 643/872] Update dockerfiles to use python 3.9 Signed-off-by: Ian Costanzo --- docker/Dockerfile | 2 +- docker/Dockerfile.indy | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 166e7b159e..ecbffe5b24 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,4 +1,4 @@ -ARG python_version=3.6.13 +ARG python_version=3.9.16 FROM python:${python_version}-slim-bullseye AS build WORKDIR /src diff --git a/docker/Dockerfile.indy b/docker/Dockerfile.indy index 93649df4ef..e4c4959cd4 100644 --- a/docker/Dockerfile.indy +++ b/docker/Dockerfile.indy @@ -1,4 +1,4 @@ -ARG python_version=3.6.13 +ARG python_version=3.9.16 ARG rust_version=1.46 # This image could be replaced with an "indy" image from another repo, From 294fd6c25a828942f92b07868cf5e7bae578460e Mon Sep 17 00:00:00 2001 From: Kim Ebert Date: Thu, 2 Feb 2023 15:51:32 -0700 Subject: [PATCH 644/872] fix: fix multi-use connection queries Signed-off-by: Kim Ebert --- aries_cloudagent/connections/models/conn_record.py | 1 + 1 file changed, 1 insertion(+) diff --git a/aries_cloudagent/connections/models/conn_record.py b/aries_cloudagent/connections/models/conn_record.py index eb0342970a..e49be19648 100644 --- a/aries_cloudagent/connections/models/conn_record.py +++ b/aries_cloudagent/connections/models/conn_record.py @@ -327,6 +327,7 @@ async def retrieve_by_invitation_key( if their_role: post_filter["their_role"] = cls.Role.get(their_role).rfc160 + tag_filter["their_role"] = cls.Role.get(their_role).rfc160 return await cls.retrieve_by_tag_filter(session, tag_filter, post_filter) From 6b9e9b54f6a3c838b087e494de242c44f72507b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Humbert?= Date: Mon, 6 Feb 2023 09:59:50 +0100 Subject: [PATCH 645/872] fix: resolver api schema inconsistency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Clément Humbert --- aries_cloudagent/resolver/routes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aries_cloudagent/resolver/routes.py b/aries_cloudagent/resolver/routes.py index 680353023a..515a653cb4 100644 --- a/aries_cloudagent/resolver/routes.py +++ b/aries_cloudagent/resolver/routes.py @@ -53,7 +53,7 @@ class ResolutionResultSchema(OpenAPISchema): """Result schema for did document query.""" - did_doc = fields.Dict(description="DID Document", required=True) + did_document = fields.Dict(description="DID Document", required=True) metadata = fields.Dict(description="Resolution metadata", required=True) From 2f65c07018d39a989cdd85886f7e3abbea708197 Mon Sep 17 00:00:00 2001 From: Victor Lee Date: Wed, 25 Jan 2023 14:51:15 -0500 Subject: [PATCH 646/872] Revert "transport.outbound.constants: list of obj to remove from webhook" This reverts commit 85a24ac0a8d2620db58cf2599993487f899ee330. Signed-off-by: Victor Lee --- aries_cloudagent/transport/outbound/constants.py | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 aries_cloudagent/transport/outbound/constants.py diff --git a/aries_cloudagent/transport/outbound/constants.py b/aries_cloudagent/transport/outbound/constants.py deleted file mode 100644 index ff4adef700..0000000000 --- a/aries_cloudagent/transport/outbound/constants.py +++ /dev/null @@ -1,14 +0,0 @@ -"""Constants for --light-weight-webhook flags""" -REMOVE_KEY = [ - "credential_request", - "cred_request", - "credential_proposal", - "cred_proposal", - "credential_offer", - "cred_offer", - "credential_preview", - "cred_preview", - "values", - "credentials~attach", - "offers~attach", -] From f5e262a59f5b9c3c01f6298e9b5df7fdc171b110 Mon Sep 17 00:00:00 2001 From: Victor Lee Date: Wed, 25 Jan 2023 14:53:05 -0500 Subject: [PATCH 647/872] Revert "transport.outbound.manager: remove redundant obj from webhook" This reverts commit 34c9805606af5ce3002ff774217973dfc37e35b2. Signed-off-by: Victor Lee --- .../transport/outbound/manager.py | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/aries_cloudagent/transport/outbound/manager.py b/aries_cloudagent/transport/outbound/manager.py index efd49fea5a..094f537f32 100644 --- a/aries_cloudagent/transport/outbound/manager.py +++ b/aries_cloudagent/transport/outbound/manager.py @@ -7,8 +7,6 @@ from typing import Callable, Type, Union from urllib.parse import urlparse -from collections import MutableMapping -from contextlib import suppress from ...connections.models.connection_target import ConnectionTarget from ...core.profile import Profile @@ -27,8 +25,6 @@ ) from .message import OutboundMessage -from .constants import REMOVE_KEY - LOGGER = logging.getLogger(__name__) MODULE_BASE_PATH = "aries_cloudagent.transport.outbound" @@ -91,13 +87,10 @@ def __init__(self, profile: Profile, handle_not_delivered: Callable = None): self.running_transports = {} self.task_queue = TaskQueue(max_active=200) self._process_task: asyncio.Task = None - self.light_webhook = False if self.root_profile.settings.get("transport.max_outbound_retry"): self.MAX_RETRY_COUNT = self.root_profile.settings[ "transport.max_outbound_retry" ] - if self.root_profile.settings.get("transport.light_weight_webhook"): - self.light_webhook = True async def setup(self): """Perform setup operations.""" @@ -316,14 +309,6 @@ async def encode_outbound_message( return outbound_message - def delete_keys_from_dict(self, dictionary, keys): - for key in keys: - with suppress(KeyError): - del dictionary[key] - for value in dictionary.values(): - if isinstance(value, MutableMapping): - self.delete_keys_from_dict(value, keys) - def enqueue_webhook( self, topic: str, @@ -358,10 +343,6 @@ def enqueue_webhook( queued.payload = json.dumps(payload) queued.state = QueuedOutboundMessage.STATE_PENDING queued.retries = 4 if max_attempts is None else max_attempts - 1 - - if self.light_webhook: - self.delete_keys_from_dict(payload, REMOVE_KEY) - queued.payload = json.dumps(payload) self.outbound_new.append(queued) self.process_queued() From 02b44d1ab20b07f1492173f4f746774984e5a983 Mon Sep 17 00:00:00 2001 From: Victor Lee Date: Wed, 8 Feb 2023 09:34:28 -0500 Subject: [PATCH 648/872] light_webhook: new webhook object Signed-off-by: Victor Lee --- .../messaging/models/light_webhook.py | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 aries_cloudagent/messaging/models/light_webhook.py diff --git a/aries_cloudagent/messaging/models/light_webhook.py b/aries_cloudagent/messaging/models/light_webhook.py new file mode 100644 index 0000000000..fc601731a2 --- /dev/null +++ b/aries_cloudagent/messaging/models/light_webhook.py @@ -0,0 +1,44 @@ +import json +from aries_cloudagent.messaging.models.base_record import BaseExchangeRecord + + +class LightWeightWebhook: + __acceptable_keys_list = [ + "connection_id", + "credential_exchange_id", + "cred_ex_id", + "cred_def_id", + "role", + "initiator", + "revoc_reg_id", + "revocation_id", + "auto_offer", + "auto_issue", + "auto_remove", + "error_msg", + "thread_id", + "parent_thread_id", + "state", + "credential_definition_id", + "schema_id", + "credential_id", + "trace", + "public_did", + "cred_id_stored", + "conn_id", + ] + + def __init__( + self, + version, # 2 = V20CredExRecord ; 1 = V10CredentialExchange + **kwargs, + ): + [ + self.__setattr__(key, kwargs.get(key)) + for key in self.__acceptable_keys_list + if kwargs.get(key) != None + ] + if version == 2: + self.cred_ex_id = kwargs.get("_id") + else: + self.credential_exchange_id = kwargs.get("_id") From d122d35c433af85b0e0bb51169869f907ffc8273 Mon Sep 17 00:00:00 2001 From: Victor Lee Date: Wed, 8 Feb 2023 09:35:34 -0500 Subject: [PATCH 649/872] Protocols issue_crendential with override emit_event to use LightWeightWebhook Signed-off-by: Victor Lee --- .../v1_0/models/credential_exchange.py | 28 +++++++++++++++++++ .../v2_0/models/cred_ex_record.py | 28 +++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py b/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py index 6ef3a3af61..96b3bf50a6 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py @@ -12,6 +12,7 @@ from .....indy.models.cred_precis import IndyCredInfo, IndyCredInfoSchema from .....indy.models.cred_request import IndyCredRequest, IndyCredRequestSchema from .....messaging.models.base_record import BaseExchangeRecord, BaseExchangeSchema +from .....messaging.models.light_webhook import LightWeightWebhook from .....messaging.valid import INDY_CRED_DEF_ID, INDY_SCHEMA_ID, UUIDFour from .....storage.base import StorageError @@ -221,6 +222,33 @@ async def save_error_state( except StorageError: LOGGER.exception("Error saving credential exchange error state") + # Override + async def emit_event(self, session: ProfileSession, payload: Any = None): + """ + Emit an event. + + Args: + session: The profile session to use + payload: The event payload + """ + + if not self.RECORD_TOPIC: + return + + if self.state: + topic = f"{self.EVENT_NAMESPACE}::{self.RECORD_TOPIC}::{self.state}" + else: + topic = f"{self.EVENT_NAMESPACE}::{self.RECORD_TOPIC}" + + if not payload: + payload = self.serialize() + + if session.profile.settings.get("transport.light_weight_webhook"): + payload = LightWeightWebhook(1, **self.__dict__) + payload = payload.__dict__ + + await session.profile.notify(topic, payload) + @property def record_value(self) -> dict: """Accessor for the JSON record value generated for this invitation.""" diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/models/cred_ex_record.py b/aries_cloudagent/protocols/issue_credential/v2_0/models/cred_ex_record.py index fbbc0adaf5..7979c95a04 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/models/cred_ex_record.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/models/cred_ex_record.py @@ -8,6 +8,7 @@ from .....core.profile import ProfileSession from .....messaging.models.base_record import BaseExchangeRecord, BaseExchangeSchema +from .....messaging.models.light_webhook import LightWeightWebhook from .....messaging.valid import UUIDFour from .....storage.base import StorageError @@ -181,6 +182,33 @@ async def save_error_state( except StorageError as err: LOGGER.exception(err) + # Override + async def emit_event(self, session: ProfileSession, payload: Any = None): + """ + Emit an event. + + Args: + session: The profile session to use + payload: The event payload + """ + + if not self.RECORD_TOPIC: + return + + if self.state: + topic = f"{self.EVENT_NAMESPACE}::{self.RECORD_TOPIC}::{self.state}" + else: + topic = f"{self.EVENT_NAMESPACE}::{self.RECORD_TOPIC}" + + if not payload: + payload = self.serialize() + + if session.profile.settings.get("transport.light_weight_webhook"): + payload = LightWeightWebhook(2, **self.__dict__) + payload = payload.__dict__ + + await session.profile.notify(topic, payload) + @property def record_value(self) -> Mapping: """Accessor for the JSON record value generated for this credential exchange.""" From dd6fcf3135640fae825718033b602ef438ba7eaa Mon Sep 17 00:00:00 2001 From: Victor Lee Date: Wed, 8 Feb 2023 10:21:51 -0500 Subject: [PATCH 650/872] LightWeightWebhook: clean up un-used import Signed-off-by: Victor Lee --- aries_cloudagent/messaging/models/light_webhook.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/aries_cloudagent/messaging/models/light_webhook.py b/aries_cloudagent/messaging/models/light_webhook.py index fc601731a2..0d94b1b6d4 100644 --- a/aries_cloudagent/messaging/models/light_webhook.py +++ b/aries_cloudagent/messaging/models/light_webhook.py @@ -1,7 +1,3 @@ -import json -from aries_cloudagent.messaging.models.base_record import BaseExchangeRecord - - class LightWeightWebhook: __acceptable_keys_list = [ "connection_id", From d1c68fc747480846772775344a68040ab8718ae5 Mon Sep 17 00:00:00 2001 From: Victor Lee Date: Wed, 8 Feb 2023 11:49:28 -0500 Subject: [PATCH 651/872] fixed FLAKE8 style Signed-off-by: Victor Lee --- aries_cloudagent/messaging/models/light_webhook.py | 2 +- .../issue_credential/v1_0/models/credential_exchange.py | 4 ++-- .../issue_credential/v2_0/models/cred_ex_record.py | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/aries_cloudagent/messaging/models/light_webhook.py b/aries_cloudagent/messaging/models/light_webhook.py index 0d94b1b6d4..e6c51f8884 100644 --- a/aries_cloudagent/messaging/models/light_webhook.py +++ b/aries_cloudagent/messaging/models/light_webhook.py @@ -32,7 +32,7 @@ def __init__( [ self.__setattr__(key, kwargs.get(key)) for key in self.__acceptable_keys_list - if kwargs.get(key) != None + if kwargs.get(key) is not None ] if version == 2: self.cred_ex_id = kwargs.get("_id") diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py b/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py index 96b3bf50a6..4ca0ee71a5 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py @@ -242,11 +242,11 @@ async def emit_event(self, session: ProfileSession, payload: Any = None): if not payload: payload = self.serialize() - + if session.profile.settings.get("transport.light_weight_webhook"): payload = LightWeightWebhook(1, **self.__dict__) payload = payload.__dict__ - + await session.profile.notify(topic, payload) @property diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/models/cred_ex_record.py b/aries_cloudagent/protocols/issue_credential/v2_0/models/cred_ex_record.py index 1e200ae134..208df8ff7e 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/models/cred_ex_record.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/models/cred_ex_record.py @@ -202,13 +202,13 @@ async def emit_event(self, session: ProfileSession, payload: Any = None): if not payload: payload = self.serialize() - + if session.profile.settings.get("transport.light_weight_webhook"): payload = LightWeightWebhook(2, **self.__dict__) payload = payload.__dict__ - + await session.profile.notify(topic, payload) - + @property def record_value(self) -> Mapping: """Accessor for the JSON record value generated for this credential exchange.""" From b875872665ab98ffec9e4eed466712abe630454b Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Wed, 8 Feb 2023 15:55:55 -0800 Subject: [PATCH 652/872] 0.8.0-rc0 release updates Signed-off-by: Stephen Curran --- CHANGELOG.md | 223 +++++++++++++-------- PUBLISHING.md | 107 +++++++++- aries_cloudagent/version.py | 2 +- docs/generated/aries_cloudagent.wallet.rst | 8 + open-api/openapi.json | 2 +- 5 files changed, 245 insertions(+), 97 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e90d82bd2..d0bd9ffcdd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,109 +1,164 @@ -# 1.0.0-rc1 +# 0.8.0-rc0 -## November 6, 2022 +## February 8, 2023 -1.0.0 is a breaking update to ACA-Py whose version is intended to indicate the -maturity of the implementation. The final 1.0.0 release will be Aries Interop -Profile 2.0-complete, and based on Python 3.7 or higher. +0.8.0 is a breaking change that contains all updates since release 0.7.5. It +extends the previously tagged `1.0.0-rc1` release because it is not clear +when that release will be finalized. -### Breaking Changes +With this release, a new automated process publishes container +images in the Hyperledger container image repository for this version of ACA-Py +based on both Python 3.6 and 3.9. We recommend using Python 3.9 with ACA-Py. -As of release candidate 1.0.0-rc1, the only identified breaking change is the -handling of "unrevealed attributes" during verification (see -[\#1913](https://github.com/hyperledger/aries-cloudagent-python/pull/1913) for -details). As few implementations of Aries Wallets support unrevealed attributes -in an AnonCreds presentation, this is unlikely to impact any deployments. - -### Categorized List of Pull Requests - -In rc1, there are not a lot of new features, as the focus is on cleanup and -optimization. The biggest is the inclusion with ACA-Py of a universal resolver -interface, allowing an instance to have both local resolvers for some DID -Methods and a call out to an external universal resolver for other DID Methods. -Another significant feature is full support for Hyperledger Indy transaction -endorsement for Authors and Endorsers. A new repo +There are not a lot of new features in this release, as the focus has been on +cleanup and optimization. The biggest addition is the inclusion with ACA-Py of a +universal resolver interface, allowing an instance to have both local resolvers +for some DID Methods and a call out to an external universal resolver for other +DID Methods. Another significant new capability is full support for Hyperledger +Indy transaction endorsement for Authors and Endorsers. A new repo [aries-endorser-service](https://github.com/hyperledger/aries-endorser-service) has been created that is a pre-configured instance of ACA-Py for use as an -Endorser service. While some work has been done on moving the default Python -version beyond 3.6, more work is still to be done on that before the final -v1.0.0 release. +Endorser service. + +## Breaking Changes + +### PR [\#2034](https://github.com/hyperledger/aries-cloudagent-python/pull/2034) -- Implicit connections + +The break impacts existing deployments that supported connections coming via a +public DID. Such deployments need to add the configuration parameter +`--requests-through-public-did` to continue to support that feature. The use +case is that an ACA-Py instance publishes a public DID on a ledger with a +DIDComm `service` in the DIDDoc. Other agents resolve that DID, and attempt to +establish a connection with the ACA-Py instance using the `service` endpoint. +This is called an "implicit" connection in [RFC 0023 DID +Exchange](https://github.com/hyperledger/aries-rfcs/blob/main/features/0023-did-exchange/README.md). + +### PR [\#1913](https://github.com/hyperledger/aries-cloudagent-python/pull/1913) -- Unrevealed attributes in presentations + +Updates the handling of "unrevealed attributes" during verification of AnonCreds +presentations, allowing them to be used in a presentation, with additional data +that can be checked if for unrevealed attributes. As few implementations of +Aries wallets support unrevealed attributes in an AnonCreds presentation, this +is unlikely to impact any deployments. ### Categorized List of Pull Requests - Verifiable credential, presentation and revocation handling updates - - Refactor ledger correction code and insert into revocation error handling [\#1892](https://github.com/hyperledger/aries-cloudagent-python/pull/1892) ([ianco](https://github.com/ianco)) - - Indy ledger fixes and cleanups [\#1870](https://github.com/hyperledger/aries-cloudagent-python/pull/1870) ([andrewwhitehead](https://github.com/andrewwhitehead)) - - Refactoring of revocation registry creation [\#1813](https://github.com/hyperledger/aries-cloudagent-python/pull/1813) ([andrewwhitehead](https://github.com/andrewwhitehead)) - - Fix: the type of tails file path to string. [\#1925](https://github.com/hyperledger/aries-cloudagent-python/pull/1925) ([baegjae](https://github.com/baegjae)) - - Pre-populate revoc\_reg\_id on IssuerRevRegRecord [\#1924](https://github.com/hyperledger/aries-cloudagent-python/pull/1924) ([andrewwhitehead](https://github.com/andrewwhitehead)) - - Leave credentialStatus element in the LD credential [\#1921](https://github.com/hyperledger/aries-cloudagent-python/pull/1921) ([tsabolov](https://github.com/tsabolov)) - - **BREAKING:** Remove aca-py check for unrevealed revealed attrs on proof validation [\#1913](https://github.com/hyperledger/aries-cloudagent-python/pull/1913) ([ianco](https://github.com/ianco)) - - Send webhooks upon record/credential deletion [\#1906](https://github.com/hyperledger/aries-cloudagent-python/pull/1906) ([frostyfrog](https://github.com/frostyfrog)) - -- Out of Band (OOB) and DID Exchange / Connection Handling - - Fix: `--mediator-invitation` with OOB invitation + cleanup [\#1970](https://github.com/hyperledger/aries-cloudagent-python/pull/1970) ([shaangill025](https://github.com/shaangill025)) - - include image\_url in oob invitation [\#1966](https://github.com/hyperledger/aries-cloudagent-python/pull/1966) ([Zzocker](https://github.com/Zzocker)) - - feat: 00B v1.1 support [\#1962](https://github.com/hyperledger/aries-cloudagent-python/pull/1962) ([shaangill025](https://github.com/shaangill025)) - - Fix: OOB - Handling of minor versions [\#1940](https://github.com/hyperledger/aries-cloudagent-python/pull/1940) ([shaangill025](https://github.com/shaangill025)) - - fix: failed connectionless proof request on some case [\#1933](https://github.com/hyperledger/aries-cloudagent-python/pull/1933) ([kukgini](https://github.com/kukgini)) - - fix: propagate endpoint from mediation record [\#1922](https://github.com/hyperledger/aries-cloudagent-python/pull/1922) ([cjhowland](https://github.com/cjhowland)) - - Feat/public did endpoints for agents behind mediators [\#1899](https://github.com/hyperledger/aries-cloudagent-python/pull/1899) ([cjhowland](https://github.com/cjhowland)) + - Feature: enabled handling VPs \(request, creation, verification\) with different VCs [\#1956](https://github.com/hyperledger/aries-cloudagent-python/pull/1956) ([teanas](https://github.com/teanas)) + - fix: update issue-credential endpoint summaries [\#1997](https://github.com/hyperledger/aries-cloudagent-python/pull/1997) ([PeterStrob](https://github.com/PeterStrob)) + - fix claim format designation in presentation submission [\#2013](https://github.com/hyperledger/aries-cloudagent-python/pull/2013) ([rmnre](https://github.com/rmnre)) + - \#2041 - Issue JSON-LD has invalid Admin API documentation [\#2046](https://github.com/hyperledger/aries-cloudagent-python/pull/2046) ([jfblier-amplitude](https://github.com/jfblier-amplitude)) + - Previously flagged in release 1.0.0-rc1 + - Refactor ledger correction code and insert into revocation error handling [\#1892](https://github.com/hyperledger/aries-cloudagent-python/pull/1892) ([ianco](https://github.com/ianco)) + - Indy ledger fixes and cleanups [\#1870](https://github.com/hyperledger/aries-cloudagent-python/pull/1870) ([andrewwhitehead](https://github.com/andrewwhitehead)) + - Refactoring of revocation registry creation [\#1813](https://github.com/hyperledger/aries-cloudagent-python/pull/1813) ([andrewwhitehead](https://github.com/andrewwhitehead)) + - Fix: the type of tails file path to string. [\#1925](https://github.com/hyperledger/aries-cloudagent-python/pull/1925) ([baegjae](https://github.com/baegjae)) + - Pre-populate revoc\_reg\_id on IssuerRevRegRecord [\#1924](https://github.com/hyperledger/aries-cloudagent-python/pull/1924) ([andrewwhitehead](https://github.com/andrewwhitehead)) + - Leave credentialStatus element in the LD credential [\#1921](https://github.com/hyperledger/aries-cloudagent-python/pull/1921) ([tsabolov](https://github.com/tsabolov)) + - **BREAKING:** Remove aca-py check for unrevealed revealed attrs on proof validation [\#1913](https://github.com/hyperledger/aries-cloudagent-python/pull/1913) ([ianco](https://github.com/ianco)) + - Send webhooks upon record/credential deletion [\#1906](https://github.com/hyperledger/aries-cloudagent-python/pull/1906) ([frostyfrog](https://github.com/frostyfrog)) + +- Out of Band (OOB) and DID Exchange / Connection Handling / Mediator + - fix: public did mediator routing keys as did keys [\#1977](https://github.com/hyperledger/aries-cloudagent-python/pull/1977) ([dbluhm](https://github.com/dbluhm)) + - Fix for mediator load testing race condition when scaling horizontally [\#2009](https://github.com/hyperledger/aries-cloudagent-python/pull/2009) ([ianco](https://github.com/ianco)) + - BREAKING: Allow multi-use public invites and public invites with metadata [\#2034](https://github.com/hyperledger/aries-cloudagent-python/pull/2034) ([mepeltier](https://github.com/mepeltier)) + - Do not reject OOB invitation with unknown handshake protocol\(s\) [\#2060](https://github.com/hyperledger/aries-cloudagent-python/pull/2060) ([andrewwhitehead](https://github.com/andrewwhitehead)) + - fix: fix connection timing bug [\#2099](https://github.com/hyperledger/aries-cloudagent-python/pull/2099) ([reflectivedevelopment](https://github.com/reflectivedevelopment)) + - Previously flagged in release 1.0.0-rc1 + - Fix: `--mediator-invitation` with OOB invitation + cleanup [\#1970](https://github.com/hyperledger/aries-cloudagent-python/pull/1970) ([shaangill025](https://github.com/shaangill025)) + - include image\_url in oob invitation [\#1966](https://github.com/hyperledger/aries-cloudagent-python/pull/1966) ([Zzocker](https://github.com/Zzocker)) + - feat: 00B v1.1 support [\#1962](https://github.com/hyperledger/aries-cloudagent-python/pull/1962) ([shaangill025](https://github.com/shaangill025)) + - Fix: OOB - Handling of minor versions [\#1940](https://github.com/hyperledger/aries-cloudagent-python/pull/1940) ([shaangill025](https://github.com/shaangill025)) + - fix: failed connectionless proof request on some case [\#1933](https://github.com/hyperledger/aries-cloudagent-python/pull/1933) ([kukgini](https://github.com/kukgini)) + - fix: propagate endpoint from mediation record [\#1922](https://github.com/hyperledger/aries-cloudagent-python/pull/1922) ([cjhowland](https://github.com/cjhowland)) + - Feat/public did endpoints for agents behind mediators [\#1899](https://github.com/hyperledger/aries-cloudagent-python/pull/1899) ([cjhowland](https://github.com/cjhowland)) - DID Registration and Resolution related updates - - feat: add universal resolver [\#1866](https://github.com/hyperledger/aries-cloudagent-python/pull/1866) ([dbluhm](https://github.com/dbluhm)) - - fix: resolve dids following new endpoint rules [\#1863](https://github.com/hyperledger/aries-cloudagent-python/pull/1863) ([dbluhm](https://github.com/dbluhm)) - - fix: didx request cannot be accepted [\#1881](https://github.com/hyperledger/aries-cloudagent-python/pull/1881) ([rmnre](https://github.com/rmnre)) - - did method & key type registry [\#1986](https://github.com/hyperledger/aries-cloudagent-python/pull/1986) ([burdettadam](https://github.com/burdettadam)) - - Fix/endpoint attrib structure [\#1934](https://github.com/hyperledger/aries-cloudagent-python/pull/1934) ([cjhowland](https://github.com/cjhowland)) - - Simple did registry [\#1920](https://github.com/hyperledger/aries-cloudagent-python/pull/1920) ([burdettadam](https://github.com/burdettadam)) - - Use did:key for recipient keys [\#1886](https://github.com/hyperledger/aries-cloudagent-python/pull/1886) ([frostyfrog](https://github.com/frostyfrog)) + - feat: enable creation of DIDs for all registered methods [\#2067](https://github.com/hyperledger/aries-cloudagent-python/pull/2067) ([chumbert](https://github.com/chumbert)) + - fix: create local DID return schema [\#2086](https://github.com/hyperledger/aries-cloudagent-python/pull/2086) ([chumbert](https://github.com/chumbert)) + - feat: universal resolver - configurable authentication [\#2095](https://github.com/hyperledger/aries-cloudagent-python/pull/2095) ([chumbert](https://github.com/chumbert)) + - Previously flagged in release 1.0.0-rc1 + - feat: add universal resolver [\#1866](https://github.com/hyperledger/aries-cloudagent-python/pull/1866) ([dbluhm](https://github.com/dbluhm)) + - fix: resolve dids following new endpoint rules [\#1863](https://github.com/hyperledger/aries-cloudagent-python/pull/1863) ([dbluhm](https://github.com/dbluhm)) + - fix: didx request cannot be accepted [\#1881](https://github.com/hyperledger/aries-cloudagent-python/pull/1881) ([rmnre](https://github.com/rmnre)) + - did method & key type registry [\#1986](https://github.com/hyperledger/aries-cloudagent-python/pull/1986) ([burdettadam](https://github.com/burdettadam)) + - Fix/endpoint attrib structure [\#1934](https://github.com/hyperledger/aries-cloudagent-python/pull/1934) ([cjhowland](https://github.com/cjhowland)) + - Simple did registry [\#1920](https://github.com/hyperledger/aries-cloudagent-python/pull/1920) ([burdettadam](https://github.com/burdettadam)) + - Use did:key for recipient keys [\#1886](https://github.com/hyperledger/aries-cloudagent-python/pull/1886) ([frostyfrog](https://github.com/frostyfrog)) - Hyperledger Indy Endorser/Author Transaction Handling - - Fix/txn job setting [\#1994](https://github.com/hyperledger/aries-cloudagent-python/pull/1994) ([ianco](https://github.com/ianco)) - - chore: fix ACAPY\_PROMOTE-AUTHOR-DID flag [\#1978](https://github.com/hyperledger/aries-cloudagent-python/pull/1978) ([morrieinmaas](https://github.com/morrieinmaas)) - - - Endorser write DID transaction [\#1938](https://github.com/hyperledger/aries-cloudagent-python/pull/1938) ([ianco](https://github.com/ianco)) - - Endorser doc updates and some bug fixes [\#1926](https://github.com/hyperledger/aries-cloudagent-python/pull/1926) ([ianco](https://github.com/ianco)) + - Special handling for the write ledger [\#2030](https://github.com/hyperledger/aries-cloudagent-python/pull/2030) ([ianco](https://github.com/ianco)) + - Previously flagged in release 1.0.0-rc1 + - Fix/txn job setting [\#1994](https://github.com/hyperledger/aries-cloudagent-python/pull/1994) ([ianco](https://github.com/ianco)) + - chore: fix ACAPY\_PROMOTE-AUTHOR-DID flag [\#1978](https://github.com/hyperledger/aries-cloudagent-python/pull/1978) ([morrieinmaas](https://github.com/morrieinmaas)) + - Endorser write DID transaction [\#1938](https://github.com/hyperledger/aries-cloudagent-python/pull/1938) ([ianco](https://github.com/ianco)) + - Endorser doc updates and some bug fixes [\#1926](https://github.com/hyperledger/aries-cloudagent-python/pull/1926) ([ianco](https://github.com/ianco)) - Startup Command Line / Environment / YAML Parameter Updates - - Add seed command line parameter but use only if also an "allow insecure seed" parameter is set [\#1714](https://github.com/hyperledger/aries-cloudagent-python/pull/1714) ([DaevMithran](https://github.com/DaevMithran)) + - Add missing --mediator-connections-invite cmd arg info to docs [\#2051](https://github.com/hyperledger/aries-cloudagent-python/pull/2051) ([matrixik](https://github.com/matrixik)) + - Issue \#2068 boolean flag change to support HEAD requests to default route [\#2077](https://github.com/hyperledger/aries-cloudagent-python/pull/2077) ([johnekent](https://github.com/johnekent)) + - Previously flagged in release 1.0.0-rc1 + - Add seed command line parameter but use only if also an "allow insecure seed" parameter is set [\#1714](https://github.com/hyperledger/aries-cloudagent-python/pull/1714) ([DaevMithran](https://github.com/DaevMithran)) - Internal Aries framework data handling updates - - fix: update RouteManager methods use to pass profile as parameter [\#1902](https://github.com/hyperledger/aries-cloudagent-python/pull/1902) ([chumbert](https://github.com/chumbert)) - - Allow fully qualified class names for profile managers [\#1880](https://github.com/hyperledger/aries-cloudagent-python/pull/1880) ([chumbert](https://github.com/chumbert)) - - fix: unable to use askar with in memory db [\#1878](https://github.com/hyperledger/aries-cloudagent-python/pull/1878) ([dbluhm](https://github.com/dbluhm)) - - Enable manually triggering keylist updates during connection [\#1851](https://github.com/hyperledger/aries-cloudagent-python/pull/1851) ([dbluhm](https://github.com/dbluhm)) - - feat: make base wallet route access configurable [\#1836](https://github.com/hyperledger/aries-cloudagent-python/pull/1836) ([dbluhm](https://github.com/dbluhm)) - - feat: event and webhook on keylist update stored [\#1769](https://github.com/hyperledger/aries-cloudagent-python/pull/1769) ([dbluhm](https://github.com/dbluhm)) - - fix: Safely shutdown when root\_profile uninitialized [\#1960](https://github.com/hyperledger/aries-cloudagent-python/pull/1960) ([frostyfrog](https://github.com/frostyfrog)) - - feat: include connection ids in keylist update webhook [\#1914](https://github.com/hyperledger/aries-cloudagent-python/pull/1914) ([dbluhm](https://github.com/dbluhm)) - - fix: incorrect response schema for discover features [\#1912](https://github.com/hyperledger/aries-cloudagent-python/pull/1912) ([dbluhm](https://github.com/dbluhm)) - - Fix: SchemasInputDescriptorFilter: broken deserialization renders generated clients unusable [\#1894](https://github.com/hyperledger/aries-cloudagent-python/pull/1894) ([rmnre](https://github.com/rmnre)) - - fix: schema class can set Meta.unknown [\#1885](https://github.com/hyperledger/aries-cloudagent-python/pull/1885) ([dbluhm](https://github.com/dbluhm)) - -- Unit, Integration and Aries Agent Test Harness Test updates - - Fixes a few AATH failures [\#1897](https://github.com/hyperledger/aries-cloudagent-python/pull/1897) ([ianco](https://github.com/ianco)) - - fix: warnings in tests from IndySdkProfile [\#1865](https://github.com/hyperledger/aries-cloudagent-python/pull/1865) ([dbluhm](https://github.com/dbluhm)) - - Unit test fixes for python 3.9 [\#1858](https://github.com/hyperledger/aries-cloudagent-python/pull/1858) ([andrewwhitehead](https://github.com/andrewwhitehead)) - - Update pip-audit.yml [\#1945](https://github.com/hyperledger/aries-cloudagent-python/pull/1945) ([ryjones](https://github.com/ryjones)) - - Update pip-audit.yml [\#1944](https://github.com/hyperledger/aries-cloudagent-python/pull/1944) ([ryjones](https://github.com/ryjones)) - -- Dependency Updates - - feat: update pynacl version from 1.4.0 to 1.50 [\#1981](https://github.com/hyperledger/aries-cloudagent-python/pull/1981) ([morrieinmaas](https://github.com/morrieinmaas)) - - Fix: web.py dependency - integration tests & demos [\#1973](https://github.com/hyperledger/aries-cloudagent-python/pull/1973) ([shaangill025](https://github.com/shaangill025)) - - chore: update pydid [\#1915](https://github.com/hyperledger/aries-cloudagent-python/pull/1915) ([dbluhm](https://github.com/dbluhm)) + - fix: return if return route but no response [\#1853](https://github.com/hyperledger/aries-cloudagent-python/pull/1853) ([TimoGlastra](https://github.com/TimoGlastra)) + - Multi-ledger/Multi-tenant issues [\#2022](https://github.com/hyperledger/aries-cloudagent-python/pull/2022) ([ianco](https://github.com/ianco)) + - fix: Correct typo in model -- required spelled incorrectly [\#2031](https://github.com/hyperledger/aries-cloudagent-python/pull/2031) ([swcurran](https://github.com/swcurran)) + - Code formatting [\#2053](https://github.com/hyperledger/aries-cloudagent-python/pull/2053) ([ianco](https://github.com/ianco)) + - Improved validation of record state attributes [\#2071](https://github.com/hyperledger/aries-cloudagent-python/pull/2071) ([rmnre](https://github.com/rmnre)) + - Previously flagged in release 1.0.0-rc1 + - fix: update RouteManager methods use to pass profile as parameter [\#1902](https://github.com/hyperledger/aries-cloudagent-python/pull/1902) ([chumbert](https://github.com/chumbert)) + - Allow fully qualified class names for profile managers [\#1880](https://github.com/hyperledger/aries-cloudagent-python/pull/1880) ([chumbert](https://github.com/chumbert)) + - fix: unable to use askar with in memory db [\#1878](https://github.com/hyperledger/aries-cloudagent-python/pull/1878) ([dbluhm](https://github.com/dbluhm)) + - Enable manually triggering keylist updates during connection [\#1851](https://github.com/hyperledger/aries-cloudagent-python/pull/1851) ([dbluhm](https://github.com/dbluhm)) + - feat: make base wallet route access configurable [\#1836](https://github.com/hyperledger/aries-cloudagent-python/pull/1836) ([dbluhm](https://github.com/dbluhm)) + - feat: event and webhook on keylist update stored [\#1769](https://github.com/hyperledger/aries-cloudagent-python/pull/1769) ([dbluhm](https://github.com/dbluhm)) + - fix: Safely shutdown when root\_profile uninitialized [\#1960](https://github.com/hyperledger/aries-cloudagent-python/pull/1960) ([frostyfrog](https://github.com/frostyfrog)) + - feat: include connection ids in keylist update webhook [\#1914](https://github.com/hyperledger/aries-cloudagent-python/pull/1914) ([dbluhm](https://github.com/dbluhm)) + - fix: incorrect response schema for discover features [\#1912](https://github.com/hyperledger/aries-cloudagent-python/pull/1912) ([dbluhm](https://github.com/dbluhm)) + - Fix: SchemasInputDescriptorFilter: broken deserialization renders generated clients unusable [\#1894](https://github.com/hyperledger/aries-cloudagent-python/pull/1894) ([rmnre](https://github.com/rmnre)) + - fix: schema class can set Meta.unknown [\#1885](https://github.com/hyperledger/aries-cloudagent-python/pull/1885) ([dbluhm](https://github.com/dbluhm)) + +- Unit, Integration, and Aries Agent Test Harness Test updates + - Additional integration tests for revocation scenarios [\#2055](https://github.com/hyperledger/aries-cloudagent-python/pull/2055) ([ianco](https://github.com/ianco)) + - Previously flagged in release 1.0.0-rc1 + - Fixes a few AATH failures [\#1897](https://github.com/hyperledger/aries-cloudagent-python/pull/1897) ([ianco](https://github.com/ianco)) + - fix: warnings in tests from IndySdkProfile [\#1865](https://github.com/hyperledger/aries-cloudagent-python/pull/1865) ([dbluhm](https://github.com/dbluhm)) + - Unit test fixes for python 3.9 [\#1858](https://github.com/hyperledger/aries-cloudagent-python/pull/1858) ([andrewwhitehead](https://github.com/andrewwhitehead)) + - Update pip-audit.yml [\#1945](https://github.com/hyperledger/aries-cloudagent-python/pull/1945) ([ryjones](https://github.com/ryjones)) + - Update pip-audit.yml [\#1944](https://github.com/hyperledger/aries-cloudagent-python/pull/1944) ([ryjones](https://github.com/ryjones)) + +- Dependency, Python version, GitHub Actions and Container Image Changes + - fix: indy dependency version format [\#2054](https://github.com/hyperledger/aries-cloudagent-python/pull/2054) ([chumbert](https://github.com/chumbert)) + - ci: add gha for pr-tests [\#2058](https://github.com/hyperledger/aries-cloudagent-python/pull/2058) ([dbluhm](https://github.com/dbluhm)) + - ci: test additional versions of python nightly [\#2059](https://github.com/hyperledger/aries-cloudagent-python/pull/2059) ([dbluhm](https://github.com/dbluhm)) + - Update github actions dependencies \(for node16 support\) [\#2066](https://github.com/hyperledger/aries-cloudagent-python/pull/2066) ([andrewwhitehead](https://github.com/andrewwhitehead)) + - Docker images and GHA for publishing images [\#2076](https://github.com/hyperledger/aries-cloudagent-python/pull/2076) ([dbluhm](https://github.com/dbluhm)) + - Update dockerfiles to use python 3.9 [\#2109](https://github.com/hyperledger/aries-cloudagent-python/pull/2109) ([ianco](https://github.com/ianco)) + - Updating base images from slim-buster to slim-bullseye [\#2105](https://github.com/hyperledger/aries-cloudagent-python/pull/2105) ([pradeepp88](https://github.com/pradeepp88)) + - Previously flagged in release 1.0.0-rc1 + - feat: update pynacl version from 1.4.0 to 1.50 [\#1981](https://github.com/hyperledger/aries-cloudagent-python/pull/1981) ([morrieinmaas](https://github.com/morrieinmaas)) + - Fix: web.py dependency - integration tests & demos [\#1973](https://github.com/hyperledger/aries-cloudagent-python/pull/1973) ([shaangill025](https://github.com/shaangill025)) + - chore: update pydid [\#1915](https://github.com/hyperledger/aries-cloudagent-python/pull/1915) ([dbluhm](https://github.com/dbluhm)) - Demo and Documentation Updates - - Fixes to acme exercise code [\#1990](https://github.com/hyperledger/aries-cloudagent-python/pull/1990) ([ianco](https://github.com/ianco)) - - Fixed bug in run\_demo script [\#1982](https://github.com/hyperledger/aries-cloudagent-python/pull/1982) ([pasquale95](https://github.com/pasquale95)) - - Transaction Author with Endorser demo [\#1975](https://github.com/hyperledger/aries-cloudagent-python/pull/1975) ([ianco](https://github.com/ianco)) - - Redis Plugins \[redis\_cache & redis\_queue\] related updates [\#1937](https://github.com/hyperledger/aries-cloudagent-python/pull/1937) ([shaangill025](https://github.com/shaangill025)) + - Fix typos in alice-local.sh & faber-local.sh [\#2010](https://github.com/hyperledger/aries-cloudagent-python/pull/2010) ([naonishijima](https://github.com/naonishijima)) + - Added a bit about manually creating a revoc reg tails file [\#2012](https://github.com/hyperledger/aries-cloudagent-python/pull/2012) ([ianco](https://github.com/ianco)) + - Add ability to set docker container name [\#2024](https://github.com/hyperledger/aries-cloudagent-python/pull/2024) ([matrixik](https://github.com/matrixik)) + - Doc updates for json demo [\#2026](https://github.com/hyperledger/aries-cloudagent-python/pull/2026) ([ianco](https://github.com/ianco)) + - Multitenancy demo \(docker-compose with postgres and ngrok\) [\#2089](https://github.com/hyperledger/aries-cloudagent-python/pull/2089) ([ianco](https://github.com/ianco)) + - Allow using YAML configuration file with run\_docker [\#2091](https://github.com/hyperledger/aries-cloudagent-python/pull/2091) ([matrixik](https://github.com/matrixik)) + - Previously flagged in release 1.0.0-rc1 + - Fixes to acme exercise code [\#1990](https://github.com/hyperledger/aries-cloudagent-python/pull/1990) ([ianco](https://github.com/ianco)) + - Fixed bug in run\_demo script [\#1982](https://github.com/hyperledger/aries-cloudagent-python/pull/1982) ([pasquale95](https://github.com/pasquale95)) + - Transaction Author with Endorser demo [\#1975](https://github.com/hyperledger/aries-cloudagent-python/pull/1975) ([ianco](https://github.com/ianco)) + - Redis Plugins \[redis\_cache & redis\_queue\] related updates [\#1937](https://github.com/hyperledger/aries-cloudagent-python/pull/1937) ([shaangill025](https://github.com/shaangill025)) - Release management pull requests - - Release 1.0.0-rc0 [\#1904](https://github.com/hyperledger/aries-cloudagent-python/pull/1904) ([swcurran](https://github.com/swcurran)) - - Add 0.7.5 patch Changelog entry to main branch Changelog [\#1996](https://github.com/hyperledger/aries-cloudagent-python/pull/1996) ([swcurran](https://github.com/swcurran)) - - Release 1.0.0-rc1 [\#2005](https://github.com/hyperledger/aries-cloudagent-python/pull/2005) ([swcurran](https://github.com/swcurran)) - + - Previously flagged in release 1.0.0-rc1 + - Release 1.0.0-rc0 [\#1904](https://github.com/hyperledger/aries-cloudagent-python/pull/1904) ([swcurran](https://github.com/swcurran)) + - Add 0.7.5 patch Changelog entry to main branch Changelog [\#1996](https://github.com/hyperledger/aries-cloudagent-python/pull/1996) ([swcurran](https://github.com/swcurran)) + - Release 1.0.0-rc1 [\#2005](https://github.com/hyperledger/aries-cloudagent-python/pull/2005) ([swcurran](https://github.com/swcurran)) # 0.7.5 diff --git a/PUBLISHING.md b/PUBLISHING.md index d8bd13a15d..a45f0e9cc8 100644 --- a/PUBLISHING.md +++ b/PUBLISHING.md @@ -12,23 +12,108 @@ Once ready to do a release, create a local branch that includes the following up 3. Include details of the merged PRs included in this release. General process to follow: -- Gather the set of PRs since the last release and put them into a list. A good tool to use for this is the [github-changelog-generator](https://github.com/github-changelog-generator/github-changelog-generator). Steps: - - Create a read only GitHub token for your account on this page: [https://github.com/settings/tokens](https://github.com/settings/tokens/new?description=GitHub%20Changelog%20Generator%20token) with a scope of `repo` / `public_repo`. - - Use a command like the following, adjusting the tag parameters as appropriate. `docker run -it --rm -v "$(pwd)":/usr/local/src/your-app githubchangeloggenerator/github-changelog-generator --user hyperledger --project aries-cloudagent-python --output 0.7.4-rc0.md --since-tag 0.7.3 --future-release 0.7.4-rc0 --release-branch main --token ` - - In the generated file, use only the PR list -- we don't include the list of closed issues in the Change Log. +- Gather the set of PRs since the last release and put them into a list. A good + tool to use for this is the + [github-changelog-generator](https://github.com/github-changelog-generator/github-changelog-generator). + Steps: + - Create a read only GitHub token for your account on this page: + [https://github.com/settings/tokens](https://github.com/settings/tokens/new?description=GitHub%20Changelog%20Generator%20token) + with a scope of `repo` / `public_repo`. + - Use a command like the following, adjusting the tag parameters as + appropriate. `docker run -it --rm -v "$(pwd)":/usr/local/src/your-app + githubchangeloggenerator/github-changelog-generator --user hyperledger + --project aries-cloudagent-python --output 0.7.4-rc0.md --since-tag 0.7.3 + --future-release 0.7.4-rc0 --release-branch main --token ` + - In the generated file, use only the PR list -- we don't include the list of + closed issues in the Change Log. + +In some cases, the approach above fails because of too many API calls. An +alternate approach to getting the list of PRs in the right format is to use this +scary `sed` pipeline process to get the same output.¥ + +- Put the following commands into a file called `changelog.sed` + +``` bash +/Approved/d +/updated /d +/^$/d +/^ [0-9]/d +s/was merged.*// +/^@/d +s# by \(.*\) # [\1](https://github.com/\1)# +s/^ // +s# \#\([0-9]*\)# [\#\1](https://github.com/hyperledger/aries-cloudagent-python/pull/\1) # +s/ / /g +/^Version/d +/tasks done/d +s/^/- / +``` + +- Navigate in your browser to the paged list of PRs merged since the last + release (using in the GitHub UI a filter such as `is:pr is:merged sort:updated + merged:>2022-04-07`) and for each page, highlight, and copy the text + of only the list of PRs on the page to use in the following step. +- For each page, run the command `sed -e :a -e '$!N;s/\n#/ #/;ta' -e 'P;D' < Date: Wed, 8 Feb 2023 16:04:40 -0800 Subject: [PATCH 653/872] Add this PR to the CHANGELOG.md file Signed-off-by: Stephen Curran --- CHANGELOG.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d0bd9ffcdd..71b3f592da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,8 +3,10 @@ ## February 8, 2023 0.8.0 is a breaking change that contains all updates since release 0.7.5. It -extends the previously tagged `1.0.0-rc1` release because it is not clear -when that release will be finalized. +extends the previously tagged `1.0.0-rc1` release because it is not clear when +that release will be finalized. Many of the PRs in this release were previously +included in the `1.0.0-rc1` release. The categorized list of PRs separates those +that are new from those in the `1.0.0-rc1` release candidate. With this release, a new automated process publishes container images in the Hyperledger container image repository for this version of ACA-Py @@ -155,6 +157,7 @@ is unlikely to impact any deployments. - Redis Plugins \[redis\_cache & redis\_queue\] related updates [\#1937](https://github.com/hyperledger/aries-cloudagent-python/pull/1937) ([shaangill025](https://github.com/shaangill025)) - Release management pull requests + - 0.8.0-rc0 release updates [\#2115](https://github.com/hyperledger/aries-cloudagent-python/pull/2115) ([swcurran](https://github.com/swcurran)) - Previously flagged in release 1.0.0-rc1 - Release 1.0.0-rc0 [\#1904](https://github.com/hyperledger/aries-cloudagent-python/pull/1904) ([swcurran](https://github.com/swcurran)) - Add 0.7.5 patch Changelog entry to main branch Changelog [\#1996](https://github.com/hyperledger/aries-cloudagent-python/pull/1996) ([swcurran](https://github.com/swcurran)) From 12ffeda64375284a1a2288792fbac6ccf4f8807e Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Thu, 9 Feb 2023 09:05:53 -0800 Subject: [PATCH 654/872] Updates to the PUBLISHING and CHANGELOG regards publishing images Signed-off-by: Stephen Curran --- CHANGELOG.md | 48 ++++++++++++++++++++++++++++++++++++------------ PUBLISHING.md | 20 ++++++++++++++------ 2 files changed, 50 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 71b3f592da..6de4e488cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,26 +8,50 @@ that release will be finalized. Many of the PRs in this release were previously included in the `1.0.0-rc1` release. The categorized list of PRs separates those that are new from those in the `1.0.0-rc1` release candidate. -With this release, a new automated process publishes container -images in the Hyperledger container image repository for this version of ACA-Py -based on both Python 3.6 and 3.9. We recommend using Python 3.9 with ACA-Py. - -There are not a lot of new features in this release, as the focus has been on -cleanup and optimization. The biggest addition is the inclusion with ACA-Py of a -universal resolver interface, allowing an instance to have both local resolvers -for some DID Methods and a call out to an external universal resolver for other -DID Methods. Another significant new capability is full support for Hyperledger -Indy transaction endorsement for Authors and Endorsers. A new repo +With this release, a new automated process publishes container images in the +Hyperledger container image repository. New images for the release are automatically published by the GitHubAction +Workflows: [publish.yml] and [publish-indy.yml]. The actions are triggered when +a release is tagged, so no manual action is needed. The images are published in +the [Hyperledger Package Repository under +aries-cloudagent-python](https://github.com/orgs/hyperledger/packages?repo_name=aries-cloudagent-python) +and a link to the packages added to the repositories main page (under +"Packages"). Additional information about the container image publication process can be +found in the document [Container Images and Github Actions]. + +There are not a lot of new Aries Framework features in this release, as the +focus has been on cleanup and optimization. The biggest addition is the +inclusion with ACA-Py of a universal resolver interface, allowing an instance to +have both local resolvers for some DID Methods and a call out to an external +universal resolver for other DID Methods. Another significant new capability is +full support for Hyperledger Indy transaction endorsement for Authors and +Endorsers. A new repo [aries-endorser-service](https://github.com/hyperledger/aries-endorser-service) has been created that is a pre-configured instance of ACA-Py for use as an Endorser service. +The images are based on [Python 3.6 and 3.9 `slim-bullseye` +images](https://hub.docker.com/_/python), and are built to support `linux/386 +(x86)`, `linux/amd64 (x64)`, and `linux/arm64`. There are two flavors of image +built for each Python version. One containing the Indy/Aries Shared Libraries +only ([Aries Askar](https://github.com/hyperledger/aries-askar), [Indy +VDR](https://github.com/hyperledger/indy-vdr) and [Indy Shared +RS](https://github.com/hyperledger/indy-shared-rs), supporting only the use of +`--wallet-type askar`), and one containing the Indy/Aries shared libraries and +the Indy SDK (considered deprecated). The images containing the Indy SDK are +labeled `indy`. For new deployments, we recommend using the Python 3.9 Shared +Library images. For existing deployments, we recommend migrating to those +images. For those migrating an Indy SDK deployment, a new secure storage +migration capability from Indy SDK to Aries Askar is available--contact the +ACA-Py maintainers on Hyperledger Discord for details. + + ## Breaking Changes ### PR [\#2034](https://github.com/hyperledger/aries-cloudagent-python/pull/2034) -- Implicit connections -The break impacts existing deployments that supported connections coming via a -public DID. Such deployments need to add the configuration parameter +The break impacts existing deployments that support implicit connections, those +initiated by another agent using a Public DID for this instance instead of an +explicit invitation. Such deployments need to add the configuration parameter `--requests-through-public-did` to continue to support that feature. The use case is that an ACA-Py instance publishes a public DID on a ledger with a DIDComm `service` in the DIDDoc. Other agents resolve that DID, and attempt to diff --git a/PUBLISHING.md b/PUBLISHING.md index a45f0e9cc8..4e561692dc 100644 --- a/PUBLISHING.md +++ b/PUBLISHING.md @@ -107,12 +107,20 @@ Once you have the list of PRs: PRs in the release, to complement the manually curated Changelog. Verify on PyPi that the version is published. -9. Publish a new docker container on Docker Hub - ([bcgovimages/aries-cloudagent](https://hub.docker.com/r/bcgovimages/aries-cloudagent/)) - by following the README.md instructions to create a PR for the release in the - repository - [https://github.com/bcgov/aries-cloudagent-container](https://github.com/bcgov/aries-cloudagent-container). - Appropriate permissions are required to publish the image. +9. New images for the release are automatically published by the GitHubAction + Workflows: [publish.yml] and [publish-indy.yml]. The actions are triggered + when a release is tagged, so no manual action is needed. The images are + published in the [Hyperledger Package Repository under + aries-cloudagent-python](https://github.com/orgs/hyperledger/packages?repo_name=aries-cloudagent-python) + and a link to the packages added to the repositories main page (under + "Packages"). + + Additional information about the container image publication process can be + found in the document [Container Images and Github Actions](). + +[publish.yml]: https://github.com/hyperledger/aries-cloudagent-python/blob/main/.github/workflows/publish.yml +[publish-indy.yml]: https://github.com/hyperledger/aries-cloudagent-python/blob/main/.github/workflows/publish-indy.yml +[Container Images and Github Actions]: https://github.com/hyperledger/aries-cloudagent-python/blob/main/ContainerImagesAndGithubActions.md 10. Update the ACA-Py Read The Docs site by building the new "latest" (main branch) and activating and building the new release. Appropriate permissions From f1563ab21e4217f7a866fd6f67d59474a55f9f47 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Thu, 9 Feb 2023 09:11:30 -0800 Subject: [PATCH 655/872] Additional CHANGELOG updates Signed-off-by: Stephen Curran --- CHANGELOG.md | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6de4e488cf..be42e508bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,14 +9,14 @@ included in the `1.0.0-rc1` release. The categorized list of PRs separates those that are new from those in the `1.0.0-rc1` release candidate. With this release, a new automated process publishes container images in the -Hyperledger container image repository. New images for the release are automatically published by the GitHubAction -Workflows: [publish.yml] and [publish-indy.yml]. The actions are triggered when -a release is tagged, so no manual action is needed. The images are published in -the [Hyperledger Package Repository under -aries-cloudagent-python](https://github.com/orgs/hyperledger/packages?repo_name=aries-cloudagent-python) -and a link to the packages added to the repositories main page (under -"Packages"). Additional information about the container image publication process can be -found in the document [Container Images and Github Actions]. +Hyperledger container image repository. New images for the release are +automatically published by the GitHubAction Workflows: [publish.yml] and +[publish-indy.yml]. The actions are triggered when a release is tagged, so no +manual action is needed. The images are published in the [Hyperledger Package +Repository under aries-cloudagent-python] and a link to the packages added to +the repositories main page (under "Packages"). Additional information about the +container image publication process can be found in the document [Container +Images and Github Actions]. There are not a lot of new Aries Framework features in this release, as the focus has been on cleanup and optimization. The biggest addition is the @@ -32,18 +32,24 @@ Endorser service. The images are based on [Python 3.6 and 3.9 `slim-bullseye` images](https://hub.docker.com/_/python), and are built to support `linux/386 (x86)`, `linux/amd64 (x64)`, and `linux/arm64`. There are two flavors of image -built for each Python version. One containing the Indy/Aries Shared Libraries +built for each Python version. One contains only the Indy/Aries Shared Libraries only ([Aries Askar](https://github.com/hyperledger/aries-askar), [Indy VDR](https://github.com/hyperledger/indy-vdr) and [Indy Shared RS](https://github.com/hyperledger/indy-shared-rs), supporting only the use of -`--wallet-type askar`), and one containing the Indy/Aries shared libraries and -the Indy SDK (considered deprecated). The images containing the Indy SDK are -labeled `indy`. For new deployments, we recommend using the Python 3.9 Shared -Library images. For existing deployments, we recommend migrating to those -images. For those migrating an Indy SDK deployment, a new secure storage -migration capability from Indy SDK to Aries Askar is available--contact the -ACA-Py maintainers on Hyperledger Discord for details. - +`--wallet-type askar`). The other (labelled `indy`) contains the Indy/Aries +shared libraries and the Indy SDK (considered deprecated). For new deployments, +we recommend using the Python 3.9 Shared Library images. For existing +deployments, we recommend migrating to those images. For those migrating an Indy +SDK deployment, a new secure storage database migration capability from Indy SDK +to Aries Askar is available--contact the ACA-Py maintainers on Hyperledger +Discord for details. + +Those currently using the container images published by [BC Gov on Docker +Hub](https://hub.docker.com/r/bcgovimages/aries-cloudagent) should change to use +those published to the [Hyperledger Package Repository under +aries-cloudagent-python]. + +[Hyperledger Package Repository under aries-cloudagent-python]: https://github.com/orgs/hyperledger/packages?repo_name=aries-cloudagent-python ## Breaking Changes From 4be5cf126baf97d15e9cefbc9863a402a5556772 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Thu, 9 Feb 2023 12:15:22 -0800 Subject: [PATCH 656/872] Updates based on feedback Signed-off-by: Stephen Curran --- CHANGELOG.md | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index be42e508bc..4949573e13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,16 +8,6 @@ that release will be finalized. Many of the PRs in this release were previously included in the `1.0.0-rc1` release. The categorized list of PRs separates those that are new from those in the `1.0.0-rc1` release candidate. -With this release, a new automated process publishes container images in the -Hyperledger container image repository. New images for the release are -automatically published by the GitHubAction Workflows: [publish.yml] and -[publish-indy.yml]. The actions are triggered when a release is tagged, so no -manual action is needed. The images are published in the [Hyperledger Package -Repository under aries-cloudagent-python] and a link to the packages added to -the repositories main page (under "Packages"). Additional information about the -container image publication process can be found in the document [Container -Images and Github Actions]. - There are not a lot of new Aries Framework features in this release, as the focus has been on cleanup and optimization. The biggest addition is the inclusion with ACA-Py of a universal resolver interface, allowing an instance to @@ -29,7 +19,19 @@ Endorsers. A new repo has been created that is a pre-configured instance of ACA-Py for use as an Endorser service. -The images are based on [Python 3.6 and 3.9 `slim-bullseye` +### Container Publishing Updated + +With this release, a new automated process publishes container images in the +Hyperledger container image repository. New images for the release are +automatically published by the GitHubAction Workflows: [publish.yml] and +[publish-indy.yml]. The actions are triggered when a release is tagged, so no +manual action is needed. The images are published in the [Hyperledger Package +Repository under aries-cloudagent-python] and a link to the packages added to +the repositories main page (under "Packages"). Additional information about the +container image publication process can be found in the document [Container +Images and Github Actions]. + +The ACA-Py container images are based on [Python 3.6 and 3.9 `slim-bullseye` images](https://hub.docker.com/_/python), and are built to support `linux/386 (x86)`, `linux/amd64 (x64)`, and `linux/arm64`. There are two flavors of image built for each Python version. One contains only the Indy/Aries Shared Libraries @@ -50,6 +52,10 @@ those published to the [Hyperledger Package Repository under aries-cloudagent-python]. [Hyperledger Package Repository under aries-cloudagent-python]: https://github.com/orgs/hyperledger/packages?repo_name=aries-cloudagent-python +[publish.yml]: https://github.com/hyperledger/aries-cloudagent-python/blob/main/.github/workflows/publish.yml +[publish-indy.yml]: https://github.com/hyperledger/aries-cloudagent-python/blob/main/.github/workflows/publish-indy.yml +[Container Images and Github Actions]: https://github.com/hyperledger/aries-cloudagent-python/blob/main/ContainerImagesAndGithubActions.md + ## Breaking Changes From 523249ea6b7949c449cb6e383239724ed845bae8 Mon Sep 17 00:00:00 2001 From: Victor Lee Date: Fri, 10 Feb 2023 11:17:56 -0500 Subject: [PATCH 657/872] refactored light weight webhook object for v1 & v2 cred exchange Signed-off-by: Victor Lee --- .../messages/credential_exchange_webhook.py | 45 +++++++++++++++++++ .../v1_0/models/credential_exchange.py | 6 ++- .../v2_0/messages/cred_ex_record_webhook.py} | 15 ++++--- .../v2_0/models/cred_ex_record.py | 4 +- 4 files changed, 61 insertions(+), 9 deletions(-) create mode 100644 aries_cloudagent/protocols/issue_credential/v1_0/messages/credential_exchange_webhook.py rename aries_cloudagent/{messaging/models/light_webhook.py => protocols/issue_credential/v2_0/messages/cred_ex_record_webhook.py} (71%) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/messages/credential_exchange_webhook.py b/aries_cloudagent/protocols/issue_credential/v1_0/messages/credential_exchange_webhook.py new file mode 100644 index 0000000000..16ecb66561 --- /dev/null +++ b/aries_cloudagent/protocols/issue_credential/v1_0/messages/credential_exchange_webhook.py @@ -0,0 +1,45 @@ +"""v1.0 credential exchange light weight webhook""" + +class LightWeightV10CredentialExchangeWebhook: + """Class representing a state only credential exchange webhook.""" + + __acceptable_keys_list = [ + "connection_id", + "credential_exchange_id", + "cred_ex_id", + "cred_def_id", + "role", + "initiator", + "revoc_reg_id", + "revocation_id", + "auto_offer", + "auto_issue", + "auto_remove", + "error_msg", + "thread_id", + "parent_thread_id", + "state", + "credential_definition_id", + "schema_id", + "credential_id", + "trace", + "public_did", + "cred_id_stored", + "conn_id", + ] + + def __init__( + self, + **kwargs, + ): + """ + Initialize webhook object from V10CredentialExchange + from a list of accepted attributes. + """ + [ + self.__setattr__(key, kwargs.get(key)) + for key in self.__acceptable_keys_list + if kwargs.get(key) is not None + ] + if kwargs.get("_id") is not None: + self.credential_exchange_id = kwargs.get("_id") diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py b/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py index 4ca0ee71a5..0daeb81e47 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py @@ -12,12 +12,14 @@ from .....indy.models.cred_precis import IndyCredInfo, IndyCredInfoSchema from .....indy.models.cred_request import IndyCredRequest, IndyCredRequestSchema from .....messaging.models.base_record import BaseExchangeRecord, BaseExchangeSchema -from .....messaging.models.light_webhook import LightWeightWebhook from .....messaging.valid import INDY_CRED_DEF_ID, INDY_SCHEMA_ID, UUIDFour from .....storage.base import StorageError from ..messages.credential_proposal import CredentialProposal, CredentialProposalSchema from ..messages.credential_offer import CredentialOffer, CredentialOfferSchema +from ..messages.credential_exchange_webhook import ( + LightWeightV10CredentialExchangeWebhook, +) from . import UNENCRYPTED_TAGS @@ -244,7 +246,7 @@ async def emit_event(self, session: ProfileSession, payload: Any = None): payload = self.serialize() if session.profile.settings.get("transport.light_weight_webhook"): - payload = LightWeightWebhook(1, **self.__dict__) + payload = LightWeightV10CredentialExchangeWebhook(**self.__dict__) payload = payload.__dict__ await session.profile.notify(topic, payload) diff --git a/aries_cloudagent/messaging/models/light_webhook.py b/aries_cloudagent/protocols/issue_credential/v2_0/messages/cred_ex_record_webhook.py similarity index 71% rename from aries_cloudagent/messaging/models/light_webhook.py rename to aries_cloudagent/protocols/issue_credential/v2_0/messages/cred_ex_record_webhook.py index e6c51f8884..8eb77669ae 100644 --- a/aries_cloudagent/messaging/models/light_webhook.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/messages/cred_ex_record_webhook.py @@ -1,4 +1,8 @@ -class LightWeightWebhook: +"""v2.0 credential exchange light weight webhook""" + +class LightWeightV20CredExRecordWebhook: + """Class representing a state only credential exchange webhook.""" + __acceptable_keys_list = [ "connection_id", "credential_exchange_id", @@ -26,15 +30,16 @@ class LightWeightWebhook: def __init__( self, - version, # 2 = V20CredExRecord ; 1 = V10CredentialExchange **kwargs, ): + """ + Initialize webhook object from V20CredExRecord + from a list of accepted attributes. + """ [ self.__setattr__(key, kwargs.get(key)) for key in self.__acceptable_keys_list if kwargs.get(key) is not None ] - if version == 2: + if kwargs.get("_id") is not None: self.cred_ex_id = kwargs.get("_id") - else: - self.credential_exchange_id = kwargs.get("_id") diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/models/cred_ex_record.py b/aries_cloudagent/protocols/issue_credential/v2_0/models/cred_ex_record.py index 208df8ff7e..da88600943 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/models/cred_ex_record.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/models/cred_ex_record.py @@ -8,7 +8,6 @@ from .....core.profile import ProfileSession from .....messaging.models.base_record import BaseExchangeRecord, BaseExchangeSchema -from .....messaging.models.light_webhook import LightWeightWebhook from .....messaging.valid import UUIDFour from .....storage.base import StorageError @@ -18,6 +17,7 @@ from ..messages.cred_offer import V20CredOffer, V20CredOfferSchema from ..messages.cred_request import V20CredRequest, V20CredRequestSchema from ..messages.inner.cred_preview import V20CredPreviewSchema +from ..messages.cred_ex_record_webhook import LightWeightV20CredExRecordWebhook from . import UNENCRYPTED_TAGS @@ -204,7 +204,7 @@ async def emit_event(self, session: ProfileSession, payload: Any = None): payload = self.serialize() if session.profile.settings.get("transport.light_weight_webhook"): - payload = LightWeightWebhook(2, **self.__dict__) + payload = LightWeightV20CredExRecordWebhook(**self.__dict__) payload = payload.__dict__ await session.profile.notify(topic, payload) From ab3f7f7ad25a782d7532336e10bf4e5d1ea27aaa Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Fri, 10 Feb 2023 12:52:14 -0800 Subject: [PATCH 658/872] Added 2112 to changelog Signed-off-by: Stephen Curran --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4949573e13..0af211ba2b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -139,6 +139,7 @@ is unlikely to impact any deployments. - Add seed command line parameter but use only if also an "allow insecure seed" parameter is set [\#1714](https://github.com/hyperledger/aries-cloudagent-python/pull/1714) ([DaevMithran](https://github.com/DaevMithran)) - Internal Aries framework data handling updates + - fix: resolver api schema inconsistency [\#2112](https://github.com/hyperledger/aries-cloudagent-python/pull/2112) ([TimoGlastra](https://github.com/chumbert)) - fix: return if return route but no response [\#1853](https://github.com/hyperledger/aries-cloudagent-python/pull/1853) ([TimoGlastra](https://github.com/TimoGlastra)) - Multi-ledger/Multi-tenant issues [\#2022](https://github.com/hyperledger/aries-cloudagent-python/pull/2022) ([ianco](https://github.com/ianco)) - fix: Correct typo in model -- required spelled incorrectly [\#2031](https://github.com/hyperledger/aries-cloudagent-python/pull/2031) ([swcurran](https://github.com/swcurran)) From b2d73559113cdffb0e2564ee51de03fd89dad233 Mon Sep 17 00:00:00 2001 From: Kim Ebert Date: Fri, 10 Feb 2023 16:58:50 -0700 Subject: [PATCH 659/872] feat: add state to tags and use in filter for multi-use invite Signed-off-by: Kim Ebert --- aries_cloudagent/connections/models/conn_record.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/connections/models/conn_record.py b/aries_cloudagent/connections/models/conn_record.py index e49be19648..f6332d90f3 100644 --- a/aries_cloudagent/connections/models/conn_record.py +++ b/aries_cloudagent/connections/models/conn_record.py @@ -174,6 +174,7 @@ def __eq__(self, other: Union[str, "ConnRecord.State"]) -> bool: "invitation_key", "their_public_did", "invitation_msg_id", + "state", "their_role", } @@ -322,7 +323,10 @@ async def retrieve_by_invitation_key( invitation_key: The key on the originating invitation initiator: Filter by the initiator value """ - tag_filter = {"invitation_key": invitation_key} + tag_filter = { + "invitation_key": invitation_key, + "state": cls.State.INVITATION.rfc160 + } post_filter = {"state": cls.State.INVITATION.rfc160} if their_role: From a235627bd39ab638fb2bc91ca239d8f021049824 Mon Sep 17 00:00:00 2001 From: Wade Barnes Date: Sat, 11 Feb 2023 05:16:12 -0800 Subject: [PATCH 660/872] Fix publish workflows - Trigger when releases and prereleases are published. Signed-off-by: Wade Barnes --- .github/workflows/publish-indy.yml | 2 +- .github/workflows/publish.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish-indy.yml b/.github/workflows/publish-indy.yml index 874851d5c9..44000535c6 100644 --- a/.github/workflows/publish-indy.yml +++ b/.github/workflows/publish-indy.yml @@ -2,7 +2,7 @@ name: Publish ACA-Py Image (Indy) run-name: Publish ACA-Py ${{ inputs.tag || github.event.release.tag_name }} Image (Indy ${{ inputs.indy_version || '1.16.0' }}) on: release: - types: [released] + types: [published] workflow_dispatch: inputs: diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 6ee9378c61..f9ecc4ebe3 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -2,7 +2,7 @@ name: Publish ACA-Py Image run-name: Publish ACA-Py ${{ inputs.tag || github.event.release.tag_name }} Image on: release: - types: [released] + types: [published] workflow_dispatch: inputs: From ed6a67fa370990773d12f92e4029a8f32afda3bf Mon Sep 17 00:00:00 2001 From: "ram.challa" Date: Sun, 12 Feb 2023 17:35:44 -0500 Subject: [PATCH 661/872] test cases Signed-off-by: ram.challa --- .../revocation/tests/test_routes.py | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/aries_cloudagent/revocation/tests/test_routes.py b/aries_cloudagent/revocation/tests/test_routes.py index 34cb11d323..481175de6c 100644 --- a/aries_cloudagent/revocation/tests/test_routes.py +++ b/aries_cloudagent/revocation/tests/test_routes.py @@ -1,3 +1,7 @@ +import os +import shutil +import unittest + from aiohttp.web import HTTPBadRequest, HTTPNotFound from asynctest import TestCase as AsyncTestCase from asynctest import mock as async_mock @@ -881,6 +885,34 @@ async def test_set_rev_reg_state_not_found(self): result = await test_module.set_rev_reg_state(self.request) mock_json_response.assert_not_called() + # async def test_delete_tail(self): + # CRED_DEF_ID = f"{self.test_did}:3:CL:1234:default" + # REV_REG_ID = "{}:4:{}:3:CL:1234:default:CL_ACCUM:default".format( + # self.test_did, self.test_did + # ) + # self.request.query = {"cred_def_id": CRED_DEF_ID, + # "rev_reg_id": REV_REG_ID} + # self.request.json = async_mock.CoroutineMock( + # return_value={ + # "message": "All files deleted successfully" + # } + # ) + # + # with async_mock.patch.object( + # test_module, "IndyRevocation", autospec=True + # ) as mock_indy_revoc, async_mock.patch.object( + # test_module.web, "json_response", async_mock.Mock() + # ) as mock_json_response: + # mock_indy_revoc.return_value = async_mock.MagicMock( + # get_issuer_rev_reg_record=async_mock.CoroutineMock( + # tails_local_path=f"/tmp/tails/{REV_REG_ID}", + # return_value={"dummy": "dummy"}) + # ) + # + # result = await test_module.delete_tails(self.request) + # mock_json_response.assert_called_once_with({"message": "All files deleted successfully"}) + # assert result is mock_json_response.return_value + async def test_register(self): mock_app = async_mock.MagicMock() mock_app.add_routes = async_mock.MagicMock() @@ -906,3 +938,55 @@ async def test_post_process_routes(self): ]["get"]["responses"]["200"]["schema"] == {"type": "string", "format": "binary"} assert "tags" in mock_app._state["swagger_dict"] + +class TestDeleteTails(unittest.TestCase): + def setUp(self): + self.rev_reg_id = "rev_reg_id_123" + self.cred_def_id = "cred_def_id_456" + + self.main_dir_rev = "path/to/main/dir/rev" + self.tails_path = os.path.join(self.main_dir_rev, "tails.txt") + os.makedirs(self.main_dir_rev) + open(self.tails_path, "w").close() + + async def test_delete_tails_by_rev_reg_id(self): + # Setup + rev_reg_id = self.rev_reg_id + + # Test + result = await test_module.delete_tails({"context": None, "query": {"rev_reg_id": rev_reg_id}}) + + # Assert + self.assertEqual(result, {"message": "All files deleted successfully"}) + self.assertFalse(os.path.exists(self.main_dir_rev)) + + async def test_delete_tails_by_cred_def_id(self): + # Setup + cred_def_id = self.cred_def_id + main_dir_cred = "path/to/main/dir/cred" + os.makedirs(main_dir_cred) + cred_dir = os.path.join(main_dir_cred, cred_def_id) + os.makedirs(cred_dir) + + # Test + result = await test_module.delete_tails({"context": None, "query": {"cred_def_id": cred_def_id}}) + + # Assert + self.assertEqual(result, {"message": "All files deleted successfully"}) + self.assertFalse(os.path.exists(cred_dir)) + self.assertTrue(os.path.exists(main_dir_cred)) + + async def test_delete_tails_not_found(self): + # Setup + cred_def_id = "invalid_cred_def_id" + + # Test + result = await test_module.delete_tails({"context": None, "query": {"cred_def_id": cred_def_id}}) + + # Assert + self.assertEqual(result, {"message": "No such file or directory"}) + self.assertTrue(os.path.exists(self.main_dir_rev)) + + async def tearDown(self): + if os.path.exists(self.main_dir_rev): + shutil.rmtree(self.main_dir_rev) \ No newline at end of file From 50e6a3ede0bfae9823459c6fe19b491dbda8ca98 Mon Sep 17 00:00:00 2001 From: "ram.challa" Date: Sun, 12 Feb 2023 17:41:49 -0500 Subject: [PATCH 662/872] test_routes.py Signed-off-by: ram.challa --- .../revocation/tests/test_routes.py | 27 ------------------- 1 file changed, 27 deletions(-) diff --git a/aries_cloudagent/revocation/tests/test_routes.py b/aries_cloudagent/revocation/tests/test_routes.py index 481175de6c..2b2d3d0f6b 100644 --- a/aries_cloudagent/revocation/tests/test_routes.py +++ b/aries_cloudagent/revocation/tests/test_routes.py @@ -885,33 +885,6 @@ async def test_set_rev_reg_state_not_found(self): result = await test_module.set_rev_reg_state(self.request) mock_json_response.assert_not_called() - # async def test_delete_tail(self): - # CRED_DEF_ID = f"{self.test_did}:3:CL:1234:default" - # REV_REG_ID = "{}:4:{}:3:CL:1234:default:CL_ACCUM:default".format( - # self.test_did, self.test_did - # ) - # self.request.query = {"cred_def_id": CRED_DEF_ID, - # "rev_reg_id": REV_REG_ID} - # self.request.json = async_mock.CoroutineMock( - # return_value={ - # "message": "All files deleted successfully" - # } - # ) - # - # with async_mock.patch.object( - # test_module, "IndyRevocation", autospec=True - # ) as mock_indy_revoc, async_mock.patch.object( - # test_module.web, "json_response", async_mock.Mock() - # ) as mock_json_response: - # mock_indy_revoc.return_value = async_mock.MagicMock( - # get_issuer_rev_reg_record=async_mock.CoroutineMock( - # tails_local_path=f"/tmp/tails/{REV_REG_ID}", - # return_value={"dummy": "dummy"}) - # ) - # - # result = await test_module.delete_tails(self.request) - # mock_json_response.assert_called_once_with({"message": "All files deleted successfully"}) - # assert result is mock_json_response.return_value async def test_register(self): mock_app = async_mock.MagicMock() From 4afe7d0c15201d8f4d54bb985faf1b3f91c2ff23 Mon Sep 17 00:00:00 2001 From: Victor Lee Date: Tue, 14 Feb 2023 14:25:13 -0500 Subject: [PATCH 663/872] fixed FLAKE8 on webhook object Signed-off-by: Victor Lee --- .../v1_0/messages/credential_exchange_webhook.py | 6 ++++-- .../v2_0/messages/cred_ex_record_webhook.py | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/messages/credential_exchange_webhook.py b/aries_cloudagent/protocols/issue_credential/v1_0/messages/credential_exchange_webhook.py index 16ecb66561..87a20b0322 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/messages/credential_exchange_webhook.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/messages/credential_exchange_webhook.py @@ -1,4 +1,5 @@ -"""v1.0 credential exchange light weight webhook""" +"""v1.0 credential exchange light weight webhook.""" + class LightWeightV10CredentialExchangeWebhook: """Class representing a state only credential exchange webhook.""" @@ -33,7 +34,8 @@ def __init__( **kwargs, ): """ - Initialize webhook object from V10CredentialExchange + Initialize webhook object from V10CredentialExchange. + from a list of accepted attributes. """ [ diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/messages/cred_ex_record_webhook.py b/aries_cloudagent/protocols/issue_credential/v2_0/messages/cred_ex_record_webhook.py index 8eb77669ae..65cf999b7e 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/messages/cred_ex_record_webhook.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/messages/cred_ex_record_webhook.py @@ -1,4 +1,5 @@ -"""v2.0 credential exchange light weight webhook""" +"""v2.0 credential exchange light weight webhook.""" + class LightWeightV20CredExRecordWebhook: """Class representing a state only credential exchange webhook.""" @@ -33,7 +34,8 @@ def __init__( **kwargs, ): """ - Initialize webhook object from V20CredExRecord + Initialize webhook object from V20CredExRecord. + from a list of accepted attributes. """ [ From 4eba244f4ad9b9e3fb79795e86f785d59f69b641 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Tue, 14 Feb 2023 14:27:59 -0800 Subject: [PATCH 664/872] Update some of the demo Readme and Endorser instructions Signed-off-by: Stephen Curran --- demo/Endorser.md | 12 ++++---- demo/README.md | 75 +++++++++++++++++++++++++++++------------------- 2 files changed, 53 insertions(+), 34 deletions(-) diff --git a/demo/Endorser.md b/demo/Endorser.md index 5b81d70d66..2bf2543c3b 100644 --- a/demo/Endorser.md +++ b/demo/Endorser.md @@ -2,12 +2,14 @@ There are two ways to run the alice/faber demo with endorser support enabled. - ## Run Faber as an Author, with a dedicated Endorser agent -This approach runs Faber as an un-privileged agent, and starts a dedicated Endorser sub-process to endorse Faber's transactions. +This approach runs Faber as an un-privileged agent, and starts a dedicated Endorser Agent in a sub-process (an instance of ACA-Py) to endorse Faber's transactions. + +Start a VON Network instance and a Tails server: -Start a VON Network and a Tails server. +- Following the [Building and Starting](https://github.com/bcgov/von-network/blob/main/docs/UsingVONNetwork.md#building-and-starting) section of the VON Network Tutorial to get ledger started. You can leave off the `--logs` option if you want to use the same terminal for running both VON Network and the Tails server. When you are finished with VON Network, follow the [Stopping And Removing a VON Network](https://github.com/bcgov/von-network/blob/main/docs/UsingVONNetwork.md#stopping-and-removing-a-von-network) instructions. +- Run an AnonCreds revocation registry tails server in order to support revocation by following the instructions in the [Alice gets a Phone](https://github.com/hyperledger/aries-cloudagent-python/blob/master/demo/AliceGetsAPhone.md#run-an-instance-of-indy-tails-server) demo. Start up Faber as Author (note the tails file size override, to allow testing of the revocation registry roll-over): @@ -15,7 +17,7 @@ Start up Faber as Author (note the tails file size override, to allow testing of TAILS_FILE_COUNT=5 ./run_demo faber --endorser-role author --revocation ``` -Start up Alcie as normal: +Start up Alice as normal: ```bash ./run_demo alice @@ -33,7 +35,7 @@ This approach sets up the endorser roles to allow manual testing using the agent - Faber runs as an Endorser (all of Faber's functions - issue credential, request proof, etc.) run normally, since Faber has ledger write access - Alice starts up with a DID aith Author privileges (no ledger write access) and Faber is setup as Alice's Endorser -Start a VON Network and a Tails server. +Start a VON Network and a Tails server using the instructions above. Start up Faber as Endorser: diff --git a/demo/README.md b/demo/README.md index 15128bdcb9..a8c4a63573 100644 --- a/demo/README.md +++ b/demo/README.md @@ -4,13 +4,12 @@ There are several demos available for ACA-Py mostly (but not only) aimed at deve ## Table of Contents -- [The IIWBook Demo](#the-iiwbook-demo) - [The Alice/Faber Python demo](#the-alicefaber-python-demo) - [Running in a Browser](#running-in-a-browser) - [Running in Docker](#running-in-docker) - [Running Locally](#running-locally) - [Installing Prerequisites](#installing-prerequisites) - - [Start a local indy ledger](#start-a-local-indy-ledger) + - [Start a local Indy ledger](#start-a-local-indy-ledger) - [Genesis File handling](#genesis-file-handling) - [Run a local Postgres instance](#run-a-local-postgres-instance) - [Optional: Run a von-network ledger browser](#optional-run-a-von-network-ledger-browser) @@ -20,21 +19,18 @@ There are several demos available for ACA-Py mostly (but not only) aimed at deve - [Issuing and Proving Credentials](#issuing-and-proving-credentials) - [Additional Options in the Alice/Faber demo](#additional-options-in-the-alicefaber-demo) - [Revocation](#revocation) - - [Mediation](#mediation) - - [Multi-tenancy](#multi-tenancy) - - [Multi-ledger](#multi-ledger) - [DID Exchange](#did-exchange) - [Endorser](#endorser) - [Run Indy-SDK Backend](#run-indy-sdk-backend) + - [Mediation](#mediation) + - [Multi-ledger](#multi-ledger) + - [Multi-tenancy](#multi-tenancy) + - [Multi-tenancy *with Mediation*!!!](#multi-tenancy-with-mediation) - [Learning about the Alice/Faber code](#learning-about-the-alicefaber-code) - [OpenAPI (Swagger) Demo](#openapi-swagger-demo) - [Performance Demo](#performance-demo) - [Coding Challenge: Adding ACME](#coding-challenge-adding-acme) -## The IIWBook Demo - -The IIWBook demo is a real (play) self-sovereign identity demonstration. During the demo, you will get a mobile agent (sorry - IOS only right now), and use that agent to connect with several enterprise services to collect and prove credentials. The two services in the demo (the [email verification service](https://github.com/bcgov/indy-email-verification) and [IIWBook](https://github.com/bcgov/iiwbook)) are both instances of ACA-Py, and all the agents are using DIDComm to communicate. Learn about and run the demo at [https://vonx.io/how_to/iiwbook](https://vonx.io/how_to/iiwbook). Developers, when you are ready, check out the code in the repos of the two services to see how they implement Django web server-based controller and agent. - ## The Alice/Faber Python demo The Alice/Faber demo is the (in)famous first verifiable credentials demo. Alice, a former student of Faber College ("Knowledge is Good"), connects with the College, is issued a credential about her degree and then is asked by the College for a proof. There are a variety of ways of running the demo. The easiest is in your browser using a site ("Play with VON") that let's you run docker containers without installing anything. Alternatively, you can run locally on docker (our recommendation), or using python on your local machine. Each approach is covered below. @@ -63,19 +59,20 @@ Jump to the [Follow the Script](#follow-the-script) section below for further in ### Running in Docker -Running the demo in docker requires having a `von-network` (a Hyperledger Indy public ledger sandbox) instance running in docker locally. See the [Running the Network Locally](https://github.com/bcgov/von-network#running-the-network-locally) section of the `von-network` readme file for more info. +Running the demo in docker requires having a `von-network` (a Hyperledger Indy public ledger sandbox) instance running in docker locally. See the [VON Network Tutorial](https://github.com/bcgov/von-network/blob/main/docs/UsingVONNetwork.md) for guidance +on starting and stopping your own local Hyperledger Indy instance. Open three `bash` shells. For Windows users, `git-bash` is highly recommended. bash is the default shell in Linux and Mac terminal sessions. -In the first terminal window, start `von-network` by following the [Running the Network Locally](https://github.com/bcgov/von-network#running-the-network-locally) instructions. +In the first terminal window, start `von-network` by following the [Building and Starting](https://github.com/bcgov/von-network/blob/main/docs/UsingVONNetwork.md#building-and-starting) instructions. -In the second terminal, change directory into `demo` directory of your clone of this repository. Start the `faber` agent by issuing the following command: +In the second terminal, change directory into `demo` directory of your clone of the Aries Cloud Agent Python repository. Start the `faber` agent by issuing the following command: ``` bash ./run_demo faber ``` -In the third terminal, change directory into `demo` directory of your clone of this repository. Start the `alice` agent by issuing the following command: +In the third terminal, change directory into `demo` directory of your clone of the Aries Cloud Agent Python repository. Start the `alice` agent by issuing the following command: ``` bash ./run_demo alice @@ -87,6 +84,8 @@ Jump to the [Follow the Script](#follow-the-script) section below for further in The following is an approach to to running the Alice and Faber demo using Python3 running on a bare machine. There are other ways to run the components, but this covers the general approach. +We don't recommend this approach if you are just trying this demo, as you will likely run into issues with the specific setup of your machine. + #### Installing Prerequisites We assume you have a running Python 3 environment. To install the prerequisites specific to running the agent/controller examples in your Python environment, run the following command from this repo's `demo` folder. The precise command to run may vary based on your Python environment setup. @@ -97,12 +96,16 @@ pip3 install -r demo/requirements.txt While that process will include the installation of the Indy python prerequisite, you still have to build and install the `libindy` code for your platform. Follow the [installation instructions](https://github.com/hyperledger/indy-sdk#installing-the-sdk) in the indy-sdk repo for your platform. -#### Start a local indy ledger +#### Start a local Indy ledger -Use instructions in the [indy-sdk repo](https://github.com/hyperledger/indy-sdk#how-to-start-local-nodes-pool-with-docker) to run a local ledger. +Start a local `von-network` Hyperledger Indy network running in Docker by following the VON Network [Building and Starting](https://github.com/bcgov/von-network/blob/main/docs/UsingVONNetwork.md#building-and-starting) instructions. + +We strongly recommend you use Docker for the local Indy network until you really, really need to know the details of running an Indy Node instance on a bare machine. #### Genesis File handling +> Assuming you followed our advice and are using a VON Network instance of Hyperledger Indy, you can ignore this section. If you started the Indy ledger **without** using VON Network, this information might be helpful. + An Aries agent (or other client) connecting to an Indy ledger must know the contents of the `genesis` file for the ledger. The genesis file lets the agent/client know the IP addresses of the initial nodes of the ledger, and the agent/client sends ledger requests to those IP addresses. When using the `indy-sdk` ledger, look for the instructions in that repo for how to find/update the ledger genesis file, and note the path to that file on your local system. The envrionment variable `GENESIS_FILE` is used to let the Aries demo agents know the location of the genesis file. Use the path to that file as value of the `GENESIS_FILE` environment variable in the instructions below. You might want to copy that file to be local to the demo so the path is shorter. @@ -117,7 +120,9 @@ docker run --name some-postgres -e POSTGRES_PASSWORD=mysecretpassword -d -p 5432 #### Optional: Run a von-network ledger browser -If you want to be able to browse your local ledger as you run the demo, clone the [von-network](https://github.com/bcgov/von-network) repo, go into the root of the cloned instance and run the following command, replacing the `/path/to/local-genesis.txt` with a path to the same genesis file as was used in starting the ledger. +If you followed our advice and are using a VON Network instance of Hyperledger Indy, you can ignore this section, as you already have a Ledger browser running, accessible on http://localhost:9000. + + If you started the Indy ledger **without** using VON Network, and you want to be able to browse your local ledger as you run the demo, clone the [von-network](https://github.com/bcgov/von-network) repo, go into the root of the cloned instance and run the following command, replacing the `/path/to/local-genesis.txt` with a path to the same genesis file as was used in starting the ledger. ``` bash GENESIS_FILE=/path/to/local-genesis.txt PORT=9000 REGISTER_NEW_DIDS=true python -m server.server @@ -125,7 +130,19 @@ GENESIS_FILE=/path/to/local-genesis.txt PORT=9000 REGISTER_NEW_DIDS=true python #### Run the Alice and Faber Controllers/Agents -With the rest of the pieces running, you can run the Alice and Faber controllers and agents. To do so, `cd` into the `demo` folder your clone of this repo in two terminal windows and run the following, replacing the `/path/to/local-genesis.txt`. +With the rest of the pieces running, you can run the Alice and Faber controllers and agents. To do so, `cd` into the `demo` folder your clone of this repo in two terminal windows. + +If you are using a VON Network instance of Hyperledger, run the following commands: + +``` bash +DEFAULT_POSTGRES=true python3 -m runners.faber --port 8020 +``` + +``` bash +DEFAULT_POSTGRES=true python3 -m runners.alice --port 8030 +``` + +If you started the Indy ledger **without** using VON Network, use the following commands, replacing the `/path/to/local-genesis.txt` with the one for your configuration. ``` bash GENESIS_FILE=/path/to/local-genesis.txt DEFAULT_POSTGRES=true python3 -m runners.faber --port 8020 @@ -135,7 +152,7 @@ GENESIS_FILE=/path/to/local-genesis.txt DEFAULT_POSTGRES=true python3 -m runners GENESIS_FILE=/path/to/local-genesis.txt DEFAULT_POSTGRES=true python3 -m runners.alice --port 8030 ``` -Note that Alice and Faber will each use 5 ports, e.g. using the parameter `... --port 8020` actually uses ports 8020 through 8024. Feel free to use different ports if you want. +Note that Alice and Faber will each use 5 ports, e.g., using the parameter `... --port 8020` actually uses ports 8020 through 8024. Feel free to use different ports if you want. Everything running? See the [Follow the Script](#follow-the-script) section below for further instructions. @@ -201,7 +218,7 @@ To enable support for revoking credentials, run the `faber` demo with the `--rev Note that you don't specify this option with `alice` because it's only applicable for the credential `issuer` (who has to enable revocation when creating a credential definition, and explicitely revoke credentials as appropriate; alice doesn't have to do anything special when revocation is enabled). -You need to run a revocation registry in order to support revocation - the details are described in the [Alice gets a Phone](https://github.com/hyperledger/aries-cloudagent-python/blob/master/demo/AliceGetsAPhone.md#run-an-instance-of-indy-tails-server) demo instructions. +You need to run an AnonCreds revocation registry tails server in order to support revocation - the details are described in the [Alice gets a Phone](https://github.com/hyperledger/aries-cloudagent-python/blob/master/demo/AliceGetsAPhone.md#run-an-instance-of-indy-tails-server) demo instructions. Faber will setup support for revocation automatically, and you will see an extra option in faber's menu to revoke a credential: @@ -249,7 +266,7 @@ This is described in [Endorser.md](Endorser.md) ### Run Indy-SDK Backend -This runs using the indy-sdk libraries instead of askar: +This runs using the older (and not recommended) indy-sdk libraries instead of [Aries Askar](:uhttps://github.com/hyperledger/aries-ask): ```bash ./run_demo faber --wallet-type indy @@ -263,27 +280,27 @@ To enable mediation, run the `alice` or `faber` demo with the `--mediation` opti ./run_demo faber --mediation ``` -This will start up a second "mediator" agent and automatically set the alice/faber connection to use the mediator. +This will start up a "mediator" agent with Alice or Faber and automatically set the alice/faber connection to use the mediator. -### Multi-tenancy +### Multi-ledger -To enable support for multi-tenancy, run the `alice` or `faber` demo with the `--multitenant` option: +To enable multiple ledger mode, run the `alice` or `faber` demo with the `--multi-ledger` option: ```bash -./run_demo faber --multitenant +./run_demo faber --multi-ledger ``` -(This option can be used with both (or either) `alice` and/or `faber`.) +The configuration file for setting up multiple ledgers (for the demo) can be found at `./demo/multiple_ledger_config.yml`. -### Multi-ledger +### Multi-tenancy -To enable multiple ledger mode, run the `alice` or `faber` demo with the `--multi-ledger` option: +To enable support for multi-tenancy, run the `alice` or `faber` demo with the `--multitenant` option: ```bash -./run_demo faber --multi-ledger +./run_demo faber --multitenant ``` -The configuration file for setting up multiple ledgers (for the demo) can be found at `./demo/multiple_ledger_config.yml`. +(This option can be used with both (or either) `alice` and/or `faber`.) You will see an additional menu option to create new sub-wallets (or they can be considered to be "virtual agents"). From 3d60b9b444a92b35743b20d919f669fda044bf2c Mon Sep 17 00:00:00 2001 From: Wade Barnes Date: Tue, 14 Feb 2023 15:02:42 -0800 Subject: [PATCH 665/872] Fix ACA-py image builds - Ensure the final images install ACA-py from the wheel rather than from PyPI. Signed-off-by: Wade Barnes --- docker/Dockerfile | 6 +++++- docker/Dockerfile.indy | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index ecbffe5b24..39695f1775 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -87,7 +87,11 @@ RUN chmod -R ug+rw $HOME/log $HOME/ledger $HOME/.aries_cloudagent $HOME/.cache COPY --from=build /src/dist/aries_cloudagent*.whl . -RUN pip install --no-cache-dir --find-links=. aries_cloudagent${acapy_reqs} && rm aries_cloudagent*.whl +# Install ACA-py from the wheel. +RUN aries_cloudagent_package=$(find ./ -name "aries_cloudagent*.whl" | head -n 1) && \ + echo "Installing ${aries_cloudagent_package} ..." && \ + pip install --no-cache-dir --find-links=. ${aries_cloudagent_package}${acapy_reqs} && \ + rm aries_cloudagent*.whl # Clean-up unneccessary build dependencies and reduce final image size RUN apt-get purge -y --auto-remove build-essential diff --git a/docker/Dockerfile.indy b/docker/Dockerfile.indy index e4c4959cd4..1c5e1ae3da 100644 --- a/docker/Dockerfile.indy +++ b/docker/Dockerfile.indy @@ -256,7 +256,11 @@ RUN chmod -R ug+rw $HOME/.aries_cloudagent COPY --from=acapy-builder /src/dist/aries_cloudagent*.whl . -RUN pip install --no-cache-dir --find-links=. aries_cloudagent${acapy_reqs} && rm aries_cloudagent*.whl +# Install ACA-py from the wheel. +RUN aries_cloudagent_package=$(find ./ -name "aries_cloudagent*.whl" | head -n 1) && \ + echo "Installing ${aries_cloudagent_package} ..." && \ + pip install --no-cache-dir --find-links=. ${aries_cloudagent_package}${acapy_reqs} && \ + rm aries_cloudagent*.whl # Clean-up unneccessary build dependencies and reduce final image size # RUN apt-get purge -y --auto-remove build-essential From 3eba90987918bc623cc31e83a160d78b9cb00272 Mon Sep 17 00:00:00 2001 From: pradeepp88 Date: Wed, 15 Feb 2023 16:40:49 +0000 Subject: [PATCH 666/872] Fixing formatting issues and unit tests Signed-off-by: pradeepp88 --- aries_cloudagent/revocation/routes.py | 141 +++++++++++++----- .../revocation/tests/test_routes.py | 23 ++- 2 files changed, 121 insertions(+), 43 deletions(-) diff --git a/aries_cloudagent/revocation/routes.py b/aries_cloudagent/revocation/routes.py index 3be671b567..b637757b43 100644 --- a/aries_cloudagent/revocation/routes.py +++ b/aries_cloudagent/revocation/routes.py @@ -101,7 +101,9 @@ class TxnOrRevRegResultSchema(OpenAPISchema): """Result schema for credential definition send request.""" sent = fields.Nested( - RevRegResultSchema(), required=False, definition="Content sent" + RevRegResultSchema(), + required=False, + definition="Content sent", ) txn = fields.Nested( TransactionRecordSchema(), @@ -130,7 +132,9 @@ def validate_fields(self, data, **kwargs): ) rev_reg_id = fields.Str( - description="Revocation registry identifier", required=False, **INDY_REV_REG_ID + description="Revocation registry identifier", + required=False, + **INDY_REV_REG_ID, ) cred_rev_id = fields.Str( description="Credential revocation identifier", @@ -138,7 +142,9 @@ def validate_fields(self, data, **kwargs): **INDY_CRED_REV_ID, ) cred_ex_id = fields.Str( - description="Credential exchange identifier", required=False, **UUID4 + description="Credential exchange identifier", + required=False, + **UUID4, ) @@ -147,13 +153,18 @@ class RevRegId(OpenAPISchema): @validates_schema def validate_fields(self, data, **kwargs): - """Validate schema fields - must have (rr-id and cr-id) xor cx-id.""" + """Validate schema fields - must have either rr-id or cr-id.""" rev_reg_id = data.get("rev_reg_id") cred_def_id = data.get("cred_def_id") + if not (rev_reg_id or cred_def_id): + raise ValidationError("Request must have either rev_reg_id or cred_def_id") + rev_reg_id = fields.Str( - description="Revocation registry identifier", required=False, **INDY_REV_REG_ID + description="Revocation registry identifier", + required=False, + **INDY_REV_REG_ID, ) cred_def_id = fields.Str( description="Credential definition identifier", @@ -191,7 +202,8 @@ def validate_fields(self, data, **kwargs): required=False, ) notify = fields.Boolean( - description="Send a notification to the credential recipient", required=False + description="Send a notification to the credential recipient", + required=False, ) notify_version = fields.String( description="Specify which version of the revocation notification should be sent", @@ -238,7 +250,9 @@ class TxnOrPublishRevocationsResultSchema(OpenAPISchema): """Result schema for credential definition send request.""" sent = fields.Nested( - PublishRevocationsSchema(), required=False, definition="Content sent" + PublishRevocationsSchema(), + required=False, + definition="Content sent", ) txn = fields.Nested( TransactionRecordSchema(), @@ -280,7 +294,9 @@ class CredRevRecordDetailsResultSchema(OpenAPISchema): class CredRevIndyRecordsResultSchema(OpenAPISchema): """Result schema for revoc reg delta.""" - rev_reg_delta = fields.Dict(description="Indy revocation registry delta") + rev_reg_delta = fields.Dict( + description="Indy revocation registry delta", + ) class RevRegIssuedResultSchema(OpenAPISchema): @@ -297,19 +313,22 @@ class RevRegUpdateRequestMatchInfoSchema(OpenAPISchema): """Path parameters and validators for request taking rev reg id.""" apply_ledger_update = fields.Bool( - description="Apply updated accumulator transaction to ledger", required=True + description="Apply updated accumulator transaction to ledger", + required=True, ) class RevRegWalletUpdatedResultSchema(OpenAPISchema): """Number of wallet revocation entries status updated.""" - rev_reg_delta = fields.Dict(description="Indy revocation registry delta") + rev_reg_delta = fields.Dict( + description="Indy revocation registry delta", + ) accum_calculated = fields.Dict( - description="Calculated accumulator for phantom revocations" + description="Calculated accumulator for phantom revocations", ) accum_fixed = fields.Dict( - description="Applied ledger transaction to fix revocations" + description="Applied ledger transaction to fix revocations", ) @@ -375,7 +394,9 @@ class RevRegIdMatchInfoSchema(OpenAPISchema): """Path parameters and validators for request taking rev reg id.""" rev_reg_id = fields.Str( - description="Revocation Registry identifier", required=True, **INDY_REV_REG_ID + description="Revocation Registry identifier", + required=True, + **INDY_REV_REG_ID, ) @@ -393,7 +414,8 @@ class CreateRevRegTxnForEndorserOptionSchema(OpenAPISchema): """Class for user to input whether to create a transaction for endorser or not.""" create_transaction_for_endorser = fields.Boolean( - description="Create Transaction For Endorser's signature", required=False + description="Create Transaction For Endorser's signature", + required=False, ) @@ -405,7 +427,10 @@ class RevRegConnIdMatchInfoSchema(OpenAPISchema): ) -@docs(tags=["revocation"], summary="Revoke an issued credential") +@docs( + tags=["revocation"], + summary="Revoke an issued credential", +) @request_schema(RevokeRequestSchema()) @response_schema(RevocationModuleResponseSchema(), description="") async def revoke(request: web.BaseRequest): @@ -477,7 +502,9 @@ async def publish_revocations(request: web.BaseRequest): rev_manager = RevocationManager(context.profile) try: - rev_reg_resp = await rev_manager.publish_pending_revocations(rrid2crid) + rev_reg_resp = await rev_manager.publish_pending_revocations( + rrid2crid, + ) except (RevocationError, StorageError, IndyIssuerError, LedgerError) as err: raise web.HTTPBadRequest(reason=err.roll_up) from err @@ -548,7 +575,9 @@ async def create_rev_reg(request: web.BaseRequest): try: revoc = IndyRevocation(profile) issuer_rev_reg_rec = await revoc.init_issuer_registry( - credential_definition_id, max_cred_num=max_cred_num, notify=False + credential_definition_id, + max_cred_num=max_cred_num, + notify=False, ) except RevocationNotSupportedError as e: raise web.HTTPBadRequest(reason=e.message) from e @@ -598,7 +627,10 @@ async def rev_regs_created(request: web.BaseRequest): ) -@docs(tags=["revocation"], summary="Get revocation registry by revocation registry id") +@docs( + tags=["revocation"], + summary="Get revocation registry by revocation registry id", +) @match_info_schema(RevRegIdMatchInfoSchema()) @response_schema(RevRegResultSchema(), 200, description="") async def get_rev_reg(request: web.BaseRequest): @@ -693,7 +725,10 @@ async def get_rev_reg_issued(request: web.BaseRequest): return web.json_response(results) -@docs(tags=["revocation"], summary="Get details of revoked credentials from ledger") +@docs( + tags=["revocation"], + summary="Get details of revoked credentials from ledger", +) @match_info_schema(RevRegIdMatchInfoSchema()) @response_schema(CredRevIndyRecordsResultSchema(), 200, description="") async def get_rev_reg_indy_recs(request: web.BaseRequest): @@ -714,7 +749,11 @@ async def get_rev_reg_indy_recs(request: web.BaseRequest): revoc = IndyRevocation(context.profile) rev_reg_delta = await revoc.get_issuer_rev_reg_delta(rev_reg_id) - return web.json_response({"rev_reg_delta": rev_reg_delta}) + return web.json_response( + { + "rev_reg_delta": rev_reg_delta, + } + ) @docs( @@ -805,7 +844,10 @@ async def update_rev_reg_revoked_state(request: web.BaseRequest): ) -@docs(tags=["revocation"], summary="Get credential revocation status") +@docs( + tags=["revocation"], + summary="Get credential revocation status", +) @querystring_schema(CredRevRecordQueryStringSchema()) @response_schema(CredRevRecordResultSchema(), 200, description="") async def get_cred_rev_record(request: web.BaseRequest): @@ -902,7 +944,10 @@ async def get_tails_file(request: web.BaseRequest) -> web.FileResponse: return web.FileResponse(path=rev_reg.tails_local_path, status=200) -@docs(tags=["revocation"], summary="Upload local tails file to server") +@docs( + tags=["revocation"], + summary="Upload local tails file to server", +) @match_info_schema(RevRegIdMatchInfoSchema()) @response_schema(RevocationModuleResponseSchema(), description="") async def upload_tails_file(request: web.BaseRequest): @@ -933,7 +978,10 @@ async def upload_tails_file(request: web.BaseRequest): return web.json_response({}) -@docs(tags=["revocation"], summary="Send revocation registry definition to ledger") +@docs( + tags=["revocation"], + summary="Send revocation registry definition to ledger", +) @match_info_schema(RevRegIdMatchInfoSchema()) @querystring_schema(CreateRevRegTxnForEndorserOptionSchema()) @querystring_schema(RevRegConnIdMatchInfoSchema()) @@ -1003,7 +1051,9 @@ async def send_rev_reg_def(request: web.BaseRequest): rev_reg = await revoc.get_issuer_rev_reg_record(rev_reg_id) rev_reg_resp = await rev_reg.send_def( - profile, write_ledger=write_ledger, endorser_did=endorser_did + profile, + write_ledger=write_ledger, + endorser_did=endorser_did, ) LOGGER.debug("published rev reg definition: %s", rev_reg_id) except StorageNotFoundError as err: @@ -1043,7 +1093,10 @@ async def send_rev_reg_def(request: web.BaseRequest): return web.json_response({"txn": transaction.serialize()}) -@docs(tags=["revocation"], summary="Send revocation registry entry to ledger") +@docs( + tags=["revocation"], + summary="Send revocation registry entry to ledger", +) @match_info_schema(RevRegIdMatchInfoSchema()) @querystring_schema(CreateRevRegTxnForEndorserOptionSchema()) @querystring_schema(RevRegConnIdMatchInfoSchema()) @@ -1111,7 +1164,9 @@ async def send_rev_reg_entry(request: web.BaseRequest): revoc = IndyRevocation(profile) rev_reg = await revoc.get_issuer_rev_reg_record(rev_reg_id) rev_entry_resp = await rev_reg.send_entry( - profile, write_ledger=write_ledger, endorser_did=endorser_did + profile, + write_ledger=write_ledger, + endorser_did=endorser_did, ) LOGGER.debug("published registry entry: %s", rev_reg_id) @@ -1127,7 +1182,8 @@ async def send_rev_reg_entry(request: web.BaseRequest): transaction_mgr = TransactionManager(profile) try: transaction = await transaction_mgr.create_record( - messages_attach=rev_entry_resp["result"], connection_id=connection_id + messages_attach=rev_entry_resp["result"], + connection_id=connection_id, ) except StorageError as err: raise web.HTTPBadRequest(reason=err.roll_up) from err @@ -1268,7 +1324,9 @@ async def generate(rr_record: IssuerRevRegRecord) -> dict: public_uri = tails_base_url.rstrip("/") + f"/{registry_record.revoc_reg_id}" await rr_record.set_tails_file_public_uri(profile, public_uri) rev_reg_resp = await rr_record.send_def( - profile, write_ledger=write_ledger, endorser_did=endorser_did + profile, + write_ledger=write_ledger, + endorser_did=endorser_did, ) if write_ledger: # Upload the tails file @@ -1305,7 +1363,8 @@ async def generate(rr_record: IssuerRevRegRecord) -> dict: responder = profile.inject_or(BaseResponder) if responder: await responder.send( - revo_transaction_request, connection_id=connection.connection_id + revo_transaction_request, + connection_id=connection.connection_id, ) else: LOGGER.warning( @@ -1352,7 +1411,9 @@ async def on_revocation_entry_event(profile: Profile, event: Event): async with profile.session() as session: registry_record = await IssuerRevRegRecord.retrieve_by_id(session, record_id) rev_entry_resp = await registry_record.send_entry( - profile, write_ledger=write_ledger, endorser_did=endorser_did + profile, + write_ledger=write_ledger, + endorser_did=endorser_did, ) if not write_ledger: @@ -1384,7 +1445,8 @@ async def on_revocation_entry_event(profile: Profile, event: Event): responder = profile.inject_or(BaseResponder) if responder: await responder.send( - revo_transaction_request, connection_id=connection.connection_id + revo_transaction_request, + connection_id=connection.connection_id, ) else: LOGGER.warning( @@ -1435,6 +1497,7 @@ async def on_revocation_registry_endorsed_event(profile: Profile, event: Event): @querystring_schema(RevRegId()) @docs(tags=["revocation"], summary="Delete the tail files") async def delete_tails(request: web.BaseRequest) -> json: + """Delete Tails Files.""" context: AdminRequestContext = request["context"] rev_reg_id = request.query.get("rev_reg_id") cred_def_id = request.query.get("cred_def_id") @@ -1463,7 +1526,8 @@ async def delete_tails(request: web.BaseRequest) -> json: try: flag = 0 for i in filenames: - if re.search(cred_def_id, i): + safe_cred_def_id = re.escape(cred_def_id) + if re.search(safe_cred_def_id, i): shutil.rmtree(main_dir_cred + "/" + i) flag = 1 if flag: @@ -1482,13 +1546,16 @@ async def register(app: web.Application): web.post("/revocation/revoke", revoke), web.post("/revocation/publish-revocations", publish_revocations), web.post( - "/revocation/clear-pending-revocations", clear_pending_revocations + "/revocation/clear-pending-revocations", + clear_pending_revocations, ), web.get( "/revocation/credential-record", get_cred_rev_record, allow_head=False ), web.get( - "/revocation/registries/created", rev_regs_created, allow_head=False + "/revocation/registries/created", + rev_regs_created, + allow_head=False, ), web.get("/revocation/registry/{rev_reg_id}", get_rev_reg, allow_head=False), web.get( @@ -1521,11 +1588,15 @@ async def register(app: web.Application): get_tails_file, allow_head=False, ), - web.patch("/revocation/registry/{rev_reg_id}/set-state", set_rev_reg_state), + web.patch( + "/revocation/registry/{rev_reg_id}/set-state", + set_rev_reg_state, + ), web.put( "/revocation/registry/{rev_reg_id}/fix-revocation-entry-state", update_rev_reg_revoked_state, ), + web.delete("/revocation/registry/delete-tails-file", delete_tails), ] ) diff --git a/aries_cloudagent/revocation/tests/test_routes.py b/aries_cloudagent/revocation/tests/test_routes.py index 1f542cdb3b..9972d1b146 100644 --- a/aries_cloudagent/revocation/tests/test_routes.py +++ b/aries_cloudagent/revocation/tests/test_routes.py @@ -882,7 +882,6 @@ async def test_set_rev_reg_state_not_found(self): result = await test_module.set_rev_reg_state(self.request) mock_json_response.assert_not_called() - async def test_register(self): mock_app = async_mock.MagicMock() mock_app.add_routes = async_mock.MagicMock() @@ -909,14 +908,16 @@ async def test_post_process_routes(self): assert "tags" in mock_app._state["swagger_dict"] + class TestDeleteTails(unittest.TestCase): def setUp(self): self.rev_reg_id = "rev_reg_id_123" self.cred_def_id = "cred_def_id_456" self.main_dir_rev = "path/to/main/dir/rev" - self.tails_path = os.path.join(self.main_dir_rev, "tails.txt") - os.makedirs(self.main_dir_rev) + self.tails_path = os.path.join(self.main_dir_rev, "tails") + if not (os.path.exists(self.main_dir_rev)): + os.makedirs(self.main_dir_rev) open(self.tails_path, "w").close() async def test_delete_tails_by_rev_reg_id(self): @@ -924,11 +925,13 @@ async def test_delete_tails_by_rev_reg_id(self): rev_reg_id = self.rev_reg_id # Test - result = await test_module.delete_tails({"context": None, "query": {"rev_reg_id": rev_reg_id}}) + result = await test_module.delete_tails( + {"context": None, "query": {"rev_reg_id": rev_reg_id}} + ) # Assert self.assertEqual(result, {"message": "All files deleted successfully"}) - self.assertFalse(os.path.exists(self.main_dir_rev)) + self.assertFalse(os.path.exists(self.tails_path)) async def test_delete_tails_by_cred_def_id(self): # Setup @@ -939,7 +942,9 @@ async def test_delete_tails_by_cred_def_id(self): os.makedirs(cred_dir) # Test - result = await test_module.delete_tails({"context": None, "query": {"cred_def_id": cred_def_id}}) + result = await test_module.delete_tails( + {"context": None, "query": {"cred_def_id": cred_def_id}} + ) # Assert self.assertEqual(result, {"message": "All files deleted successfully"}) @@ -951,7 +956,9 @@ async def test_delete_tails_not_found(self): cred_def_id = "invalid_cred_def_id" # Test - result = await test_module.delete_tails({"context": None, "query": {"cred_def_id": cred_def_id}}) + result = await test_module.delete_tails( + {"context": None, "query": {"cred_def_id": cred_def_id}} + ) # Assert self.assertEqual(result, {"message": "No such file or directory"}) @@ -959,4 +966,4 @@ async def test_delete_tails_not_found(self): async def tearDown(self): if os.path.exists(self.main_dir_rev): - shutil.rmtree(self.main_dir_rev) \ No newline at end of file + shutil.rmtree(self.main_dir_rev) From df8eb25f0cbf3831d165404a05107eedce2998fc Mon Sep 17 00:00:00 2001 From: Kim Ebert Date: Wed, 15 Feb 2023 14:43:33 -0700 Subject: [PATCH 667/872] feat: add upgrade path for connection records. Signed-off-by: Kim Ebert --- aries_cloudagent/commands/default_version_upgrade_config.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/aries_cloudagent/commands/default_version_upgrade_config.yml b/aries_cloudagent/commands/default_version_upgrade_config.yml index 666f57ee37..67b0b4a022 100644 --- a/aries_cloudagent/commands/default_version_upgrade_config.yml +++ b/aries_cloudagent/commands/default_version_upgrade_config.yml @@ -1,3 +1,8 @@ +v0.8.0: + resave_records: + base_record_path: + - "aries_cloudagent.connections.models.conn_record.ConnRecord" + update_existing_records: false v0.7.2: resave_records: base_record_path: From fdbf848c02c3568e67a680ddadf0bba577d6a5ea Mon Sep 17 00:00:00 2001 From: Kim Ebert Date: Wed, 15 Feb 2023 14:54:51 -0700 Subject: [PATCH 668/872] fix: fix formatting Signed-off-by: Kim Ebert --- aries_cloudagent/connections/models/conn_record.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aries_cloudagent/connections/models/conn_record.py b/aries_cloudagent/connections/models/conn_record.py index f6332d90f3..b1b9725889 100644 --- a/aries_cloudagent/connections/models/conn_record.py +++ b/aries_cloudagent/connections/models/conn_record.py @@ -325,7 +325,7 @@ async def retrieve_by_invitation_key( """ tag_filter = { "invitation_key": invitation_key, - "state": cls.State.INVITATION.rfc160 + "state": cls.State.INVITATION.rfc160, } post_filter = {"state": cls.State.INVITATION.rfc160} From 61e7ff0d8f1a57f04829d3f48b95b9c57fc734f9 Mon Sep 17 00:00:00 2001 From: Wade Barnes Date: Thu, 16 Feb 2023 08:30:35 -0800 Subject: [PATCH 669/872] Temporarily disable multi-architecture image builds - Temporarily disable multi-architecture image builds until the required dependencies publish compatible packages. - Details here; https://github.com/hyperledger/aries-cloudagent-python/issues/2124 - Add support for defining the image platforms when running the image publishing workflows manually. Signed-off-by: Wade Barnes --- .github/workflows/publish-indy.yml | 18 ++++++++++++++---- .github/workflows/publish.yml | 12 +++++++++++- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/.github/workflows/publish-indy.yml b/.github/workflows/publish-indy.yml index 44000535c6..6cc365d43e 100644 --- a/.github/workflows/publish-indy.yml +++ b/.github/workflows/publish-indy.yml @@ -15,6 +15,11 @@ on: description: 'Image tag' required: true type: string + platforms: + description: 'Platforms - Comma separated list of the platforms to support.' + required: true + default: linux/amd64 + type: string # Note: # - ACA-Py with Indy SDK image builds do not include support for the linux/arm64 platform. @@ -23,6 +28,14 @@ on: env: INDY_VERSION: ${{ inputs.indy_version || '1.16.0' }} + # Images do not include support for the linux/arm64 platform due to a known issue compiling the postgres plugin + # - https://github.com/hyperledger/indy-sdk/issues/2445 + # There is a pending PR to fix this issue here; https://github.com/hyperledger/indy-sdk/pull/2453 + # + # linux/386 platform support has been disabled pending a permanent fix for https://github.com/hyperledger/aries-cloudagent-python/issues/2124 + # PLATFORMS: ${{ inputs.platforms || 'linux/amd64,linux/386' }} + PLATFORMS: ${{ inputs.platforms || 'linux/amd64' }} + jobs: publish-image: strategy: @@ -83,10 +96,7 @@ jobs: acapy_version=${{ inputs.tag || github.event.release.tag_name }} cache-from: type=local,src=/tmp/.buildx-cache cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max - # Images do not include support for the linux/arm64 platform due to a known issue compiling the postgres plugin - # - https://github.com/hyperledger/indy-sdk/issues/2445 - # There is a pending PR to fix this issue here; https://github.com/hyperledger/indy-sdk/pull/2453 - platforms: linux/amd64,linux/386 + platforms: ${{ env.PLATFORMS }} # Temp fix # https://github.com/docker/build-push-action/issues/252 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index f9ecc4ebe3..118231f0d1 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -10,6 +10,16 @@ on: description: 'Image tag' required: true type: string + platforms: + description: 'Platforms - Comma separated list of the platforms to support.' + required: true + default: linux/amd64 + type: string + +env: + # linux/386 platform support has been disabled pending a permanent fix for https://github.com/hyperledger/aries-cloudagent-python/issues/2124 + # PLATFORMS: ${{ inputs.platforms || 'linux/amd64,linux/arm64,linux/386' }} + PLATFORMS: ${{ inputs.platforms || 'linux/amd64' }} jobs: publish-image: @@ -70,7 +80,7 @@ jobs: acapy_version=${{ inputs.tag || github.event.release.tag_name }} cache-from: type=local,src=/tmp/.buildx-cache cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max - platforms: linux/amd64,linux/arm64,linux/386 + platforms: ${{ env.PLATFORMS }} # Temp fix # https://github.com/docker/build-push-action/issues/252 From 70839bd4dfa5df2647a8390c45d10306f4b2d297 Mon Sep 17 00:00:00 2001 From: Wade Barnes Date: Thu, 16 Feb 2023 09:03:14 -0800 Subject: [PATCH 670/872] Add support for building off a git ref Signed-off-by: Wade Barnes --- .github/workflows/publish-indy.yml | 6 ++++++ .github/workflows/publish.yml | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/.github/workflows/publish-indy.yml b/.github/workflows/publish-indy.yml index 6cc365d43e..0fd219e0f6 100644 --- a/.github/workflows/publish-indy.yml +++ b/.github/workflows/publish-indy.yml @@ -20,6 +20,10 @@ on: required: true default: linux/amd64 type: string + ref: + description: 'Optional - The branch, tag or SHA to checkout.' + required: false + type: string # Note: # - ACA-Py with Indy SDK image builds do not include support for the linux/arm64 platform. @@ -48,6 +52,8 @@ jobs: steps: - name: Checkout Code uses: actions/checkout@v3 + with: + ref: ${{ inputs.ref || '' }} - name: Gather image info id: info diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 118231f0d1..bb057f432e 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -15,6 +15,10 @@ on: required: true default: linux/amd64 type: string + ref: + description: 'Optional - The branch, tag or SHA to checkout.' + required: false + type: string env: # linux/386 platform support has been disabled pending a permanent fix for https://github.com/hyperledger/aries-cloudagent-python/issues/2124 @@ -33,6 +37,8 @@ jobs: steps: - name: Checkout Code uses: actions/checkout@v3 + with: + ref: ${{ inputs.ref || '' }} - name: Gather image info id: info From a28cfee321e56f0141d632cd90b368bc59304ae3 Mon Sep 17 00:00:00 2001 From: Lucas ONeil Date: Thu, 16 Feb 2023 11:43:20 -0800 Subject: [PATCH 671/872] OpenAPI validation fixes Signed-off-by: Lucas ONeil --- open-api/openapi.json | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/open-api/openapi.json b/open-api/openapi.json index 7655cac58b..463eaf3bff 100644 --- a/open-api/openapi.json +++ b/open-api/openapi.json @@ -109,9 +109,10 @@ } }, { "name" : "resolver", - "description" : "did resolver interface.", + "description" : "DID resolver interface", "externalDocs" : { - "description" : "Specification" + "description" : "Specification", + "url" : "https://github.com/hyperledger/aries-rfcs/tree/fa4b1947c6077168d2c69f45ed6bee2bb1eae4c8/features/0124-did-resolution-protocol" } }, { "name" : "revocation", @@ -2442,8 +2443,7 @@ "required" : false, "type" : "array", "items" : { - "type" : "string", - "description" : "Indicate terms to which the mediator requires the recipient to agree" + "type" : "string" }, "collectionFormat" : "multi" }, { @@ -2453,8 +2453,7 @@ "required" : false, "type" : "array", "items" : { - "type" : "string", - "description" : "Indicate terms to which the recipient requires the mediator to agree" + "type" : "string" }, "collectionFormat" : "multi" }, { From 261800b4c7af48a574db5cd50cadb434edac9cec Mon Sep 17 00:00:00 2001 From: Wade Barnes Date: Fri, 17 Feb 2023 11:36:20 -0800 Subject: [PATCH 672/872] Update ACA-Py docker files to support OpenShift - and separate aca-py and indy version labels. Signed-off-by: Wade Barnes --- docker/Dockerfile | 17 ++++++++++------- docker/Dockerfile.indy | 41 ++++++++++++++++++----------------------- 2 files changed, 28 insertions(+), 30 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 39695f1775..f0fe9c506f 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -33,7 +33,7 @@ LABEL summary="$SUMMARY" \ io.k8s.description="$DESCRIPTION" \ io.k8s.display-name="aries-cloudagent $acapy_version" \ name="aries-cloudagent" \ - version="$acapy_version" \ + acapy.version="$acapy_version" \ maintainer="" # Add aries user @@ -81,19 +81,22 @@ RUN mkdir -p \ $HOME/ledger/sandbox/data \ $HOME/log -# The root group needs access the directories under $HOME for the container to function in OpenShift. -# Also ensure the permissions on the python 'site-packages' folder are set correctly. -RUN chmod -R ug+rw $HOME/log $HOME/ledger $HOME/.aries_cloudagent $HOME/.cache +# The root group needs access the directories under $HOME/.aries_cloudagent for the container to function in OpenShift. +RUN chown -R $user:root $HOME/.aries_cloudagent && \ + chmod -R ug+rw $HOME/log $HOME/ledger $HOME/.aries_cloudagent $HOME/.cache +# Install ACA-py from the wheel as $user, +# and ensure the permissions on the python 'site-packages' and $HOME/.local folders are set correctly. +USER $user COPY --from=build /src/dist/aries_cloudagent*.whl . - -# Install ACA-py from the wheel. RUN aries_cloudagent_package=$(find ./ -name "aries_cloudagent*.whl" | head -n 1) && \ echo "Installing ${aries_cloudagent_package} ..." && \ pip install --no-cache-dir --find-links=. ${aries_cloudagent_package}${acapy_reqs} && \ - rm aries_cloudagent*.whl + rm aries_cloudagent*.whl && \ + chmod +rx $(python -m site --user-site) $HOME/.local # Clean-up unneccessary build dependencies and reduce final image size +USER root RUN apt-get purge -y --auto-remove build-essential USER $user diff --git a/docker/Dockerfile.indy b/docker/Dockerfile.indy index 1c5e1ae3da..edd7200b8a 100644 --- a/docker/Dockerfile.indy +++ b/docker/Dockerfile.indy @@ -104,7 +104,7 @@ LABEL summary="$SUMMARY" \ io.k8s.description="$DESCRIPTION" \ io.k8s.display-name="indy-python $indy_version" \ name="indy-python" \ - version="$indy_version" \ + indy-sdk.version="$indy_version" \ maintainer="" # Add indy user @@ -159,18 +159,17 @@ RUN usermod -a -G 0 $user # Create standard directories to allow volume mounting and set permissions # Note: PIP_NO_CACHE_DIR environment variable should be cleared to allow caching RUN mkdir -p \ + $HOME/.aries_cloudagent \ $HOME/.cache/pip/http \ - $HOME/.indy-cli/networks \ $HOME/.indy_client/wallet \ $HOME/.indy_client/pool \ $HOME/.indy_client/ledger-cache \ $HOME/ledger/sandbox/data \ $HOME/log -# The root group needs access the directories under $HOME/.indy_client for the container to function in OpenShift. -# Also ensure the permissions on the python 'site-packages' folder are set correctly. -RUN chown -R $user:root $HOME/.indy_client \ - && chmod -R ug+rw $HOME/log $HOME/ledger $HOME/.cache $HOME/.indy_client +# The root group needs access the directories under $HOME/.indy_client and $HOME/.aries_cloudagent for the container to function in OpenShift. +RUN chown -R $user:root $HOME/.indy_client $HOME/.aries_cloudagent && \ + chmod -R ug+rw $HOME/log $HOME/ledger $HOME/.aries_cloudagent $HOME/.cache $HOME/.indy_client USER $user @@ -193,10 +192,10 @@ ADD requirements*.txt ./ USER root RUN pip3 install --no-cache-dir \ - -r requirements.txt \ - -r requirements.askar.txt \ - -r requirements.bbs.txt \ - -r requirements.dev.txt + -r requirements.txt \ + -r requirements.askar.txt \ + -r requirements.bbs.txt \ + -r requirements.dev.txt ADD --chown=indy:root . . USER indy @@ -243,26 +242,22 @@ LABEL summary="$SUMMARY" \ io.k8s.description="$DESCRIPTION" \ io.k8s.display-name="aries-cloudagent $acapy_version" \ name="aries-cloudagent" \ - version="$acapy_version" \ + acapy.version="$acapy_version" \ maintainer="" -# Create standard directories to allow volume mounting and set permissions -# Note: PIP_NO_CACHE_DIR environment variable should be cleared to allow caching -RUN mkdir -p $HOME/.aries_cloudagent - -# The root group needs access the directories under $HOME/.indy_client for the container to function in OpenShift. -# Also ensure the permissions on the python 'site-packages' folder are set correctly. -RUN chmod -R ug+rw $HOME/.aries_cloudagent - +# Install ACA-py from the wheel as $user, +# and ensure the permissions on the python 'site-packages' folder are set correctly. COPY --from=acapy-builder /src/dist/aries_cloudagent*.whl . - -# Install ACA-py from the wheel. RUN aries_cloudagent_package=$(find ./ -name "aries_cloudagent*.whl" | head -n 1) && \ echo "Installing ${aries_cloudagent_package} ..." && \ pip install --no-cache-dir --find-links=. ${aries_cloudagent_package}${acapy_reqs} && \ - rm aries_cloudagent*.whl + rm aries_cloudagent*.whl && \ + chmod +rx $(python -m site --user-site) # Clean-up unneccessary build dependencies and reduce final image size -# RUN apt-get purge -y --auto-remove build-essential +USER root +RUN apt-get purge -y --auto-remove build-essential + +USER $user ENTRYPOINT ["aca-py"] From 9fdc86fd8388e9f8a9cfbc93e148a888b3ec982d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Humbert?= Date: Mon, 20 Feb 2023 14:11:51 +0100 Subject: [PATCH 673/872] fix: response type on delete-tails-files endpoint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Clément Humbert --- aries_cloudagent/revocation/routes.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/aries_cloudagent/revocation/routes.py b/aries_cloudagent/revocation/routes.py index b637757b43..1350ff3063 100644 --- a/aries_cloudagent/revocation/routes.py +++ b/aries_cloudagent/revocation/routes.py @@ -1494,7 +1494,14 @@ async def on_revocation_registry_endorsed_event(profile: Profile, event: Event): ) +class TailsDeleteResponseSchema(OpenAPISchema): + """Return schema for tails failes deletion.""" + + message = fields.Str() + + @querystring_schema(RevRegId()) +@response_schema(TailsDeleteResponseSchema()) @docs(tags=["revocation"], summary="Delete the tail files") async def delete_tails(request: web.BaseRequest) -> json: """Delete Tails Files.""" From ab1d54a8acc5bc366c5755c2fe5a1d63a70d1d4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Humbert?= Date: Mon, 20 Feb 2023 10:37:48 +0100 Subject: [PATCH 674/872] feat: add verification method parameter to issue-credentials-2.0/send endpoint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Accomodate credential sending for did methods that are not did:sov or did:key by adding an optional `verification_method` parameter to the send endpoint options. Signed-off-by: Clément Humbert --- .../v2_0/formats/ld_proof/handler.py | 18 +++++++++++++----- .../formats/ld_proof/tests/test_handler.py | 2 +- .../protocols/issue_credential/v2_0/manager.py | 6 ++++-- .../v2_0/models/cred_ex_record.py | 3 +++ .../protocols/issue_credential/v2_0/routes.py | 10 ++++++++++ 5 files changed, 31 insertions(+), 8 deletions(-) diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py index fc850070b4..c4fccc02d5 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py @@ -5,7 +5,7 @@ from ......vc.ld_proofs.check import get_properties_without_context import logging -from typing import Mapping +from typing import Mapping, Optional from marshmallow import EXCLUDE, INCLUDE @@ -253,7 +253,9 @@ async def _did_info_for_did(self, did: str) -> DIDInfo: # All other methods we can just query return await wallet.get_local_did(did) - async def _get_suite_for_detail(self, detail: LDProofVCDetail) -> LinkedDataProof: + async def _get_suite_for_detail( + self, detail: LDProofVCDetail, verification_method: Optional[str] = None + ) -> LinkedDataProof: issuer_id = detail.credential.issuer_id proof_type = detail.options.proof_type @@ -268,7 +270,9 @@ async def _get_suite_for_detail(self, detail: LDProofVCDetail) -> LinkedDataProo ) did_info = await self._did_info_for_did(issuer_id) - verification_method = self._get_verification_method(issuer_id) + verification_method = verification_method or self._get_verification_method( + issuer_id + ) suite = await self._get_suite( proof_type=proof_type, @@ -457,7 +461,9 @@ async def receive_request( """Receive linked data proof request.""" async def issue_credential( - self, cred_ex_record: V20CredExRecord, retries: int = 5 + self, + cred_ex_record: V20CredExRecord, + retries: int = 5, ) -> CredFormatAttachment: """Issue linked data proof credential.""" if not cred_ex_record.cred_request: @@ -472,7 +478,9 @@ async def issue_credential( detail = await self._prepare_detail(detail) # Get signature suite, proof purpose and document loader - suite = await self._get_suite_for_detail(detail) + suite = await self._get_suite_for_detail( + detail, cred_ex_record.verification_method + ) proof_purpose = self._get_proof_purpose( proof_purpose=detail.options.proof_purpose, challenge=detail.options.challenge, diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/tests/test_handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/tests/test_handler.py index f0e11070e6..b89dad7da5 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/tests/test_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/tests/test_handler.py @@ -588,7 +588,7 @@ async def test_issue_credential(self): detail = LDProofVCDetail.deserialize(LD_PROOF_VC_DETAIL) - mock_get_suite.assert_called_once_with(detail) + mock_get_suite.assert_called_once_with(detail, None) mock_issue.assert_called_once_with( credential=LD_PROOF_VC_DETAIL["credential"], suite=mock_get_suite.return_value, diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/manager.py b/aries_cloudagent/protocols/issue_credential/v2_0/manager.py index ad6f3f9313..006217fabf 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/manager.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/manager.py @@ -55,6 +55,7 @@ async def prepare_send( self, connection_id: str, cred_proposal: V20CredProposal, + verification_method: Optional[str] = None, auto_remove: bool = None, ) -> Tuple[V20CredExRecord, V20CredOffer]: """ @@ -63,6 +64,7 @@ async def prepare_send( Args: connection_id: connection for which to create offer cred_proposal: credential proposal with preview + verification_method: an optional verification method to be used when issuing auto_remove: flag to remove the record automatically on completion Returns: @@ -73,6 +75,7 @@ async def prepare_send( auto_remove = not self._profile.settings.get("preserve_exchange_records") cred_ex_record = V20CredExRecord( connection_id=connection_id, + verification_method=verification_method, initiator=V20CredExRecord.INITIATOR_SELF, role=V20CredExRecord.ROLE_ISSUER, cred_proposal=cred_proposal, @@ -80,12 +83,11 @@ async def prepare_send( auto_remove=auto_remove, trace=(cred_proposal._trace is not None), ) - (cred_ex_record, cred_offer) = await self.create_offer( + return await self.create_offer( cred_ex_record=cred_ex_record, counter_proposal=None, comment="create automated v2.0 credential exchange record", ) - return (cred_ex_record, cred_offer) async def create_proposal( self, diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/models/cred_ex_record.py b/aries_cloudagent/protocols/issue_credential/v2_0/models/cred_ex_record.py index da88600943..448a82052a 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/models/cred_ex_record.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/models/cred_ex_record.py @@ -59,6 +59,7 @@ def __init__( *, cred_ex_id: str = None, connection_id: str = None, + verification_method: Optional[str] = None, thread_id: str = None, parent_thread_id: str = None, initiator: str = None, @@ -82,6 +83,7 @@ def __init__( super().__init__(cred_ex_id, state, trace=trace, **kwargs) self._id = cred_ex_id self.connection_id = connection_id or conn_id + self.verification_method = verification_method self.thread_id = thread_id self.parent_thread_id = parent_thread_id self.initiator = initiator @@ -217,6 +219,7 @@ def record_value(self) -> Mapping: prop: getattr(self, prop) for prop in ( "connection_id", + "verification_method", "parent_thread_id", "initiator", "role", diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/routes.py b/aries_cloudagent/protocols/issue_credential/v2_0/routes.py index e3a066d339..129b98250f 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/routes.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/routes.py @@ -277,6 +277,13 @@ class V20CredExFreeSchema(V20IssueCredSchemaCore): example=UUIDFour.EXAMPLE, # typically but not necessarily a UUID4 ) + verification_method = fields.Str( + required=False, + default=None, + allow_none=True, + description="For ld-proofs. Verification method for signing.", + ) + class V20CredBoundOfferRequestSchema(OpenAPISchema): """Request schema for sending bound credential offer admin message.""" @@ -628,6 +635,8 @@ async def credential_exchange_send(request: web.BaseRequest): comment = body.get("comment") connection_id = body.get("connection_id") + verification_method = body.get("verification_method") + filt_spec = body.get("filter") if not filt_spec: raise web.HTTPBadRequest(reason="Missing filter") @@ -668,6 +677,7 @@ async def credential_exchange_send(request: web.BaseRequest): cred_manager = V20CredManager(profile) (cred_ex_record, cred_offer_message) = await cred_manager.prepare_send( connection_id, + verification_method=verification_method, cred_proposal=cred_proposal, auto_remove=auto_remove, ) From ce87c9f06fb5744d8ab4ebfddccdb8c34a78c1fe Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Tue, 21 Feb 2023 14:00:56 -0800 Subject: [PATCH 675/872] Initial plugin docs Signed-off-by: Ian Costanzo --- README.md | 2 + docs/GettingStartedAriesDev/PlugIns.md | 172 +++++++++++++++++++++++++ docs/GettingStartedAriesDev/README.md | 1 + 3 files changed, 175 insertions(+) create mode 100644 docs/GettingStartedAriesDev/PlugIns.md diff --git a/README.md b/README.md index 936847fbc5..f5fc6cd485 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,8 @@ There is an [architectural deep dive webinar](https://www.youtube.com/watch?v=FX ![drawing](./aca-py_architecture.png) +You can extend Aca-Py using plug-ins, which can be loaded at runtime. Plug-ins are mentioned in the [webinar](https://docs.google.com/presentation/d/1K7qiQkVi4n-lpJ3nUZY27OniUEM0c8HAIk4imCWCx5Q/edit#slide=id.g5d43fe05cc_0_145) and are [described in more detail here](/docs/GettingStartedAriesDev/PlugIns.md). + ### Installation and Usage An ["install and go" page for developers](https://github.com/hyperledger/aries-cloudagent-python/blob/main/DevReadMe.md) is available if you are comfortable with Trust over IP and Aries concepts. ACA-Py can be run with Docker without installation (highly recommended), or can be installed [from PyPi](https://pypi.org/project/aries-cloudagent/). In the [/demo directory](/demo) there is a full set of demos for developers to use in getting started, and the [demo read me](/demo/README.md) is a great starting point for developers to use an "in-browser" approach to run a zero-install example. The [Read the Docs](https://aries-cloud-agent-python.readthedocs.io/en/latest/) overview is also a way to reference the modules and APIs that make up an ACA-Py instance. diff --git a/docs/GettingStartedAriesDev/PlugIns.md b/docs/GettingStartedAriesDev/PlugIns.md new file mode 100644 index 0000000000..6f2aa493b2 --- /dev/null +++ b/docs/GettingStartedAriesDev/PlugIns.md @@ -0,0 +1,172 @@ +# Deeper Dive: Aca-Py Plug-Ins + +## What's in a Plug-In and How does it Work? + +Plug-ins are loaded on Aca-Py startup based on the following parameters: + +* `--plug-in` - identifies the plug-in library to load +* `--block-plugin` - identifies plug-ins (includign built-ins) that are *not* to be loaded +* `--plugin-config` - identify a configuration parameter for a plug-in +* `--plugin-config-value` - identify a *value* for a plug-in configuration + + +The `--plug-in` parameter specifies a package that is loaded by Aca-Py at runtime, and extends Aca-Py by adding support for additional protocols and message types, and/or extending the Admin API with additional endpoints. + +The original plug-in design (which we will call the "old" model) explicitely indluded `message_types.py` `routes.py` (to add Admin API's). But functionality was added later (we'll call this the "new" model) to allow the plug-in to include a generic `setup` package that could perform arbitrary initialization. The "new" model also includes support for a `definition.py` file that can specify plug-in version information (major/minor plug-in version, as well as the minimum supported version (if another agent is running an older version of the plug-in)). + +### setup.py + +If a setup method is provided, it will be called. If not, the `message_types.py` and `routes.py` will be explicitely loaded. + +TODO I couldn't find an implementation of a custom `setup` in any of the existing plug-ins, so I'm not completly sure what are the best practices for this option. + +### message_types.py + +When loading a plug-in, if there is a `message_types.py` available, Aca-Py will check the following attributes to initialize the protocol(s): + +- `MESSAGE_TYPES` - identifies message types supported by the protocol +- `CONTRROLLERS` - identifies protocol controllers + +### routes.py + +If `routes.py` is available, then Aca-Py will call the following functions to initialize the Admin endpoints: + +- `register()` - registers routes for the new Admin endpoints +- `register_events()` - registers an events this package will listen for/respond to + +### definition.py + +If `definition.py` is available, Aca-Py will read this package to determine protocol version information. An example follows (this is an example that specifies two protocol versions): + +``` +versions = [ + { + "major_version": 1, + "minimum_minor_version": 0, + "current_minor_version": 0, + "path": "v1_0", + }, + { + "major_version": 2, + "minimum_minor_version": 0, + "current_minor_version": 0, + "path": "v2_0", + }, +] +``` + +The attributes are: + +- `major_version` - specifies the protocol major version +- `current_minor_version` - specifies the protocol minor version +- `minimum_minor_version` - specifies the minimum supported version (if a lower version is installed in another agent) +- `path` - specifies the sub-path within the package for this version + + +## Loading Aca-Py Plug-Ins at Runtime + +The load sequence for a plug-in (the "Startup" class depends on how Aca-Py is running - `upgrade`, `provision` or `start`): + +```mermaid +sequenceDiagram + participant Startup + Note right of Startup: Configuration is loaded on startup
from aca-py config params + Startup->>+ArgParse: configure + ArgParse->>settings: ["external_plugins"] + ArgParse->>settings: ["blocked_plugins"] + + Note right of Startup: Each configured plug-in is validated and loaded + Startup->>+DefaultContext: build_context() + DefaultContext->>DefaultContext: load_plugins() + DefaultContext->>+PluginRegistry: register_package() (for built-in protocols) + PluginRegistry->>PluginRegistry: register_plugin() (for each sub-package) + DefaultContext->>PluginRegistry: register_plugin() (for non-protocol built-ins) + loop for each external plug-in + DefaultContext->>PluginRegistry: register_plugin() + alt if a setup method is provided + PluginRegistry->>ExternalPlugIn: has setup + else if routes and/or message_types are provided + PluginRegistry->>ExternalPlugIn: has routes + PluginRegistry->>ExternalPlugIn: has message_types + end + opt if definition is provided + PluginRegistry->>ExternalPlugIn: definition() + end + end + DefaultContext->>PluginRegistry: init_context() + loop for each external plug-in + alt if a setup method is provided + PluginRegistry->>ExternalPlugIn: setup() + else if a setup method is NOT provided + PluginRegistry->>PluginRegistry: load_protocols() + PluginRegistry->>PluginRegistry: load_protocol_version() + PluginRegistry->>ProtocolRegistry: register_message_types() + PluginRegistry->>ProtocolRegistry: register_controllers() + end + PluginRegistry->>PluginRegistry: register_protocol_events() + end + + Note right of Startup: If the admin server is enabled, plug-in routes are added + Startup->>AdminServer: create admin server if enabled + Startup->>AdminServer: setup_context() (called on each request) + AdminServer->>PluginRegistry: register_admin_routes() + loop for each external plug-in + PluginRegistry->>ExternalPlugIn: routes.register() (to register endpoints) + end +``` + +## Developing a New Plug-In + +When developing a new plug-in: + +- If you are providing a new protocol or defining message types, you *should* include a `definition.py` file. +- If you are providing a new protocol or defining message types, you *should* include a `message_types.py` file. +- If you are providing additional Admin endpoints, you *should* include a `routes.py` file. +- If you are providing any other functionality, you should provide a `setup.py` file to initialize the custom functionality. No guidance is *currently* available for this option. + +### PIP vs Poetry Support + +Most Aca-Py plug-ins provide support for installing the plug-in using [poetry](https://python-poetry.org/). It is *recommended* to include support in your package for installing using *either* pip or poetry, to provide maximum support for users of your plug-in. + +### Plug-In Demo + +TBD + +# Aca-Py Plug-ins + +This list was originally published in [this hackmd document](https://hackmd.io/m2AZebwJRkm6sWgO64-5xQ). + +| Maintainer | Name | Features | Last Update | Link | +| ----------- | -------------------------- | -------------------------------- | ----------- | ----------------------------------------------------------------------- | +| BCGov | Redis Events | Inbound/Outbound message queue | Sep 2022 | https://github.com/bcgov/aries-acapy-plugin-redis-events | +| Hyperledger | Aries Toolbox | UI for ACA-py | Aug 2022 | https://github.com/hyperledger/aries-toolbox | +| Hyperledger | Aries ACApy Plugin Toolbox | Protocol Handlers | Aug 2022 | https://github.com/hyperledger/aries-acapy-plugin-toolbox | +| Indicio | Data Transfer | Specific Data import | Aug 2022 | https://github.com/Indicio-tech/aries-acapy-plugin-data-transfer | +| Indicio | Question & Answer | Non-Aries Protocol | Aug 2022 | https://github.com/Indicio-tech/acapy-plugin-qa | +| Indicio | Acapy-plugin-pickup | Fetching Messages from Mediator | Aug 2022 | https://github.com/Indicio-tech/acapy-plugin-pickup | +| Indicio | Machine Readable GF | Governance Framework | Mar 2022 | https://github.com/Indicio-tech/mrgf | +| Indicio | Cache Redis | Cache for Scaleability | Jul 2022 | https://github.com/Indicio-tech/aries-acapy-cache-redis | +| SICPA Dlab | Kafka Events | Event Bus Integration | Aug 2022 | https://github.com/sicpa-dlab/aries-acapy-plugin-kafka-events | +| SICPA Dlab | DidComm Resolver | Unversal Resolver for DIDComm | Aug 2022 | https://github.com/sicpa-dlab/acapy-resolver-didcomm | +| SICPA Dlab | Universal Resolver | Multi-ledger Reading | Jul 2021 | https://github.com/sicpa-dlab/acapy-resolver-universal | +| DDX | mydata-did-protocol | | Oct 2022 | https://github.com/decentralised-dataexchange/acapy-mydata-did-protocol | +| BCGov | Basic Message Storage | Basic message storage (traction) | Dec 2022 | https://github.com/bcgov/traction/tree/develop/plugins/basicmessage_storage | +| BCGov | Multi-tenant Provider | Multi-tenant Provider (traction) | Dec 2022 | https://github.com/bcgov/traction/tree/develop/plugins/multitenant_provider | +| BCGov | Traction Innkeeper | Innkeeper (traction) | Feb 2023 | https://github.com/bcgov/traction/tree/develop/plugins/traction_innkeeper | + + +# Reference + +The following links may be helpful or provide additional context for the current plug-in support. (These are links to issues or pull requests that were raised during plug-in development.) + +Configuration params: + https://github.com/hyperledger/aries-cloudagent-python/issues/1121 + https://hackmd.io/ROUzENdpQ12cz3UB9qk1nA + https://github.com/hyperledger/aries-cloudagent-python/pull/1226 + +Loading plug-ins: + https://github.com/hyperledger/aries-cloudagent-python/pull/1086 + +Versioning for plug-ins: + https://github.com/hyperledger/aries-cloudagent-python/pull/443 + diff --git a/docs/GettingStartedAriesDev/README.md b/docs/GettingStartedAriesDev/README.md index e6b98aa655..dad297c007 100644 --- a/docs/GettingStartedAriesDev/README.md +++ b/docs/GettingStartedAriesDev/README.md @@ -21,5 +21,6 @@ Note that in the guidance we have here, we include not only the links to look at * [Deeper Dive: Routing Example](AriesRoutingExample.md) * To Do: [Deeper Dive: Running and Connecting to an Indy Network](ConnectIndyNetwork.md) * [Steps and APIs to support credential revocation with Aries agent](CredentialRevocation.md) +* [Deeper Dive: Aca-Py Plug-Ins](PlugIns.md) Want to help with this guide? Please add issues or submit a pull request to improve the document. Point out things that are missing, things to improve and especially things that are wrong. From 56f2571cc1b40fb10633c5192e843a4c3397c03f Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Wed, 22 Feb 2023 14:03:57 -0800 Subject: [PATCH 676/872] Added note re plug-in related endpoints Signed-off-by: Ian Costanzo --- docs/GettingStartedAriesDev/PlugIns.md | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/docs/GettingStartedAriesDev/PlugIns.md b/docs/GettingStartedAriesDev/PlugIns.md index 6f2aa493b2..f2042f1978 100644 --- a/docs/GettingStartedAriesDev/PlugIns.md +++ b/docs/GettingStartedAriesDev/PlugIns.md @@ -5,27 +5,36 @@ Plug-ins are loaded on Aca-Py startup based on the following parameters: * `--plug-in` - identifies the plug-in library to load -* `--block-plugin` - identifies plug-ins (includign built-ins) that are *not* to be loaded +* `--block-plugin` - identifies plug-ins (including built-ins) that are *not* to be loaded * `--plugin-config` - identify a configuration parameter for a plug-in * `--plugin-config-value` - identify a *value* for a plug-in configuration The `--plug-in` parameter specifies a package that is loaded by Aca-Py at runtime, and extends Aca-Py by adding support for additional protocols and message types, and/or extending the Admin API with additional endpoints. -The original plug-in design (which we will call the "old" model) explicitely indluded `message_types.py` `routes.py` (to add Admin API's). But functionality was added later (we'll call this the "new" model) to allow the plug-in to include a generic `setup` package that could perform arbitrary initialization. The "new" model also includes support for a `definition.py` file that can specify plug-in version information (major/minor plug-in version, as well as the minimum supported version (if another agent is running an older version of the plug-in)). +The original plug-in design (which we will call the "old" model) explicitly indluded `message_types.py` `routes.py` (to add Admin API's). But functionality was added later (we'll call this the "new" model) to allow the plug-in to include a generic `setup` package that could perform arbitrary initialization. The "new" model also includes support for a `definition.py` file that can specify plug-in version information (major/minor plug-in version, as well as the minimum supported version (if another agent is running an older version of the plug-in)). -### setup.py +You can discover which plug-ins are installed in an aca-py instance by calling (in the "server" section) the `GET /plugins` endpoint. (Note that this will return all loaded protocols, including the built-ins. You can call the `GET /status/config` to inspect the Aca-Py configuration, which will include the configuration for the *external* plug-ins.) -If a setup method is provided, it will be called. If not, the `message_types.py` and `routes.py` will be explicitely loaded. +### setup method -TODO I couldn't find an implementation of a custom `setup` in any of the existing plug-ins, so I'm not completly sure what are the best practices for this option. +If a setup method is provided, it will be called. If not, the `message_types.py` and `routes.py` will be explicitly loaded. + +This would be in the `package/module __init__.py`: + +``` +async def setup(context: InjectionContext): + pass +``` + +TODO I couldn't find an implementation of a custom `setup` in any of the existing plug-ins, so I'm not completely sure what are the best practices for this option. ### message_types.py When loading a plug-in, if there is a `message_types.py` available, Aca-Py will check the following attributes to initialize the protocol(s): - `MESSAGE_TYPES` - identifies message types supported by the protocol -- `CONTRROLLERS` - identifies protocol controllers +- `CONTROLLERS` - identifies protocol controllers ### routes.py From 9a93fc2665e5317912970a6fde624c6bd77471e7 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Wed, 22 Feb 2023 14:17:41 -0800 Subject: [PATCH 677/872] Update docs/GettingStartedAriesDev/PlugIns.md Co-authored-by: Daniel Bluhm Signed-off-by: Ian Costanzo --- docs/GettingStartedAriesDev/PlugIns.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/GettingStartedAriesDev/PlugIns.md b/docs/GettingStartedAriesDev/PlugIns.md index f2042f1978..8f742bd57d 100644 --- a/docs/GettingStartedAriesDev/PlugIns.md +++ b/docs/GettingStartedAriesDev/PlugIns.md @@ -4,7 +4,7 @@ Plug-ins are loaded on Aca-Py startup based on the following parameters: -* `--plug-in` - identifies the plug-in library to load +* `--plugin` - identifies the plug-in library to load * `--block-plugin` - identifies plug-ins (including built-ins) that are *not* to be loaded * `--plugin-config` - identify a configuration parameter for a plug-in * `--plugin-config-value` - identify a *value* for a plug-in configuration From 9111a7ecd7344c54fec105c39e6ad0919b5b6773 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Humbert?= Date: Fri, 24 Feb 2023 16:43:02 +0100 Subject: [PATCH 678/872] feat: allow marking non-SOV DIDs as public MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the restriction for did:sov when marking a DID as public **at the wallet level**. Signed-off-by: Clément Humbert --- aries_cloudagent/wallet/askar.py | 3 --- aries_cloudagent/wallet/in_memory.py | 5 +--- aries_cloudagent/wallet/indy.py | 3 --- .../wallet/tests/test_in_memory_wallet.py | 23 ------------------- 4 files changed, 1 insertion(+), 33 deletions(-) diff --git a/aries_cloudagent/wallet/askar.py b/aries_cloudagent/wallet/askar.py index 2a0093d276..d104d32d36 100644 --- a/aries_cloudagent/wallet/askar.py +++ b/aries_cloudagent/wallet/askar.py @@ -396,9 +396,6 @@ async def set_public_did(self, did: Union[str, DIDInfo]) -> DIDInfo: info = did item = None - if info.method != SOV: - raise WalletError("Setting public DID is only allowed for did:sov DIDs") - public = await self.get_public_did() if not public or public.did != info.did: storage = AskarStorage(self._session) diff --git a/aries_cloudagent/wallet/in_memory.py b/aries_cloudagent/wallet/in_memory.py index 5023e8d6c0..03fc4fb8aa 100644 --- a/aries_cloudagent/wallet/in_memory.py +++ b/aries_cloudagent/wallet/in_memory.py @@ -17,7 +17,7 @@ ) from .did_info import KeyInfo, DIDInfo from .did_posture import DIDPosture -from .did_method import SOV, DIDMethod, DIDMethods +from .did_method import DIDMethod, DIDMethods from .error import WalletError, WalletDuplicateError, WalletNotFoundError from .key_type import KeyType from .util import b58_to_bytes, bytes_to_b58, random_seed @@ -383,9 +383,6 @@ async def set_public_did(self, did: Union[str, DIDInfo]) -> DIDInfo: info = did did = info.did - if info.method != SOV: - raise WalletError("Setting public DID is only allowed for did:sov DIDs") - public = await self.get_public_did() if public and public.did == did: info = public diff --git a/aries_cloudagent/wallet/indy.py b/aries_cloudagent/wallet/indy.py index 78356946bb..7a99858044 100644 --- a/aries_cloudagent/wallet/indy.py +++ b/aries_cloudagent/wallet/indy.py @@ -687,9 +687,6 @@ async def set_public_did(self, did: Union[str, DIDInfo]) -> DIDInfo: else: info = did - if info.method != SOV: - raise WalletError("Setting public DID is only allowed for did:sov DIDs") - public = await self.get_public_did() if not public or public.did != info.did: if not info.metadata.get("posted"): diff --git a/aries_cloudagent/wallet/tests/test_in_memory_wallet.py b/aries_cloudagent/wallet/tests/test_in_memory_wallet.py index e9d81ffa27..2558f60914 100644 --- a/aries_cloudagent/wallet/tests/test_in_memory_wallet.py +++ b/aries_cloudagent/wallet/tests/test_in_memory_wallet.py @@ -346,17 +346,6 @@ async def test_create_public_did(self, wallet: InMemoryWallet): info_replace.did, } - @pytest.mark.asyncio - async def test_create_public_did_x_not_sov(self, wallet: InMemoryWallet): - with pytest.raises(WalletError) as context: - await wallet.create_public_did( - KEY, - ED25519, - ) - assert "Setting public DID is only allowed for did:sov DIDs" in str( - context.value - ) - @pytest.mark.asyncio async def test_create_public_did_x_unsupported_key_type_method( self, wallet: InMemoryWallet @@ -400,18 +389,6 @@ async def test_set_public_did(self, wallet: InMemoryWallet): assert info_final.did == info_new.did assert info_final.metadata.get("posted") - @pytest.mark.asyncio - async def test_set_public_did_x_not_sov(self, wallet: InMemoryWallet): - info = await wallet.create_local_did( - KEY, - ED25519, - ) - with pytest.raises(WalletError) as context: - await wallet.set_public_did(info.did) - assert "Setting public DID is only allowed for did:sov DIDs" in str( - context.value - ) - @pytest.mark.asyncio async def test_sign_verify(self, wallet: InMemoryWallet): info = await wallet.create_local_did( From c6bcf7cb8ee910463b5b2f19e2c03b6e699e7380 Mon Sep 17 00:00:00 2001 From: Victor Lee Date: Thu, 23 Feb 2023 16:46:18 -0500 Subject: [PATCH 679/872] cred_ex webhook fixed missing timestamp Signed-off-by: Victor Lee --- .../v1_0/messages/credential_exchange_webhook.py | 2 ++ .../issue_credential/v2_0/messages/cred_ex_record_webhook.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/messages/credential_exchange_webhook.py b/aries_cloudagent/protocols/issue_credential/v1_0/messages/credential_exchange_webhook.py index 87a20b0322..6df9b6672a 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/messages/credential_exchange_webhook.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/messages/credential_exchange_webhook.py @@ -27,6 +27,8 @@ class LightWeightV10CredentialExchangeWebhook: "public_did", "cred_id_stored", "conn_id", + "created_at", + "updated_at", ] def __init__( diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/messages/cred_ex_record_webhook.py b/aries_cloudagent/protocols/issue_credential/v2_0/messages/cred_ex_record_webhook.py index 65cf999b7e..70e15814b0 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/messages/cred_ex_record_webhook.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/messages/cred_ex_record_webhook.py @@ -27,6 +27,8 @@ class LightWeightV20CredExRecordWebhook: "public_did", "cred_id_stored", "conn_id", + "created_at", + "updated_at", ] def __init__( From e38d0cca43e424db27903d98b5827b454bdb36fc Mon Sep 17 00:00:00 2001 From: Victor Lee Date: Thu, 23 Feb 2023 16:49:06 -0500 Subject: [PATCH 680/872] pres_exch light weight webhook object Signed-off-by: Victor Lee --- .../v1_0/messages/presentation_webhook.py | 39 +++++++++++++++++++ .../v2_0/messages/pres_webhook.py | 39 +++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 aries_cloudagent/protocols/present_proof/v1_0/messages/presentation_webhook.py create mode 100644 aries_cloudagent/protocols/present_proof/v2_0/messages/pres_webhook.py diff --git a/aries_cloudagent/protocols/present_proof/v1_0/messages/presentation_webhook.py b/aries_cloudagent/protocols/present_proof/v1_0/messages/presentation_webhook.py new file mode 100644 index 0000000000..080e50de69 --- /dev/null +++ b/aries_cloudagent/protocols/present_proof/v1_0/messages/presentation_webhook.py @@ -0,0 +1,39 @@ +"""v1.0 presentation exchange information webhook.""" + + +class LightWeightV10PresentationExchangeWebhook: + """Class representing a state only presentation exchange webhook.""" + + __acceptable_keys_list = [ + "connection_id", + "presentation_exchange_id", + "role", + "initiator", + "auto_present", + "auto_verify", + "error_msg", + "state", + "thread_id", + "trace", + "verified", + "verified_msgs", + "created_at", + "updated_at", + ] + + def __init__( + self, + **kwargs, + ): + """ + Initialize webhook object from V10PresentationExchange. + + from a list of accepted attributes. + """ + [ + self.__setattr__(key, kwargs.get(key)) + for key in self.__acceptable_keys_list + if kwargs.get(key) is not None + ] + if kwargs.get("_id") is not None: + self.presentation_exchange_id = kwargs.get("_id") diff --git a/aries_cloudagent/protocols/present_proof/v2_0/messages/pres_webhook.py b/aries_cloudagent/protocols/present_proof/v2_0/messages/pres_webhook.py new file mode 100644 index 0000000000..9325bde2a8 --- /dev/null +++ b/aries_cloudagent/protocols/present_proof/v2_0/messages/pres_webhook.py @@ -0,0 +1,39 @@ +"""v2.0 Presentation exchange record webhook.""" + + +class LightWeightV20PresExRecordWebhook: + """Class representing a state only Presentation exchange record webhook.""" + + __acceptable_keys_list = [ + "connection_id", + "pres_ex_id", + "role", + "initiator", + "auto_present", + "auto_verify", + "error_msg", + "thread_id", + "state", + "trace", + "verified", + "verified_msgs", + "created_at", + "updated_at", + ] + + def __init__( + self, + **kwargs, + ): + """ + Initialize webhook object from V20PresExRecord. + + from a list of accepted attributes. + """ + [ + self.__setattr__(key, kwargs.get(key)) + for key in self.__acceptable_keys_list + if kwargs.get(key) is not None + ] + if kwargs.get("_id") is not None: + self.pres_ex_id = kwargs.get("_id") From a3d869896abce2e5290110ba64d6abf9fcbf09ee Mon Sep 17 00:00:00 2001 From: Devin Lo Date: Fri, 24 Feb 2023 11:04:04 -0500 Subject: [PATCH 681/872] pres_exch emit light weight webhook Signed-off-by: Devin Lo --- .../v1_0/models/presentation_exchange.py | 28 +++++++++++++++++++ .../v2_0/models/pres_exchange.py | 28 +++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/aries_cloudagent/protocols/present_proof/v1_0/models/presentation_exchange.py b/aries_cloudagent/protocols/present_proof/v1_0/models/presentation_exchange.py index 80db45f86c..aff5182f43 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/models/presentation_exchange.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/models/presentation_exchange.py @@ -21,6 +21,7 @@ PresentationRequest, PresentationRequestSchema, ) +from ..messages.presentation_webhook import LightWeightV10PresentationExchangeWebhook from . import UNENCRYPTED_TAGS @@ -195,6 +196,33 @@ async def save_error_state( except StorageError as err: LOGGER.exception(err) + # Override + async def emit_event(self, session: ProfileSession, payload: Any = None): + """ + Emit an event. + + Args: + session: The profile session to use + payload: The event payload + """ + + if not self.RECORD_TOPIC: + return + + if self.state: + topic = f"{self.EVENT_NAMESPACE}::{self.RECORD_TOPIC}::{self.state}" + else: + topic = f"{self.EVENT_NAMESPACE}::{self.RECORD_TOPIC}" + + if not payload: + payload = self.serialize() + + if session.profile.settings.get("transport.light_weight_webhook"): + payload = LightWeightV10PresentationExchangeWebhook(**self.__dict__) + payload = payload.__dict__ + + await session.profile.notify(topic, payload) + @property def record_value(self) -> Mapping: """Accessor for the JSON record value generated for this credential exchange.""" diff --git a/aries_cloudagent/protocols/present_proof/v2_0/models/pres_exchange.py b/aries_cloudagent/protocols/present_proof/v2_0/models/pres_exchange.py index 4b5e22a10b..965a5a0c42 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/models/pres_exchange.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/models/pres_exchange.py @@ -15,6 +15,7 @@ from ..messages.pres_format import V20PresFormat from ..messages.pres_proposal import V20PresProposal, V20PresProposalSchema from ..messages.pres_request import V20PresRequest, V20PresRequestSchema +from ..messages.pres_webhook import LightWeightV20PresExRecordWebhook from . import UNENCRYPTED_TAGS @@ -181,6 +182,33 @@ async def save_error_state( except StorageError as err: LOGGER.exception(err) + # Override + async def emit_event(self, session: ProfileSession, payload: Any = None): + """ + Emit an event. + + Args: + session: The profile session to use + payload: The event payload + """ + + if not self.RECORD_TOPIC: + return + + if self.state: + topic = f"{self.EVENT_NAMESPACE}::{self.RECORD_TOPIC}::{self.state}" + else: + topic = f"{self.EVENT_NAMESPACE}::{self.RECORD_TOPIC}" + + if not payload: + payload = self.serialize() + + if session.profile.settings.get("transport.light_weight_webhook"): + payload = LightWeightV20PresExRecordWebhook(**self.__dict__) + payload = payload.__dict__ + + await session.profile.notify(topic, payload) + @property def record_value(self) -> Mapping: """Accessor for the JSON record value generated for this credential exchange.""" From 5316cd9b69309b4da435329bad20674f8353e425 Mon Sep 17 00:00:00 2001 From: Devin Lo Date: Fri, 24 Feb 2023 11:09:12 -0500 Subject: [PATCH 682/872] added --debug-webhooks flag Signed-off-by: Devin Lo --- aries_cloudagent/config/argparse.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index 2d32f54830..ac36ab4d4f 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -271,6 +271,15 @@ def add_arguments(self, parser: ArgumentParser): "Default: false." ), ) + parser.add_argument( + "--debug-webhooks", + action="store_true", + env_var="ACAPY_DEBUG_WEBHOOKS", + help=( + "Emit protocol state object as webhook. " + "Default: false." + ), + ) parser.add_argument( "--invite", action="store_true", @@ -424,6 +433,8 @@ def get_settings(self, args: Namespace) -> dict: settings["debug.credentials"] = True if args.debug_presentations: settings["debug.presentations"] = True + if args.debug_webhooks: + settings["debug.webhooks"] = True if args.debug_seed: settings["debug.seed"] = args.debug_seed if args.invite: From 5cd9a7e8178af388fb4aa2f160dfe0f54369ee54 Mon Sep 17 00:00:00 2001 From: Devin Lo Date: Fri, 24 Feb 2023 11:12:10 -0500 Subject: [PATCH 683/872] removed --light-weight-webhook flag Signed-off-by: Devin Lo --- aries_cloudagent/config/argparse.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index ac36ab4d4f..224d11f2d5 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -1320,12 +1320,6 @@ def add_arguments(self, parser: ArgumentParser): env_var="ACAPY_MAX_MESSAGE_SIZE", help="Set the maximum size in bytes for inbound agent messages.", ) - parser.add_argument( - "--light-weight-webhook", - action="store_true", - env_var="ACAPY_LIGHT_WEIGHT_WEBHOOK", - help="omitted client's info from issue-credential related webhook", - ) parser.add_argument( "--enable-undelivered-queue", action="store_true", @@ -1389,8 +1383,6 @@ def get_settings(self, args: Namespace): settings["image_url"] = args.image_url if args.max_message_size: settings["transport.max_message_size"] = args.max_message_size - if args.light_weight_webhook: - settings["transport.light_weight_webhook"] = True if args.max_outbound_retry: settings["transport.max_outbound_retry"] = args.max_outbound_retry if args.ws_heartbeat_interval: From b951d0dfa72f97aeec6608d364914fa3a74f45e0 Mon Sep 17 00:00:00 2001 From: Devin Lo Date: Fri, 24 Feb 2023 11:30:03 -0500 Subject: [PATCH 684/872] use --debug-webhooks flag for full protocol state object as webhook Signed-off-by: Devin Lo --- .../issue_credential/v1_0/models/credential_exchange.py | 8 ++++---- .../issue_credential/v2_0/models/cred_ex_record.py | 8 ++++---- .../present_proof/v1_0/models/presentation_exchange.py | 8 ++++---- .../protocols/present_proof/v2_0/models/pres_exchange.py | 8 ++++---- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py b/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py index 0daeb81e47..0634034ed8 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py @@ -242,10 +242,10 @@ async def emit_event(self, session: ProfileSession, payload: Any = None): else: topic = f"{self.EVENT_NAMESPACE}::{self.RECORD_TOPIC}" - if not payload: - payload = self.serialize() - - if session.profile.settings.get("transport.light_weight_webhook"): + if session.profile.settings.get("debug.webhooks"): + if not payload: + payload = self.serialize() + else: payload = LightWeightV10CredentialExchangeWebhook(**self.__dict__) payload = payload.__dict__ diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/models/cred_ex_record.py b/aries_cloudagent/protocols/issue_credential/v2_0/models/cred_ex_record.py index 448a82052a..e42d984910 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/models/cred_ex_record.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/models/cred_ex_record.py @@ -202,10 +202,10 @@ async def emit_event(self, session: ProfileSession, payload: Any = None): else: topic = f"{self.EVENT_NAMESPACE}::{self.RECORD_TOPIC}" - if not payload: - payload = self.serialize() - - if session.profile.settings.get("transport.light_weight_webhook"): + if session.profile.settings.get("debug.webhooks"): + if not payload: + payload = self.serialize() + else: payload = LightWeightV20CredExRecordWebhook(**self.__dict__) payload = payload.__dict__ diff --git a/aries_cloudagent/protocols/present_proof/v1_0/models/presentation_exchange.py b/aries_cloudagent/protocols/present_proof/v1_0/models/presentation_exchange.py index aff5182f43..31f2e619f4 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/models/presentation_exchange.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/models/presentation_exchange.py @@ -214,10 +214,10 @@ async def emit_event(self, session: ProfileSession, payload: Any = None): else: topic = f"{self.EVENT_NAMESPACE}::{self.RECORD_TOPIC}" - if not payload: - payload = self.serialize() - - if session.profile.settings.get("transport.light_weight_webhook"): + if session.profile.settings.get("debug.webhooks"): + if not payload: + payload = self.serialize() + else: payload = LightWeightV10PresentationExchangeWebhook(**self.__dict__) payload = payload.__dict__ diff --git a/aries_cloudagent/protocols/present_proof/v2_0/models/pres_exchange.py b/aries_cloudagent/protocols/present_proof/v2_0/models/pres_exchange.py index 965a5a0c42..8c0eaae9d1 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/models/pres_exchange.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/models/pres_exchange.py @@ -200,10 +200,10 @@ async def emit_event(self, session: ProfileSession, payload: Any = None): else: topic = f"{self.EVENT_NAMESPACE}::{self.RECORD_TOPIC}" - if not payload: - payload = self.serialize() - - if session.profile.settings.get("transport.light_weight_webhook"): + if session.profile.settings.get("debug.webhooks"): + if not payload: + payload = self.serialize() + else: payload = LightWeightV20PresExRecordWebhook(**self.__dict__) payload = payload.__dict__ From 42cd00896703a13c2966143b95ae91063aa0f542 Mon Sep 17 00:00:00 2001 From: Victor Lee Date: Fri, 24 Feb 2023 17:09:05 -0500 Subject: [PATCH 685/872] refactor: remove light weight keyword Signed-off-by: Victor Lee --- .../v1_0/messages/credential_exchange_webhook.py | 4 ++-- .../issue_credential/v1_0/models/credential_exchange.py | 4 ++-- .../issue_credential/v2_0/messages/cred_ex_record_webhook.py | 4 ++-- .../protocols/issue_credential/v2_0/models/cred_ex_record.py | 4 ++-- .../present_proof/v1_0/messages/presentation_webhook.py | 2 +- .../present_proof/v1_0/models/presentation_exchange.py | 4 ++-- .../protocols/present_proof/v2_0/messages/pres_webhook.py | 2 +- .../protocols/present_proof/v2_0/models/pres_exchange.py | 4 ++-- 8 files changed, 14 insertions(+), 14 deletions(-) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/messages/credential_exchange_webhook.py b/aries_cloudagent/protocols/issue_credential/v1_0/messages/credential_exchange_webhook.py index 6df9b6672a..52641ff8c7 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/messages/credential_exchange_webhook.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/messages/credential_exchange_webhook.py @@ -1,7 +1,7 @@ -"""v1.0 credential exchange light weight webhook.""" +"""v1.0 credential exchange webhook.""" -class LightWeightV10CredentialExchangeWebhook: +class V10CredentialExchangeWebhook: """Class representing a state only credential exchange webhook.""" __acceptable_keys_list = [ diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py b/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py index 0634034ed8..f12a82f258 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py @@ -18,7 +18,7 @@ from ..messages.credential_proposal import CredentialProposal, CredentialProposalSchema from ..messages.credential_offer import CredentialOffer, CredentialOfferSchema from ..messages.credential_exchange_webhook import ( - LightWeightV10CredentialExchangeWebhook, + V10CredentialExchangeWebhook, ) from . import UNENCRYPTED_TAGS @@ -246,7 +246,7 @@ async def emit_event(self, session: ProfileSession, payload: Any = None): if not payload: payload = self.serialize() else: - payload = LightWeightV10CredentialExchangeWebhook(**self.__dict__) + payload = V10CredentialExchangeWebhook(**self.__dict__) payload = payload.__dict__ await session.profile.notify(topic, payload) diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/messages/cred_ex_record_webhook.py b/aries_cloudagent/protocols/issue_credential/v2_0/messages/cred_ex_record_webhook.py index 70e15814b0..aa566db23d 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/messages/cred_ex_record_webhook.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/messages/cred_ex_record_webhook.py @@ -1,7 +1,7 @@ -"""v2.0 credential exchange light weight webhook.""" +"""v2.0 credential exchange webhook.""" -class LightWeightV20CredExRecordWebhook: +class V20CredExRecordWebhook: """Class representing a state only credential exchange webhook.""" __acceptable_keys_list = [ diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/models/cred_ex_record.py b/aries_cloudagent/protocols/issue_credential/v2_0/models/cred_ex_record.py index e42d984910..339c884c70 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/models/cred_ex_record.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/models/cred_ex_record.py @@ -17,7 +17,7 @@ from ..messages.cred_offer import V20CredOffer, V20CredOfferSchema from ..messages.cred_request import V20CredRequest, V20CredRequestSchema from ..messages.inner.cred_preview import V20CredPreviewSchema -from ..messages.cred_ex_record_webhook import LightWeightV20CredExRecordWebhook +from ..messages.cred_ex_record_webhook import V20CredExRecordWebhook from . import UNENCRYPTED_TAGS @@ -206,7 +206,7 @@ async def emit_event(self, session: ProfileSession, payload: Any = None): if not payload: payload = self.serialize() else: - payload = LightWeightV20CredExRecordWebhook(**self.__dict__) + payload = V20CredExRecordWebhook(**self.__dict__) payload = payload.__dict__ await session.profile.notify(topic, payload) diff --git a/aries_cloudagent/protocols/present_proof/v1_0/messages/presentation_webhook.py b/aries_cloudagent/protocols/present_proof/v1_0/messages/presentation_webhook.py index 080e50de69..0a58f25e71 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/messages/presentation_webhook.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/messages/presentation_webhook.py @@ -1,7 +1,7 @@ """v1.0 presentation exchange information webhook.""" -class LightWeightV10PresentationExchangeWebhook: +class V10PresentationExchangeWebhook: """Class representing a state only presentation exchange webhook.""" __acceptable_keys_list = [ diff --git a/aries_cloudagent/protocols/present_proof/v1_0/models/presentation_exchange.py b/aries_cloudagent/protocols/present_proof/v1_0/models/presentation_exchange.py index 31f2e619f4..98a49bed34 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/models/presentation_exchange.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/models/presentation_exchange.py @@ -21,7 +21,7 @@ PresentationRequest, PresentationRequestSchema, ) -from ..messages.presentation_webhook import LightWeightV10PresentationExchangeWebhook +from ..messages.presentation_webhook import V10PresentationExchangeWebhook from . import UNENCRYPTED_TAGS @@ -218,7 +218,7 @@ async def emit_event(self, session: ProfileSession, payload: Any = None): if not payload: payload = self.serialize() else: - payload = LightWeightV10PresentationExchangeWebhook(**self.__dict__) + payload = V10PresentationExchangeWebhook(**self.__dict__) payload = payload.__dict__ await session.profile.notify(topic, payload) diff --git a/aries_cloudagent/protocols/present_proof/v2_0/messages/pres_webhook.py b/aries_cloudagent/protocols/present_proof/v2_0/messages/pres_webhook.py index 9325bde2a8..cec96cd089 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/messages/pres_webhook.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/messages/pres_webhook.py @@ -1,7 +1,7 @@ """v2.0 Presentation exchange record webhook.""" -class LightWeightV20PresExRecordWebhook: +class V20PresExRecordWebhook: """Class representing a state only Presentation exchange record webhook.""" __acceptable_keys_list = [ diff --git a/aries_cloudagent/protocols/present_proof/v2_0/models/pres_exchange.py b/aries_cloudagent/protocols/present_proof/v2_0/models/pres_exchange.py index 8c0eaae9d1..ec798b2803 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/models/pres_exchange.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/models/pres_exchange.py @@ -15,7 +15,7 @@ from ..messages.pres_format import V20PresFormat from ..messages.pres_proposal import V20PresProposal, V20PresProposalSchema from ..messages.pres_request import V20PresRequest, V20PresRequestSchema -from ..messages.pres_webhook import LightWeightV20PresExRecordWebhook +from ..messages.pres_webhook import V20PresExRecordWebhook from . import UNENCRYPTED_TAGS @@ -204,7 +204,7 @@ async def emit_event(self, session: ProfileSession, payload: Any = None): if not payload: payload = self.serialize() else: - payload = LightWeightV20PresExRecordWebhook(**self.__dict__) + payload = V20PresExRecordWebhook(**self.__dict__) payload = payload.__dict__ await session.profile.notify(topic, payload) From 89fc81391a58ba78a2e9185d6212cd87ba2d3da7 Mon Sep 17 00:00:00 2001 From: Victor Lee Date: Sat, 25 Feb 2023 16:21:01 -0500 Subject: [PATCH 686/872] argparse.py fixed black code format Signed-off-by: Victor Lee --- aries_cloudagent/config/argparse.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index 224d11f2d5..2951e6969f 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -275,10 +275,7 @@ def add_arguments(self, parser: ArgumentParser): "--debug-webhooks", action="store_true", env_var="ACAPY_DEBUG_WEBHOOKS", - help=( - "Emit protocol state object as webhook. " - "Default: false." - ), + help=("Emit protocol state object as webhook. " "Default: false."), ) parser.add_argument( "--invite", From 310902960fa4c21ebe0608f31dcccdc4b00d0ba0 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Mon, 27 Feb 2023 04:32:50 -0800 Subject: [PATCH 687/872] check impl Signed-off-by: Shaanjot Gill --- aries_cloudagent/admin/server.py | 4 +- aries_cloudagent/core/dispatcher.py | 49 ++++- .../core/tests/test_dispatcher.py | 199 +++++++++++++++++- aries_cloudagent/messaging/responder.py | 92 +++++++- .../protocols/connections/v1_0/routes.py | 4 + .../connections/v1_0/tests/test_routes.py | 22 ++ 6 files changed, 358 insertions(+), 12 deletions(-) diff --git a/aries_cloudagent/admin/server.py b/aries_cloudagent/admin/server.py index def29588eb..544bc8f585 100644 --- a/aries_cloudagent/admin/server.py +++ b/aries_cloudagent/admin/server.py @@ -124,7 +124,9 @@ def __init__( self._profile = weakref.ref(profile) self._send = send - async def send_outbound(self, message: OutboundMessage) -> OutboundSendStatus: + async def send_outbound( + self, message: OutboundMessage, **kwargs + ) -> OutboundSendStatus: """ Send outbound message. diff --git a/aries_cloudagent/core/dispatcher.py b/aries_cloudagent/core/dispatcher.py index bfc6f95e4a..ca371806b9 100644 --- a/aries_cloudagent/core/dispatcher.py +++ b/aries_cloudagent/core/dispatcher.py @@ -7,6 +7,7 @@ import asyncio import logging +import json import os import warnings @@ -15,7 +16,6 @@ from aiohttp.web import HTTPException - from ..connections.models.conn_record import ConnRecord from ..core.profile import Profile from ..messaging.agent_message import AgentMessage @@ -377,7 +377,28 @@ async def create_outbound( return await super().create_outbound(message, **kwargs) - async def send_outbound(self, message: OutboundMessage) -> OutboundSendStatus: + async def _get_msg_type_from_enc_payload( + self, profile: Profile, parsed_msg: dict + ) -> Optional[Tuple[str, str]]: + """Get message type and id tuple from enc_payload.""" + try: + if not isinstance(parsed_msg, dict): + return None + message_type = parsed_msg.get("@type") + if not message_type: + return None + registry: ProtocolRegistry = profile.inject(ProtocolRegistry) + message_cls = registry.resolve_message_class(message_type) + if not message_cls: + return None + instance = message_cls.deserialize(parsed_msg) + return instance._message_type, instance._id + except (ProtocolMinorVersionNotSupported, BaseModelError, AttributeError): + return None + + async def send_outbound( + self, message: OutboundMessage, **kwargs + ) -> OutboundSendStatus: """ Send outbound message. @@ -388,6 +409,30 @@ async def send_outbound(self, message: OutboundMessage) -> OutboundSendStatus: if not context: raise RuntimeError("weakref to context has expired") + msg_type = kwargs.get("message_type") + msg_id = kwargs.get("message_id") + if not msg_type and not msg_id and (message.enc_payload or message.payload): + msg_dict = json.loads(message.enc_payload or message.payload) + msg_type_id_tuple = await self._get_msg_type_from_enc_payload( + context.profile, msg_dict + ) + if msg_type_id_tuple: + msg_type, msg_id = msg_type_id_tuple + + if ( + message.connection_id + and msg_type + and not await super().conn_rec_active_state_check( + profile=context.profile, + connection_id=message.connection_id, + msg_type=msg_type, + ) + ): + raise RuntimeError( + f"Connection {message.connection_id} is not ready" + " which is required for sending outbound" + f" message {msg_id} of type {msg_type}." + ) return await self._send(context.profile, message, self._inbound_message) async def send_webhook(self, topic: str, payload: dict): diff --git a/aries_cloudagent/core/tests/test_dispatcher.py b/aries_cloudagent/core/tests/test_dispatcher.py index ffca07ce14..ec18696731 100644 --- a/aries_cloudagent/core/tests/test_dispatcher.py +++ b/aries_cloudagent/core/tests/test_dispatcher.py @@ -6,6 +6,8 @@ from marshmallow import EXCLUDE +from ...cache.base import BaseCache +from ...cache.in_memory import InMemoryCache from ...config.injection_context import InjectionContext from ...core.event_bus import EventBus from ...core.in_memory import InMemoryProfile @@ -413,12 +415,129 @@ async def test_create_send_outbound(self): profile, settings={"timing.enabled": True}, ) + registry = profile.inject(ProtocolRegistry) + registry.register_message_types( + { + pfx.qualify(StubAgentMessage.Meta.message_type): StubAgentMessage + for pfx in DIDCommPrefix + } + ) message = StubAgentMessage() responder = test_module.DispatcherResponder(context, message, None) - outbound_message = await responder.create_outbound(message) - with async_mock.patch.object(responder, "_send", async_mock.AsyncMock()): + outbound_message = await responder.create_outbound( + json.dumps(message.serialize()) + ) + with async_mock.patch.object( + responder, "_send", async_mock.AsyncMock() + ), async_mock.patch.object( + test_module.BaseResponder, + "conn_rec_active_state_check", + async_mock.AsyncMock(return_value=True), + ): await responder.send_outbound(outbound_message) + async def test_create_send_outbound_with_msg_attrs(self): + profile = make_profile() + context = RequestContext( + profile, + settings={"timing.enabled": True}, + ) + registry = profile.inject(ProtocolRegistry) + registry.register_message_types( + { + pfx.qualify(StubAgentMessage.Meta.message_type): StubAgentMessage + for pfx in DIDCommPrefix + } + ) + message = StubAgentMessage() + responder = test_module.DispatcherResponder(context, message, None) + outbound_message = await responder.create_outbound(message) + with async_mock.patch.object( + responder, "_send", async_mock.AsyncMock() + ), async_mock.patch.object( + test_module.BaseResponder, + "conn_rec_active_state_check", + async_mock.AsyncMock(return_value=True), + ): + await responder.send_outbound( + message=outbound_message, + message_type=message._message_type, + message_id=message._id, + ) + + async def test_create_send_outbound_with_msg_attrs_x(self): + profile = make_profile() + context = RequestContext( + profile, + settings={"timing.enabled": True}, + ) + registry = profile.inject(ProtocolRegistry) + registry.register_message_types( + { + pfx.qualify(StubAgentMessage.Meta.message_type): StubAgentMessage + for pfx in DIDCommPrefix + } + ) + message = StubAgentMessage() + responder = test_module.DispatcherResponder(context, message, None) + outbound_message = await responder.create_outbound(message) + outbound_message.connection_id = "123" + with async_mock.patch.object( + test_module.BaseResponder, + "conn_rec_active_state_check", + async_mock.AsyncMock(return_value=False), + ): + with self.assertRaises(RuntimeError): + await responder.send_outbound( + message=outbound_message, + message_type=message._message_type, + message_id=message._id, + ) + + async def test_get_msg_type_from_enc_payload(self): + profile = make_profile() + context = RequestContext( + profile, + settings={"timing.enabled": True}, + ) + message = StubAgentMessage() + responder = test_module.DispatcherResponder(context, message, None) + assert not await responder._get_msg_type_from_enc_payload( + profile, ["...", "..."] + ) + assert not await responder._get_msg_type_from_enc_payload( + profile, {"...": "..."} + ) + msg_dict = message.serialize() + not await responder._get_msg_type_from_enc_payload(profile, msg_dict) + registry = profile.inject(ProtocolRegistry) + registry.register_message_types( + { + pfx.qualify(StubAgentMessage.Meta.message_type): StubAgentMessage + for pfx in DIDCommPrefix + } + ) + message = StubAgentMessage() + responder = test_module.DispatcherResponder(context, message, None) + message._id = "test123" + msg_dict = message.serialize() + msg_type, msg_id = await responder._get_msg_type_from_enc_payload( + profile, msg_dict + ) + assert msg_type == StubAgentMessage.Meta.message_type + assert msg_id == "test123" + + msg_dict["@type"] = "proto-name/3.0/message-type" + with async_mock.patch.object( + registry, "resolve_message_class", async_mock.MagicMock() + ) as mock_resolve: + mock_resolve.return_value = async_mock.MagicMock( + deserialize=async_mock.MagicMock( + side_effect=test_module.ProtocolMinorVersionNotSupported() + ) + ) + assert not await responder._get_msg_type_from_enc_payload(profile, msg_dict) + async def test_create_send_webhook(self): profile = make_profile() context = RequestContext(profile) @@ -427,10 +546,65 @@ async def test_create_send_webhook(self): with pytest.deprecated_call(): await responder.send_webhook("topic", {"pay": "load"}) + async def test_conn_rec_active_state_check_a(self): + profile = make_profile() + profile.context.injector.bind_instance(BaseCache, InMemoryCache()) + context = RequestContext(profile) + message = StubAgentMessage() + responder = test_module.DispatcherResponder(context, message, None) + check_flag = await responder.conn_rec_active_state_check( + profile, + "conn-id", + "didexchange/1.0/request", + ) + assert check_flag + with async_mock.patch.object( + test_module.ConnRecord, "retrieve_by_id", async_mock.AsyncMock() + ) as mock_conn_ret_by_id: + conn_rec = test_module.ConnRecord() + conn_rec.state = test_module.ConnRecord.State.COMPLETED + mock_conn_ret_by_id.return_value = conn_rec + check_flag = await responder.conn_rec_active_state_check( + profile, + "conn-id", + "proto-name/1.1/message-type", + ) + assert check_flag + check_flag = await responder.conn_rec_active_state_check( + profile, + "conn-id", + "proto-name/2.1/message-type", + ) + assert check_flag + + async def test_conn_rec_active_state_check_b(self): + profile = make_profile() + profile.context.injector.bind_instance(BaseCache, InMemoryCache()) + profile.context.injector.bind_instance( + EventBus, async_mock.MagicMock(notify=async_mock.AsyncMock()) + ) + context = RequestContext(profile) + message = StubAgentMessage() + responder = test_module.DispatcherResponder(context, message, None) + with async_mock.patch.object( + test_module.ConnRecord, "retrieve_by_id", async_mock.AsyncMock() + ) as mock_conn_ret_by_id: + conn_rec_a = test_module.ConnRecord() + conn_rec_a.state = test_module.ConnRecord.State.REQUEST + conn_rec_b = test_module.ConnRecord() + conn_rec_b.state = test_module.ConnRecord.State.COMPLETED + mock_conn_ret_by_id.side_effect = [conn_rec_a, conn_rec_b] + check_flag = await responder.conn_rec_active_state_check( + profile, + "conn-id", + "proto-name/1.1/message-type", + ) + assert check_flag + async def test_create_enc_outbound(self): profile = make_profile() context = RequestContext(profile) - message = b"abc123xyz7890000" + message = StubAgentMessage() responder = test_module.DispatcherResponder(context, message, None) with async_mock.patch.object( responder, "send_outbound", async_mock.AsyncMock() @@ -438,6 +612,25 @@ async def test_create_enc_outbound(self): await responder.send(message) assert mock_send_outbound.called_once() + message = b"abc123xyz7890000" + with async_mock.patch.object( + responder, "send_outbound", async_mock.AsyncMock() + ) as mock_send_outbound: + await responder.send(message) + + message = StubAgentMessage() + with async_mock.patch.object( + responder, "send_outbound", async_mock.AsyncMock() + ) as mock_send_outbound: + await responder.send_reply(message) + assert mock_send_outbound.called_once() + + message = b"abc123xyz7890000" + with async_mock.patch.object( + responder, "send_outbound", async_mock.AsyncMock() + ) as mock_send_outbound: + await responder.send_reply(message) + async def test_expired_context_x(self): def _smaller_scope(): profile = make_profile() diff --git a/aries_cloudagent/messaging/responder.py b/aries_cloudagent/messaging/responder.py index 32c5af641a..d7e7de0513 100644 --- a/aries_cloudagent/messaging/responder.py +++ b/aries_cloudagent/messaging/responder.py @@ -4,13 +4,19 @@ The responder is provided to message handlers to enable them to send a new message in response to the message being handled. """ +import asyncio +import json +import re from abc import ABC, abstractmethod -import json -from typing import Sequence, Union +from typing import Sequence, Union, Optional, Tuple +from ..cache.base import BaseCache from ..connections.models.connection_target import ConnectionTarget +from ..connections.models.conn_record import ConnRecord from ..core.error import BaseError +from ..core.event_bus import EventBus +from ..core.profile import Profile from ..transport.outbound.message import OutboundMessage from .base_message import BaseMessage @@ -79,7 +85,14 @@ async def send( ) -> OutboundSendStatus: """Convert a message to an OutboundMessage and send it.""" outbound = await self.create_outbound(message, **kwargs) - return await self.send_outbound(outbound) + try: + return await self.send_outbound( + message=outbound, + message_type=message._message_type, + message_id=message._id, + ) + except AttributeError: + return await self.send_outbound(message=outbound) async def send_reply( self, @@ -109,10 +122,75 @@ async def send_reply( target=target, target_list=target_list, ) - return await self.send_outbound(outbound) + try: + return await self.send_outbound( + outbound, message_type=message._message_type, message_id=message._id + ) + except AttributeError: + return await self.send_outbound(outbound) + + async def conn_rec_active_state_check( + self, profile: Profile, connection_id: str, msg_type: str, timeout: int = 7 + ) -> bool: + """Check if the connection record is ready for sending outbound message.""" + CONNECTION_READY_EVENT = re.compile( + "^acapy::record::connections::(active|completed|response)$" + ) + WHITELIST_MSG_TYPE = [ + "didexchange/1.0/request", + "didexchange/1.0/response", + "connections/1.0/invitation", + "connections/1.0/request", + "connections/1.0/response", + ] + if msg_type in WHITELIST_MSG_TYPE: + return True + + async def _wait_for_state() -> Tuple[bool, Optional[str]]: + async with profile.session() as session: + conn_record = await ConnRecord.retrieve_by_id(session, connection_id) + if conn_record.is_ready: + return (True, conn_record.state) + event = profile.inject(EventBus) + with event.wait_for_event( + profile, + CONNECTION_READY_EVENT, + lambda event: event.payload.get("connection_id") == connection_id, + ) as await_event: + async with profile.session() as session: + conn_record = await ConnRecord.retrieve_by_id( + session, connection_id + ) + if conn_record.is_ready: + return (True, conn_record.state) + event = await await_event + conn_record = ConnRecord.deserialize(event.payload) + return (True, conn_record.state) + + try: + cache_key = f"conn_rec_state::{connection_id}" + connection_state = None + cache = profile.inject_or(BaseCache) + if cache: + connection_state = await cache.get(cache_key) + if connection_state and ConnRecord.State.get(connection_state) in ( + ConnRecord.State.COMPLETED, + ConnRecord.State.RESPONSE, + ): + return True + check_flag, connection_state = await asyncio.wait_for( + _wait_for_state(), timeout + ) + if cache and connection_state: + await cache.set(cache_key, connection_state) + return check_flag + except asyncio.TimeoutError: + return False @abstractmethod - async def send_outbound(self, message: OutboundMessage) -> OutboundSendStatus: + async def send_outbound( + self, message: OutboundMessage, **kwargs + ) -> OutboundSendStatus: """ Send an outbound message. @@ -152,7 +230,9 @@ async def send_reply( self.messages.append((message, kwargs)) return OutboundSendStatus.QUEUED_FOR_DELIVERY - async def send_outbound(self, message: OutboundMessage) -> OutboundSendStatus: + async def send_outbound( + self, message: OutboundMessage, **kwargs + ) -> OutboundSendStatus: """Send an outbound message.""" self.messages.append((message, None)) return OutboundSendStatus.QUEUED_FOR_DELIVERY diff --git a/aries_cloudagent/protocols/connections/v1_0/routes.py b/aries_cloudagent/protocols/connections/v1_0/routes.py index 3da5e71f95..11d8ba5651 100644 --- a/aries_cloudagent/protocols/connections/v1_0/routes.py +++ b/aries_cloudagent/protocols/connections/v1_0/routes.py @@ -15,6 +15,7 @@ from ....admin.request_context import AdminRequestContext from ....connections.models.conn_record import ConnRecord, ConnRecordSchema +from ....cache.base import BaseCache from ....messaging.models.base import BaseModelError from ....messaging.models.openapi import OpenAPISchema from ....messaging.valid import ( @@ -739,6 +740,9 @@ async def connections_remove(request: web.BaseRequest): async with profile.session() as session: connection = await ConnRecord.retrieve_by_id(session, connection_id) await connection.delete_record(session) + cache = session.inject_or(BaseCache) + if cache: + await cache.clear(f"conn_rec_state::{connection_id}") except StorageNotFoundError as err: raise web.HTTPNotFound(reason=err.roll_up) from err except StorageError as err: diff --git a/aries_cloudagent/protocols/connections/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/connections/v1_0/tests/test_routes.py index 274ef8b6e6..adc23cf242 100644 --- a/aries_cloudagent/protocols/connections/v1_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/connections/v1_0/tests/test_routes.py @@ -5,6 +5,8 @@ from asynctest import mock as async_mock from .....admin.request_context import AdminRequestContext +from .....cache.base import BaseCache +from .....cache.in_memory import InMemoryCache from .....connections.models.conn_record import ConnRecord from .....storage.error import StorageNotFoundError @@ -705,6 +707,26 @@ async def test_connections_remove(self): await test_module.connections_remove(self.request) mock_response.assert_called_once_with({}) + async def test_connections_remove_cache_key(self): + cache = InMemoryCache() + profile = self.context.profile + await cache.set("conn_rec_state::dummy", "active") + profile.context.injector.bind_instance(BaseCache, cache) + self.request.match_info = {"conn_id": "dummy"} + mock_conn_rec = async_mock.MagicMock() + mock_conn_rec.delete_record = async_mock.CoroutineMock() + assert (await cache.get("conn_rec_state::dummy")) == "active" + with async_mock.patch.object( + test_module.ConnRecord, "retrieve_by_id", async_mock.CoroutineMock() + ) as mock_conn_rec_retrieve_by_id, async_mock.patch.object( + test_module.web, "json_response" + ) as mock_response: + mock_conn_rec_retrieve_by_id.return_value = mock_conn_rec + + await test_module.connections_remove(self.request) + mock_response.assert_called_once_with({}) + assert not (await cache.get("conn_rec_state::dummy")) + async def test_connections_remove_not_found(self): self.request.match_info = {"conn_id": "dummy"} From 2da7608c90567fc3b311fff6518a6ad2b5d233d1 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Mon, 27 Feb 2023 09:26:34 -0800 Subject: [PATCH 688/872] [fix] Removes extra comma that prevents swagger from accepting the presentation request Addresses issue #2146 Signed-off-by: Stephen Curran Signed-off-by: Stephen Curran Signed-off-by: Stephen Curran --- demo/AliceWantsAJsonCredential.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/AliceWantsAJsonCredential.md b/demo/AliceWantsAJsonCredential.md index 9626d18f21..8243968b64 100644 --- a/demo/AliceWantsAJsonCredential.md +++ b/demo/AliceWantsAJsonCredential.md @@ -376,7 +376,7 @@ To request a proof, submit the following (with appropriate `connection_id`) to F "directive": "required", "field_id": [ "1f44d55f-f161-4938-a659-f8026467f126" - ], + ] } ], "fields": [ From 3c9bdc9ce4d65c26c6412ccfb6ba4e713690e0db Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Tue, 28 Feb 2023 08:32:59 -0800 Subject: [PATCH 689/872] performance demo fix Signed-off-by: Shaanjot Gill --- demo/runners/performance.py | 12 ++++++++++-- demo/runners/support/agent.py | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/demo/runners/performance.py b/demo/runners/performance.py index 02b6d3283e..4c79bf6437 100644 --- a/demo/runners/performance.py +++ b/demo/runners/performance.py @@ -108,7 +108,15 @@ async def check_received_creds(self) -> Tuple[int, int]: pending = 0 total = len(self.credential_state) for result in self.credential_state.values(): - if result != "done" and result != "credential_acked": + # Since cred_ex_record is set to be auto-removed + # the state in self.credential_state for completed + # exchanges will be deleted. Any problematic + # exchanges will be in abandoned state. + if ( + result != "done" + and result != "deleted" + and result != "credential_acked" + ): pending += 1 if self.credential_event.is_set(): continue @@ -422,7 +430,7 @@ async def check_received_creds(agent, issue_count, pb): pending, total = await agent.check_received_creds() complete = total - pending if reported == complete: - await asyncio.wait_for(agent.update_creds(), 30) + await asyncio.wait_for(agent.update_creds(), 45) continue if iter_pb and complete > reported: try: diff --git a/demo/runners/support/agent.py b/demo/runners/support/agent.py index 1ec42565dc..0aa7f995ed 100644 --- a/demo/runners/support/agent.py +++ b/demo/runners/support/agent.py @@ -724,7 +724,7 @@ def _terminate(self): if self.proc and self.proc.poll() is None: self.proc.terminate() try: - self.proc.wait(timeout=0.5) + self.proc.wait(timeout=1.5) self.log(f"Exited with return code {self.proc.returncode}") except subprocess.TimeoutExpired: msg = "Process did not terminate in time" From 1a47c3a7d178db87bfa09e9c1904a44a26f7c19e Mon Sep 17 00:00:00 2001 From: Victor Lee Date: Thu, 2 Mar 2023 16:16:30 -0500 Subject: [PATCH 690/872] Dockerfile.demo: added ACAPY_DEBUG_WEBHOOKS as startup env Signed-off-by: Victor Lee --- docker/Dockerfile.demo | 1 + 1 file changed, 1 insertion(+) diff --git a/docker/Dockerfile.demo b/docker/Dockerfile.demo index 89dcba682d..cd15363bf0 100644 --- a/docker/Dockerfile.demo +++ b/docker/Dockerfile.demo @@ -3,6 +3,7 @@ FROM bcgovimages/von-image:py36-1.15-1 ENV ENABLE_PTVSD 0 ENV ENABLE_PYDEVD_PYCHARM 0 ENV PYDEVD_PYCHARM_HOST "host.docker.internal" +ENV ACAPY_DEBUG_WEBHOOKS 1 RUN mkdir bin && curl -L -o bin/jq \ https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64 && \ From 9014ce3fd9b35a7f67bc99bb8112185be0e680a6 Mon Sep 17 00:00:00 2001 From: Pritam Singh Date: Fri, 3 Mar 2023 13:56:40 +0530 Subject: [PATCH 691/872] feat: add aca-py deployment with nginx Signed-off-by: Pritam Singh --- Dockerfile | 28 ++++++++++++++++++++++++++++ deployment/nginx.conf | 23 +++++++++++++++++++++++ deployment/start.sh | 8 ++++++++ 3 files changed, 59 insertions(+) create mode 100644 Dockerfile create mode 100644 deployment/nginx.conf create mode 100644 deployment/start.sh diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000..d9c441110c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,28 @@ +FROM bcgovimages/von-image:py36-1.16-1 + +ADD requirements*.txt ./ + +RUN pip3 install --no-cache-dir \ + -r requirements.txt \ + -r requirements.askar.txt \ + -r requirements.bbs.txt \ + -r requirements.dev.txt + +RUN mkdir aries_cloudagent && touch aries_cloudagent/__init__.py +ADD aries_cloudagent/version.py aries_cloudagent/version.py +ADD bin ./bin +ADD README.md ./ +ADD setup.py ./ + +RUN pip3 install --no-cache-dir -e . +ADD aries_cloudagent ./aries_cloudagent + +USER root +RUN apt-get -y update && apt-get -y install nginx + +EXPOSE 5000 + +COPY ./deployment/nginx.conf /etc/nginx/conf.d/default.conf +COPY ./deployment/start.sh ./start.sh + +ENTRYPOINT ["/bin/bash", "./start.sh"] \ No newline at end of file diff --git a/deployment/nginx.conf b/deployment/nginx.conf new file mode 100644 index 0000000000..7398a85f49 --- /dev/null +++ b/deployment/nginx.conf @@ -0,0 +1,23 @@ +events {} + +http{ + server { + listen 5000; + + # Notice the additional / at the end of the proxy_pass directive. + # NGINX will strip the matched prefix /cloudRunAdmin and pass the remainder + # to the backend server at the URI /. + # Therefore, http://myserver:80/cloudRunAdmin/api will + # post to the backend at http://localhost:11021/api + location / { + proxy_pass http://127.0.0.1:11021/; + } + + location /agent { + proxy_pass http://127.0.0.1:11020/; + } + } +} + +daemon off; +pid /run/nginx.pid; \ No newline at end of file diff --git a/deployment/start.sh b/deployment/start.sh new file mode 100644 index 0000000000..06a0c33bd0 --- /dev/null +++ b/deployment/start.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -e + +aca-py start --inbound-transport http 0.0.0.0 11020 --outbound-transport http --admin 0.0.0.0 11021 & + +nginx -c "/etc/nginx/conf.d/default.conf" & + +wait -n \ No newline at end of file From 51e355a018edc944fc3b5b7775663f4939649aba Mon Sep 17 00:00:00 2001 From: Pritam Singh Date: Fri, 3 Mar 2023 14:41:27 +0530 Subject: [PATCH 692/872] fix: add connection_id to oob proof request pres Signed-off-by: Pritam Singh --- aries_cloudagent/protocols/present_proof/v1_0/manager.py | 1 + aries_cloudagent/protocols/present_proof/v2_0/manager.py | 1 + 2 files changed, 2 insertions(+) diff --git a/aries_cloudagent/protocols/present_proof/v1_0/manager.py b/aries_cloudagent/protocols/present_proof/v1_0/manager.py index 50cf219c41..a92522ae92 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/manager.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/manager.py @@ -325,6 +325,7 @@ async def receive_presentation( ) = await V10PresentationExchange.retrieve_by_tag_filter( session, {"thread_id": thread_id}, None ) + presentation_exchange_record.connection_id = connection_record.connection_id # Check for bait-and-switch in presented attribute values vs. proposal if presentation_exchange_record.presentation_proposal_dict: diff --git a/aries_cloudagent/protocols/present_proof/v2_0/manager.py b/aries_cloudagent/protocols/present_proof/v2_0/manager.py index 688f4ca35f..b6d055f770 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/manager.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/manager.py @@ -319,6 +319,7 @@ async def receive_pres(self, message: V20Pres, conn_record: ConnRecord): pres_ex_record = await V20PresExRecord.retrieve_by_tag_filter( session, {"thread_id": thread_id}, None ) + pres_ex_record.connection_id = conn_record.connection_id input_formats = message.formats From 36304c512333b383bfc0890378c79df4a36e1448 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Humbert?= Date: Mon, 6 Mar 2023 11:56:47 +0100 Subject: [PATCH 693/872] fix: askar exception message always displaying null DID MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Clément Humbert --- aries_cloudagent/wallet/askar.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/aries_cloudagent/wallet/askar.py b/aries_cloudagent/wallet/askar.py index d104d32d36..dac9a4cf5d 100644 --- a/aries_cloudagent/wallet/askar.py +++ b/aries_cloudagent/wallet/askar.py @@ -267,12 +267,12 @@ async def get_local_did(self, did: str) -> DIDInfo: if not did: raise WalletNotFoundError("No identifier provided") try: - did = await self._session.handle.fetch(CATEGORY_DID, did) + did_entry = await self._session.handle.fetch(CATEGORY_DID, did) except AskarError as err: raise WalletError("Error when fetching local DID") from err - if not did: + if not did_entry: raise WalletNotFoundError("Unknown DID: {}".format(did)) - return self._load_did_entry(did) + return self._load_did_entry(did_entry) async def get_local_did_for_verkey(self, verkey: str) -> DIDInfo: """ From b9aae368d8b1ae53e6cf5f900038abee609d4fef Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Thu, 9 Mar 2023 11:11:48 -0800 Subject: [PATCH 694/872] updates based on feedback Signed-off-by: Shaanjot Gill --- aries_cloudagent/core/dispatcher.py | 31 +------- .../core/tests/test_dispatcher.py | 59 +------------- aries_cloudagent/messaging/responder.py | 78 +++++++++---------- 3 files changed, 41 insertions(+), 127 deletions(-) diff --git a/aries_cloudagent/core/dispatcher.py b/aries_cloudagent/core/dispatcher.py index ca371806b9..062a33f3a1 100644 --- a/aries_cloudagent/core/dispatcher.py +++ b/aries_cloudagent/core/dispatcher.py @@ -7,7 +7,6 @@ import asyncio import logging -import json import os import warnings @@ -23,7 +22,7 @@ from ..messaging.error import MessageParseError from ..messaging.models.base import BaseModelError from ..messaging.request_context import RequestContext -from ..messaging.responder import BaseResponder +from ..messaging.responder import BaseResponder, SKIP_ACTIVE_CONN_CHECK_MSG_TYPES from ..messaging.util import datetime_now from ..protocols.connections.v1_0.manager import ConnectionManager from ..protocols.problem_report.v1_0.message import ProblemReport @@ -377,25 +376,6 @@ async def create_outbound( return await super().create_outbound(message, **kwargs) - async def _get_msg_type_from_enc_payload( - self, profile: Profile, parsed_msg: dict - ) -> Optional[Tuple[str, str]]: - """Get message type and id tuple from enc_payload.""" - try: - if not isinstance(parsed_msg, dict): - return None - message_type = parsed_msg.get("@type") - if not message_type: - return None - registry: ProtocolRegistry = profile.inject(ProtocolRegistry) - message_cls = registry.resolve_message_class(message_type) - if not message_cls: - return None - instance = message_cls.deserialize(parsed_msg) - return instance._message_type, instance._id - except (ProtocolMinorVersionNotSupported, BaseModelError, AttributeError): - return None - async def send_outbound( self, message: OutboundMessage, **kwargs ) -> OutboundSendStatus: @@ -411,21 +391,14 @@ async def send_outbound( msg_type = kwargs.get("message_type") msg_id = kwargs.get("message_id") - if not msg_type and not msg_id and (message.enc_payload or message.payload): - msg_dict = json.loads(message.enc_payload or message.payload) - msg_type_id_tuple = await self._get_msg_type_from_enc_payload( - context.profile, msg_dict - ) - if msg_type_id_tuple: - msg_type, msg_id = msg_type_id_tuple if ( message.connection_id and msg_type + and msg_type not in SKIP_ACTIVE_CONN_CHECK_MSG_TYPES and not await super().conn_rec_active_state_check( profile=context.profile, connection_id=message.connection_id, - msg_type=msg_type, ) ): raise RuntimeError( diff --git a/aries_cloudagent/core/tests/test_dispatcher.py b/aries_cloudagent/core/tests/test_dispatcher.py index ec18696731..450bfb4767 100644 --- a/aries_cloudagent/core/tests/test_dispatcher.py +++ b/aries_cloudagent/core/tests/test_dispatcher.py @@ -494,50 +494,6 @@ async def test_create_send_outbound_with_msg_attrs_x(self): message_id=message._id, ) - async def test_get_msg_type_from_enc_payload(self): - profile = make_profile() - context = RequestContext( - profile, - settings={"timing.enabled": True}, - ) - message = StubAgentMessage() - responder = test_module.DispatcherResponder(context, message, None) - assert not await responder._get_msg_type_from_enc_payload( - profile, ["...", "..."] - ) - assert not await responder._get_msg_type_from_enc_payload( - profile, {"...": "..."} - ) - msg_dict = message.serialize() - not await responder._get_msg_type_from_enc_payload(profile, msg_dict) - registry = profile.inject(ProtocolRegistry) - registry.register_message_types( - { - pfx.qualify(StubAgentMessage.Meta.message_type): StubAgentMessage - for pfx in DIDCommPrefix - } - ) - message = StubAgentMessage() - responder = test_module.DispatcherResponder(context, message, None) - message._id = "test123" - msg_dict = message.serialize() - msg_type, msg_id = await responder._get_msg_type_from_enc_payload( - profile, msg_dict - ) - assert msg_type == StubAgentMessage.Meta.message_type - assert msg_id == "test123" - - msg_dict["@type"] = "proto-name/3.0/message-type" - with async_mock.patch.object( - registry, "resolve_message_class", async_mock.MagicMock() - ) as mock_resolve: - mock_resolve.return_value = async_mock.MagicMock( - deserialize=async_mock.MagicMock( - side_effect=test_module.ProtocolMinorVersionNotSupported() - ) - ) - assert not await responder._get_msg_type_from_enc_payload(profile, msg_dict) - async def test_create_send_webhook(self): profile = make_profile() context = RequestContext(profile) @@ -552,12 +508,6 @@ async def test_conn_rec_active_state_check_a(self): context = RequestContext(profile) message = StubAgentMessage() responder = test_module.DispatcherResponder(context, message, None) - check_flag = await responder.conn_rec_active_state_check( - profile, - "conn-id", - "didexchange/1.0/request", - ) - assert check_flag with async_mock.patch.object( test_module.ConnRecord, "retrieve_by_id", async_mock.AsyncMock() ) as mock_conn_ret_by_id: @@ -567,13 +517,11 @@ async def test_conn_rec_active_state_check_a(self): check_flag = await responder.conn_rec_active_state_check( profile, "conn-id", - "proto-name/1.1/message-type", ) assert check_flag check_flag = await responder.conn_rec_active_state_check( profile, "conn-id", - "proto-name/2.1/message-type", ) assert check_flag @@ -597,7 +545,6 @@ async def test_conn_rec_active_state_check_b(self): check_flag = await responder.conn_rec_active_state_check( profile, "conn-id", - "proto-name/1.1/message-type", ) assert check_flag @@ -611,8 +558,8 @@ async def test_create_enc_outbound(self): ) as mock_send_outbound: await responder.send(message) assert mock_send_outbound.called_once() - - message = b"abc123xyz7890000" + msg_json = json.dumps(StubAgentMessage().serialize()) + message = msg_json.encode("utf-8") with async_mock.patch.object( responder, "send_outbound", async_mock.AsyncMock() ) as mock_send_outbound: @@ -625,7 +572,7 @@ async def test_create_enc_outbound(self): await responder.send_reply(message) assert mock_send_outbound.called_once() - message = b"abc123xyz7890000" + message = json.dumps(StubAgentMessage().serialize()) with async_mock.patch.object( responder, "send_outbound", async_mock.AsyncMock() ) as mock_send_outbound: diff --git a/aries_cloudagent/messaging/responder.py b/aries_cloudagent/messaging/responder.py index d7e7de0513..75913a0645 100644 --- a/aries_cloudagent/messaging/responder.py +++ b/aries_cloudagent/messaging/responder.py @@ -6,7 +6,6 @@ """ import asyncio import json -import re from abc import ABC, abstractmethod from typing import Sequence, Union, Optional, Tuple @@ -15,13 +14,20 @@ from ..connections.models.connection_target import ConnectionTarget from ..connections.models.conn_record import ConnRecord from ..core.error import BaseError -from ..core.event_bus import EventBus from ..core.profile import Profile from ..transport.outbound.message import OutboundMessage from .base_message import BaseMessage from ..transport.outbound.status import OutboundSendStatus +SKIP_ACTIVE_CONN_CHECK_MSG_TYPES = [ + "didexchange/1.0/request", + "didexchange/1.0/response", + "connections/1.0/invitation", + "connections/1.0/request", + "connections/1.0/response", +] + class ResponderError(BaseError): """Responder error.""" @@ -85,14 +91,18 @@ async def send( ) -> OutboundSendStatus: """Convert a message to an OutboundMessage and send it.""" outbound = await self.create_outbound(message, **kwargs) - try: - return await self.send_outbound( - message=outbound, - message_type=message._message_type, - message_id=message._id, - ) - except AttributeError: - return await self.send_outbound(message=outbound) + if isinstance(message, BaseMessage): + msg_type = message._message_type + msg_id = message._id + else: + msg_dict = json.loads(message) + msg_type = msg_dict.get("@type") + msg_id = msg_dict.get("@id") + return await self.send_outbound( + message=outbound, + message_type=msg_type, + message_id=msg_id, + ) async def send_reply( self, @@ -122,50 +132,34 @@ async def send_reply( target=target, target_list=target_list, ) - try: - return await self.send_outbound( - outbound, message_type=message._message_type, message_id=message._id - ) - except AttributeError: - return await self.send_outbound(outbound) + if isinstance(message, BaseMessage): + msg_type = message._message_type + msg_id = message._id + else: + msg_dict = json.loads(message) + msg_type = msg_dict.get("@type") + msg_id = msg_dict.get("@id") + return await self.send_outbound( + message=outbound, message_type=msg_type, message_id=msg_id + ) async def conn_rec_active_state_check( - self, profile: Profile, connection_id: str, msg_type: str, timeout: int = 7 + self, profile: Profile, connection_id: str, timeout: int = 7 ) -> bool: """Check if the connection record is ready for sending outbound message.""" - CONNECTION_READY_EVENT = re.compile( - "^acapy::record::connections::(active|completed|response)$" - ) - WHITELIST_MSG_TYPE = [ - "didexchange/1.0/request", - "didexchange/1.0/response", - "connections/1.0/invitation", - "connections/1.0/request", - "connections/1.0/response", - ] - if msg_type in WHITELIST_MSG_TYPE: - return True async def _wait_for_state() -> Tuple[bool, Optional[str]]: - async with profile.session() as session: - conn_record = await ConnRecord.retrieve_by_id(session, connection_id) - if conn_record.is_ready: - return (True, conn_record.state) - event = profile.inject(EventBus) - with event.wait_for_event( - profile, - CONNECTION_READY_EVENT, - lambda event: event.payload.get("connection_id") == connection_id, - ) as await_event: + while True: async with profile.session() as session: conn_record = await ConnRecord.retrieve_by_id( session, connection_id ) if conn_record.is_ready: + # if ConnRecord.State.get(conn_record.state) in ( + # ConnRecord.State.COMPLETED, + # ): return (True, conn_record.state) - event = await await_event - conn_record = ConnRecord.deserialize(event.payload) - return (True, conn_record.state) + await asyncio.sleep(1) try: cache_key = f"conn_rec_state::{connection_id}" From 1e3af0b9ef2884c62375a846449495f94fde5348 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Mon, 13 Mar 2023 19:38:24 -0700 Subject: [PATCH 695/872] Remove CircleCI Status since we aren't using CircleCI anymore Signed-off-by: Stephen Curran Signed-off-by: Stephen Curran Signed-off-by: Stephen Curran --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index f5fc6cd485..f1f1f18157 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ # Hyperledger Aries Cloud Agent - Python [![pypi releases](https://img.shields.io/pypi/v/aries_cloudagent)](https://pypi.org/project/aries-cloudagent/) -[![CircleCI](https://circleci.com/gh/hyperledger/aries-cloudagent-python.svg?style=shield)](https://circleci.com/gh/hyperledger/aries-cloudagent-python) [![codecov](https://codecov.io/gh/hyperledger/aries-cloudagent-python/branch/main/graph/badge.svg)](https://codecov.io/gh/hyperledger/aries-cloudagent-python) From a878e4cbbaca2a80fc8613f9b11f39d171553d47 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Mon, 13 Mar 2023 19:53:14 -0700 Subject: [PATCH 696/872] Delete circleci config file Signed-off-by: Stephen Curran Signed-off-by: Stephen Curran Signed-off-by: Stephen Curran --- .circleci/config.yml | 48 -------------------------------------------- 1 file changed, 48 deletions(-) delete mode 100644 .circleci/config.yml diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index f753ba2685..0000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,48 +0,0 @@ -version: 2 -jobs: - agent-build: - docker: - - image: bcgovimages/von-image:py36-1.15-1 - steps: - - checkout - - restore_cache: - keys: - - v5-pip-dependencies-{{ .Branch }}-{{ checksum "requirements.txt" }}-{{ checksum "requirements.dev.txt" }}-{{ checksum "requirements.bbs.txt" }} - - v5-pip-dependencies-{{ .Branch }}- - - run: - name: Install Python Dependencies - command: | - pip install \ - --user \ - -r requirements.txt \ - -r requirements.askar.txt \ - -r requirements.bbs.txt \ - -r requirements.dev.txt - - - save_cache: - paths: - - /home/indy/.local/lib/python3.6/site-packages - key: v5-pip-dependencies-{{ .Branch }}-{{ checksum "requirements.txt" }}-{{ checksum "requirements.dev.txt" }}-{{ checksum "requirements.bbs.txt" }}-{{ checksum "requirements.askar.txt" }} - - - run: - name: Run Agent Tests - command: | - [ ! -d test-reports ] && mkdir test-reports - python -m pytest - - - run: - name: Push to Codecov.io - command: | - bash <(curl -s https://codecov.io/bash) -f test-reports/coverage.xml - - - store_test_results: - path: test-reports - - - store_artifacts: - path: test-reports - -workflows: - version: 2 - aries_cloudagent: - jobs: - - agent-build From 2774cd5d3dca9504c3617cb7ae49c177e1dba202 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Tue, 14 Mar 2023 15:27:31 -0700 Subject: [PATCH 697/872] 0.8.0 release Signed-off-by: Stephen Curran --- CHANGELOG.md | 69 +++++++++--- PUBLISHING.md | 13 ++- aries_cloudagent/version.py | 2 +- docs/README.md | 12 +-- docs/conf.py | 3 + docs/generated/aries_cloudagent.admin.rst | 32 +++--- .../aries_cloudagent.askar.didcomm.rst | 20 ++-- docs/generated/aries_cloudagent.askar.rst | 23 ++-- docs/generated/aries_cloudagent.cache.rst | 20 ++-- docs/generated/aries_cloudagent.commands.rst | 32 +++--- docs/generated/aries_cloudagent.config.rst | 98 ++++++++--------- ...s_cloudagent.connections.models.diddoc.rst | 32 +++--- .../aries_cloudagent.connections.models.rst | 23 ++-- .../aries_cloudagent.connections.rst | 17 +-- ...ries_cloudagent.core.in_memory.didcomm.rst | 20 ++-- .../aries_cloudagent.core.in_memory.rst | 17 +-- docs/generated/aries_cloudagent.core.rst | 71 ++++++------ docs/generated/aries_cloudagent.did.rst | 14 +-- docs/generated/aries_cloudagent.holder.rst | 14 +-- .../generated/aries_cloudagent.indy.credx.rst | 26 ++--- .../aries_cloudagent.indy.models.rst | 92 ++++++++-------- docs/generated/aries_cloudagent.indy.rst | 39 +++---- docs/generated/aries_cloudagent.indy.sdk.rst | 56 +++++----- ...es_cloudagent.ledger.merkel_validation.rst | 44 ++++---- ...ries_cloudagent.ledger.multiple_ledger.rst | 44 ++++---- docs/generated/aries_cloudagent.ledger.rst | 55 +++++----- ...agent.messaging.credential_definitions.rst | 20 ++-- .../aries_cloudagent.messaging.decorators.rst | 74 ++++++------- .../aries_cloudagent.messaging.jsonld.rst | 32 +++--- .../aries_cloudagent.messaging.models.rst | 26 ++--- docs/generated/aries_cloudagent.messaging.rst | 67 ++++++------ .../aries_cloudagent.messaging.schemas.rst | 20 ++-- .../aries_cloudagent.multitenant.admin.rst | 14 +-- .../aries_cloudagent.multitenant.rst | 53 ++++----- .../aries_cloudagent.protocols.actionmenu.rst | 17 +-- ...ent.protocols.actionmenu.v1_0.handlers.rst | 26 ++--- ...ent.protocols.actionmenu.v1_0.messages.rst | 26 ++--- ...agent.protocols.actionmenu.v1_0.models.rst | 26 ++--- ...s_cloudagent.protocols.actionmenu.v1_0.rst | 51 ++++----- ...ries_cloudagent.protocols.basicmessage.rst | 17 +-- ...t.protocols.basicmessage.v1_0.handlers.rst | 14 +-- ...t.protocols.basicmessage.v1_0.messages.rst | 14 +-- ...cloudagent.protocols.basicmessage.v1_0.rst | 25 ++--- ...aries_cloudagent.protocols.connections.rst | 17 +-- ...nt.protocols.connections.v1_0.handlers.rst | 26 ++--- ...nt.protocols.connections.v1_0.messages.rst | 32 +++--- ...gent.protocols.connections.v1_0.models.rst | 14 +-- ..._cloudagent.protocols.connections.v1_0.rst | 33 +++--- ...udagent.protocols.coordinate_mediation.rst | 23 ++-- ...ols.coordinate_mediation.v1_0.handlers.rst | 56 +++++----- ...ordinate_mediation.v1_0.messages.inner.rst | 32 +++--- ...ols.coordinate_mediation.v1_0.messages.rst | 59 +++++----- ...ocols.coordinate_mediation.v1_0.models.rst | 14 +-- ...nt.protocols.coordinate_mediation.v1_0.rst | 57 +++++----- ...aries_cloudagent.protocols.didexchange.rst | 17 +-- ...nt.protocols.didexchange.v1_0.handlers.rst | 32 +++--- ...nt.protocols.didexchange.v1_0.messages.rst | 32 +++--- ..._cloudagent.protocols.didexchange.v1_0.rst | 31 +++--- .../aries_cloudagent.protocols.discovery.rst | 19 ++-- ...gent.protocols.discovery.v1_0.handlers.rst | 20 ++-- ...gent.protocols.discovery.v1_0.messages.rst | 20 ++-- ...dagent.protocols.discovery.v1_0.models.rst | 14 +-- ...es_cloudagent.protocols.discovery.v1_0.rst | 33 +++--- ...gent.protocols.discovery.v2_0.handlers.rst | 20 ++-- ...gent.protocols.discovery.v2_0.messages.rst | 20 ++-- ...dagent.protocols.discovery.v2_0.models.rst | 14 +-- ...es_cloudagent.protocols.discovery.v2_0.rst | 33 +++--- ...oudagent.protocols.endorse_transaction.rst | 17 +-- ...cols.endorse_transaction.v1_0.handlers.rst | 50 ++++----- ...cols.endorse_transaction.v1_0.messages.rst | 56 +++++----- ...tocols.endorse_transaction.v1_0.models.rst | 14 +-- ...ent.protocols.endorse_transaction.v1_0.rst | 51 ++++----- ...ries_cloudagent.protocols.introduction.rst | 17 +-- ...t.protocols.introduction.v0_1.handlers.rst | 26 ++--- ...t.protocols.introduction.v0_1.messages.rst | 26 ++--- ...cloudagent.protocols.introduction.v0_1.rst | 37 +++---- ..._cloudagent.protocols.issue_credential.rst | 19 ++-- ...otocols.issue_credential.v1_0.handlers.rst | 44 ++++---- ...s.issue_credential.v1_0.messages.inner.rst | 14 +-- ...otocols.issue_credential.v1_0.messages.rst | 55 ++++++---- ...protocols.issue_credential.v1_0.models.rst | 14 +-- ...dagent.protocols.issue_credential.v1_0.rst | 39 +++---- ...ols.issue_credential.v2_0.formats.indy.rst | 14 +-- ...redential.v2_0.formats.ld_proof.models.rst | 20 ++-- ...issue_credential.v2_0.formats.ld_proof.rst | 17 +-- ...rotocols.issue_credential.v2_0.formats.rst | 19 ++-- ...otocols.issue_credential.v2_0.handlers.rst | 44 ++++---- ...s.issue_credential.v2_0.messages.inner.rst | 14 +-- ...otocols.issue_credential.v2_0.messages.rst | 61 ++++++----- ...ls.issue_credential.v2_0.models.detail.rst | 20 ++-- ...protocols.issue_credential.v2_0.models.rst | 17 +-- ...dagent.protocols.issue_credential.v2_0.rst | 41 +++---- ...ries_cloudagent.protocols.notification.rst | 17 +-- ...t.protocols.notification.v1_0.handlers.rst | 14 +-- ...t.protocols.notification.v1_0.messages.rst | 14 +-- ...cloudagent.protocols.notification.v1_0.rst | 19 ++-- ...aries_cloudagent.protocols.out_of_band.rst | 17 +-- ...nt.protocols.out_of_band.v1_0.handlers.rst | 26 ++--- ...nt.protocols.out_of_band.v1_0.messages.rst | 38 +++---- ...gent.protocols.out_of_band.v1_0.models.rst | 20 ++-- ..._cloudagent.protocols.out_of_band.v1_0.rst | 39 +++---- ...cloudagent.protocols.present_proof.dif.rst | 38 +++---- ...loudagent.protocols.present_proof.indy.rst | 14 +-- ...ies_cloudagent.protocols.present_proof.rst | 23 ++-- ....protocols.present_proof.v1_0.handlers.rst | 38 +++---- ....protocols.present_proof.v1_0.messages.rst | 46 ++++---- ...nt.protocols.present_proof.v1_0.models.rst | 14 +-- ...loudagent.protocols.present_proof.v1_0.rst | 39 +++---- ...otocols.present_proof.v2_0.formats.dif.rst | 14 +-- ...tocols.present_proof.v2_0.formats.indy.rst | 14 +-- ...t.protocols.present_proof.v2_0.formats.rst | 19 ++-- ....protocols.present_proof.v2_0.handlers.rst | 38 +++---- ....protocols.present_proof.v2_0.messages.rst | 52 +++++---- ...nt.protocols.present_proof.v2_0.models.rst | 14 +-- ...loudagent.protocols.present_proof.v2_0.rst | 41 +++---- ...es_cloudagent.protocols.problem_report.rst | 17 +-- ...oudagent.protocols.problem_report.v1_0.rst | 26 ++--- ...gent.protocols.revocation_notification.rst | 19 ++-- ....revocation_notification.v1_0.handlers.rst | 14 +-- ....revocation_notification.v1_0.messages.rst | 14 +-- ...ls.revocation_notification.v1_0.models.rst | 14 +-- ...protocols.revocation_notification.v1_0.rst | 27 ++--- ....revocation_notification.v2_0.handlers.rst | 14 +-- ....revocation_notification.v2_0.messages.rst | 14 +-- ...ls.revocation_notification.v2_0.models.rst | 14 +-- ...protocols.revocation_notification.v2_0.rst | 27 ++--- .../aries_cloudagent.protocols.routing.rst | 17 +-- ...dagent.protocols.routing.v1_0.handlers.rst | 38 +++---- ...dagent.protocols.routing.v1_0.messages.rst | 38 +++---- ...oudagent.protocols.routing.v1_0.models.rst | 44 ++++---- ...ries_cloudagent.protocols.routing.v1_0.rst | 27 ++--- docs/generated/aries_cloudagent.protocols.rst | 49 ++++----- .../aries_cloudagent.protocols.trustping.rst | 17 +-- ...gent.protocols.trustping.v1_0.handlers.rst | 20 ++-- ...gent.protocols.trustping.v1_0.messages.rst | 20 ++-- ...es_cloudagent.protocols.trustping.v1_0.rst | 25 ++--- .../aries_cloudagent.resolver.default.rst | 32 +++--- docs/generated/aries_cloudagent.resolver.rst | 29 ++--- .../aries_cloudagent.revocation.models.rst | 32 +++--- .../generated/aries_cloudagent.revocation.rst | 47 ++++---- docs/generated/aries_cloudagent.rst | 61 +++++------ docs/generated/aries_cloudagent.storage.rst | 47 ++++---- .../aries_cloudagent.storage.vc_holder.rst | 44 ++++---- docs/generated/aries_cloudagent.tails.rst | 26 ++--- .../aries_cloudagent.transport.inbound.rst | 56 +++++----- .../aries_cloudagent.transport.outbound.rst | 44 ++++---- .../aries_cloudagent.transport.queue.rst | 20 ++-- docs/generated/aries_cloudagent.transport.rst | 39 +++---- docs/generated/aries_cloudagent.utils.rst | 68 ++++++------ .../aries_cloudagent.vc.ld_proofs.crypto.rst | 20 ++-- ...aries_cloudagent.vc.ld_proofs.purposes.rst | 38 +++---- .../aries_cloudagent.vc.ld_proofs.rst | 57 +++++----- .../aries_cloudagent.vc.ld_proofs.suites.rst | 50 ++++----- docs/generated/aries_cloudagent.vc.rst | 12 +-- .../aries_cloudagent.vc.vc_ld.models.rst | 20 ++-- docs/generated/aries_cloudagent.vc.vc_ld.rst | 35 +++--- .../aries_cloudagent.wallet.models.rst | 14 +-- docs/generated/aries_cloudagent.wallet.rst | 101 +++++++++--------- open-api/openapi.json | 2 +- 159 files changed, 2571 insertions(+), 2250 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0af211ba2b..e3fe2756c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,10 @@ -# 0.8.0-rc0 +# 0.8.0 -## February 8, 2023 +## March 14, 2023 0.8.0 is a breaking change that contains all updates since release 0.7.5. It extends the previously tagged `1.0.0-rc1` release because it is not clear when -that release will be finalized. Many of the PRs in this release were previously +the 1.0.0 release will be finalized. Many of the PRs in this release were previously included in the `1.0.0-rc1` release. The categorized list of PRs separates those that are new from those in the `1.0.0-rc1` release candidate. @@ -19,6 +19,12 @@ Endorsers. A new repo has been created that is a pre-configured instance of ACA-Py for use as an Endorser service. +A recently completed feature that is outside of ACA-Py is a script to migrate +existing ACA-Py storage from Indy SDK format to Aries Askar format. This +enables existing deployments to switch to using the newer Aries Askar +components. For details see the converter in the +[aries-acapy-tools](https://github.com/hyperledger/aries-acapy-tools) repository. + ### Container Publishing Updated With this release, a new automated process publishes container images in the @@ -32,19 +38,19 @@ container image publication process can be found in the document [Container Images and Github Actions]. The ACA-Py container images are based on [Python 3.6 and 3.9 `slim-bullseye` -images](https://hub.docker.com/_/python), and are built to support `linux/386 -(x86)`, `linux/amd64 (x64)`, and `linux/arm64`. There are two flavors of image -built for each Python version. One contains only the Indy/Aries Shared Libraries -only ([Aries Askar](https://github.com/hyperledger/aries-askar), [Indy +images](https://hub.docker.com/_/python), and are designed to support `linux/386 +(x86)`, `linux/amd64 (x64)`, and `linux/arm64`. However, for this release, the +publication of multi-architecture containers is disabled. We are working to +enable that through the updating of some dependencies that lack that capability. +There are two flavors of image built for each Python version. One contains only +the Indy/Aries Shared Libraries only ([Aries +Askar](https://github.com/hyperledger/aries-askar), [Indy VDR](https://github.com/hyperledger/indy-vdr) and [Indy Shared RS](https://github.com/hyperledger/indy-shared-rs), supporting only the use of `--wallet-type askar`). The other (labelled `indy`) contains the Indy/Aries shared libraries and the Indy SDK (considered deprecated). For new deployments, we recommend using the Python 3.9 Shared Library images. For existing -deployments, we recommend migrating to those images. For those migrating an Indy -SDK deployment, a new secure storage database migration capability from Indy SDK -to Aries Askar is available--contact the ACA-Py maintainers on Hyperledger -Discord for details. +deployments, we recommend migrating to those images. Those currently using the container images published by [BC Gov on Docker Hub](https://hub.docker.com/r/bcgovimages/aries-cloudagent) should change to use @@ -56,7 +62,6 @@ aries-cloudagent-python]. [publish-indy.yml]: https://github.com/hyperledger/aries-cloudagent-python/blob/main/.github/workflows/publish-indy.yml [Container Images and Github Actions]: https://github.com/hyperledger/aries-cloudagent-python/blob/main/ContainerImagesAndGithubActions.md - ## Breaking Changes ### PR [\#2034](https://github.com/hyperledger/aries-cloudagent-python/pull/2034) -- Implicit connections @@ -79,10 +84,26 @@ that can be checked if for unrevealed attributes. As few implementations of Aries wallets support unrevealed attributes in an AnonCreds presentation, this is unlikely to impact any deployments. +### PR [\#2145](https://github.com/hyperledger/aries-cloudagent-python/pull/2145) - Update webhook message to terse form by default, added startup flag --debug-webhooks for full form + +The default behavior in ACA-Py has been to keep the full text of all messages in +the protocol state object, and include the full protocol state object in the +webhooks sent to the controller. When the messages include an object that is +very large in all the messages, the webhook may become too big to be passed via +HTTP. For example, issuing a credential with a photo as one of the claims may +result in a number of copies of the photo in the protocol state object and +hence, very large webhooks. This change reduces the size of the webhook message +by eliminating redundant data in the protocol state of the "Issue Credential" +message as the default, and adds a new parameter to use the old behavior. + ### Categorized List of Pull Requests - Verifiable credential, presentation and revocation handling updates - - Feature: enabled handling VPs \(request, creation, verification\) with different VCs [\#1956](https://github.com/hyperledger/aries-cloudagent-python/pull/1956) ([teanas](https://github.com/teanas)) + - **BREAKING:** Update webhook message to terse form [default, added startup flag --debug-webhooks for full form [\#2145](https://github.com/hyperledger/aries-cloudagent-python/pull/2145) by [victorlee0505](victorlee0505) + - Add startup flag --light-weight-webhook to trim down outbound webhook payload [\#1941](https://github.com/hyperledger/aries-cloudagent-python/pull/1941) [victorlee0505](https://github.com/victorlee0505) + - feat: add verification method issue-credentials-2.0/send endpoint [\#2135](https://github.com/hyperledger/aries-cloudagent-python/pull/2135) [chumbert](https://github.com/chumbert) + - Respect auto-verify-presentation flag in present proof v1 and v2 [\#2097](https://github.com/hyperledger/aries-cloudagent-python/pull/2097) [dbluhm](https://github.com/dbluhm) + - Feature: enabled handling VPs (request, creation, verification) with different VCs [\#1956](https://github.com/hyperledger/aries-cloudagent-python/pull/1956) ([teanas](https://github.com/teanas)) - fix: update issue-credential endpoint summaries [\#1997](https://github.com/hyperledger/aries-cloudagent-python/pull/1997) ([PeterStrob](https://github.com/PeterStrob)) - fix claim format designation in presentation submission [\#2013](https://github.com/hyperledger/aries-cloudagent-python/pull/2013) ([rmnre](https://github.com/rmnre)) - \#2041 - Issue JSON-LD has invalid Admin API documentation [\#2046](https://github.com/hyperledger/aries-cloudagent-python/pull/2046) ([jfblier-amplitude](https://github.com/jfblier-amplitude)) @@ -97,9 +118,10 @@ is unlikely to impact any deployments. - Send webhooks upon record/credential deletion [\#1906](https://github.com/hyperledger/aries-cloudagent-python/pull/1906) ([frostyfrog](https://github.com/frostyfrog)) - Out of Band (OOB) and DID Exchange / Connection Handling / Mediator + - UPGRADE: Fix multi-use invitation performance [\#2116](https://github.com/hyperledger/aries-cloudagent-python/pull/2116) [reflectivedevelopment](https://github.com/reflectivedevelopment) - fix: public did mediator routing keys as did keys [\#1977](https://github.com/hyperledger/aries-cloudagent-python/pull/1977) ([dbluhm](https://github.com/dbluhm)) - Fix for mediator load testing race condition when scaling horizontally [\#2009](https://github.com/hyperledger/aries-cloudagent-python/pull/2009) ([ianco](https://github.com/ianco)) - - BREAKING: Allow multi-use public invites and public invites with metadata [\#2034](https://github.com/hyperledger/aries-cloudagent-python/pull/2034) ([mepeltier](https://github.com/mepeltier)) + - **BREAKING:** Allow multi-use public invites and public invites with metadata [\#2034](https://github.com/hyperledger/aries-cloudagent-python/pull/2034) ([mepeltier](https://github.com/mepeltier)) - Do not reject OOB invitation with unknown handshake protocol\(s\) [\#2060](https://github.com/hyperledger/aries-cloudagent-python/pull/2060) ([andrewwhitehead](https://github.com/andrewwhitehead)) - fix: fix connection timing bug [\#2099](https://github.com/hyperledger/aries-cloudagent-python/pull/2099) ([reflectivedevelopment](https://github.com/reflectivedevelopment)) - Previously flagged in release 1.0.0-rc1 @@ -112,6 +134,8 @@ is unlikely to impact any deployments. - Feat/public did endpoints for agents behind mediators [\#1899](https://github.com/hyperledger/aries-cloudagent-python/pull/1899) ([cjhowland](https://github.com/cjhowland)) - DID Registration and Resolution related updates + - feat: allow marking non-SOV DIDs as public [\#2144](https://github.com/hyperledger/aries-cloudagent-python/pull/2144) [chumbert](https://github.com/chumbert) + - fix: askar exception message always displaying null DID [\#2155](https://github.com/hyperledger/aries-cloudagent-python/pull/2155) [chumbert](https://github.com/chumbert) - feat: enable creation of DIDs for all registered methods [\#2067](https://github.com/hyperledger/aries-cloudagent-python/pull/2067) ([chumbert](https://github.com/chumbert)) - fix: create local DID return schema [\#2086](https://github.com/hyperledger/aries-cloudagent-python/pull/2086) ([chumbert](https://github.com/chumbert)) - feat: universal resolver - configurable authentication [\#2095](https://github.com/hyperledger/aries-cloudagent-python/pull/2095) ([chumbert](https://github.com/chumbert)) @@ -125,6 +149,7 @@ is unlikely to impact any deployments. - Use did:key for recipient keys [\#1886](https://github.com/hyperledger/aries-cloudagent-python/pull/1886) ([frostyfrog](https://github.com/frostyfrog)) - Hyperledger Indy Endorser/Author Transaction Handling + - Update some of the demo Readme and Endorser instructions [\#2122](https://github.com/hyperledger/aries-cloudagent-python/pull/2122) [swcurran](https://github.com/swcurran) - Special handling for the write ledger [\#2030](https://github.com/hyperledger/aries-cloudagent-python/pull/2030) ([ianco](https://github.com/ianco)) - Previously flagged in release 1.0.0-rc1 - Fix/txn job setting [\#1994](https://github.com/hyperledger/aries-cloudagent-python/pull/1994) ([ianco](https://github.com/ianco)) @@ -132,7 +157,14 @@ is unlikely to impact any deployments. - Endorser write DID transaction [\#1938](https://github.com/hyperledger/aries-cloudagent-python/pull/1938) ([ianco](https://github.com/ianco)) - Endorser doc updates and some bug fixes [\#1926](https://github.com/hyperledger/aries-cloudagent-python/pull/1926) ([ianco](https://github.com/ianco)) +- Admin API Additions + - fix: response type on delete-tails-files endpoint [\#2133](https://github.com/hyperledger/aries-cloudagent-python/pull/2133) [chumbert](https://github.com/chumbert) + - OpenAPI validation fixes [\#2127](https://github.com/hyperledger/aries-cloudagent-python/pull/2127) [loneil](https://github.com/loneil) + - Delete tail files [\#2103](https://github.com/hyperledger/aries-cloudagent-python/pull/2103) [ramreddychalla94](https://github.com/ramreddychalla94) + - Startup Command Line / Environment / YAML Parameter Updates + - Update webhook message to terse form [default, added startup flag --debug-webhooks for full form [\#2145](https://github.com/hyperledger/aries-cloudagent-python/pull/2145) by [victorlee0505](victorlee0505) + - Add startup flag --light-weight-webhook to trim down outbound webhook payload [\#1941](https://github.com/hyperledger/aries-cloudagent-python/pull/1941) [victorlee0505](https://github.com/victorlee0505) - Add missing --mediator-connections-invite cmd arg info to docs [\#2051](https://github.com/hyperledger/aries-cloudagent-python/pull/2051) ([matrixik](https://github.com/matrixik)) - Issue \#2068 boolean flag change to support HEAD requests to default route [\#2077](https://github.com/hyperledger/aries-cloudagent-python/pull/2077) ([johnekent](https://github.com/johnekent)) - Previously flagged in release 1.0.0-rc1 @@ -168,6 +200,11 @@ is unlikely to impact any deployments. - Update pip-audit.yml [\#1944](https://github.com/hyperledger/aries-cloudagent-python/pull/1944) ([ryjones](https://github.com/ryjones)) - Dependency, Python version, GitHub Actions and Container Image Changes + - Remove CircleCI Status since we aren't using CircleCI anymore [\#2163](https://github.com/hyperledger/aries-cloudagent-python/pull/2163) [swcurran](https://github.com/swcurran) + - Update ACA-Py docker files to produce OpenShift compatible images [\#2130](https://github.com/hyperledger/aries-cloudagent-python/pull/2130) [WadeBarnes](https://github.com/WadeBarnes) + - Temporarily disable multi-architecture image builds [\#2125](https://github.com/hyperledger/aries-cloudagent-python/pull/2125) [WadeBarnes](https://github.com/WadeBarnes) + - Fix ACA-py image builds [\#2123](https://github.com/hyperledger/aries-cloudagent-python/pull/2123) [WadeBarnes](https://github.com/WadeBarnes) + - Fix publish workflows [\#2117](https://github.com/hyperledger/aries-cloudagent-python/pull/2117) [WadeBarnes](https://github.com/WadeBarnes) - fix: indy dependency version format [\#2054](https://github.com/hyperledger/aries-cloudagent-python/pull/2054) ([chumbert](https://github.com/chumbert)) - ci: add gha for pr-tests [\#2058](https://github.com/hyperledger/aries-cloudagent-python/pull/2058) ([dbluhm](https://github.com/dbluhm)) - ci: test additional versions of python nightly [\#2059](https://github.com/hyperledger/aries-cloudagent-python/pull/2059) ([dbluhm](https://github.com/dbluhm)) @@ -181,6 +218,10 @@ is unlikely to impact any deployments. - chore: update pydid [\#1915](https://github.com/hyperledger/aries-cloudagent-python/pull/1915) ([dbluhm](https://github.com/dbluhm)) - Demo and Documentation Updates + - [fix] Removes extra comma that prevents swagger from accepting the presentation request [\#2149](https://github.com/hyperledger/aries-cloudagent-python/pull/2149) [swcurran](https://github.com/swcurran) + - Initial plugin docs [\#2138](https://github.com/hyperledger/aries-cloudagent-python/pull/2138) [ianco](https://github.com/ianco) + - Acme workshop [\#2137](https://github.com/hyperledger/aries-cloudagent-python/pull/2137) [ianco](https://github.com/ianco) + - Fix: Performance Demo [no --revocation] [\#2151](https://github.com/ hyperledger/aries-cloudagent-python/pull/2151) [shaangill025](https://github.com/shaangill025) - Fix typos in alice-local.sh & faber-local.sh [\#2010](https://github.com/hyperledger/aries-cloudagent-python/pull/2010) ([naonishijima](https://github.com/naonishijima)) - Added a bit about manually creating a revoc reg tails file [\#2012](https://github.com/hyperledger/aries-cloudagent-python/pull/2012) ([ianco](https://github.com/ianco)) - Add ability to set docker container name [\#2024](https://github.com/hyperledger/aries-cloudagent-python/pull/2024) ([matrixik](https://github.com/matrixik)) diff --git a/PUBLISHING.md b/PUBLISHING.md index 4e561692dc..13f7921055 100644 --- a/PUBLISHING.md +++ b/PUBLISHING.md @@ -53,17 +53,18 @@ s/^/- / release (using in the GitHub UI a filter such as `is:pr is:merged sort:updated merged:>2022-04-07`) and for each page, highlight, and copy the text of only the list of PRs on the page to use in the following step. -- For each page, run the command `sed -e :a -e '$!N;s/\n#/ #/;ta' -e 'P;D' < TODO: Automate this when new tags are applied to the repository. \ No newline at end of file +> TODO: Automate this when new tags are applied to the repository. diff --git a/docs/conf.py b/docs/conf.py index 6ed81e5982..db981d6321 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -47,6 +47,9 @@ "qrcode", "rlp", "nest_asyncio", + "marshmallow", + "typing_extensions", + "async_timeout" ] # "aries_cloudagent.tests.test_conductor", diff --git a/docs/generated/aries_cloudagent.admin.rst b/docs/generated/aries_cloudagent.admin.rst index efcb59a1cb..250d1b3da5 100644 --- a/docs/generated/aries_cloudagent.admin.rst +++ b/docs/generated/aries_cloudagent.admin.rst @@ -2,9 +2,9 @@ aries\_cloudagent.admin package =============================== .. automodule:: aries_cloudagent.admin - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,30 +13,32 @@ aries\_cloudagent.admin.base\_server module ------------------------------------------- .. automodule:: aries_cloudagent.admin.base_server - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.admin.error module ------------------------------------ .. automodule:: aries_cloudagent.admin.error - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.admin.request\_context module ----------------------------------------------- .. automodule:: aries_cloudagent.admin.request_context - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.admin.server module ------------------------------------- .. automodule:: aries_cloudagent.admin.server - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.askar.didcomm.rst b/docs/generated/aries_cloudagent.askar.didcomm.rst index 6e599dcc31..10b52b2b30 100644 --- a/docs/generated/aries_cloudagent.askar.didcomm.rst +++ b/docs/generated/aries_cloudagent.askar.didcomm.rst @@ -2,9 +2,9 @@ aries\_cloudagent.askar.didcomm package ======================================= .. automodule:: aries_cloudagent.askar.didcomm - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,14 +13,16 @@ aries\_cloudagent.askar.didcomm.v1 module ----------------------------------------- .. automodule:: aries_cloudagent.askar.didcomm.v1 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.askar.didcomm.v2 module ----------------------------------------- .. automodule:: aries_cloudagent.askar.didcomm.v2 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.askar.rst b/docs/generated/aries_cloudagent.askar.rst index 2787ae97f8..e9f39149ec 100644 --- a/docs/generated/aries_cloudagent.askar.rst +++ b/docs/generated/aries_cloudagent.askar.rst @@ -2,17 +2,16 @@ aries\_cloudagent.askar package =============================== .. automodule:: aries_cloudagent.askar - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.askar.didcomm + aries_cloudagent.askar.didcomm Submodules ---------- @@ -21,14 +20,16 @@ aries\_cloudagent.askar.profile module -------------------------------------- .. automodule:: aries_cloudagent.askar.profile - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.askar.store module ------------------------------------ .. automodule:: aries_cloudagent.askar.store - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.cache.rst b/docs/generated/aries_cloudagent.cache.rst index 8fa69fb650..c77763aa50 100644 --- a/docs/generated/aries_cloudagent.cache.rst +++ b/docs/generated/aries_cloudagent.cache.rst @@ -2,9 +2,9 @@ aries\_cloudagent.cache package =============================== .. automodule:: aries_cloudagent.cache - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,14 +13,16 @@ aries\_cloudagent.cache.base module ----------------------------------- .. automodule:: aries_cloudagent.cache.base - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.cache.in\_memory module ----------------------------------------- .. automodule:: aries_cloudagent.cache.in_memory - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.commands.rst b/docs/generated/aries_cloudagent.commands.rst index f4353c5984..e04e946ca8 100644 --- a/docs/generated/aries_cloudagent.commands.rst +++ b/docs/generated/aries_cloudagent.commands.rst @@ -2,9 +2,9 @@ aries\_cloudagent.commands package ================================== .. automodule:: aries_cloudagent.commands - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,30 +13,32 @@ aries\_cloudagent.commands.help module -------------------------------------- .. automodule:: aries_cloudagent.commands.help - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.commands.provision module ------------------------------------------- .. automodule:: aries_cloudagent.commands.provision - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.commands.start module --------------------------------------- .. automodule:: aries_cloudagent.commands.start - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.commands.upgrade module ----------------------------------------- .. automodule:: aries_cloudagent.commands.upgrade - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.config.rst b/docs/generated/aries_cloudagent.config.rst index 43b1ef34a5..56ba88a314 100644 --- a/docs/generated/aries_cloudagent.config.rst +++ b/docs/generated/aries_cloudagent.config.rst @@ -2,9 +2,9 @@ aries\_cloudagent.config package ================================ .. automodule:: aries_cloudagent.config - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,118 +13,120 @@ aries\_cloudagent.config.argparse module ---------------------------------------- .. automodule:: aries_cloudagent.config.argparse - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.config.banner module -------------------------------------- .. automodule:: aries_cloudagent.config.banner - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.config.base module ------------------------------------ .. automodule:: aries_cloudagent.config.base - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.config.base\_context module --------------------------------------------- .. automodule:: aries_cloudagent.config.base_context - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.config.default\_context module ------------------------------------------------ .. automodule:: aries_cloudagent.config.default_context - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.config.error module ------------------------------------- .. automodule:: aries_cloudagent.config.error - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.config.injection\_context module -------------------------------------------------- .. automodule:: aries_cloudagent.config.injection_context - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.config.injector module ---------------------------------------- .. automodule:: aries_cloudagent.config.injector - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.config.ledger module -------------------------------------- .. automodule:: aries_cloudagent.config.ledger - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.config.logging module --------------------------------------- .. automodule:: aries_cloudagent.config.logging - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.config.plugin\_settings module ------------------------------------------------ .. automodule:: aries_cloudagent.config.plugin_settings - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.config.provider module ---------------------------------------- .. automodule:: aries_cloudagent.config.provider - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.config.settings module ---------------------------------------- .. automodule:: aries_cloudagent.config.settings - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.config.util module ------------------------------------ .. automodule:: aries_cloudagent.config.util - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.config.wallet module -------------------------------------- .. automodule:: aries_cloudagent.config.wallet - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.connections.models.diddoc.rst b/docs/generated/aries_cloudagent.connections.models.diddoc.rst index 01f1124a60..bed72812ab 100644 --- a/docs/generated/aries_cloudagent.connections.models.diddoc.rst +++ b/docs/generated/aries_cloudagent.connections.models.diddoc.rst @@ -2,9 +2,9 @@ aries\_cloudagent.connections.models.diddoc package =================================================== .. automodule:: aries_cloudagent.connections.models.diddoc - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,30 +13,32 @@ aries\_cloudagent.connections.models.diddoc.diddoc module --------------------------------------------------------- .. automodule:: aries_cloudagent.connections.models.diddoc.diddoc - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.connections.models.diddoc.publickey module ------------------------------------------------------------ .. automodule:: aries_cloudagent.connections.models.diddoc.publickey - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.connections.models.diddoc.service module ---------------------------------------------------------- .. automodule:: aries_cloudagent.connections.models.diddoc.service - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.connections.models.diddoc.util module ------------------------------------------------------- .. automodule:: aries_cloudagent.connections.models.diddoc.util - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.connections.models.rst b/docs/generated/aries_cloudagent.connections.models.rst index 4ee733ae04..0d1ad79dc7 100644 --- a/docs/generated/aries_cloudagent.connections.models.rst +++ b/docs/generated/aries_cloudagent.connections.models.rst @@ -2,17 +2,16 @@ aries\_cloudagent.connections.models package ============================================ .. automodule:: aries_cloudagent.connections.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.connections.models.diddoc + aries_cloudagent.connections.models.diddoc Submodules ---------- @@ -21,14 +20,16 @@ aries\_cloudagent.connections.models.conn\_record module -------------------------------------------------------- .. automodule:: aries_cloudagent.connections.models.conn_record - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.connections.models.connection\_target module -------------------------------------------------------------- .. automodule:: aries_cloudagent.connections.models.connection_target - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.connections.rst b/docs/generated/aries_cloudagent.connections.rst index 90d1f68626..9630382119 100644 --- a/docs/generated/aries_cloudagent.connections.rst +++ b/docs/generated/aries_cloudagent.connections.rst @@ -2,17 +2,16 @@ aries\_cloudagent.connections package ===================================== .. automodule:: aries_cloudagent.connections - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.connections.models + aries_cloudagent.connections.models Submodules ---------- @@ -21,6 +20,8 @@ aries\_cloudagent.connections.base\_manager module -------------------------------------------------- .. automodule:: aries_cloudagent.connections.base_manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.core.in_memory.didcomm.rst b/docs/generated/aries_cloudagent.core.in_memory.didcomm.rst index 1bb9828e02..17b9a3a68a 100644 --- a/docs/generated/aries_cloudagent.core.in_memory.didcomm.rst +++ b/docs/generated/aries_cloudagent.core.in_memory.didcomm.rst @@ -2,9 +2,9 @@ aries\_cloudagent.core.in\_memory.didcomm package ================================================= .. automodule:: aries_cloudagent.core.in_memory.didcomm - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,14 +13,16 @@ aries\_cloudagent.core.in\_memory.didcomm.derive\_1pu module ------------------------------------------------------------ .. automodule:: aries_cloudagent.core.in_memory.didcomm.derive_1pu - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.core.in\_memory.didcomm.derive\_ecdh module ------------------------------------------------------------- .. automodule:: aries_cloudagent.core.in_memory.didcomm.derive_ecdh - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.core.in_memory.rst b/docs/generated/aries_cloudagent.core.in_memory.rst index 20dfaa765f..e7aba5fb2a 100644 --- a/docs/generated/aries_cloudagent.core.in_memory.rst +++ b/docs/generated/aries_cloudagent.core.in_memory.rst @@ -2,17 +2,16 @@ aries\_cloudagent.core.in\_memory package ========================================= .. automodule:: aries_cloudagent.core.in_memory - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.core.in_memory.didcomm + aries_cloudagent.core.in_memory.didcomm Submodules ---------- @@ -21,6 +20,8 @@ aries\_cloudagent.core.in\_memory.profile module ------------------------------------------------ .. automodule:: aries_cloudagent.core.in_memory.profile - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.core.rst b/docs/generated/aries_cloudagent.core.rst index f304ae0aa0..c0ffc0d393 100644 --- a/docs/generated/aries_cloudagent.core.rst +++ b/docs/generated/aries_cloudagent.core.rst @@ -2,17 +2,16 @@ aries\_cloudagent.core package ============================== .. automodule:: aries_cloudagent.core - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.core.in_memory + aries_cloudagent.core.in_memory Submodules ---------- @@ -21,78 +20,80 @@ aries\_cloudagent.core.conductor module --------------------------------------- .. automodule:: aries_cloudagent.core.conductor - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.core.dispatcher module ---------------------------------------- .. automodule:: aries_cloudagent.core.dispatcher - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.core.error module ----------------------------------- .. automodule:: aries_cloudagent.core.error - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.core.event\_bus module ---------------------------------------- .. automodule:: aries_cloudagent.core.event_bus - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.core.goal\_code\_registry module -------------------------------------------------- .. automodule:: aries_cloudagent.core.goal_code_registry - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.core.oob\_processor module -------------------------------------------- .. automodule:: aries_cloudagent.core.oob_processor - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.core.plugin\_registry module ---------------------------------------------- .. automodule:: aries_cloudagent.core.plugin_registry - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.core.profile module ------------------------------------- .. automodule:: aries_cloudagent.core.profile - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.core.protocol\_registry module ------------------------------------------------ .. automodule:: aries_cloudagent.core.protocol_registry - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.core.util module ---------------------------------- .. automodule:: aries_cloudagent.core.util - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.did.rst b/docs/generated/aries_cloudagent.did.rst index 4eb9394a21..696571f08d 100644 --- a/docs/generated/aries_cloudagent.did.rst +++ b/docs/generated/aries_cloudagent.did.rst @@ -2,9 +2,9 @@ aries\_cloudagent.did package ============================= .. automodule:: aries_cloudagent.did - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.did.did\_key module ------------------------------------- .. automodule:: aries_cloudagent.did.did_key - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.holder.rst b/docs/generated/aries_cloudagent.holder.rst index 47287253ae..a3f35fbad8 100644 --- a/docs/generated/aries_cloudagent.holder.rst +++ b/docs/generated/aries_cloudagent.holder.rst @@ -2,9 +2,9 @@ aries\_cloudagent.holder package ================================ .. automodule:: aries_cloudagent.holder - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.holder.routes module -------------------------------------- .. automodule:: aries_cloudagent.holder.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.indy.credx.rst b/docs/generated/aries_cloudagent.indy.credx.rst index b80d4017f7..bbb6a380b3 100644 --- a/docs/generated/aries_cloudagent.indy.credx.rst +++ b/docs/generated/aries_cloudagent.indy.credx.rst @@ -2,9 +2,9 @@ aries\_cloudagent.indy.credx package ==================================== .. automodule:: aries_cloudagent.indy.credx - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,22 +13,24 @@ aries\_cloudagent.indy.credx.holder module ------------------------------------------ .. automodule:: aries_cloudagent.indy.credx.holder - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.credx.issuer module ------------------------------------------ .. automodule:: aries_cloudagent.indy.credx.issuer - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.credx.verifier module -------------------------------------------- .. automodule:: aries_cloudagent.indy.credx.verifier - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.indy.models.rst b/docs/generated/aries_cloudagent.indy.models.rst index ae04af6ddb..487ed7b5f9 100644 --- a/docs/generated/aries_cloudagent.indy.models.rst +++ b/docs/generated/aries_cloudagent.indy.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.indy.models package ===================================== .. automodule:: aries_cloudagent.indy.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,110 +13,112 @@ aries\_cloudagent.indy.models.cred module ----------------------------------------- .. automodule:: aries_cloudagent.indy.models.cred - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.models.cred\_abstract module --------------------------------------------------- .. automodule:: aries_cloudagent.indy.models.cred_abstract - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.models.cred\_def module ---------------------------------------------- .. automodule:: aries_cloudagent.indy.models.cred_def - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.models.cred\_precis module ------------------------------------------------- .. automodule:: aries_cloudagent.indy.models.cred_precis - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.models.cred\_request module -------------------------------------------------- .. automodule:: aries_cloudagent.indy.models.cred_request - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.models.non\_rev\_interval module ------------------------------------------------------- .. automodule:: aries_cloudagent.indy.models.non_rev_interval - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.models.predicate module ---------------------------------------------- .. automodule:: aries_cloudagent.indy.models.predicate - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.models.pres\_preview module -------------------------------------------------- .. automodule:: aries_cloudagent.indy.models.pres_preview - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.models.proof module ------------------------------------------ .. automodule:: aries_cloudagent.indy.models.proof - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.models.proof\_request module --------------------------------------------------- .. automodule:: aries_cloudagent.indy.models.proof_request - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.models.requested\_creds module ----------------------------------------------------- .. automodule:: aries_cloudagent.indy.models.requested_creds - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.models.revocation module ----------------------------------------------- .. automodule:: aries_cloudagent.indy.models.revocation - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.models.schema module ------------------------------------------- .. automodule:: aries_cloudagent.indy.models.schema - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.models.xform module ------------------------------------------ .. automodule:: aries_cloudagent.indy.models.xform - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.indy.rst b/docs/generated/aries_cloudagent.indy.rst index 0e5c50f637..dff7b78f07 100644 --- a/docs/generated/aries_cloudagent.indy.rst +++ b/docs/generated/aries_cloudagent.indy.rst @@ -2,19 +2,18 @@ aries\_cloudagent.indy package ============================== .. automodule:: aries_cloudagent.indy - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.indy.credx - aries_cloudagent.indy.models - aries_cloudagent.indy.sdk + aries_cloudagent.indy.credx + aries_cloudagent.indy.models + aries_cloudagent.indy.sdk Submodules ---------- @@ -23,30 +22,32 @@ aries\_cloudagent.indy.holder module ------------------------------------ .. automodule:: aries_cloudagent.indy.holder - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.issuer module ------------------------------------ .. automodule:: aries_cloudagent.indy.issuer - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.util module ---------------------------------- .. automodule:: aries_cloudagent.indy.util - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.verifier module -------------------------------------- .. automodule:: aries_cloudagent.indy.verifier - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.indy.sdk.rst b/docs/generated/aries_cloudagent.indy.sdk.rst index be5adbf5ae..8de7333f14 100644 --- a/docs/generated/aries_cloudagent.indy.sdk.rst +++ b/docs/generated/aries_cloudagent.indy.sdk.rst @@ -2,9 +2,9 @@ aries\_cloudagent.indy.sdk package ================================== .. automodule:: aries_cloudagent.indy.sdk - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,62 +13,64 @@ aries\_cloudagent.indy.sdk.error module --------------------------------------- .. automodule:: aries_cloudagent.indy.sdk.error - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.sdk.holder module ---------------------------------------- .. automodule:: aries_cloudagent.indy.sdk.holder - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.sdk.issuer module ---------------------------------------- .. automodule:: aries_cloudagent.indy.sdk.issuer - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.sdk.profile module ----------------------------------------- .. automodule:: aries_cloudagent.indy.sdk.profile - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.sdk.util module -------------------------------------- .. automodule:: aries_cloudagent.indy.sdk.util - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.sdk.verifier module ------------------------------------------ .. automodule:: aries_cloudagent.indy.sdk.verifier - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.sdk.wallet\_plugin module ------------------------------------------------ .. automodule:: aries_cloudagent.indy.sdk.wallet_plugin - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.sdk.wallet\_setup module ----------------------------------------------- .. automodule:: aries_cloudagent.indy.sdk.wallet_setup - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.ledger.merkel_validation.rst b/docs/generated/aries_cloudagent.ledger.merkel_validation.rst index 2aba21f5ce..9dbc246bfa 100644 --- a/docs/generated/aries_cloudagent.ledger.merkel_validation.rst +++ b/docs/generated/aries_cloudagent.ledger.merkel_validation.rst @@ -2,9 +2,9 @@ aries\_cloudagent.ledger.merkel\_validation package =================================================== .. automodule:: aries_cloudagent.ledger.merkel_validation - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,46 +13,48 @@ aries\_cloudagent.ledger.merkel\_validation.constants module ------------------------------------------------------------ .. automodule:: aries_cloudagent.ledger.merkel_validation.constants - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.merkel\_validation.domain\_txn\_handler module ----------------------------------------------------------------------- .. automodule:: aries_cloudagent.ledger.merkel_validation.domain_txn_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.merkel\_validation.hasher module --------------------------------------------------------- .. automodule:: aries_cloudagent.ledger.merkel_validation.hasher - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.merkel\_validation.merkel\_verifier module ------------------------------------------------------------------- .. automodule:: aries_cloudagent.ledger.merkel_validation.merkel_verifier - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.merkel\_validation.trie module ------------------------------------------------------- .. automodule:: aries_cloudagent.ledger.merkel_validation.trie - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.merkel\_validation.utils module -------------------------------------------------------- .. automodule:: aries_cloudagent.ledger.merkel_validation.utils - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.ledger.multiple_ledger.rst b/docs/generated/aries_cloudagent.ledger.multiple_ledger.rst index ed257bc7cf..37dd4d2e10 100644 --- a/docs/generated/aries_cloudagent.ledger.multiple_ledger.rst +++ b/docs/generated/aries_cloudagent.ledger.multiple_ledger.rst @@ -2,9 +2,9 @@ aries\_cloudagent.ledger.multiple\_ledger package ================================================= .. automodule:: aries_cloudagent.ledger.multiple_ledger - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,46 +13,48 @@ aries\_cloudagent.ledger.multiple\_ledger.base\_manager module -------------------------------------------------------------- .. automodule:: aries_cloudagent.ledger.multiple_ledger.base_manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.multiple\_ledger.indy\_manager module -------------------------------------------------------------- .. automodule:: aries_cloudagent.ledger.multiple_ledger.indy_manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.multiple\_ledger.indy\_vdr\_manager module ------------------------------------------------------------------- .. automodule:: aries_cloudagent.ledger.multiple_ledger.indy_vdr_manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.multiple\_ledger.ledger\_config\_schema module ----------------------------------------------------------------------- .. automodule:: aries_cloudagent.ledger.multiple_ledger.ledger_config_schema - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.multiple\_ledger.ledger\_requests\_executor module --------------------------------------------------------------------------- .. automodule:: aries_cloudagent.ledger.multiple_ledger.ledger_requests_executor - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.multiple\_ledger.manager\_provider module ------------------------------------------------------------------ .. automodule:: aries_cloudagent.ledger.multiple_ledger.manager_provider - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.ledger.rst b/docs/generated/aries_cloudagent.ledger.rst index aff0574fef..53ee6a0bc5 100644 --- a/docs/generated/aries_cloudagent.ledger.rst +++ b/docs/generated/aries_cloudagent.ledger.rst @@ -2,18 +2,17 @@ aries\_cloudagent.ledger package ================================ .. automodule:: aries_cloudagent.ledger - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.ledger.merkel_validation - aries_cloudagent.ledger.multiple_ledger + aries_cloudagent.ledger.merkel_validation + aries_cloudagent.ledger.multiple_ledger Submodules ---------- @@ -22,54 +21,56 @@ aries\_cloudagent.ledger.base module ------------------------------------ .. automodule:: aries_cloudagent.ledger.base - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.endpoint\_type module ---------------------------------------------- .. automodule:: aries_cloudagent.ledger.endpoint_type - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.error module ------------------------------------- .. automodule:: aries_cloudagent.ledger.error - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.indy module ------------------------------------ .. automodule:: aries_cloudagent.ledger.indy - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.indy\_vdr module ----------------------------------------- .. automodule:: aries_cloudagent.ledger.indy_vdr - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.routes module -------------------------------------- .. automodule:: aries_cloudagent.ledger.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.util module ------------------------------------ .. automodule:: aries_cloudagent.ledger.util - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.messaging.credential_definitions.rst b/docs/generated/aries_cloudagent.messaging.credential_definitions.rst index 0de38530c2..d0fdb8879b 100644 --- a/docs/generated/aries_cloudagent.messaging.credential_definitions.rst +++ b/docs/generated/aries_cloudagent.messaging.credential_definitions.rst @@ -2,9 +2,9 @@ aries\_cloudagent.messaging.credential\_definitions package =========================================================== .. automodule:: aries_cloudagent.messaging.credential_definitions - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,14 +13,16 @@ aries\_cloudagent.messaging.credential\_definitions.routes module ----------------------------------------------------------------- .. automodule:: aries_cloudagent.messaging.credential_definitions.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.credential\_definitions.util module --------------------------------------------------------------- .. automodule:: aries_cloudagent.messaging.credential_definitions.util - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.messaging.decorators.rst b/docs/generated/aries_cloudagent.messaging.decorators.rst index 64fb269fb1..1deab83a09 100644 --- a/docs/generated/aries_cloudagent.messaging.decorators.rst +++ b/docs/generated/aries_cloudagent.messaging.decorators.rst @@ -2,9 +2,9 @@ aries\_cloudagent.messaging.decorators package ============================================== .. automodule:: aries_cloudagent.messaging.decorators - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,86 +13,88 @@ aries\_cloudagent.messaging.decorators.attach\_decorator module --------------------------------------------------------------- .. automodule:: aries_cloudagent.messaging.decorators.attach_decorator - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.decorators.base module -------------------------------------------------- .. automodule:: aries_cloudagent.messaging.decorators.base - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.decorators.default module ----------------------------------------------------- .. automodule:: aries_cloudagent.messaging.decorators.default - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.decorators.localization\_decorator module --------------------------------------------------------------------- .. automodule:: aries_cloudagent.messaging.decorators.localization_decorator - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.decorators.please\_ack\_decorator module -------------------------------------------------------------------- .. automodule:: aries_cloudagent.messaging.decorators.please_ack_decorator - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.decorators.service\_decorator module ---------------------------------------------------------------- .. automodule:: aries_cloudagent.messaging.decorators.service_decorator - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.decorators.signature\_decorator module ------------------------------------------------------------------ .. automodule:: aries_cloudagent.messaging.decorators.signature_decorator - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.decorators.thread\_decorator module --------------------------------------------------------------- .. automodule:: aries_cloudagent.messaging.decorators.thread_decorator - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.decorators.timing\_decorator module --------------------------------------------------------------- .. automodule:: aries_cloudagent.messaging.decorators.timing_decorator - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.decorators.trace\_decorator module -------------------------------------------------------------- .. automodule:: aries_cloudagent.messaging.decorators.trace_decorator - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.decorators.transport\_decorator module ------------------------------------------------------------------ .. automodule:: aries_cloudagent.messaging.decorators.transport_decorator - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.messaging.jsonld.rst b/docs/generated/aries_cloudagent.messaging.jsonld.rst index 02ce783dd1..af0f5bf8e5 100644 --- a/docs/generated/aries_cloudagent.messaging.jsonld.rst +++ b/docs/generated/aries_cloudagent.messaging.jsonld.rst @@ -2,9 +2,9 @@ aries\_cloudagent.messaging.jsonld package ========================================== .. automodule:: aries_cloudagent.messaging.jsonld - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,30 +13,32 @@ aries\_cloudagent.messaging.jsonld.create\_verify\_data module -------------------------------------------------------------- .. automodule:: aries_cloudagent.messaging.jsonld.create_verify_data - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.jsonld.credential module ---------------------------------------------------- .. automodule:: aries_cloudagent.messaging.jsonld.credential - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.jsonld.error module ----------------------------------------------- .. automodule:: aries_cloudagent.messaging.jsonld.error - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.jsonld.routes module ------------------------------------------------ .. automodule:: aries_cloudagent.messaging.jsonld.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.messaging.models.rst b/docs/generated/aries_cloudagent.messaging.models.rst index cc951ac8e0..ecf4281974 100644 --- a/docs/generated/aries_cloudagent.messaging.models.rst +++ b/docs/generated/aries_cloudagent.messaging.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.messaging.models package ========================================== .. automodule:: aries_cloudagent.messaging.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,22 +13,24 @@ aries\_cloudagent.messaging.models.base module ---------------------------------------------- .. automodule:: aries_cloudagent.messaging.models.base - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.models.base\_record module ------------------------------------------------------ .. automodule:: aries_cloudagent.messaging.models.base_record - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.models.openapi module ------------------------------------------------- .. automodule:: aries_cloudagent.messaging.models.openapi - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.messaging.rst b/docs/generated/aries_cloudagent.messaging.rst index 21bd62a4f3..29b5a3c101 100644 --- a/docs/generated/aries_cloudagent.messaging.rst +++ b/docs/generated/aries_cloudagent.messaging.rst @@ -2,21 +2,20 @@ aries\_cloudagent.messaging package =================================== .. automodule:: aries_cloudagent.messaging - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.messaging.credential_definitions - aries_cloudagent.messaging.decorators - aries_cloudagent.messaging.jsonld - aries_cloudagent.messaging.models - aries_cloudagent.messaging.schemas + aries_cloudagent.messaging.credential_definitions + aries_cloudagent.messaging.decorators + aries_cloudagent.messaging.jsonld + aries_cloudagent.messaging.models + aries_cloudagent.messaging.schemas Submodules ---------- @@ -25,62 +24,64 @@ aries\_cloudagent.messaging.agent\_message module ------------------------------------------------- .. automodule:: aries_cloudagent.messaging.agent_message - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.base\_handler module ------------------------------------------------ .. automodule:: aries_cloudagent.messaging.base_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.base\_message module ------------------------------------------------ .. automodule:: aries_cloudagent.messaging.base_message - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.error module ---------------------------------------- .. automodule:: aries_cloudagent.messaging.error - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.request\_context module --------------------------------------------------- .. automodule:: aries_cloudagent.messaging.request_context - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.responder module -------------------------------------------- .. automodule:: aries_cloudagent.messaging.responder - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.util module --------------------------------------- .. automodule:: aries_cloudagent.messaging.util - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.valid module ---------------------------------------- .. automodule:: aries_cloudagent.messaging.valid - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.messaging.schemas.rst b/docs/generated/aries_cloudagent.messaging.schemas.rst index 83deffa2a4..06f847b39a 100644 --- a/docs/generated/aries_cloudagent.messaging.schemas.rst +++ b/docs/generated/aries_cloudagent.messaging.schemas.rst @@ -2,9 +2,9 @@ aries\_cloudagent.messaging.schemas package =========================================== .. automodule:: aries_cloudagent.messaging.schemas - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,14 +13,16 @@ aries\_cloudagent.messaging.schemas.routes module ------------------------------------------------- .. automodule:: aries_cloudagent.messaging.schemas.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.schemas.util module ----------------------------------------------- .. automodule:: aries_cloudagent.messaging.schemas.util - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.multitenant.admin.rst b/docs/generated/aries_cloudagent.multitenant.admin.rst index 6a1a695efe..e1f44a2a9e 100644 --- a/docs/generated/aries_cloudagent.multitenant.admin.rst +++ b/docs/generated/aries_cloudagent.multitenant.admin.rst @@ -2,9 +2,9 @@ aries\_cloudagent.multitenant.admin package =========================================== .. automodule:: aries_cloudagent.multitenant.admin - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.multitenant.admin.routes module ------------------------------------------------- .. automodule:: aries_cloudagent.multitenant.admin.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.multitenant.rst b/docs/generated/aries_cloudagent.multitenant.rst index 913a17a652..44d798d511 100644 --- a/docs/generated/aries_cloudagent.multitenant.rst +++ b/docs/generated/aries_cloudagent.multitenant.rst @@ -2,17 +2,16 @@ aries\_cloudagent.multitenant package ===================================== .. automodule:: aries_cloudagent.multitenant - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.multitenant.admin + aries_cloudagent.multitenant.admin Submodules ---------- @@ -21,54 +20,56 @@ aries\_cloudagent.multitenant.askar\_profile\_manager module ------------------------------------------------------------ .. automodule:: aries_cloudagent.multitenant.askar_profile_manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.multitenant.base module ----------------------------------------- .. automodule:: aries_cloudagent.multitenant.base - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.multitenant.cache module ------------------------------------------ .. automodule:: aries_cloudagent.multitenant.cache - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.multitenant.error module ------------------------------------------ .. automodule:: aries_cloudagent.multitenant.error - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.multitenant.manager module -------------------------------------------- .. automodule:: aries_cloudagent.multitenant.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.multitenant.manager\_provider module ------------------------------------------------------ .. automodule:: aries_cloudagent.multitenant.manager_provider - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.multitenant.route\_manager module --------------------------------------------------- .. automodule:: aries_cloudagent.multitenant.route_manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.actionmenu.rst b/docs/generated/aries_cloudagent.protocols.actionmenu.rst index 3688c5eef5..70d47336ca 100644 --- a/docs/generated/aries_cloudagent.protocols.actionmenu.rst +++ b/docs/generated/aries_cloudagent.protocols.actionmenu.rst @@ -2,17 +2,16 @@ aries\_cloudagent.protocols.actionmenu package ============================================== .. automodule:: aries_cloudagent.protocols.actionmenu - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.actionmenu.v1_0 + aries_cloudagent.protocols.actionmenu.v1_0 Submodules ---------- @@ -21,6 +20,8 @@ aries\_cloudagent.protocols.actionmenu.definition module -------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.actionmenu.definition - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.handlers.rst index c3f66b0796..a230fae21f 100644 --- a/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.actionmenu.v1\_0.handlers package ============================================================= .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,22 +13,24 @@ aries\_cloudagent.protocols.actionmenu.v1\_0.handlers.menu\_handler module -------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.handlers.menu_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.actionmenu.v1\_0.handlers.menu\_request\_handler module ----------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.handlers.menu_request_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.actionmenu.v1\_0.handlers.perform\_handler module ----------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.handlers.perform_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.messages.rst b/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.messages.rst index 98578667f1..e3fe2a1522 100644 --- a/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.actionmenu.v1\_0.messages package ============================================================= .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,22 +13,24 @@ aries\_cloudagent.protocols.actionmenu.v1\_0.messages.menu module ----------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.messages.menu - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.actionmenu.v1\_0.messages.menu\_request module -------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.messages.menu_request - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.actionmenu.v1\_0.messages.perform module -------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.messages.perform - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.models.rst b/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.models.rst index 133a5c5207..2d84e02fac 100644 --- a/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.models.rst +++ b/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.actionmenu.v1\_0.models package =========================================================== .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,22 +13,24 @@ aries\_cloudagent.protocols.actionmenu.v1\_0.models.menu\_form module --------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.models.menu_form - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.actionmenu.v1\_0.models.menu\_form\_param module ---------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.models.menu_form_param - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.actionmenu.v1\_0.models.menu\_option module ----------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.models.menu_option - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.rst b/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.rst index 40a91c0756..a1b1097a49 100644 --- a/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.rst @@ -2,19 +2,18 @@ aries\_cloudagent.protocols.actionmenu.v1\_0 package ==================================================== .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.actionmenu.v1_0.handlers - aries_cloudagent.protocols.actionmenu.v1_0.messages - aries_cloudagent.protocols.actionmenu.v1_0.models + aries_cloudagent.protocols.actionmenu.v1_0.handlers + aries_cloudagent.protocols.actionmenu.v1_0.messages + aries_cloudagent.protocols.actionmenu.v1_0.models Submodules ---------- @@ -23,46 +22,48 @@ aries\_cloudagent.protocols.actionmenu.v1\_0.base\_service module ----------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.base_service - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.actionmenu.v1\_0.controller module -------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.controller - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.actionmenu.v1\_0.driver\_service module ------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.driver_service - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.actionmenu.v1\_0.message\_types module ------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.actionmenu.v1\_0.routes module ---------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.actionmenu.v1\_0.util module -------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.util - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.basicmessage.rst b/docs/generated/aries_cloudagent.protocols.basicmessage.rst index 70ae0a38f0..78bbf25c31 100644 --- a/docs/generated/aries_cloudagent.protocols.basicmessage.rst +++ b/docs/generated/aries_cloudagent.protocols.basicmessage.rst @@ -2,17 +2,16 @@ aries\_cloudagent.protocols.basicmessage package ================================================ .. automodule:: aries_cloudagent.protocols.basicmessage - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.basicmessage.v1_0 + aries_cloudagent.protocols.basicmessage.v1_0 Submodules ---------- @@ -21,6 +20,8 @@ aries\_cloudagent.protocols.basicmessage.definition module ---------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.basicmessage.definition - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.basicmessage.v1_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.basicmessage.v1_0.handlers.rst index 9ba540d3d8..9bc951b81e 100644 --- a/docs/generated/aries_cloudagent.protocols.basicmessage.v1_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.basicmessage.v1_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.basicmessage.v1\_0.handlers package =============================================================== .. automodule:: aries_cloudagent.protocols.basicmessage.v1_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.basicmessage.v1\_0.handlers.basicmessage\_handler mo ------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.basicmessage.v1_0.handlers.basicmessage_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.basicmessage.v1_0.messages.rst b/docs/generated/aries_cloudagent.protocols.basicmessage.v1_0.messages.rst index 069d11564b..edc0a395f5 100644 --- a/docs/generated/aries_cloudagent.protocols.basicmessage.v1_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.basicmessage.v1_0.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.basicmessage.v1\_0.messages package =============================================================== .. automodule:: aries_cloudagent.protocols.basicmessage.v1_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.basicmessage.v1\_0.messages.basicmessage module --------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.basicmessage.v1_0.messages.basicmessage - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.basicmessage.v1_0.rst b/docs/generated/aries_cloudagent.protocols.basicmessage.v1_0.rst index 3899ab9e88..3eae1842a4 100644 --- a/docs/generated/aries_cloudagent.protocols.basicmessage.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.basicmessage.v1_0.rst @@ -2,18 +2,17 @@ aries\_cloudagent.protocols.basicmessage.v1\_0 package ====================================================== .. automodule:: aries_cloudagent.protocols.basicmessage.v1_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.basicmessage.v1_0.handlers - aries_cloudagent.protocols.basicmessage.v1_0.messages + aries_cloudagent.protocols.basicmessage.v1_0.handlers + aries_cloudagent.protocols.basicmessage.v1_0.messages Submodules ---------- @@ -22,14 +21,16 @@ aries\_cloudagent.protocols.basicmessage.v1\_0.message\_types module -------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.basicmessage.v1_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.basicmessage.v1\_0.routes module ------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.basicmessage.v1_0.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.connections.rst b/docs/generated/aries_cloudagent.protocols.connections.rst index 523288cad4..fe31957acb 100644 --- a/docs/generated/aries_cloudagent.protocols.connections.rst +++ b/docs/generated/aries_cloudagent.protocols.connections.rst @@ -2,17 +2,16 @@ aries\_cloudagent.protocols.connections package =============================================== .. automodule:: aries_cloudagent.protocols.connections - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.connections.v1_0 + aries_cloudagent.protocols.connections.v1_0 Submodules ---------- @@ -21,6 +20,8 @@ aries\_cloudagent.protocols.connections.definition module --------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.connections.definition - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.connections.v1_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.connections.v1_0.handlers.rst index 1d74cb8497..3b2e92fe39 100644 --- a/docs/generated/aries_cloudagent.protocols.connections.v1_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.connections.v1_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.connections.v1\_0.handlers package ============================================================== .. automodule:: aries_cloudagent.protocols.connections.v1_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,22 +13,24 @@ aries\_cloudagent.protocols.connections.v1\_0.handlers.connection\_invitation\_h --------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.connections.v1_0.handlers.connection_invitation_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.connections.v1\_0.handlers.connection\_request\_handler module ------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.connections.v1_0.handlers.connection_request_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.connections.v1\_0.handlers.connection\_response\_handler module ------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.connections.v1_0.handlers.connection_response_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.connections.v1_0.messages.rst b/docs/generated/aries_cloudagent.protocols.connections.v1_0.messages.rst index 4714c6e9c9..69ad939486 100644 --- a/docs/generated/aries_cloudagent.protocols.connections.v1_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.connections.v1_0.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.connections.v1\_0.messages package ============================================================== .. automodule:: aries_cloudagent.protocols.connections.v1_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,30 +13,32 @@ aries\_cloudagent.protocols.connections.v1\_0.messages.connection\_invitation mo ------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.connections.v1_0.messages.connection_invitation - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.connections.v1\_0.messages.connection\_request module --------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.connections.v1_0.messages.connection_request - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.connections.v1\_0.messages.connection\_response module ---------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.connections.v1_0.messages.connection_response - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.connections.v1\_0.messages.problem\_report module ----------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.connections.v1_0.messages.problem_report - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.connections.v1_0.models.rst b/docs/generated/aries_cloudagent.protocols.connections.v1_0.models.rst index 33cc83e115..3b45b5c9fe 100644 --- a/docs/generated/aries_cloudagent.protocols.connections.v1_0.models.rst +++ b/docs/generated/aries_cloudagent.protocols.connections.v1_0.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.connections.v1\_0.models package ============================================================ .. automodule:: aries_cloudagent.protocols.connections.v1_0.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.connections.v1\_0.models.connection\_detail module ------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.connections.v1_0.models.connection_detail - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.connections.v1_0.rst b/docs/generated/aries_cloudagent.protocols.connections.v1_0.rst index b328e2772f..6c1f05abf6 100644 --- a/docs/generated/aries_cloudagent.protocols.connections.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.connections.v1_0.rst @@ -2,19 +2,18 @@ aries\_cloudagent.protocols.connections.v1\_0 package ===================================================== .. automodule:: aries_cloudagent.protocols.connections.v1_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.connections.v1_0.handlers - aries_cloudagent.protocols.connections.v1_0.messages - aries_cloudagent.protocols.connections.v1_0.models + aries_cloudagent.protocols.connections.v1_0.handlers + aries_cloudagent.protocols.connections.v1_0.messages + aries_cloudagent.protocols.connections.v1_0.models Submodules ---------- @@ -23,22 +22,24 @@ aries\_cloudagent.protocols.connections.v1\_0.manager module ------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.connections.v1_0.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.connections.v1\_0.message\_types module ------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.connections.v1_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.connections.v1\_0.routes module ----------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.connections.v1_0.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.coordinate_mediation.rst b/docs/generated/aries_cloudagent.protocols.coordinate_mediation.rst index e356c5a0b0..a6fd7ddcae 100644 --- a/docs/generated/aries_cloudagent.protocols.coordinate_mediation.rst +++ b/docs/generated/aries_cloudagent.protocols.coordinate_mediation.rst @@ -2,17 +2,16 @@ aries\_cloudagent.protocols.coordinate\_mediation package ========================================================= .. automodule:: aries_cloudagent.protocols.coordinate_mediation - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.coordinate_mediation.v1_0 + aries_cloudagent.protocols.coordinate_mediation.v1_0 Submodules ---------- @@ -21,14 +20,16 @@ aries\_cloudagent.protocols.coordinate\_mediation.definition module ------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.definition - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.mediation\_invite\_store module --------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.mediation_invite_store - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.handlers.rst index fe7d289bf5..596449dc6c 100644 --- a/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.handlers package ======================================================================== .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,62 +13,64 @@ aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.handlers.keylist\_handle ---------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.handlers.keylist_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.handlers.keylist\_query\_handler module ----------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.handlers.keylist_query_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.handlers.keylist\_update\_handler module ------------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.handlers.keylist_update_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.handlers.keylist\_update\_response\_handler module ---------------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.handlers.keylist_update_response_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.handlers.mediation\_deny\_handler module ------------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.handlers.mediation_deny_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.handlers.mediation\_grant\_handler module ------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.handlers.mediation_grant_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.handlers.mediation\_request\_handler module --------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.handlers.mediation_request_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.handlers.problem\_report\_handler module ------------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.handlers.problem_report_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.inner.rst b/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.inner.rst index 431a1485d5..c8a16d75da 100644 --- a/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.inner.rst +++ b/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.inner.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.messages.inner package ============================================================================== .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.inner - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,30 +13,32 @@ aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.messages.inner.keylist\_ ------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.inner.keylist_key - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.messages.inner.keylist\_query\_paginate module ------------------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.inner.keylist_query_paginate - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.messages.inner.keylist\_update\_rule module --------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.inner.keylist_update_rule - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.messages.inner.keylist\_updated module ---------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.inner.keylist_updated - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.rst b/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.rst index dff68b4d21..44ce3b3b26 100644 --- a/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.rst @@ -2,17 +2,16 @@ aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.messages package ======================================================================== .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.inner + aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.inner Submodules ---------- @@ -21,62 +20,64 @@ aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.messages.keylist module ------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.keylist - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.messages.keylist\_query module -------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.keylist_query - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.messages.keylist\_update module --------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.keylist_update - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.messages.keylist\_update\_response module ------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.keylist_update_response - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.messages.mediate\_deny module ------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.mediate_deny - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.messages.mediate\_grant module -------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.mediate_grant - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.messages.mediate\_request module ---------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.mediate_request - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.messages.problem\_report module --------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.problem_report - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.models.rst b/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.models.rst index ad6bf8060d..defcdf0d48 100644 --- a/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.models.rst +++ b/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.models package ====================================================================== .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.models.mediation\_record --------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.models.mediation_record - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.rst b/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.rst index fb05fd04d8..6dca2451a0 100644 --- a/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.rst @@ -2,19 +2,18 @@ aries\_cloudagent.protocols.coordinate\_mediation.v1\_0 package =============================================================== .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.coordinate_mediation.v1_0.handlers - aries_cloudagent.protocols.coordinate_mediation.v1_0.messages - aries_cloudagent.protocols.coordinate_mediation.v1_0.models + aries_cloudagent.protocols.coordinate_mediation.v1_0.handlers + aries_cloudagent.protocols.coordinate_mediation.v1_0.messages + aries_cloudagent.protocols.coordinate_mediation.v1_0.models Submodules ---------- @@ -23,54 +22,56 @@ aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.controller module ------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.controller - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.manager module ---------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.message\_types module ----------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.normalization module ---------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.normalization - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.route\_manager module ----------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.route_manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.route\_manager\_provider module --------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.route_manager_provider - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.routes module --------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.didexchange.rst b/docs/generated/aries_cloudagent.protocols.didexchange.rst index 84c84dd7df..9d591e9af8 100644 --- a/docs/generated/aries_cloudagent.protocols.didexchange.rst +++ b/docs/generated/aries_cloudagent.protocols.didexchange.rst @@ -2,17 +2,16 @@ aries\_cloudagent.protocols.didexchange package =============================================== .. automodule:: aries_cloudagent.protocols.didexchange - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.didexchange.v1_0 + aries_cloudagent.protocols.didexchange.v1_0 Submodules ---------- @@ -21,6 +20,8 @@ aries\_cloudagent.protocols.didexchange.definition module --------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.didexchange.definition - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.didexchange.v1_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.didexchange.v1_0.handlers.rst index 999485395d..352fd0e8dc 100644 --- a/docs/generated/aries_cloudagent.protocols.didexchange.v1_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.didexchange.v1_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.didexchange.v1\_0.handlers package ============================================================== .. automodule:: aries_cloudagent.protocols.didexchange.v1_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,30 +13,32 @@ aries\_cloudagent.protocols.didexchange.v1\_0.handlers.complete\_handler module ------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.didexchange.v1_0.handlers.complete_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.didexchange.v1\_0.handlers.invitation\_handler module --------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.didexchange.v1_0.handlers.invitation_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.didexchange.v1\_0.handlers.request\_handler module ------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.didexchange.v1_0.handlers.request_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.didexchange.v1\_0.handlers.response\_handler module ------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.didexchange.v1_0.handlers.response_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.didexchange.v1_0.messages.rst b/docs/generated/aries_cloudagent.protocols.didexchange.v1_0.messages.rst index bcd9ffdf7a..eecc1e878a 100644 --- a/docs/generated/aries_cloudagent.protocols.didexchange.v1_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.didexchange.v1_0.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.didexchange.v1\_0.messages package ============================================================== .. automodule:: aries_cloudagent.protocols.didexchange.v1_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,30 +13,32 @@ aries\_cloudagent.protocols.didexchange.v1\_0.messages.complete module ---------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.didexchange.v1_0.messages.complete - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.didexchange.v1\_0.messages.problem\_report\_reason module ------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.didexchange.v1_0.messages.problem_report_reason - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.didexchange.v1\_0.messages.request module --------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.didexchange.v1_0.messages.request - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.didexchange.v1\_0.messages.response module ---------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.didexchange.v1_0.messages.response - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.didexchange.v1_0.rst b/docs/generated/aries_cloudagent.protocols.didexchange.v1_0.rst index 5d74e58b7a..e63c3ec552 100644 --- a/docs/generated/aries_cloudagent.protocols.didexchange.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.didexchange.v1_0.rst @@ -2,18 +2,17 @@ aries\_cloudagent.protocols.didexchange.v1\_0 package ===================================================== .. automodule:: aries_cloudagent.protocols.didexchange.v1_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.didexchange.v1_0.handlers - aries_cloudagent.protocols.didexchange.v1_0.messages + aries_cloudagent.protocols.didexchange.v1_0.handlers + aries_cloudagent.protocols.didexchange.v1_0.messages Submodules ---------- @@ -22,22 +21,24 @@ aries\_cloudagent.protocols.didexchange.v1\_0.manager module ------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.didexchange.v1_0.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.didexchange.v1\_0.message\_types module ------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.didexchange.v1_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.didexchange.v1\_0.routes module ----------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.didexchange.v1_0.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.discovery.rst b/docs/generated/aries_cloudagent.protocols.discovery.rst index 4c736183c7..92383fe5fd 100644 --- a/docs/generated/aries_cloudagent.protocols.discovery.rst +++ b/docs/generated/aries_cloudagent.protocols.discovery.rst @@ -2,18 +2,17 @@ aries\_cloudagent.protocols.discovery package ============================================= .. automodule:: aries_cloudagent.protocols.discovery - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.discovery.v1_0 - aries_cloudagent.protocols.discovery.v2_0 + aries_cloudagent.protocols.discovery.v1_0 + aries_cloudagent.protocols.discovery.v2_0 Submodules ---------- @@ -22,6 +21,8 @@ aries\_cloudagent.protocols.discovery.definition module ------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.definition - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.discovery.v1_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.discovery.v1_0.handlers.rst index 3c81111991..260e5a7f44 100644 --- a/docs/generated/aries_cloudagent.protocols.discovery.v1_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.discovery.v1_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.discovery.v1\_0.handlers package ============================================================ .. automodule:: aries_cloudagent.protocols.discovery.v1_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,14 +13,16 @@ aries\_cloudagent.protocols.discovery.v1\_0.handlers.disclose\_handler module ----------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v1_0.handlers.disclose_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.discovery.v1\_0.handlers.query\_handler module -------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v1_0.handlers.query_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.discovery.v1_0.messages.rst b/docs/generated/aries_cloudagent.protocols.discovery.v1_0.messages.rst index 35a9e49889..0c489c0845 100644 --- a/docs/generated/aries_cloudagent.protocols.discovery.v1_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.discovery.v1_0.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.discovery.v1\_0.messages package ============================================================ .. automodule:: aries_cloudagent.protocols.discovery.v1_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,14 +13,16 @@ aries\_cloudagent.protocols.discovery.v1\_0.messages.disclose module -------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v1_0.messages.disclose - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.discovery.v1\_0.messages.query module ----------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v1_0.messages.query - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.discovery.v1_0.models.rst b/docs/generated/aries_cloudagent.protocols.discovery.v1_0.models.rst index baec81e6d7..e080f6bf52 100644 --- a/docs/generated/aries_cloudagent.protocols.discovery.v1_0.models.rst +++ b/docs/generated/aries_cloudagent.protocols.discovery.v1_0.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.discovery.v1\_0.models package ========================================================== .. automodule:: aries_cloudagent.protocols.discovery.v1_0.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.discovery.v1\_0.models.discovery\_record module --------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v1_0.models.discovery_record - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.discovery.v1_0.rst b/docs/generated/aries_cloudagent.protocols.discovery.v1_0.rst index f221130077..410eef398f 100644 --- a/docs/generated/aries_cloudagent.protocols.discovery.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.discovery.v1_0.rst @@ -2,19 +2,18 @@ aries\_cloudagent.protocols.discovery.v1\_0 package =================================================== .. automodule:: aries_cloudagent.protocols.discovery.v1_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.discovery.v1_0.handlers - aries_cloudagent.protocols.discovery.v1_0.messages - aries_cloudagent.protocols.discovery.v1_0.models + aries_cloudagent.protocols.discovery.v1_0.handlers + aries_cloudagent.protocols.discovery.v1_0.messages + aries_cloudagent.protocols.discovery.v1_0.models Submodules ---------- @@ -23,22 +22,24 @@ aries\_cloudagent.protocols.discovery.v1\_0.manager module ---------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v1_0.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.discovery.v1\_0.message\_types module ----------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v1_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.discovery.v1\_0.routes module --------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v1_0.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.discovery.v2_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.discovery.v2_0.handlers.rst index b79d280002..3c17b21340 100644 --- a/docs/generated/aries_cloudagent.protocols.discovery.v2_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.discovery.v2_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.discovery.v2\_0.handlers package ============================================================ .. automodule:: aries_cloudagent.protocols.discovery.v2_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,14 +13,16 @@ aries\_cloudagent.protocols.discovery.v2\_0.handlers.disclosures\_handler module -------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v2_0.handlers.disclosures_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.discovery.v2\_0.handlers.queries\_handler module ---------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v2_0.handlers.queries_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.discovery.v2_0.messages.rst b/docs/generated/aries_cloudagent.protocols.discovery.v2_0.messages.rst index 99fdbc332b..e180e31f23 100644 --- a/docs/generated/aries_cloudagent.protocols.discovery.v2_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.discovery.v2_0.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.discovery.v2\_0.messages package ============================================================ .. automodule:: aries_cloudagent.protocols.discovery.v2_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,14 +13,16 @@ aries\_cloudagent.protocols.discovery.v2\_0.messages.disclosures module ----------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v2_0.messages.disclosures - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.discovery.v2\_0.messages.queries module ------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v2_0.messages.queries - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.discovery.v2_0.models.rst b/docs/generated/aries_cloudagent.protocols.discovery.v2_0.models.rst index 767db8e92e..19ea873489 100644 --- a/docs/generated/aries_cloudagent.protocols.discovery.v2_0.models.rst +++ b/docs/generated/aries_cloudagent.protocols.discovery.v2_0.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.discovery.v2\_0.models package ========================================================== .. automodule:: aries_cloudagent.protocols.discovery.v2_0.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.discovery.v2\_0.models.discovery\_record module --------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v2_0.models.discovery_record - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.discovery.v2_0.rst b/docs/generated/aries_cloudagent.protocols.discovery.v2_0.rst index d12fef9348..07d09fdb73 100644 --- a/docs/generated/aries_cloudagent.protocols.discovery.v2_0.rst +++ b/docs/generated/aries_cloudagent.protocols.discovery.v2_0.rst @@ -2,19 +2,18 @@ aries\_cloudagent.protocols.discovery.v2\_0 package =================================================== .. automodule:: aries_cloudagent.protocols.discovery.v2_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.discovery.v2_0.handlers - aries_cloudagent.protocols.discovery.v2_0.messages - aries_cloudagent.protocols.discovery.v2_0.models + aries_cloudagent.protocols.discovery.v2_0.handlers + aries_cloudagent.protocols.discovery.v2_0.messages + aries_cloudagent.protocols.discovery.v2_0.models Submodules ---------- @@ -23,22 +22,24 @@ aries\_cloudagent.protocols.discovery.v2\_0.manager module ---------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v2_0.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.discovery.v2\_0.message\_types module ----------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v2_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.discovery.v2\_0.routes module --------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v2_0.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.endorse_transaction.rst b/docs/generated/aries_cloudagent.protocols.endorse_transaction.rst index d940678a45..6cb027abe4 100644 --- a/docs/generated/aries_cloudagent.protocols.endorse_transaction.rst +++ b/docs/generated/aries_cloudagent.protocols.endorse_transaction.rst @@ -2,17 +2,16 @@ aries\_cloudagent.protocols.endorse\_transaction package ======================================================== .. automodule:: aries_cloudagent.protocols.endorse_transaction - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.endorse_transaction.v1_0 + aries_cloudagent.protocols.endorse_transaction.v1_0 Submodules ---------- @@ -21,6 +20,8 @@ aries\_cloudagent.protocols.endorse\_transaction.definition module ------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.endorse_transaction.definition - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.handlers.rst index e76ffa3ddc..055c2142c6 100644 --- a/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.endorse\_transaction.v1\_0.handlers package ======================================================================= .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,54 +13,56 @@ aries\_cloudagent.protocols.endorse\_transaction.v1\_0.handlers.endorsed\_transa --------------------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.handlers.endorsed_transaction_response_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.handlers.refused\_transaction\_response\_handler module -------------------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.handlers.refused_transaction_response_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.handlers.transaction\_acknowledgement\_handler module ------------------------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.handlers.transaction_acknowledgement_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.handlers.transaction\_cancel\_handler module --------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.handlers.transaction_cancel_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.handlers.transaction\_job\_to\_send\_handler module ---------------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.handlers.transaction_job_to_send_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.handlers.transaction\_request\_handler module ---------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.handlers.transaction_request_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.handlers.transaction\_resend\_handler module --------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.handlers.transaction_resend_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.messages.rst b/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.messages.rst index 3e14c4194c..c8809f0993 100644 --- a/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.endorse\_transaction.v1\_0.messages package ======================================================================= .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,62 +13,64 @@ aries\_cloudagent.protocols.endorse\_transaction.v1\_0.messages.cancel\_transact ------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.messages.cancel_transaction - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.messages.endorsed\_transaction\_response module ------------------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.messages.endorsed_transaction_response - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.messages.messages\_attach module --------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.messages.messages_attach - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.messages.refused\_transaction\_response module ----------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.messages.refused_transaction_response - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.messages.transaction\_acknowledgement module --------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.messages.transaction_acknowledgement - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.messages.transaction\_job\_to\_send module ------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.messages.transaction_job_to_send - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.messages.transaction\_request module ------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.messages.transaction_request - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.messages.transaction\_resend module ------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.messages.transaction_resend - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.models.rst b/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.models.rst index a120e79ce3..c2f7f4cc89 100644 --- a/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.models.rst +++ b/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.endorse\_transaction.v1\_0.models package ===================================================================== .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.endorse\_transaction.v1\_0.models.transaction\_recor ---------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.models.transaction_record - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.rst b/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.rst index eaf4c9eb32..24862f2880 100644 --- a/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.rst @@ -2,19 +2,18 @@ aries\_cloudagent.protocols.endorse\_transaction.v1\_0 package ============================================================== .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.endorse_transaction.v1_0.handlers - aries_cloudagent.protocols.endorse_transaction.v1_0.messages - aries_cloudagent.protocols.endorse_transaction.v1_0.models + aries_cloudagent.protocols.endorse_transaction.v1_0.handlers + aries_cloudagent.protocols.endorse_transaction.v1_0.messages + aries_cloudagent.protocols.endorse_transaction.v1_0.models Submodules ---------- @@ -23,46 +22,48 @@ aries\_cloudagent.protocols.endorse\_transaction.v1\_0.controller module ------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.controller - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.manager module --------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.message\_types module ---------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.routes module -------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.transaction\_jobs module ------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.transaction_jobs - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.util module ------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.util - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.introduction.rst b/docs/generated/aries_cloudagent.protocols.introduction.rst index ee89b06b48..2c331fb06b 100644 --- a/docs/generated/aries_cloudagent.protocols.introduction.rst +++ b/docs/generated/aries_cloudagent.protocols.introduction.rst @@ -2,17 +2,16 @@ aries\_cloudagent.protocols.introduction package ================================================ .. automodule:: aries_cloudagent.protocols.introduction - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.introduction.v0_1 + aries_cloudagent.protocols.introduction.v0_1 Submodules ---------- @@ -21,6 +20,8 @@ aries\_cloudagent.protocols.introduction.definition module ---------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.introduction.definition - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.introduction.v0_1.handlers.rst b/docs/generated/aries_cloudagent.protocols.introduction.v0_1.handlers.rst index ded1f15cd0..f9334b202d 100644 --- a/docs/generated/aries_cloudagent.protocols.introduction.v0_1.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.introduction.v0_1.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.introduction.v0\_1.handlers package =============================================================== .. automodule:: aries_cloudagent.protocols.introduction.v0_1.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,22 +13,24 @@ aries\_cloudagent.protocols.introduction.v0\_1.handlers.forward\_invitation\_han ------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.introduction.v0_1.handlers.forward_invitation_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.introduction.v0\_1.handlers.invitation\_handler module ---------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.introduction.v0_1.handlers.invitation_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.introduction.v0\_1.handlers.invitation\_request\_handler module ------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.introduction.v0_1.handlers.invitation_request_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.introduction.v0_1.messages.rst b/docs/generated/aries_cloudagent.protocols.introduction.v0_1.messages.rst index 841d3da69c..c469049dfe 100644 --- a/docs/generated/aries_cloudagent.protocols.introduction.v0_1.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.introduction.v0_1.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.introduction.v0\_1.messages package =============================================================== .. automodule:: aries_cloudagent.protocols.introduction.v0_1.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,22 +13,24 @@ aries\_cloudagent.protocols.introduction.v0\_1.messages.forward\_invitation modu ---------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.introduction.v0_1.messages.forward_invitation - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.introduction.v0\_1.messages.invitation module ------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.introduction.v0_1.messages.invitation - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.introduction.v0\_1.messages.invitation\_request module ---------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.introduction.v0_1.messages.invitation_request - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.introduction.v0_1.rst b/docs/generated/aries_cloudagent.protocols.introduction.v0_1.rst index 0ceece45e2..8f8b63d65f 100644 --- a/docs/generated/aries_cloudagent.protocols.introduction.v0_1.rst +++ b/docs/generated/aries_cloudagent.protocols.introduction.v0_1.rst @@ -2,18 +2,17 @@ aries\_cloudagent.protocols.introduction.v0\_1 package ====================================================== .. automodule:: aries_cloudagent.protocols.introduction.v0_1 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.introduction.v0_1.handlers - aries_cloudagent.protocols.introduction.v0_1.messages + aries_cloudagent.protocols.introduction.v0_1.handlers + aries_cloudagent.protocols.introduction.v0_1.messages Submodules ---------- @@ -22,30 +21,32 @@ aries\_cloudagent.protocols.introduction.v0\_1.base\_service module ------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.introduction.v0_1.base_service - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.introduction.v0\_1.demo\_service module ------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.introduction.v0_1.demo_service - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.introduction.v0\_1.message\_types module -------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.introduction.v0_1.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.introduction.v0\_1.routes module ------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.introduction.v0_1.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.rst index 9ad3c8172e..6edd7cb25f 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.rst @@ -2,18 +2,17 @@ aries\_cloudagent.protocols.issue\_credential package ===================================================== .. automodule:: aries_cloudagent.protocols.issue_credential - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.issue_credential.v1_0 - aries_cloudagent.protocols.issue_credential.v2_0 + aries_cloudagent.protocols.issue_credential.v1_0 + aries_cloudagent.protocols.issue_credential.v2_0 Submodules ---------- @@ -22,6 +21,8 @@ aries\_cloudagent.protocols.issue\_credential.definition module --------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.definition - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.handlers.rst index 860b89508a..24e308ca87 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.issue\_credential.v1\_0.handlers package ==================================================================== .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,46 +13,48 @@ aries\_cloudagent.protocols.issue\_credential.v1\_0.handlers.credential\_ack\_ha -------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.handlers.credential_ack_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v1\_0.handlers.credential\_issue\_handler module ---------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.handlers.credential_issue_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v1\_0.handlers.credential\_offer\_handler module ---------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.handlers.credential_offer_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v1\_0.handlers.credential\_problem\_report\_handler module -------------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.handlers.credential_problem_report_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v1\_0.handlers.credential\_proposal\_handler module ------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.handlers.credential_proposal_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v1\_0.handlers.credential\_request\_handler module ------------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.handlers.credential_request_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.messages.inner.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.messages.inner.rst index ebe4cc6072..17ac8e199e 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.messages.inner.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.messages.inner.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.issue\_credential.v1\_0.messages.inner package ========================================================================== .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.messages.inner - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.issue\_credential.v1\_0.messages.inner.credential\_p --------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.messages.inner.credential_preview - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.messages.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.messages.rst index 8279cea992..460cc32ac4 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.messages.rst @@ -2,17 +2,16 @@ aries\_cloudagent.protocols.issue\_credential.v1\_0.messages package ==================================================================== .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.issue_credential.v1_0.messages.inner + aries_cloudagent.protocols.issue_credential.v1_0.messages.inner Submodules ---------- @@ -21,46 +20,56 @@ aries\_cloudagent.protocols.issue\_credential.v1\_0.messages.credential\_ack mod ----------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.messages.credential_ack - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + +aries\_cloudagent.protocols.issue\_credential.v1\_0.messages.credential\_exchange\_webhook module +------------------------------------------------------------------------------------------------- + +.. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.messages.credential_exchange_webhook + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v1\_0.messages.credential\_issue module ------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.messages.credential_issue - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v1\_0.messages.credential\_offer module ------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.messages.credential_offer - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v1\_0.messages.credential\_problem\_report module ----------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.messages.credential_problem_report - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v1\_0.messages.credential\_proposal module ---------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.messages.credential_proposal - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v1\_0.messages.credential\_request module --------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.messages.credential_request - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.models.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.models.rst index 3fa2cfdd78..1624debb7d 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.models.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.issue\_credential.v1\_0.models package ================================================================== .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.issue\_credential.v1\_0.models.credential\_exchange -------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.models.credential_exchange - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.rst index 6221c61954..d1359b1d2a 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.rst @@ -2,19 +2,18 @@ aries\_cloudagent.protocols.issue\_credential.v1\_0 package =========================================================== .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.issue_credential.v1_0.handlers - aries_cloudagent.protocols.issue_credential.v1_0.messages - aries_cloudagent.protocols.issue_credential.v1_0.models + aries_cloudagent.protocols.issue_credential.v1_0.handlers + aries_cloudagent.protocols.issue_credential.v1_0.messages + aries_cloudagent.protocols.issue_credential.v1_0.models Submodules ---------- @@ -23,30 +22,32 @@ aries\_cloudagent.protocols.issue\_credential.v1\_0.controller module --------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.controller - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v1\_0.manager module ------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v1\_0.message\_types module ------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v1\_0.routes module ----------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.indy.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.indy.rst index 34fdbaf253..d4324b778e 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.indy.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.indy.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.formats.indy package ======================================================================== .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.formats.indy - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.formats.indy.handler module ------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.formats.indy.handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.models.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.models.rst index 771f548b4e..96d8eedb71 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.models.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.formats.ld\_proof.models pac ==================================================================================== .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,14 +13,16 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.formats.ld\_proof.models.cre ------------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.models.cred_detail - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.formats.ld\_proof.models.cred\_detail\_options module --------------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.models.cred_detail_options - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.rst index 594e20f4d8..89c7256d13 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.rst @@ -2,17 +2,16 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.formats.ld\_proof package ============================================================================= .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.models + aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.models Submodules ---------- @@ -21,6 +20,8 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.formats.ld\_proof.handler mo ------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.rst index 31bc906fed..9fefa18078 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.rst @@ -2,18 +2,17 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.formats package =================================================================== .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.formats - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.issue_credential.v2_0.formats.indy - aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof + aries_cloudagent.protocols.issue_credential.v2_0.formats.indy + aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof Submodules ---------- @@ -22,6 +21,8 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.formats.handler module -------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.formats.handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.handlers.rst index d64924b2b4..6a16a66f88 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.handlers package ==================================================================== .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,46 +13,48 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.handlers.cred\_ack\_handler -------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.handlers.cred_ack_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.handlers.cred\_issue\_handler module ---------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.handlers.cred_issue_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.handlers.cred\_offer\_handler module ---------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.handlers.cred_offer_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.handlers.cred\_problem\_report\_handler module -------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.handlers.cred_problem_report_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.handlers.cred\_proposal\_handler module ------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.handlers.cred_proposal_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.handlers.cred\_request\_handler module ------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.handlers.cred_request_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.messages.inner.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.messages.inner.rst index 5591934834..631445edbb 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.messages.inner.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.messages.inner.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.messages.inner package ========================================================================== .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.messages.inner - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.messages.inner.cred\_preview --------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.messages.inner.cred_preview - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.messages.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.messages.rst index c4d72caf1d..41a2d6e82b 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.messages.rst @@ -2,17 +2,16 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.messages package ==================================================================== .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.issue_credential.v2_0.messages.inner + aries_cloudagent.protocols.issue_credential.v2_0.messages.inner Submodules ---------- @@ -21,54 +20,64 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.messages.cred\_ack module ----------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.messages.cred_ack - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + +aries\_cloudagent.protocols.issue\_credential.v2\_0.messages.cred\_ex\_record\_webhook module +--------------------------------------------------------------------------------------------- + +.. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.messages.cred_ex_record_webhook + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.messages.cred\_format module -------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.messages.cred_format - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.messages.cred\_issue module ------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.messages.cred_issue - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.messages.cred\_offer module ------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.messages.cred_offer - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.messages.cred\_problem\_report module ----------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.messages.cred_problem_report - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.messages.cred\_proposal module ---------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.messages.cred_proposal - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.messages.cred\_request module --------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.messages.cred_request - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.models.detail.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.models.detail.rst index c677e91e09..b055fb04ea 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.models.detail.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.models.detail.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.models.detail package ========================================================================= .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.models.detail - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,14 +13,16 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.models.detail.indy module ----------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.models.detail.indy - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.models.detail.ld\_proof module ---------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.models.detail.ld_proof - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.models.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.models.rst index 5e77c64a12..65f0f10156 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.models.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.models.rst @@ -2,17 +2,16 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.models package ================================================================== .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.issue_credential.v2_0.models.detail + aries_cloudagent.protocols.issue_credential.v2_0.models.detail Submodules ---------- @@ -21,6 +20,8 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.models.cred\_ex\_record modu ---------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.models.cred_ex_record - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.rst index daf64871a1..461ce7a789 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.rst @@ -2,20 +2,19 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0 package =========================================================== .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.issue_credential.v2_0.formats - aries_cloudagent.protocols.issue_credential.v2_0.handlers - aries_cloudagent.protocols.issue_credential.v2_0.messages - aries_cloudagent.protocols.issue_credential.v2_0.models + aries_cloudagent.protocols.issue_credential.v2_0.formats + aries_cloudagent.protocols.issue_credential.v2_0.handlers + aries_cloudagent.protocols.issue_credential.v2_0.messages + aries_cloudagent.protocols.issue_credential.v2_0.models Submodules ---------- @@ -24,30 +23,32 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.controller module --------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.controller - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.manager module ------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.message\_types module ------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.routes module ----------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.notification.rst b/docs/generated/aries_cloudagent.protocols.notification.rst index e7c192af55..ecded9877d 100644 --- a/docs/generated/aries_cloudagent.protocols.notification.rst +++ b/docs/generated/aries_cloudagent.protocols.notification.rst @@ -2,17 +2,16 @@ aries\_cloudagent.protocols.notification package ================================================ .. automodule:: aries_cloudagent.protocols.notification - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.notification.v1_0 + aries_cloudagent.protocols.notification.v1_0 Submodules ---------- @@ -21,6 +20,8 @@ aries\_cloudagent.protocols.notification.definition module ---------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.notification.definition - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.notification.v1_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.notification.v1_0.handlers.rst index 83fb827146..5d3124b4ad 100644 --- a/docs/generated/aries_cloudagent.protocols.notification.v1_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.notification.v1_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.notification.v1\_0.handlers package =============================================================== .. automodule:: aries_cloudagent.protocols.notification.v1_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.notification.v1\_0.handlers.ack\_handler module --------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.notification.v1_0.handlers.ack_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.notification.v1_0.messages.rst b/docs/generated/aries_cloudagent.protocols.notification.v1_0.messages.rst index 05f005efc2..d3c6dfbf96 100644 --- a/docs/generated/aries_cloudagent.protocols.notification.v1_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.notification.v1_0.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.notification.v1\_0.messages package =============================================================== .. automodule:: aries_cloudagent.protocols.notification.v1_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.notification.v1\_0.messages.ack module ------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.notification.v1_0.messages.ack - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.notification.v1_0.rst b/docs/generated/aries_cloudagent.protocols.notification.v1_0.rst index d18146087d..130125346b 100644 --- a/docs/generated/aries_cloudagent.protocols.notification.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.notification.v1_0.rst @@ -2,18 +2,17 @@ aries\_cloudagent.protocols.notification.v1\_0 package ====================================================== .. automodule:: aries_cloudagent.protocols.notification.v1_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.notification.v1_0.handlers - aries_cloudagent.protocols.notification.v1_0.messages + aries_cloudagent.protocols.notification.v1_0.handlers + aries_cloudagent.protocols.notification.v1_0.messages Submodules ---------- @@ -22,6 +21,8 @@ aries\_cloudagent.protocols.notification.v1\_0.message\_types module -------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.notification.v1_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.out_of_band.rst b/docs/generated/aries_cloudagent.protocols.out_of_band.rst index 8d68b3f53c..0d243c0bd7 100644 --- a/docs/generated/aries_cloudagent.protocols.out_of_band.rst +++ b/docs/generated/aries_cloudagent.protocols.out_of_band.rst @@ -2,17 +2,16 @@ aries\_cloudagent.protocols.out\_of\_band package ================================================= .. automodule:: aries_cloudagent.protocols.out_of_band - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.out_of_band.v1_0 + aries_cloudagent.protocols.out_of_band.v1_0 Submodules ---------- @@ -21,6 +20,8 @@ aries\_cloudagent.protocols.out\_of\_band.definition module ----------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.out_of_band.definition - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.handlers.rst index 354688b2b8..390979fa65 100644 --- a/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.out\_of\_band.v1\_0.handlers package ================================================================ .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,22 +13,24 @@ aries\_cloudagent.protocols.out\_of\_band.v1\_0.handlers.problem\_report\_handle ---------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.handlers.problem_report_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.out\_of\_band.v1\_0.handlers.reuse\_accept\_handler module -------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.handlers.reuse_accept_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.out\_of\_band.v1\_0.handlers.reuse\_handler module ------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.handlers.reuse_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.messages.rst b/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.messages.rst index d01723be08..79183ffd32 100644 --- a/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.out\_of\_band.v1\_0.messages package ================================================================ .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,38 +13,40 @@ aries\_cloudagent.protocols.out\_of\_band.v1\_0.messages.invitation module -------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.messages.invitation - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.out\_of\_band.v1\_0.messages.problem\_report module ------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.messages.problem_report - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.out\_of\_band.v1\_0.messages.reuse module --------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.messages.reuse - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.out\_of\_band.v1\_0.messages.reuse\_accept module ----------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.messages.reuse_accept - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.out\_of\_band.v1\_0.messages.service module ----------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.messages.service - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.models.rst b/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.models.rst index b1b7bfd33c..9ae0effc91 100644 --- a/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.models.rst +++ b/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.out\_of\_band.v1\_0.models package ============================================================== .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,14 +13,16 @@ aries\_cloudagent.protocols.out\_of\_band.v1\_0.models.invitation module ------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.models.invitation - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.out\_of\_band.v1\_0.models.oob\_record module ------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.models.oob_record - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.rst b/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.rst index 80443da521..8462b38004 100644 --- a/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.rst @@ -2,19 +2,18 @@ aries\_cloudagent.protocols.out\_of\_band.v1\_0 package ======================================================= .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.out_of_band.v1_0.handlers - aries_cloudagent.protocols.out_of_band.v1_0.messages - aries_cloudagent.protocols.out_of_band.v1_0.models + aries_cloudagent.protocols.out_of_band.v1_0.handlers + aries_cloudagent.protocols.out_of_band.v1_0.messages + aries_cloudagent.protocols.out_of_band.v1_0.models Submodules ---------- @@ -23,30 +22,32 @@ aries\_cloudagent.protocols.out\_of\_band.v1\_0.controller module ----------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.controller - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.out\_of\_band.v1\_0.manager module -------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.out\_of\_band.v1\_0.message\_types module --------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.out\_of\_band.v1\_0.routes module ------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.dif.rst b/docs/generated/aries_cloudagent.protocols.present_proof.dif.rst index 5d83cee955..adad4e0b16 100644 --- a/docs/generated/aries_cloudagent.protocols.present_proof.dif.rst +++ b/docs/generated/aries_cloudagent.protocols.present_proof.dif.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.present\_proof.dif package ====================================================== .. automodule:: aries_cloudagent.protocols.present_proof.dif - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,38 +13,40 @@ aries\_cloudagent.protocols.present\_proof.dif.pres\_exch module ---------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.dif.pres_exch - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.dif.pres\_exch\_handler module ------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.dif.pres_exch_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.dif.pres\_proposal\_schema module ---------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.dif.pres_proposal_schema - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.dif.pres\_request\_schema module --------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.dif.pres_request_schema - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.dif.pres\_schema module ------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.present_proof.dif.pres_schema - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.indy.rst b/docs/generated/aries_cloudagent.protocols.present_proof.indy.rst index a1954d2b86..ca2879b241 100644 --- a/docs/generated/aries_cloudagent.protocols.present_proof.indy.rst +++ b/docs/generated/aries_cloudagent.protocols.present_proof.indy.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.present\_proof.indy package ======================================================= .. automodule:: aries_cloudagent.protocols.present_proof.indy - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.present\_proof.indy.pres\_exch\_handler module -------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.indy.pres_exch_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.rst b/docs/generated/aries_cloudagent.protocols.present_proof.rst index e9b69e7de6..9318dbf8dc 100644 --- a/docs/generated/aries_cloudagent.protocols.present_proof.rst +++ b/docs/generated/aries_cloudagent.protocols.present_proof.rst @@ -2,20 +2,19 @@ aries\_cloudagent.protocols.present\_proof package ================================================== .. automodule:: aries_cloudagent.protocols.present_proof - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.present_proof.dif - aries_cloudagent.protocols.present_proof.indy - aries_cloudagent.protocols.present_proof.v1_0 - aries_cloudagent.protocols.present_proof.v2_0 + aries_cloudagent.protocols.present_proof.dif + aries_cloudagent.protocols.present_proof.indy + aries_cloudagent.protocols.present_proof.v1_0 + aries_cloudagent.protocols.present_proof.v2_0 Submodules ---------- @@ -24,6 +23,8 @@ aries\_cloudagent.protocols.present\_proof.definition module ------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.present_proof.definition - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.handlers.rst index 24c8341e2e..ef97bbb127 100644 --- a/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.present\_proof.v1\_0.handlers package ================================================================= .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,38 +13,40 @@ aries\_cloudagent.protocols.present\_proof.v1\_0.handlers.presentation\_ack\_han ------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.handlers.presentation_ack_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v1\_0.handlers.presentation\_handler module -------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.handlers.presentation_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v1\_0.handlers.presentation\_problem\_report\_handler module ------------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.handlers.presentation_problem_report_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v1\_0.handlers.presentation\_proposal\_handler module ------------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.handlers.presentation_proposal_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v1\_0.handlers.presentation\_request\_handler module ----------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.handlers.presentation_request_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.messages.rst b/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.messages.rst index a00cb2d278..e98676833d 100644 --- a/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.present\_proof.v1\_0.messages package ================================================================= .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,38 +13,48 @@ aries\_cloudagent.protocols.present\_proof.v1\_0.messages.presentation module ----------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.messages.presentation - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v1\_0.messages.presentation\_ack module ---------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.messages.presentation_ack - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v1\_0.messages.presentation\_problem\_report module ---------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.messages.presentation_problem_report - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v1\_0.messages.presentation\_proposal module --------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.messages.presentation_proposal - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v1\_0.messages.presentation\_request module -------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.messages.presentation_request - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + +aries\_cloudagent.protocols.present\_proof.v1\_0.messages.presentation\_webhook module +-------------------------------------------------------------------------------------- + +.. automodule:: aries_cloudagent.protocols.present_proof.v1_0.messages.presentation_webhook + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.models.rst b/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.models.rst index 6e97752c6a..2fbd454913 100644 --- a/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.models.rst +++ b/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.present\_proof.v1\_0.models package =============================================================== .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.present\_proof.v1\_0.models.presentation\_exchange m ------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.models.presentation_exchange - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.rst b/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.rst index 1a18103b2d..cffc7c119f 100644 --- a/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.rst @@ -2,19 +2,18 @@ aries\_cloudagent.protocols.present\_proof.v1\_0 package ======================================================== .. automodule:: aries_cloudagent.protocols.present_proof.v1_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.present_proof.v1_0.handlers - aries_cloudagent.protocols.present_proof.v1_0.messages - aries_cloudagent.protocols.present_proof.v1_0.models + aries_cloudagent.protocols.present_proof.v1_0.handlers + aries_cloudagent.protocols.present_proof.v1_0.messages + aries_cloudagent.protocols.present_proof.v1_0.models Submodules ---------- @@ -23,30 +22,32 @@ aries\_cloudagent.protocols.present\_proof.v1\_0.controller module ------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.controller - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v1\_0.manager module --------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v1\_0.message\_types module ---------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v1\_0.routes module -------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.formats.dif.rst b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.formats.dif.rst index 06b74fc41a..fd3ada060b 100644 --- a/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.formats.dif.rst +++ b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.formats.dif.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.present\_proof.v2\_0.formats.dif package ==================================================================== .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.formats.dif - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.present\_proof.v2\_0.formats.dif.handler module --------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.formats.dif.handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.formats.indy.rst b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.formats.indy.rst index fb215abc4e..2f54cdef9f 100644 --- a/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.formats.indy.rst +++ b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.formats.indy.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.present\_proof.v2\_0.formats.indy package ===================================================================== .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.formats.indy - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.present\_proof.v2\_0.formats.indy.handler module ---------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.formats.indy.handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.formats.rst b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.formats.rst index 5d44e018a1..8602dd4411 100644 --- a/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.formats.rst +++ b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.formats.rst @@ -2,18 +2,17 @@ aries\_cloudagent.protocols.present\_proof.v2\_0.formats package ================================================================ .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.formats - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.present_proof.v2_0.formats.dif - aries_cloudagent.protocols.present_proof.v2_0.formats.indy + aries_cloudagent.protocols.present_proof.v2_0.formats.dif + aries_cloudagent.protocols.present_proof.v2_0.formats.indy Submodules ---------- @@ -22,6 +21,8 @@ aries\_cloudagent.protocols.present\_proof.v2\_0.formats.handler module ----------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.formats.handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.handlers.rst index 6be81bcd53..8074e740f1 100644 --- a/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.present\_proof.v2\_0.handlers package ================================================================= .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,38 +13,40 @@ aries\_cloudagent.protocols.present\_proof.v2\_0.handlers.pres\_ack\_handler mod ----------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.handlers.pres_ack_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v2\_0.handlers.pres\_handler module ------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.handlers.pres_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v2\_0.handlers.pres\_problem\_report\_handler module ----------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.handlers.pres_problem_report_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v2\_0.handlers.pres\_proposal\_handler module ---------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.handlers.pres_proposal_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v2\_0.handlers.pres\_request\_handler module --------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.handlers.pres_request_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.messages.rst b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.messages.rst index bcaf052ad7..68139f57cd 100644 --- a/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.present\_proof.v2\_0.messages package ================================================================= .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,46 +13,56 @@ aries\_cloudagent.protocols.present\_proof.v2\_0.messages.pres module --------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.messages.pres - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v2\_0.messages.pres\_ack module -------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.messages.pres_ack - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v2\_0.messages.pres\_format module ----------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.messages.pres_format - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v2\_0.messages.pres\_problem\_report module -------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.messages.pres_problem_report - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v2\_0.messages.pres\_proposal module ------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.messages.pres_proposal - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v2\_0.messages.pres\_request module ------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.messages.pres_request - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + +aries\_cloudagent.protocols.present\_proof.v2\_0.messages.pres\_webhook module +------------------------------------------------------------------------------ + +.. automodule:: aries_cloudagent.protocols.present_proof.v2_0.messages.pres_webhook + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.models.rst b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.models.rst index 342773342d..a4b3da6f5e 100644 --- a/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.models.rst +++ b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.present\_proof.v2\_0.models package =============================================================== .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.present\_proof.v2\_0.models.pres\_exchange module ----------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.models.pres_exchange - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.rst b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.rst index ad4e408035..f372e30353 100644 --- a/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.rst +++ b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.rst @@ -2,20 +2,19 @@ aries\_cloudagent.protocols.present\_proof.v2\_0 package ======================================================== .. automodule:: aries_cloudagent.protocols.present_proof.v2_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.present_proof.v2_0.formats - aries_cloudagent.protocols.present_proof.v2_0.handlers - aries_cloudagent.protocols.present_proof.v2_0.messages - aries_cloudagent.protocols.present_proof.v2_0.models + aries_cloudagent.protocols.present_proof.v2_0.formats + aries_cloudagent.protocols.present_proof.v2_0.handlers + aries_cloudagent.protocols.present_proof.v2_0.messages + aries_cloudagent.protocols.present_proof.v2_0.models Submodules ---------- @@ -24,30 +23,32 @@ aries\_cloudagent.protocols.present\_proof.v2\_0.controller module ------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.controller - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v2\_0.manager module --------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v2\_0.message\_types module ---------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v2\_0.routes module -------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.problem_report.rst b/docs/generated/aries_cloudagent.protocols.problem_report.rst index 431b7f1387..8b80c721ac 100644 --- a/docs/generated/aries_cloudagent.protocols.problem_report.rst +++ b/docs/generated/aries_cloudagent.protocols.problem_report.rst @@ -2,17 +2,16 @@ aries\_cloudagent.protocols.problem\_report package =================================================== .. automodule:: aries_cloudagent.protocols.problem_report - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.problem_report.v1_0 + aries_cloudagent.protocols.problem_report.v1_0 Submodules ---------- @@ -21,6 +20,8 @@ aries\_cloudagent.protocols.problem\_report.definition module ------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.problem_report.definition - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.problem_report.v1_0.rst b/docs/generated/aries_cloudagent.protocols.problem_report.v1_0.rst index 7ac574e4c5..3ff5f8972f 100644 --- a/docs/generated/aries_cloudagent.protocols.problem_report.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.problem_report.v1_0.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.problem\_report.v1\_0 package ========================================================= .. automodule:: aries_cloudagent.protocols.problem_report.v1_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,22 +13,24 @@ aries\_cloudagent.protocols.problem\_report.v1\_0.handler module ---------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.problem_report.v1_0.handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.problem\_report.v1\_0.message module ---------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.problem_report.v1_0.message - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.problem\_report.v1\_0.message\_types module ----------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.problem_report.v1_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.revocation_notification.rst b/docs/generated/aries_cloudagent.protocols.revocation_notification.rst index c88d26085d..86337dc76c 100644 --- a/docs/generated/aries_cloudagent.protocols.revocation_notification.rst +++ b/docs/generated/aries_cloudagent.protocols.revocation_notification.rst @@ -2,18 +2,17 @@ aries\_cloudagent.protocols.revocation\_notification package ============================================================ .. automodule:: aries_cloudagent.protocols.revocation_notification - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.revocation_notification.v1_0 - aries_cloudagent.protocols.revocation_notification.v2_0 + aries_cloudagent.protocols.revocation_notification.v1_0 + aries_cloudagent.protocols.revocation_notification.v2_0 Submodules ---------- @@ -22,6 +21,8 @@ aries\_cloudagent.protocols.revocation\_notification.definition module ---------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.revocation_notification.definition - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.handlers.rst index 205b069dc4..98dddbe6f9 100644 --- a/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.revocation\_notification.v1\_0.handlers package =========================================================================== .. automodule:: aries_cloudagent.protocols.revocation_notification.v1_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.revocation\_notification.v1\_0.handlers.revoke\_hand ------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.revocation_notification.v1_0.handlers.revoke_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.messages.rst b/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.messages.rst index d623d66dc8..993f373e2a 100644 --- a/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.revocation\_notification.v1\_0.messages package =========================================================================== .. automodule:: aries_cloudagent.protocols.revocation_notification.v1_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.revocation\_notification.v1\_0.messages.revoke modul --------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.revocation_notification.v1_0.messages.revoke - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.models.rst b/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.models.rst index 48164ca2bf..aae0c83a81 100644 --- a/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.models.rst +++ b/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.revocation\_notification.v1\_0.models package ========================================================================= .. automodule:: aries_cloudagent.protocols.revocation_notification.v1_0.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.revocation\_notification.v1\_0.models.rev\_notificat -------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.revocation_notification.v1_0.models.rev_notification_record - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.rst b/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.rst index 06a3b4ace0..77a1d172ec 100644 --- a/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.rst @@ -2,19 +2,18 @@ aries\_cloudagent.protocols.revocation\_notification.v1\_0 package ================================================================== .. automodule:: aries_cloudagent.protocols.revocation_notification.v1_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.revocation_notification.v1_0.handlers - aries_cloudagent.protocols.revocation_notification.v1_0.messages - aries_cloudagent.protocols.revocation_notification.v1_0.models + aries_cloudagent.protocols.revocation_notification.v1_0.handlers + aries_cloudagent.protocols.revocation_notification.v1_0.messages + aries_cloudagent.protocols.revocation_notification.v1_0.models Submodules ---------- @@ -23,14 +22,16 @@ aries\_cloudagent.protocols.revocation\_notification.v1\_0.message\_types module -------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.revocation_notification.v1_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.revocation\_notification.v1\_0.routes module ------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.revocation_notification.v1_0.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.handlers.rst index 1fa7a93885..86a419dc3f 100644 --- a/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.revocation\_notification.v2\_0.handlers package =========================================================================== .. automodule:: aries_cloudagent.protocols.revocation_notification.v2_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.revocation\_notification.v2\_0.handlers.revoke\_hand ------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.revocation_notification.v2_0.handlers.revoke_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.messages.rst b/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.messages.rst index db465ff9e3..bdd311136b 100644 --- a/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.revocation\_notification.v2\_0.messages package =========================================================================== .. automodule:: aries_cloudagent.protocols.revocation_notification.v2_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.revocation\_notification.v2\_0.messages.revoke modul --------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.revocation_notification.v2_0.messages.revoke - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.models.rst b/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.models.rst index cffa7e42ae..fdc74b0332 100644 --- a/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.models.rst +++ b/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.revocation\_notification.v2\_0.models package ========================================================================= .. automodule:: aries_cloudagent.protocols.revocation_notification.v2_0.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.revocation\_notification.v2\_0.models.rev\_notificat -------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.revocation_notification.v2_0.models.rev_notification_record - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.rst b/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.rst index 8247c6853a..9a616e6c44 100644 --- a/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.rst +++ b/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.rst @@ -2,19 +2,18 @@ aries\_cloudagent.protocols.revocation\_notification.v2\_0 package ================================================================== .. automodule:: aries_cloudagent.protocols.revocation_notification.v2_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.revocation_notification.v2_0.handlers - aries_cloudagent.protocols.revocation_notification.v2_0.messages - aries_cloudagent.protocols.revocation_notification.v2_0.models + aries_cloudagent.protocols.revocation_notification.v2_0.handlers + aries_cloudagent.protocols.revocation_notification.v2_0.messages + aries_cloudagent.protocols.revocation_notification.v2_0.models Submodules ---------- @@ -23,14 +22,16 @@ aries\_cloudagent.protocols.revocation\_notification.v2\_0.message\_types module -------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.revocation_notification.v2_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.revocation\_notification.v2\_0.routes module ------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.revocation_notification.v2_0.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.routing.rst b/docs/generated/aries_cloudagent.protocols.routing.rst index 8aa2a0a91f..3dab5f14a3 100644 --- a/docs/generated/aries_cloudagent.protocols.routing.rst +++ b/docs/generated/aries_cloudagent.protocols.routing.rst @@ -2,17 +2,16 @@ aries\_cloudagent.protocols.routing package =========================================== .. automodule:: aries_cloudagent.protocols.routing - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.routing.v1_0 + aries_cloudagent.protocols.routing.v1_0 Submodules ---------- @@ -21,6 +20,8 @@ aries\_cloudagent.protocols.routing.definition module ----------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.definition - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.routing.v1_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.routing.v1_0.handlers.rst index dd2b61d077..5535c2a49f 100644 --- a/docs/generated/aries_cloudagent.protocols.routing.v1_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.routing.v1_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.routing.v1\_0.handlers package ========================================================== .. automodule:: aries_cloudagent.protocols.routing.v1_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,38 +13,40 @@ aries\_cloudagent.protocols.routing.v1\_0.handlers.forward\_handler module -------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.handlers.forward_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.routing.v1\_0.handlers.route\_query\_request\_handler module ---------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.handlers.route_query_request_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.routing.v1\_0.handlers.route\_query\_response\_handler module ----------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.handlers.route_query_response_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.routing.v1\_0.handlers.route\_update\_request\_handler module ----------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.handlers.route_update_request_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.routing.v1\_0.handlers.route\_update\_response\_handler module ------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.routing.v1_0.handlers.route_update_response_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.routing.v1_0.messages.rst b/docs/generated/aries_cloudagent.protocols.routing.v1_0.messages.rst index 640e6318fd..84a5e5d143 100644 --- a/docs/generated/aries_cloudagent.protocols.routing.v1_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.routing.v1_0.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.routing.v1\_0.messages package ========================================================== .. automodule:: aries_cloudagent.protocols.routing.v1_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,38 +13,40 @@ aries\_cloudagent.protocols.routing.v1\_0.messages.forward module ----------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.messages.forward - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.routing.v1\_0.messages.route\_query\_request module ------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.messages.route_query_request - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.routing.v1\_0.messages.route\_query\_response module -------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.messages.route_query_response - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.routing.v1\_0.messages.route\_update\_request module -------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.messages.route_update_request - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.routing.v1\_0.messages.route\_update\_response module --------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.messages.route_update_response - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.routing.v1_0.models.rst b/docs/generated/aries_cloudagent.protocols.routing.v1_0.models.rst index 53d1cc867a..1a373f1022 100644 --- a/docs/generated/aries_cloudagent.protocols.routing.v1_0.models.rst +++ b/docs/generated/aries_cloudagent.protocols.routing.v1_0.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.routing.v1\_0.models package ======================================================== .. automodule:: aries_cloudagent.protocols.routing.v1_0.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,46 +13,48 @@ aries\_cloudagent.protocols.routing.v1\_0.models.paginate module ---------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.models.paginate - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.routing.v1\_0.models.paginated module ----------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.models.paginated - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.routing.v1\_0.models.route\_query\_result module ---------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.models.route_query_result - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.routing.v1\_0.models.route\_record module --------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.models.route_record - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.routing.v1\_0.models.route\_update module --------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.models.route_update - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.routing.v1\_0.models.route\_updated module ---------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.models.route_updated - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.routing.v1_0.rst b/docs/generated/aries_cloudagent.protocols.routing.v1_0.rst index ee671a27d2..353b5c393d 100644 --- a/docs/generated/aries_cloudagent.protocols.routing.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.routing.v1_0.rst @@ -2,19 +2,18 @@ aries\_cloudagent.protocols.routing.v1\_0 package ================================================= .. automodule:: aries_cloudagent.protocols.routing.v1_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.routing.v1_0.handlers - aries_cloudagent.protocols.routing.v1_0.messages - aries_cloudagent.protocols.routing.v1_0.models + aries_cloudagent.protocols.routing.v1_0.handlers + aries_cloudagent.protocols.routing.v1_0.messages + aries_cloudagent.protocols.routing.v1_0.models Submodules ---------- @@ -23,14 +22,16 @@ aries\_cloudagent.protocols.routing.v1\_0.manager module -------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.routing.v1\_0.message\_types module --------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.rst b/docs/generated/aries_cloudagent.protocols.rst index c569e1d81b..3af0fbcff1 100644 --- a/docs/generated/aries_cloudagent.protocols.rst +++ b/docs/generated/aries_cloudagent.protocols.rst @@ -2,32 +2,31 @@ aries\_cloudagent.protocols package =================================== .. automodule:: aries_cloudagent.protocols - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - - aries_cloudagent.protocols.actionmenu - aries_cloudagent.protocols.basicmessage - aries_cloudagent.protocols.connections - aries_cloudagent.protocols.coordinate_mediation - aries_cloudagent.protocols.didexchange - aries_cloudagent.protocols.discovery - aries_cloudagent.protocols.endorse_transaction - aries_cloudagent.protocols.introduction - aries_cloudagent.protocols.issue_credential - aries_cloudagent.protocols.notification - aries_cloudagent.protocols.out_of_band - aries_cloudagent.protocols.present_proof - aries_cloudagent.protocols.problem_report - aries_cloudagent.protocols.revocation_notification - aries_cloudagent.protocols.routing - aries_cloudagent.protocols.trustping + + aries_cloudagent.protocols.actionmenu + aries_cloudagent.protocols.basicmessage + aries_cloudagent.protocols.connections + aries_cloudagent.protocols.coordinate_mediation + aries_cloudagent.protocols.didexchange + aries_cloudagent.protocols.discovery + aries_cloudagent.protocols.endorse_transaction + aries_cloudagent.protocols.introduction + aries_cloudagent.protocols.issue_credential + aries_cloudagent.protocols.notification + aries_cloudagent.protocols.out_of_band + aries_cloudagent.protocols.present_proof + aries_cloudagent.protocols.problem_report + aries_cloudagent.protocols.revocation_notification + aries_cloudagent.protocols.routing + aries_cloudagent.protocols.trustping Submodules ---------- @@ -36,6 +35,8 @@ aries\_cloudagent.protocols.didcomm\_prefix module -------------------------------------------------- .. automodule:: aries_cloudagent.protocols.didcomm_prefix - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.trustping.rst b/docs/generated/aries_cloudagent.protocols.trustping.rst index c2df3c688e..01cc9e6332 100644 --- a/docs/generated/aries_cloudagent.protocols.trustping.rst +++ b/docs/generated/aries_cloudagent.protocols.trustping.rst @@ -2,17 +2,16 @@ aries\_cloudagent.protocols.trustping package ============================================= .. automodule:: aries_cloudagent.protocols.trustping - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.trustping.v1_0 + aries_cloudagent.protocols.trustping.v1_0 Submodules ---------- @@ -21,6 +20,8 @@ aries\_cloudagent.protocols.trustping.definition module ------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.trustping.definition - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.trustping.v1_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.trustping.v1_0.handlers.rst index d8dd03ff35..19f404be0e 100644 --- a/docs/generated/aries_cloudagent.protocols.trustping.v1_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.trustping.v1_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.trustping.v1\_0.handlers package ============================================================ .. automodule:: aries_cloudagent.protocols.trustping.v1_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,14 +13,16 @@ aries\_cloudagent.protocols.trustping.v1\_0.handlers.ping\_handler module ------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.trustping.v1_0.handlers.ping_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.trustping.v1\_0.handlers.ping\_response\_handler module ----------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.trustping.v1_0.handlers.ping_response_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.trustping.v1_0.messages.rst b/docs/generated/aries_cloudagent.protocols.trustping.v1_0.messages.rst index 1e1b83a5a0..c7d0fb38f3 100644 --- a/docs/generated/aries_cloudagent.protocols.trustping.v1_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.trustping.v1_0.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.trustping.v1\_0.messages package ============================================================ .. automodule:: aries_cloudagent.protocols.trustping.v1_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,14 +13,16 @@ aries\_cloudagent.protocols.trustping.v1\_0.messages.ping module ---------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.trustping.v1_0.messages.ping - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.trustping.v1\_0.messages.ping\_response module -------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.trustping.v1_0.messages.ping_response - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.trustping.v1_0.rst b/docs/generated/aries_cloudagent.protocols.trustping.v1_0.rst index cceadfedfc..add52eafbf 100644 --- a/docs/generated/aries_cloudagent.protocols.trustping.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.trustping.v1_0.rst @@ -2,18 +2,17 @@ aries\_cloudagent.protocols.trustping.v1\_0 package =================================================== .. automodule:: aries_cloudagent.protocols.trustping.v1_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.trustping.v1_0.handlers - aries_cloudagent.protocols.trustping.v1_0.messages + aries_cloudagent.protocols.trustping.v1_0.handlers + aries_cloudagent.protocols.trustping.v1_0.messages Submodules ---------- @@ -22,14 +21,16 @@ aries\_cloudagent.protocols.trustping.v1\_0.message\_types module ----------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.trustping.v1_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.trustping.v1\_0.routes module --------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.trustping.v1_0.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.resolver.default.rst b/docs/generated/aries_cloudagent.resolver.default.rst index b7dd8eac96..00b2bff98d 100644 --- a/docs/generated/aries_cloudagent.resolver.default.rst +++ b/docs/generated/aries_cloudagent.resolver.default.rst @@ -2,9 +2,9 @@ aries\_cloudagent.resolver.default package ========================================== .. automodule:: aries_cloudagent.resolver.default - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,30 +13,32 @@ aries\_cloudagent.resolver.default.indy module ---------------------------------------------- .. automodule:: aries_cloudagent.resolver.default.indy - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.resolver.default.key module --------------------------------------------- .. automodule:: aries_cloudagent.resolver.default.key - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.resolver.default.universal module --------------------------------------------------- .. automodule:: aries_cloudagent.resolver.default.universal - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.resolver.default.web module --------------------------------------------- .. automodule:: aries_cloudagent.resolver.default.web - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.resolver.rst b/docs/generated/aries_cloudagent.resolver.rst index 7732543e90..1b181f1fae 100644 --- a/docs/generated/aries_cloudagent.resolver.rst +++ b/docs/generated/aries_cloudagent.resolver.rst @@ -2,17 +2,16 @@ aries\_cloudagent.resolver package ================================== .. automodule:: aries_cloudagent.resolver - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.resolver.default + aries_cloudagent.resolver.default Submodules ---------- @@ -21,22 +20,24 @@ aries\_cloudagent.resolver.base module -------------------------------------- .. automodule:: aries_cloudagent.resolver.base - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.resolver.did\_resolver module ----------------------------------------------- .. automodule:: aries_cloudagent.resolver.did_resolver - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.resolver.routes module ---------------------------------------- .. automodule:: aries_cloudagent.resolver.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.revocation.models.rst b/docs/generated/aries_cloudagent.revocation.models.rst index 67f5c3ffb1..89a45d9248 100644 --- a/docs/generated/aries_cloudagent.revocation.models.rst +++ b/docs/generated/aries_cloudagent.revocation.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.revocation.models package =========================================== .. automodule:: aries_cloudagent.revocation.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,30 +13,32 @@ aries\_cloudagent.revocation.models.indy module ----------------------------------------------- .. automodule:: aries_cloudagent.revocation.models.indy - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.revocation.models.issuer\_cred\_rev\_record module -------------------------------------------------------------------- .. automodule:: aries_cloudagent.revocation.models.issuer_cred_rev_record - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.revocation.models.issuer\_rev\_reg\_record module ------------------------------------------------------------------- .. automodule:: aries_cloudagent.revocation.models.issuer_rev_reg_record - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.revocation.models.revocation\_registry module --------------------------------------------------------------- .. automodule:: aries_cloudagent.revocation.models.revocation_registry - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.revocation.rst b/docs/generated/aries_cloudagent.revocation.rst index 7263263c71..7d28a08b90 100644 --- a/docs/generated/aries_cloudagent.revocation.rst +++ b/docs/generated/aries_cloudagent.revocation.rst @@ -2,17 +2,16 @@ aries\_cloudagent.revocation package ==================================== .. automodule:: aries_cloudagent.revocation - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.revocation.models + aries_cloudagent.revocation.models Submodules ---------- @@ -21,46 +20,48 @@ aries\_cloudagent.revocation.error module ----------------------------------------- .. automodule:: aries_cloudagent.revocation.error - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.revocation.indy module ---------------------------------------- .. automodule:: aries_cloudagent.revocation.indy - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.revocation.manager module ------------------------------------------- .. automodule:: aries_cloudagent.revocation.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.revocation.recover module ------------------------------------------- .. automodule:: aries_cloudagent.revocation.recover - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.revocation.routes module ------------------------------------------ .. automodule:: aries_cloudagent.revocation.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.revocation.util module ---------------------------------------- .. automodule:: aries_cloudagent.revocation.util - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.rst b/docs/generated/aries_cloudagent.rst index ef01b45059..9bcb0112f2 100644 --- a/docs/generated/aries_cloudagent.rst +++ b/docs/generated/aries_cloudagent.rst @@ -2,38 +2,37 @@ aries\_cloudagent package ========================= .. automodule:: aries_cloudagent - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - - aries_cloudagent.admin - aries_cloudagent.askar - aries_cloudagent.cache - aries_cloudagent.commands - aries_cloudagent.config - aries_cloudagent.connections - aries_cloudagent.core - aries_cloudagent.did - aries_cloudagent.holder - aries_cloudagent.indy - aries_cloudagent.ledger - aries_cloudagent.messaging - aries_cloudagent.multitenant - aries_cloudagent.protocols - aries_cloudagent.resolver - aries_cloudagent.revocation - aries_cloudagent.storage - aries_cloudagent.tails - aries_cloudagent.transport - aries_cloudagent.utils - aries_cloudagent.vc - aries_cloudagent.wallet + + aries_cloudagent.admin + aries_cloudagent.askar + aries_cloudagent.cache + aries_cloudagent.commands + aries_cloudagent.config + aries_cloudagent.connections + aries_cloudagent.core + aries_cloudagent.did + aries_cloudagent.holder + aries_cloudagent.indy + aries_cloudagent.ledger + aries_cloudagent.messaging + aries_cloudagent.multitenant + aries_cloudagent.protocols + aries_cloudagent.resolver + aries_cloudagent.revocation + aries_cloudagent.storage + aries_cloudagent.tails + aries_cloudagent.transport + aries_cloudagent.utils + aries_cloudagent.vc + aries_cloudagent.wallet Submodules ---------- @@ -42,6 +41,8 @@ aries\_cloudagent.version module -------------------------------- .. automodule:: aries_cloudagent.version - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.storage.rst b/docs/generated/aries_cloudagent.storage.rst index bd06d4d460..c62c2e21f5 100644 --- a/docs/generated/aries_cloudagent.storage.rst +++ b/docs/generated/aries_cloudagent.storage.rst @@ -2,17 +2,16 @@ aries\_cloudagent.storage package ================================= .. automodule:: aries_cloudagent.storage - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.storage.vc_holder + aries_cloudagent.storage.vc_holder Submodules ---------- @@ -21,46 +20,48 @@ aries\_cloudagent.storage.askar module -------------------------------------- .. automodule:: aries_cloudagent.storage.askar - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.storage.base module ------------------------------------- .. automodule:: aries_cloudagent.storage.base - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.storage.error module -------------------------------------- .. automodule:: aries_cloudagent.storage.error - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.storage.in\_memory module ------------------------------------------- .. automodule:: aries_cloudagent.storage.in_memory - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.storage.indy module ------------------------------------- .. automodule:: aries_cloudagent.storage.indy - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.storage.record module --------------------------------------- .. automodule:: aries_cloudagent.storage.record - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.storage.vc_holder.rst b/docs/generated/aries_cloudagent.storage.vc_holder.rst index 7bd5098aff..329a262303 100644 --- a/docs/generated/aries_cloudagent.storage.vc_holder.rst +++ b/docs/generated/aries_cloudagent.storage.vc_holder.rst @@ -2,9 +2,9 @@ aries\_cloudagent.storage.vc\_holder package ============================================ .. automodule:: aries_cloudagent.storage.vc_holder - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,46 +13,48 @@ aries\_cloudagent.storage.vc\_holder.askar module ------------------------------------------------- .. automodule:: aries_cloudagent.storage.vc_holder.askar - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.storage.vc\_holder.base module ------------------------------------------------ .. automodule:: aries_cloudagent.storage.vc_holder.base - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.storage.vc\_holder.in\_memory module ------------------------------------------------------ .. automodule:: aries_cloudagent.storage.vc_holder.in_memory - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.storage.vc\_holder.indy module ------------------------------------------------ .. automodule:: aries_cloudagent.storage.vc_holder.indy - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.storage.vc\_holder.vc\_record module ------------------------------------------------------ .. automodule:: aries_cloudagent.storage.vc_holder.vc_record - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.storage.vc\_holder.xform module ------------------------------------------------- .. automodule:: aries_cloudagent.storage.vc_holder.xform - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.tails.rst b/docs/generated/aries_cloudagent.tails.rst index 51e1746aee..5aabc0cb4e 100644 --- a/docs/generated/aries_cloudagent.tails.rst +++ b/docs/generated/aries_cloudagent.tails.rst @@ -2,9 +2,9 @@ aries\_cloudagent.tails package =============================== .. automodule:: aries_cloudagent.tails - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,22 +13,24 @@ aries\_cloudagent.tails.base module ----------------------------------- .. automodule:: aries_cloudagent.tails.base - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.tails.error module ------------------------------------ .. automodule:: aries_cloudagent.tails.error - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.tails.indy\_tails\_server module -------------------------------------------------- .. automodule:: aries_cloudagent.tails.indy_tails_server - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.transport.inbound.rst b/docs/generated/aries_cloudagent.transport.inbound.rst index 879581b75e..6a32abd804 100644 --- a/docs/generated/aries_cloudagent.transport.inbound.rst +++ b/docs/generated/aries_cloudagent.transport.inbound.rst @@ -2,9 +2,9 @@ aries\_cloudagent.transport.inbound package =========================================== .. automodule:: aries_cloudagent.transport.inbound - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,62 +13,64 @@ aries\_cloudagent.transport.inbound.base module ----------------------------------------------- .. automodule:: aries_cloudagent.transport.inbound.base - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.inbound.delivery\_queue module ---------------------------------------------------------- .. automodule:: aries_cloudagent.transport.inbound.delivery_queue - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.inbound.http module ----------------------------------------------- .. automodule:: aries_cloudagent.transport.inbound.http - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.inbound.manager module -------------------------------------------------- .. automodule:: aries_cloudagent.transport.inbound.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.inbound.message module -------------------------------------------------- .. automodule:: aries_cloudagent.transport.inbound.message - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.inbound.receipt module -------------------------------------------------- .. automodule:: aries_cloudagent.transport.inbound.receipt - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.inbound.session module -------------------------------------------------- .. automodule:: aries_cloudagent.transport.inbound.session - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.inbound.ws module --------------------------------------------- .. automodule:: aries_cloudagent.transport.inbound.ws - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.transport.outbound.rst b/docs/generated/aries_cloudagent.transport.outbound.rst index 1fde7f0379..1be069a0c7 100644 --- a/docs/generated/aries_cloudagent.transport.outbound.rst +++ b/docs/generated/aries_cloudagent.transport.outbound.rst @@ -2,9 +2,9 @@ aries\_cloudagent.transport.outbound package ============================================ .. automodule:: aries_cloudagent.transport.outbound - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,46 +13,48 @@ aries\_cloudagent.transport.outbound.base module ------------------------------------------------ .. automodule:: aries_cloudagent.transport.outbound.base - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.outbound.http module ------------------------------------------------ .. automodule:: aries_cloudagent.transport.outbound.http - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.outbound.manager module --------------------------------------------------- .. automodule:: aries_cloudagent.transport.outbound.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.outbound.message module --------------------------------------------------- .. automodule:: aries_cloudagent.transport.outbound.message - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.outbound.status module -------------------------------------------------- .. automodule:: aries_cloudagent.transport.outbound.status - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.outbound.ws module ---------------------------------------------- .. automodule:: aries_cloudagent.transport.outbound.ws - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.transport.queue.rst b/docs/generated/aries_cloudagent.transport.queue.rst index f6e9920d94..577fdf58c2 100644 --- a/docs/generated/aries_cloudagent.transport.queue.rst +++ b/docs/generated/aries_cloudagent.transport.queue.rst @@ -2,9 +2,9 @@ aries\_cloudagent.transport.queue package ========================================= .. automodule:: aries_cloudagent.transport.queue - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,14 +13,16 @@ aries\_cloudagent.transport.queue.base module --------------------------------------------- .. automodule:: aries_cloudagent.transport.queue.base - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.queue.basic module ---------------------------------------------- .. automodule:: aries_cloudagent.transport.queue.basic - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.transport.rst b/docs/generated/aries_cloudagent.transport.rst index 33aed946ad..d10d781102 100644 --- a/docs/generated/aries_cloudagent.transport.rst +++ b/docs/generated/aries_cloudagent.transport.rst @@ -2,19 +2,18 @@ aries\_cloudagent.transport package =================================== .. automodule:: aries_cloudagent.transport - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.transport.inbound - aries_cloudagent.transport.outbound - aries_cloudagent.transport.queue + aries_cloudagent.transport.inbound + aries_cloudagent.transport.outbound + aries_cloudagent.transport.queue Submodules ---------- @@ -23,30 +22,32 @@ aries\_cloudagent.transport.error module ---------------------------------------- .. automodule:: aries_cloudagent.transport.error - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.pack\_format module ----------------------------------------------- .. automodule:: aries_cloudagent.transport.pack_format - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.stats module ---------------------------------------- .. automodule:: aries_cloudagent.transport.stats - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.wire\_format module ----------------------------------------------- .. automodule:: aries_cloudagent.transport.wire_format - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.utils.rst b/docs/generated/aries_cloudagent.utils.rst index f5241f39d8..9d968756b8 100644 --- a/docs/generated/aries_cloudagent.utils.rst +++ b/docs/generated/aries_cloudagent.utils.rst @@ -2,9 +2,9 @@ aries\_cloudagent.utils package =============================== .. automodule:: aries_cloudagent.utils - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,78 +13,80 @@ aries\_cloudagent.utils.classloader module ------------------------------------------ .. automodule:: aries_cloudagent.utils.classloader - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.utils.dependencies module ------------------------------------------- .. automodule:: aries_cloudagent.utils.dependencies - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.utils.env module ---------------------------------- .. automodule:: aries_cloudagent.utils.env - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.utils.http module ----------------------------------- .. automodule:: aries_cloudagent.utils.http - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.utils.jwe module ---------------------------------- .. automodule:: aries_cloudagent.utils.jwe - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.utils.outofband module ---------------------------------------- .. automodule:: aries_cloudagent.utils.outofband - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.utils.repeat module ------------------------------------- .. automodule:: aries_cloudagent.utils.repeat - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.utils.stats module ------------------------------------ .. automodule:: aries_cloudagent.utils.stats - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.utils.task\_queue module ------------------------------------------ .. automodule:: aries_cloudagent.utils.task_queue - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.utils.tracing module -------------------------------------- .. automodule:: aries_cloudagent.utils.tracing - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.vc.ld_proofs.crypto.rst b/docs/generated/aries_cloudagent.vc.ld_proofs.crypto.rst index 66c04359cd..f84983368f 100644 --- a/docs/generated/aries_cloudagent.vc.ld_proofs.crypto.rst +++ b/docs/generated/aries_cloudagent.vc.ld_proofs.crypto.rst @@ -2,9 +2,9 @@ aries\_cloudagent.vc.ld\_proofs.crypto package ============================================== .. automodule:: aries_cloudagent.vc.ld_proofs.crypto - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,14 +13,16 @@ aries\_cloudagent.vc.ld\_proofs.crypto.key\_pair module ------------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.crypto.key_pair - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.crypto.wallet\_key\_pair module --------------------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.crypto.wallet_key_pair - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.vc.ld_proofs.purposes.rst b/docs/generated/aries_cloudagent.vc.ld_proofs.purposes.rst index 169a889c5b..6693036472 100644 --- a/docs/generated/aries_cloudagent.vc.ld_proofs.purposes.rst +++ b/docs/generated/aries_cloudagent.vc.ld_proofs.purposes.rst @@ -2,9 +2,9 @@ aries\_cloudagent.vc.ld\_proofs.purposes package ================================================ .. automodule:: aries_cloudagent.vc.ld_proofs.purposes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,38 +13,40 @@ aries\_cloudagent.vc.ld\_proofs.purposes.assertion\_proof\_purpose module ------------------------------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.purposes.assertion_proof_purpose - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.purposes.authentication\_proof\_purpose module ------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.vc.ld_proofs.purposes.authentication_proof_purpose - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.purposes.controller\_proof\_purpose module -------------------------------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.purposes.controller_proof_purpose - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.purposes.credential\_issuance\_purpose module ----------------------------------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.purposes.credential_issuance_purpose - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.purposes.proof\_purpose module -------------------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.purposes.proof_purpose - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.vc.ld_proofs.rst b/docs/generated/aries_cloudagent.vc.ld_proofs.rst index ca7276b1fc..4727ab22fc 100644 --- a/docs/generated/aries_cloudagent.vc.ld_proofs.rst +++ b/docs/generated/aries_cloudagent.vc.ld_proofs.rst @@ -2,19 +2,18 @@ aries\_cloudagent.vc.ld\_proofs package ======================================= .. automodule:: aries_cloudagent.vc.ld_proofs - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.vc.ld_proofs.crypto - aries_cloudagent.vc.ld_proofs.purposes - aries_cloudagent.vc.ld_proofs.suites + aries_cloudagent.vc.ld_proofs.crypto + aries_cloudagent.vc.ld_proofs.purposes + aries_cloudagent.vc.ld_proofs.suites Submodules ---------- @@ -23,54 +22,56 @@ aries\_cloudagent.vc.ld\_proofs.check module -------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.check - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.constants module ------------------------------------------------ .. automodule:: aries_cloudagent.vc.ld_proofs.constants - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.document\_loader module ------------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.document_loader - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.error module -------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.error - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.ld\_proofs module ------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.ld_proofs - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.proof\_set module ------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.proof_set - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.validation\_result module --------------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.validation_result - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.vc.ld_proofs.suites.rst b/docs/generated/aries_cloudagent.vc.ld_proofs.suites.rst index cbda566bca..3ba6e87d41 100644 --- a/docs/generated/aries_cloudagent.vc.ld_proofs.suites.rst +++ b/docs/generated/aries_cloudagent.vc.ld_proofs.suites.rst @@ -2,9 +2,9 @@ aries\_cloudagent.vc.ld\_proofs.suites package ============================================== .. automodule:: aries_cloudagent.vc.ld_proofs.suites - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,54 +13,56 @@ aries\_cloudagent.vc.ld\_proofs.suites.bbs\_bls\_signature\_2020 module ----------------------------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.suites.bbs_bls_signature_2020 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.suites.bbs\_bls\_signature\_2020\_base module ----------------------------------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.suites.bbs_bls_signature_2020_base - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.suites.bbs\_bls\_signature\_proof\_2020 module ------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.vc.ld_proofs.suites.bbs_bls_signature_proof_2020 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.suites.ed25519\_signature\_2018 module ---------------------------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.suites.ed25519_signature_2018 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.suites.jws\_linked\_data\_signature module -------------------------------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.suites.jws_linked_data_signature - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.suites.linked\_data\_proof module ----------------------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.suites.linked_data_proof - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.suites.linked\_data\_signature module --------------------------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.suites.linked_data_signature - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.vc.rst b/docs/generated/aries_cloudagent.vc.rst index f3c1e09d88..a0a9a0ba2e 100644 --- a/docs/generated/aries_cloudagent.vc.rst +++ b/docs/generated/aries_cloudagent.vc.rst @@ -2,15 +2,15 @@ aries\_cloudagent.vc package ============================ .. automodule:: aries_cloudagent.vc - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.vc.ld_proofs - aries_cloudagent.vc.vc_ld + aries_cloudagent.vc.ld_proofs + aries_cloudagent.vc.vc_ld + diff --git a/docs/generated/aries_cloudagent.vc.vc_ld.models.rst b/docs/generated/aries_cloudagent.vc.vc_ld.models.rst index 83bcd876d2..745e577a62 100644 --- a/docs/generated/aries_cloudagent.vc.vc_ld.models.rst +++ b/docs/generated/aries_cloudagent.vc.vc_ld.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.vc.vc\_ld.models package ========================================== .. automodule:: aries_cloudagent.vc.vc_ld.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,14 +13,16 @@ aries\_cloudagent.vc.vc\_ld.models.credential module ---------------------------------------------------- .. automodule:: aries_cloudagent.vc.vc_ld.models.credential - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.vc\_ld.models.linked\_data\_proof module ------------------------------------------------------------- .. automodule:: aries_cloudagent.vc.vc_ld.models.linked_data_proof - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.vc.vc_ld.rst b/docs/generated/aries_cloudagent.vc.vc_ld.rst index a89699f5cc..00c9235f89 100644 --- a/docs/generated/aries_cloudagent.vc.vc_ld.rst +++ b/docs/generated/aries_cloudagent.vc.vc_ld.rst @@ -2,17 +2,16 @@ aries\_cloudagent.vc.vc\_ld package =================================== .. automodule:: aries_cloudagent.vc.vc_ld - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.vc.vc_ld.models + aries_cloudagent.vc.vc_ld.models Submodules ---------- @@ -21,30 +20,32 @@ aries\_cloudagent.vc.vc\_ld.issue module ---------------------------------------- .. automodule:: aries_cloudagent.vc.vc_ld.issue - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.vc\_ld.prove module ---------------------------------------- .. automodule:: aries_cloudagent.vc.vc_ld.prove - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.vc\_ld.validation\_result module ----------------------------------------------------- .. automodule:: aries_cloudagent.vc.vc_ld.validation_result - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.vc\_ld.verify module ----------------------------------------- .. automodule:: aries_cloudagent.vc.vc_ld.verify - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.wallet.models.rst b/docs/generated/aries_cloudagent.wallet.models.rst index d59bc609c4..741b5a2246 100644 --- a/docs/generated/aries_cloudagent.wallet.models.rst +++ b/docs/generated/aries_cloudagent.wallet.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.wallet.models package ======================================= .. automodule:: aries_cloudagent.wallet.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.wallet.models.wallet\_record module ----------------------------------------------------- .. automodule:: aries_cloudagent.wallet.models.wallet_record - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.wallet.rst b/docs/generated/aries_cloudagent.wallet.rst index d7ef3b5efa..e7eaffbce5 100644 --- a/docs/generated/aries_cloudagent.wallet.rst +++ b/docs/generated/aries_cloudagent.wallet.rst @@ -2,17 +2,16 @@ aries\_cloudagent.wallet package ================================ .. automodule:: aries_cloudagent.wallet - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.wallet.models + aries_cloudagent.wallet.models Submodules ---------- @@ -21,118 +20,120 @@ aries\_cloudagent.wallet.askar module ------------------------------------- .. automodule:: aries_cloudagent.wallet.askar - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.wallet.base module ------------------------------------ .. automodule:: aries_cloudagent.wallet.base - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.wallet.bbs module ----------------------------------- .. automodule:: aries_cloudagent.wallet.bbs - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.wallet.crypto module -------------------------------------- .. automodule:: aries_cloudagent.wallet.crypto - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.wallet.did\_info module ----------------------------------------- .. automodule:: aries_cloudagent.wallet.did_info - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.wallet.did\_method module ------------------------------------------- .. automodule:: aries_cloudagent.wallet.did_method - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.wallet.did\_parameters\_validation module ----------------------------------------------------------- .. automodule:: aries_cloudagent.wallet.did_parameters_validation - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.wallet.did\_posture module -------------------------------------------- .. automodule:: aries_cloudagent.wallet.did_posture - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.wallet.error module ------------------------------------- .. automodule:: aries_cloudagent.wallet.error - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.wallet.in\_memory module ------------------------------------------ .. automodule:: aries_cloudagent.wallet.in_memory - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.wallet.indy module ------------------------------------ .. automodule:: aries_cloudagent.wallet.indy - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.wallet.key\_pair module ----------------------------------------- .. automodule:: aries_cloudagent.wallet.key_pair - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.wallet.key\_type module ----------------------------------------- .. automodule:: aries_cloudagent.wallet.key_type - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.wallet.routes module -------------------------------------- .. automodule:: aries_cloudagent.wallet.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.wallet.util module ------------------------------------ .. automodule:: aries_cloudagent.wallet.util - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/open-api/openapi.json b/open-api/openapi.json index 463eaf3bff..75d4f33e0e 100644 --- a/open-api/openapi.json +++ b/open-api/openapi.json @@ -1,7 +1,7 @@ { "swagger" : "2.0", "info" : { - "version" : "v0.8.0-rc0", + "version" : "v0.8.0", "title" : "Aries Cloud Agent" }, "tags" : [ { From 11866bddcfdb8612889d58cdb0bc192635874f25 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Tue, 14 Mar 2023 15:29:28 -0700 Subject: [PATCH 698/872] Add this PR to the Changelog Signed-off-by: Stephen Curran --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e3fe2756c1..cb5f41982f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -235,6 +235,7 @@ message as the default, and adds a new parameter to use the old behavior. - Redis Plugins \[redis\_cache & redis\_queue\] related updates [\#1937](https://github.com/hyperledger/aries-cloudagent-python/pull/1937) ([shaangill025](https://github.com/shaangill025)) - Release management pull requests + - 0.8.0 release [\#2169](https://github.com/hyperledger/aries-cloudagent-python/pull/2169) ([swcurran](https://github.com/swcurran)) - 0.8.0-rc0 release updates [\#2115](https://github.com/hyperledger/aries-cloudagent-python/pull/2115) ([swcurran](https://github.com/swcurran)) - Previously flagged in release 1.0.0-rc1 - Release 1.0.0-rc0 [\#1904](https://github.com/hyperledger/aries-cloudagent-python/pull/1904) ([swcurran](https://github.com/swcurran)) From 2ad3c43cda8f1aa9e43c9e8436280b960c2b48d5 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Tue, 14 Mar 2023 15:34:24 -0700 Subject: [PATCH 699/872] Fix lint warning on sphinx config Signed-off-by: Stephen Curran --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index db981d6321..55f83f6ec5 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -49,7 +49,7 @@ "nest_asyncio", "marshmallow", "typing_extensions", - "async_timeout" + "async_timeout", ] # "aries_cloudagent.tests.test_conductor", From 263cebccd6494b6152c2465db9148fdc7c047cee Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Wed, 15 Mar 2023 06:42:50 -0700 Subject: [PATCH 700/872] Change upgrade exception to be a print Signed-off-by: Stephen Curran --- aries_cloudagent/commands/upgrade.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aries_cloudagent/commands/upgrade.py b/aries_cloudagent/commands/upgrade.py index 4b2409e20e..13840f1e2d 100644 --- a/aries_cloudagent/commands/upgrade.py +++ b/aries_cloudagent/commands/upgrade.py @@ -137,7 +137,7 @@ async def upgrade(settings: dict): "the config." ) if upgrade_from_version == upgrade_to_version: - raise UpgradeError( + print( f"Version {upgrade_from_version} to upgrade from and " f"current version to upgrade to {upgrade_to_version} " "are same." From 8570eaa4b4795bb96dc253fbbbd6b56e43196931 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Wed, 15 Mar 2023 06:58:18 -0700 Subject: [PATCH 701/872] Updated Changelog, removed change from RaiseError to print Signed-off-by: Stephen Curran --- CHANGELOG.md | 16 +++++++++++++++- aries_cloudagent/commands/upgrade.py | 2 +- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb5f41982f..ba21b9c7ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -62,7 +62,7 @@ aries-cloudagent-python]. [publish-indy.yml]: https://github.com/hyperledger/aries-cloudagent-python/blob/main/.github/workflows/publish-indy.yml [Container Images and Github Actions]: https://github.com/hyperledger/aries-cloudagent-python/blob/main/ContainerImagesAndGithubActions.md -## Breaking Changes +## Breaking Changes and Upgrades ### PR [\#2034](https://github.com/hyperledger/aries-cloudagent-python/pull/2034) -- Implicit connections @@ -96,6 +96,20 @@ hence, very large webhooks. This change reduces the size of the webhook message by eliminating redundant data in the protocol state of the "Issue Credential" message as the default, and adds a new parameter to use the old behavior. +### UPGRADE PR [\#2116](https://github.com/hyperledger/aries-cloudagent-python/pull/2116) - UPGRADE: Fix multi-use invitation performance + +The way that multiuse invitations in previous versions of ACA-Py caused +performance to degrade over time. An update was made to add state into the tag +names that eliminated the need to scan the tags when querying storage for the +invitation. + +If you are using multiuse invitations in your existing (pre-`0.8.0` deployment +of ACA-Py, you can run an `upgrade` to apply this change. To run upgrade from +previous versions, use the following command using the `0.8.0` version of +ACA-Py, adding you wallet settings: + +`aca-py upgrade --from-version=v0.7.5 --upgrade-config-path ./upgrade.yml` + ### Categorized List of Pull Requests - Verifiable credential, presentation and revocation handling updates diff --git a/aries_cloudagent/commands/upgrade.py b/aries_cloudagent/commands/upgrade.py index 13840f1e2d..4b2409e20e 100644 --- a/aries_cloudagent/commands/upgrade.py +++ b/aries_cloudagent/commands/upgrade.py @@ -137,7 +137,7 @@ async def upgrade(settings: dict): "the config." ) if upgrade_from_version == upgrade_to_version: - print( + raise UpgradeError( f"Version {upgrade_from_version} to upgrade from and " f"current version to upgrade to {upgrade_to_version} " "are same." From 85e6e0f2bcc8a357fcab99c90a8364399bd8ec0d Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Wed, 15 Mar 2023 07:39:50 -0700 Subject: [PATCH 702/872] Exit if upgrde version is the same as current Signed-off-by: Ian Costanzo --- aries_cloudagent/commands/upgrade.py | 108 ++++++++++++++------------- 1 file changed, 55 insertions(+), 53 deletions(-) diff --git a/aries_cloudagent/commands/upgrade.py b/aries_cloudagent/commands/upgrade.py index 4b2409e20e..938797ed54 100644 --- a/aries_cloudagent/commands/upgrade.py +++ b/aries_cloudagent/commands/upgrade.py @@ -137,66 +137,68 @@ async def upgrade(settings: dict): "the config." ) if upgrade_from_version == upgrade_to_version: - raise UpgradeError( + print( f"Version {upgrade_from_version} to upgrade from and " f"current version to upgrade to {upgrade_to_version} " "are same." ) - if upgrade_from_version not in sorted_versions_found_in_config: - raise UpgradeError( - f"No upgrade configuration found for {upgrade_from_version}" + else: + if upgrade_from_version not in sorted_versions_found_in_config: + raise UpgradeError( + f"No upgrade configuration found for {upgrade_from_version}" + ) + upgrade_from_version_index = sorted_versions_found_in_config.index( + upgrade_from_version ) - upgrade_from_version_index = sorted_versions_found_in_config.index( - upgrade_from_version - ) - for config_from_version in sorted_versions_found_in_config[ - upgrade_from_version_index: - ]: - print(f"Running upgrade process for {config_from_version}") - upgrade_config = upgrade_configs.get(config_from_version) - # Step 1 re-saving all BaseRecord and BaseExchangeRecord - if "resave_records" in upgrade_config: - resave_record_paths = upgrade_config.get("resave_records") - for record_path in resave_record_paths: - try: - record_type = ClassLoader.load_class(record_path) - except ClassNotFoundError as err: - raise UpgradeError( - f"Unknown Record type {record_path}" - ) from err - if not issubclass(record_type, BaseRecord): - raise UpgradeError( - f"Only BaseRecord can be resaved, found: {str(record_type)}" - ) - async with root_profile.session() as session: - all_records = await record_type.query(session) - for record in all_records: - await record.save( - session, - reason="re-saving record during ACA-Py upgrade process", - ) - if len(all_records) == 0: - print(f"No records of {str(record_type)} found") - else: - print( - f"All records of {str(record_type)} successfully re-saved" + for config_from_version in sorted_versions_found_in_config[ + upgrade_from_version_index: + ]: + print(f"Running upgrade process for {config_from_version}") + upgrade_config = upgrade_configs.get(config_from_version) + # Step 1 re-saving all BaseRecord and BaseExchangeRecord + if "resave_records" in upgrade_config: + resave_record_paths = upgrade_config.get("resave_records") + for record_path in resave_record_paths: + try: + record_type = ClassLoader.load_class(record_path) + except ClassNotFoundError as err: + raise UpgradeError( + f"Unknown Record type {record_path}" + ) from err + if not issubclass(record_type, BaseRecord): + raise UpgradeError( + f"Only BaseRecord can be resaved, found: {str(record_type)}" ) - # Step 2 Update existing records, if required - if ( - "update_existing_records" in upgrade_config - and upgrade_config.get("update_existing_records") is True - ): - update_existing_recs_callable = ( - version_upgrade_config_inst.get_update_existing_func( - config_from_version - ) - ) - if not update_existing_recs_callable: - raise UpgradeError( - "No update_existing_records function " - f"specified for {config_from_version}" + async with root_profile.session() as session: + all_records = await record_type.query(session) + for record in all_records: + await record.save( + session, + reason="re-saving record during ACA-Py upgrade process", + ) + if len(all_records) == 0: + print(f"No records of {str(record_type)} found") + else: + print( + f"All records of {str(record_type)} successfully re-saved" + ) + # Step 2 Update existing records, if required + if ( + "update_existing_records" in upgrade_config + and upgrade_config.get("update_existing_records") is True + ): + update_existing_recs_callable = ( + version_upgrade_config_inst.get_update_existing_func( + config_from_version + ) ) - await update_existing_recs_callable(root_profile) + if not update_existing_recs_callable: + raise UpgradeError( + "No update_existing_records function " + f"specified for {config_from_version}" + ) + await update_existing_recs_callable(root_profile) + # Update storage version async with root_profile.session() as session: storage = session.inject(BaseStorage) From 0d738e86f75fb695a4931e65ceb9929908dbbcac Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Wed, 15 Mar 2023 07:50:57 -0700 Subject: [PATCH 703/872] Formatting issues Signed-off-by: Ian Costanzo --- aries_cloudagent/commands/tests/test_upgrade.py | 11 +++++------ aries_cloudagent/commands/upgrade.py | 14 +++++++------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/aries_cloudagent/commands/tests/test_upgrade.py b/aries_cloudagent/commands/tests/test_upgrade.py index a44f020b90..704f4cb43b 100644 --- a/aries_cloudagent/commands/tests/test_upgrade.py +++ b/aries_cloudagent/commands/tests/test_upgrade.py @@ -144,12 +144,11 @@ async def test_upgrade_x_same_version(self): ) ), ): - with self.assertRaises(UpgradeError): - await test_module.upgrade( - { - "upgrade.config_path": "./aries_cloudagent/commands/default_version_upgrade_config.yml", - } - ) + await test_module.upgrade( + { + "upgrade.config_path": "./aries_cloudagent/commands/default_version_upgrade_config.yml", + } + ) async def test_upgrade_missing_from_version(self): with async_mock.patch.object( diff --git a/aries_cloudagent/commands/upgrade.py b/aries_cloudagent/commands/upgrade.py index 938797ed54..fb037c6b2e 100644 --- a/aries_cloudagent/commands/upgrade.py +++ b/aries_cloudagent/commands/upgrade.py @@ -160,27 +160,27 @@ async def upgrade(settings: dict): resave_record_paths = upgrade_config.get("resave_records") for record_path in resave_record_paths: try: - record_type = ClassLoader.load_class(record_path) + rec_type = ClassLoader.load_class(record_path) except ClassNotFoundError as err: raise UpgradeError( f"Unknown Record type {record_path}" ) from err - if not issubclass(record_type, BaseRecord): + if not issubclass(rec_type, BaseRecord): raise UpgradeError( - f"Only BaseRecord can be resaved, found: {str(record_type)}" + f"Only BaseRecord can be resaved, found: {str(rec_type)}" ) async with root_profile.session() as session: - all_records = await record_type.query(session) + all_records = await rec_type.query(session) for record in all_records: await record.save( session, - reason="re-saving record during ACA-Py upgrade process", + reason="re-saving record during the upgrade process", ) if len(all_records) == 0: - print(f"No records of {str(record_type)} found") + print(f"No records of {str(rec_type)} found") else: print( - f"All records of {str(record_type)} successfully re-saved" + f"All recs of {str(rec_type)} successfully re-saved" ) # Step 2 Update existing records, if required if ( From 26520962ca6206f7f5e618fe4e163a10e11da564 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Thu, 23 Mar 2023 12:58:21 -0700 Subject: [PATCH 704/872] Adds the upgrade command YML file to the PyPi Release Signed-off-by: Stephen Curran Signed-off-by: Stephen Curran Signed-off-by: Stephen Curran --- MANIFEST.in | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST.in b/MANIFEST.in index 494b5def3a..ef5132a345 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,5 @@ include aries_cloudagent/config/default_logging_config.ini +include aries_cloudagent/commands/default_version_upgrade_config.yml include requirements.txt include requirements.dev.txt include requirements.indy.txt From b8d187ae4bb7cf792232d72f6d99547091ce2306 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Tue, 28 Mar 2023 13:55:37 -0700 Subject: [PATCH 705/872] Create UnitTests.md Signed-off-by: Stephen Curran Signed-off-by: Stephen Curran Signed-off-by: Stephen Curran --- UnitTests.md | 258 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 258 insertions(+) create mode 100644 UnitTests.md diff --git a/UnitTests.md b/UnitTests.md new file mode 100644 index 0000000000..f61e23af3d --- /dev/null +++ b/UnitTests.md @@ -0,0 +1,258 @@ +# ACA-Py Unit Tests + +The following covers the Unit Testing framework in ACA-Py, how to run the tests, and how to add unit tests. + +This [video](https://youtu.be/yJ6LpAiVNFM) is a presentation of the material covered in this document by +developer @shaangill025. + +## Running unit tests in ACA-Py + +- `./scripts/run_tests` +- `./scripts/run_tests aries_clouadagent/protocols/out_of_band/v1_0/tests` +- `./scripts/run_tests_indy` includes Indy specific tests + + +## Pytest + +Example: aries_cloudagent/core/tests/test_event_bus.py + +``` +@pytest.fixture +def event_bus(): + yield EventBus() + + +@pytest.fixture +def profile(): + yield async_mock.MagicMock() + + +@pytest.fixture +def event(): + event = Event(topic="anything", payload="payload") + yield event + +class MockProcessor: + def __init__(self): + self.profile = None + self.event = None + + async def __call__(self, profile, event): + self.profile = profile + self.event = event + + +@pytest.fixture +def processor(): + yield MockProcessor() +``` + +--- + +``` +def test_sub_unsub(event_bus: EventBus, processor): + """Test subscribe and unsubscribe.""" + event_bus.subscribe(re.compile(".*"), processor) + assert event_bus.topic_patterns_to_subscribers + assert event_bus.topic_patterns_to_subscribers[re.compile(".*")] == [processor] + event_bus.unsubscribe(re.compile(".*"), processor) + assert not event_bus.topic_patterns_to_subscribers +``` + +From aries_cloudagent/core/event_bus.py + +``` +class EventBus: + def __init__(self): + self.topic_patterns_to_subscribers: Dict[Pattern, List[Callable]] = {} + +def subscribe(self, pattern: Pattern, processor: Callable): + if pattern not in self.topic_patterns_to_subscribers: + self.topic_patterns_to_subscribers[pattern] = [] + self.topic_patterns_to_subscribers[pattern].append(processor) + +def unsubscribe(self, pattern: Pattern, processor: Callable): + if pattern in self.topic_patterns_to_subscribers: + try: + index = self.topic_patterns_to_subscribers[pattern].index(processor) + except ValueError: + return + del self.topic_patterns_to_subscribers[pattern][index] + if not self.topic_patterns_to_subscribers[pattern]: + del self.topic_patterns_to_subscribers[pattern] +``` + +--- + +``` +@pytest.mark.asyncio +async def test_sub_notify(event_bus: EventBus, profile, event, processor): + """Test subscriber receives event.""" + event_bus.subscribe(re.compile(".*"), processor) + await event_bus.notify(profile, event) + assert processor.profile == profile + assert processor.event == event +``` + +``` +async def notify(self, profile: "Profile", event: Event): + partials = [] + for pattern, subscribers in self.topic_patterns_to_subscribers.items(): + match = pattern.match(event.topic) + + if not match: + continue + + for subscriber in subscribers: + partials.append( + partial( + subscriber, + profile, + event.with_metadata(EventMetadata(pattern, match)), + ) + ) + + for processor in partials: + try: + await processor() + except Exception: + LOGGER.exception("Error occurred while processing event") +``` + +--- + +## asynctest + +From: aries_cloudagent/protocols/didexchange/v1_0/tests/test.manager.py + +``` +class TestDidExchangeManager(AsyncTestCase, TestConfig): + async def setUp(self): + self.responder = MockResponder() + + self.oob_mock = async_mock.MagicMock( + clean_finished_oob_record=async_mock.CoroutineMock(return_value=None) + ) + + self.route_manager = async_mock.MagicMock(RouteManager) + ... + self.profile = InMemoryProfile.test_profile( + { + "default_endpoint": "http://aries.ca/endpoint", + "default_label": "This guy", + "additional_endpoints": ["http://aries.ca/another-endpoint"], + "debug.auto_accept_invites": True, + "debug.auto_accept_requests": True, + "multitenant.enabled": True, + "wallet.id": True, + }, + bind={ + BaseResponder: self.responder, + OobMessageProcessor: self.oob_mock, + RouteManager: self.route_manager, + ... + }, + ) + ... + + async def test_receive_invitation_no_auto_accept(self): + async with self.profile.session() as session: + mediation_record = MediationRecord( + role=MediationRecord.ROLE_CLIENT, + state=MediationRecord.STATE_GRANTED, + connection_id=self.test_mediator_conn_id, + routing_keys=self.test_mediator_routing_keys, + endpoint=self.test_mediator_endpoint, + ) + await mediation_record.save(session) + with async_mock.patch.object( + self.multitenant_mgr, "get_default_mediator" + ) as mock_get_default_mediator: + mock_get_default_mediator.return_value = mediation_record + invi_rec = await self.oob_manager.create_invitation( + my_endpoint="testendpoint", + hs_protos=[HSProto.RFC23], + ) + + invitee_record = await self.manager.receive_invitation( + invi_rec.invitation, + auto_accept=False, + ) + assert invitee_record.state == ConnRecord.State.INVITATION.rfc23 +``` + +--- + +``` +async def receive_invitation( + self, + invitation: OOBInvitationMessage, + their_public_did: Optional[str] = None, + auto_accept: Optional[bool] = None, + alias: Optional[str] = None, + mediation_id: Optional[str] = None, +) -> ConnRecord: + ... + accept = ( + ConnRecord.ACCEPT_AUTO + if ( + auto_accept + or ( + auto_accept is None + and self.profile.settings.get("debug.auto_accept_invites") + ) + ) + else ConnRecord.ACCEPT_MANUAL + ) + service_item = invitation.services[0] + # Create connection record + conn_rec = ConnRecord( + invitation_key=( + DIDKey.from_did(service_item.recipient_keys[0]).public_key_b58 + if isinstance(service_item, OOBService) + else None + ), + invitation_msg_id=invitation._id, + their_label=invitation.label, + their_role=ConnRecord.Role.RESPONDER.rfc23, + state=ConnRecord.State.INVITATION.rfc23, + accept=accept, + alias=alias, + their_public_did=their_public_did, + connection_protocol=DIDX_PROTO, + ) + + async with self.profile.session() as session: + await conn_rec.save( + session, + reason="Created new connection record from invitation", + log_params={ + "invitation": invitation, + "their_role": ConnRecord.Role.RESPONDER.rfc23, + }, + ) + + # Save the invitation for later processing + ... + + return conn_rec +``` + +## Other details + +- Error catching + +``` + with self.assertRaises(DIDXManagerError) as ctx: + ... + assert " ... error ..." in str(ctx.exception) +``` + +- function.`assert_called_once_with(parameters)` + function.`assert_called_once()` + +- pytest.mark setup in `setup.cfg` + can be attributed at function or class level. Example, `@pytest.mark.indy` + +- Code coverage + ![](https://i.imgur.com/VhNYcje.png) From 02984c96a7eaa2ca99e24bb21f3823bd9b186ce1 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Tue, 28 Mar 2023 14:04:13 -0700 Subject: [PATCH 706/872] Add link to recorded session about the ACA-Py Integration tests Signed-off-by: Stephen Curran Signed-off-by: Stephen Curran Signed-off-by: Stephen Curran --- demo/INTEGRATION-TESTS.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/demo/INTEGRATION-TESTS.md b/demo/INTEGRATION-TESTS.md index e74008f29d..4115ff0cdc 100644 --- a/demo/INTEGRATION-TESTS.md +++ b/demo/INTEGRATION-TESTS.md @@ -2,6 +2,11 @@ Integration tests for aca-py are implemented using Behave functional tests to drive aca-py agents based on the alice/faber demo framework. +If you are new to the ACA-Py integration test suite, this [video](https://youtu.be/AbuPg4J8Pd4) from ACA-Py Maintainer @ianco describes +the Integration Tests in ACA-Py, how to run them and how to add more tests. + +## Getting Started + To run the aca-py Behave tests, open a bash shell run the following: ```bash From 961ca0d8c36b8e34a41ebbd9c4fc7df66a0c4a62 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Tue, 28 Mar 2023 14:11:33 -0700 Subject: [PATCH 707/872] Add link to video about AATH testing in ACA-Py Signed-off-by: Stephen Curran Signed-off-by: Stephen Curran Signed-off-by: Stephen Curran --- demo/INTEGRATION-TESTS.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/demo/INTEGRATION-TESTS.md b/demo/INTEGRATION-TESTS.md index 4115ff0cdc..b8a8bf456c 100644 --- a/demo/INTEGRATION-TESTS.md +++ b/demo/INTEGRATION-TESTS.md @@ -3,7 +3,8 @@ Integration tests for aca-py are implemented using Behave functional tests to drive aca-py agents based on the alice/faber demo framework. If you are new to the ACA-Py integration test suite, this [video](https://youtu.be/AbuPg4J8Pd4) from ACA-Py Maintainer @ianco describes -the Integration Tests in ACA-Py, how to run them and how to add more tests. +the Integration Tests in ACA-Py, how to run them and how to add more tests. See also the video at the end of this document about running +Aries Agent Test Harness tests before you submit your pull requests. ## Getting Started @@ -161,3 +162,7 @@ To run a specific set of Aca-py integration tests (or exclude specific tests): (All command line parameters are passed to the `behave` command, so [all parameters supported by behave](https://behave.readthedocs.io/en/stable/behave.html) can be used.) +## Aries Agent Test Harness ACA-Py Tests + +This [video](https://youtu.be/1dwyEBxQqWI) is a presentation by Aries Cloud Agent Python (ACA-Py) developer @ianco about using the Aries Agent Test Harness for local pre-release testing of ACA-Py. Have a big change that you want to test with other Aries Frameworks? Following this guidance to run AATH tests with your under-development branch of ACA-Py. + From e164523df12db19c73e2e8d0225baa5821e3805f Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Wed, 29 Mar 2023 10:46:08 -0700 Subject: [PATCH 708/872] impl all requirements Signed-off-by: Shaanjot Gill --- aries_cloudagent/commands/upgrade.py | 176 +++++++++++++++------------ aries_cloudagent/config/argparse.py | 9 ++ aries_cloudagent/core/conductor.py | 40 ++++-- 3 files changed, 137 insertions(+), 88 deletions(-) diff --git a/aries_cloudagent/commands/upgrade.py b/aries_cloudagent/commands/upgrade.py index fb037c6b2e..56bb0e27a9 100644 --- a/aries_cloudagent/commands/upgrade.py +++ b/aries_cloudagent/commands/upgrade.py @@ -5,7 +5,7 @@ from configargparse import ArgumentParser from packaging import version as package_version -from typing import Callable, Sequence, Optional +from typing import Callable, Sequence, Optional, List from ..core.profile import Profile from ..config import argparse as arg @@ -36,23 +36,13 @@ class VersionUpgradeConfig: def __init__(self, config_path: str = None): """Initialize config for use during upgrade process.""" - self.function_map_config = {} + self.function_map_config = UPGRADE_EXISTING_RECORDS_FUNCTION_MAPPING self.upgrade_configs = {} - self.setup_executable_map_config(CONFIG_v7_3) if config_path: self.setup_version_upgrade_config(config_path) else: self.setup_version_upgrade_config(DEFAULT_UPGRADE_CONFIG_PATH) - def setup_executable_map_config(self, config_dict: dict): - """Set ups config with reference to functions mapped to versions.""" - for version, config in config_dict.items(): - self.function_map_config[version] = {} - if "update_existing_function_inst" in config: - self.function_map_config[version][ - "update_existing_function_inst" - ] = config.get("update_existing_function_inst") - def setup_version_upgrade_config(self, path: str): """Set ups config dict from the provided YML file.""" with open(path, "r") as stream: @@ -73,19 +63,20 @@ def setup_version_upgrade_config(self, path: str): "resave_records" ).get("base_exch_record_path") version_config_dict[version]["resave_records"] = recs_list - version_config_dict[version]["update_existing_records"] = ( - provided_config.get("update_existing_records") or False - ) + config_key_set = set(version_config_dict.get(version).keys()) + config_key_set.remove("resave_records") + for executable in config_key_set: + version_config_dict[version][executable] = ( + provided_config.get(executable) or False + ) if version_config_dict == {}: raise UpgradeError(f"No version configs found in {path}") self.upgrade_configs = version_config_dict - def get_update_existing_func(self, ver: str) -> Optional[Callable]: - """Return callable update_existing_records function for specific version.""" - if ver in self.function_map_config: - return self.function_map_config.get(ver).get( - "update_existing_function_inst" - ) + def get_callable(self, executable: str) -> Optional[Callable]: + """Return callable function for executable name.""" + if executable in self.function_map_config: + return self.function_map_config.get(executable) else: return None @@ -95,6 +86,47 @@ def init_argument_parser(parser: ArgumentParser): return arg.load_argument_groups(parser, *arg.group.get_registered(arg.CAT_UPGRADE)) +def get_upgrade_version_list( + from_version: str, + sorted_version_list: Optional[List] = None, + config_path: Optional[str] = None, +) -> List: + if not sorted_version_list and not config_path: + raise UpgradeError( + f"No sorted version list from config or path to config provided." + ) + if not sorted_version_list: + version_upgrade_config_inst = VersionUpgradeConfig(config_path) + upgrade_configs = version_upgrade_config_inst.upgrade_configs + versions_found_in_config = upgrade_configs.keys() + sorted_version_list = sorted( + versions_found_in_config, key=lambda x: package_version.parse(x) + ) + + version_list = [] + for version in sorted_version_list: + if package_version.parse(version) >= package_version.parse(from_version): + version_list.append(version) + return version_list + + +async def add_version_record(profile: Profile, version: str): + async with profile.session() as session: + storage = session.inject(BaseStorage) + version_storage_record = await storage.find_record( + type_filter=RECORD_TYPE_ACAPY_VERSION, tag_query={} + ) + if not version_storage_record: + await storage.add_record( + StorageRecord( + RECORD_TYPE_ACAPY_VERSION, + version, + ) + ) + else: + await storage.update_record(version_storage_record, version, {}) + + async def upgrade(settings: dict): """Perform upgradation steps.""" context_builder = DefaultContextBuilder(settings) @@ -104,7 +136,7 @@ async def upgrade(settings: dict): settings.get("upgrade.config_path") ) upgrade_configs = version_upgrade_config_inst.upgrade_configs - root_profile, public_did = await wallet_config(context) + root_profile, _ = await wallet_config(context) version_storage_record = None upgrade_to_version = f"v{__version__}" versions_found_in_config = upgrade_configs.keys() @@ -136,68 +168,64 @@ async def upgrade(settings: dict): f"{upgrade_from_version} as --from-version from " "the config." ) - if upgrade_from_version == upgrade_to_version: + upgrade_version_in_config = get_upgrade_version_list( + sorted_versions_found_in_config, upgrade_from_version + ) + force_upgrade_flag = root_profile.settings.get("upgrade.force_upgrade") or False + if upgrade_from_version == upgrade_to_version and not force_upgrade_flag: print( f"Version {upgrade_from_version} to upgrade from and " f"current version to upgrade to {upgrade_to_version} " - "are same." + "are same. If you still wish to run upgrade then plese " + " run ACA-Py with --force-upgrade argument." ) else: - if upgrade_from_version not in sorted_versions_found_in_config: - raise UpgradeError( - f"No upgrade configuration found for {upgrade_from_version}" - ) - upgrade_from_version_index = sorted_versions_found_in_config.index( - upgrade_from_version - ) - for config_from_version in sorted_versions_found_in_config[ - upgrade_from_version_index: - ]: + resave_record_path_sets = set() + executables_called = set() + for config_from_version in upgrade_version_in_config: print(f"Running upgrade process for {config_from_version}") upgrade_config = upgrade_configs.get(config_from_version) # Step 1 re-saving all BaseRecord and BaseExchangeRecord if "resave_records" in upgrade_config: resave_record_paths = upgrade_config.get("resave_records") for record_path in resave_record_paths: - try: - rec_type = ClassLoader.load_class(record_path) - except ClassNotFoundError as err: - raise UpgradeError( - f"Unknown Record type {record_path}" - ) from err - if not issubclass(rec_type, BaseRecord): - raise UpgradeError( - f"Only BaseRecord can be resaved, found: {str(rec_type)}" - ) - async with root_profile.session() as session: - all_records = await rec_type.query(session) - for record in all_records: - await record.save( - session, - reason="re-saving record during the upgrade process", - ) - if len(all_records) == 0: - print(f"No records of {str(rec_type)} found") - else: - print( - f"All recs of {str(rec_type)} successfully re-saved" - ) + resave_record_path_sets.add(record_path) + # Step 2 Update existing records, if required - if ( - "update_existing_records" in upgrade_config - and upgrade_config.get("update_existing_records") is True - ): - update_existing_recs_callable = ( - version_upgrade_config_inst.get_update_existing_func( - config_from_version - ) + config_key_set = set(upgrade_config.keys()) + config_key_set.remove("resave_records") + for executable in list(config_key_set): + if ( + upgrade_config.get(executable) is False + or executable in executables_called + ): + continue + + _callable = version_upgrade_config_inst.get_callable(executable) + if not _callable: + raise UpgradeError(f"No function specified for {executable}") + executables_called.add(executable) + await _callable(root_profile) + for record_path in resave_record_path_sets: + try: + rec_type = ClassLoader.load_class(record_path) + except ClassNotFoundError as err: + raise UpgradeError(f"Unknown Record type {record_path}") from err + if not issubclass(rec_type, BaseRecord): + raise UpgradeError( + f"Only BaseRecord can be resaved, found: {str(rec_type)}" ) - if not update_existing_recs_callable: - raise UpgradeError( - "No update_existing_records function " - f"specified for {config_from_version}" + async with root_profile.session() as session: + all_records = await rec_type.query(session) + for record in all_records: + await record.save( + session, + reason="re-saving record during the upgrade process", ) - await update_existing_recs_callable(root_profile) + if len(all_records) == 0: + print(f"No records of {str(rec_type)} found") + else: + print(f"All recs of {str(rec_type)} successfully re-saved") # Update storage version async with root_profile.session() as session: @@ -247,12 +275,8 @@ def main(): execute() -# Update every release -CONFIG_v7_3 = { - "v0.7.2": { - "update_existing_function_inst": update_existing_records, - }, +UPGRADE_EXISTING_RECORDS_FUNCTION_MAPPING = { + "update_existing_records": update_existing_records } - main() diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index 2951e6969f..0b1f20b5e4 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -2053,6 +2053,13 @@ def add_arguments(self, parser: ArgumentParser): ), ) + parser.add_argument( + "--force-upgrade", + action="store_true", + env_var="ACAPY_UPGRADE_FORCE_UPGRADE", + help="Brute force the upgrade process.", + ) + def get_settings(self, args: Namespace) -> dict: """Extract ACA-Py upgrade process settings.""" settings = {} @@ -2060,4 +2067,6 @@ def get_settings(self, args: Namespace) -> dict: settings["upgrade.config_path"] = args.upgrade_config_path if args.from_version: settings["upgrade.from_version"] = args.from_version + if args.force_upgrade: + settings["upgrade.force_upgrade"] = args.force_upgrade return settings diff --git a/aries_cloudagent/core/conductor.py b/aries_cloudagent/core/conductor.py index 1f09dbc13a..1e50a83f6e 100644 --- a/aries_cloudagent/core/conductor.py +++ b/aries_cloudagent/core/conductor.py @@ -26,6 +26,7 @@ from ..config.logging import LoggingConfigurator from ..config.provider import ClassProvider from ..config.wallet import wallet_config +from ..commands.upgrade import get_upgrade_version_list, add_version_record, upgrade from ..core.profile import Profile from ..indy.verifier import IndyVerifier @@ -311,26 +312,41 @@ async def start(self) -> None: ) # record ACA-Py version in Wallet, if needed + from_version = None + agent_version = f"v{__version__}" async with self.root_profile.session() as session: storage: BaseStorage = session.context.inject(BaseStorage) - agent_version = f"v{__version__}" try: record = await storage.find_record( type_filter=RECORD_TYPE_ACAPY_VERSION, tag_query={}, ) - if record.value != agent_version: - LOGGER.exception( - ( - f"Wallet storage version {record.value} " - "does not match this ACA-Py agent " - f"version {agent_version}. Run aca-py " - "upgrade command to fix this." - ) - ) - raise + from_version = record.value except StorageNotFoundError: - pass + LOGGER.exception(("Wallet version storage record not found.")) + from_version = from_version or self.root_profile.settings.get( + "upgrade.config_path" + ) + if from_version: + config_available_list = get_upgrade_version_list( + config_path=self.root_profile.settings.get("upgrade.config_path"), + from_version=from_version, + ) + if len(config_available_list) >= 1 and ( + from_version != agent_version + or self.root_profile.settings.get("upgrade.force_upgrade") + ): + await upgrade(self.root_profile.settings) + else: + LOGGER.exception( + ( + "Wallet storage version not found. " + "Run aca-py upgrade command with " + "--from-version to fix this." + ) + ) + raise + await add_version_record(self.root_profile, agent_version) # Create a static connection for use by the test-suite if context.settings.get("debug.test_suite_endpoint"): From 3e6d8afeb8e8c7a640fb264b1f01bf607a66cb49 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Wed, 29 Mar 2023 13:33:53 -0700 Subject: [PATCH 709/872] updates based on feedback Signed-off-by: Shaanjot Gill --- aries_cloudagent/commands/upgrade.py | 128 +++++++++++++++++---------- aries_cloudagent/config/argparse.py | 5 +- aries_cloudagent/core/conductor.py | 16 ++-- 3 files changed, 92 insertions(+), 57 deletions(-) diff --git a/aries_cloudagent/commands/upgrade.py b/aries_cloudagent/commands/upgrade.py index 56bb0e27a9..1ceec9564a 100644 --- a/aries_cloudagent/commands/upgrade.py +++ b/aries_cloudagent/commands/upgrade.py @@ -1,6 +1,7 @@ """Upgrade command for handling breaking changes when updating ACA-PY versions.""" import asyncio +import logging import yaml from configargparse import ArgumentParser @@ -25,6 +26,7 @@ DEFAULT_UPGRADE_CONFIG_PATH = ( "./aries_cloudagent/commands/default_version_upgrade_config.yml" ) +LOGGER = logging.getLogger(__name__) class UpgradeError(BaseError): @@ -125,6 +127,7 @@ async def add_version_record(profile: Profile, version: str): ) else: await storage.update_record(version_storage_record, version, {}) + LOGGER.info(f"{RECORD_TYPE_ACAPY_VERSION} storage record set to {version}") async def upgrade(settings: dict): @@ -143,47 +146,68 @@ async def upgrade(settings: dict): sorted_versions_found_in_config = sorted( versions_found_in_config, key=lambda x: package_version.parse(x) ) + upgrade_from_version_config = None + upgrade_from_version_setting = None + upgrade_from_version = None async with root_profile.session() as session: storage = session.inject(BaseStorage) try: version_storage_record = await storage.find_record( type_filter=RECORD_TYPE_ACAPY_VERSION, tag_query={} ) - upgrade_from_version = version_storage_record.value - if "upgrade.from_version" in settings: - print( - ( - f"version {upgrade_from_version} found in storage" - ", --from-version will be ignored." - ) - ) + upgrade_from_version_config = version_storage_record.value except StorageNotFoundError: - if "upgrade.from_version" in settings: - upgrade_from_version = settings.get("upgrade.from_version") - else: - upgrade_from_version = sorted_versions_found_in_config[-1] - print( - "No ACA-Py version found in wallet storage and " - "no --from-version specified. Selecting " - f"{upgrade_from_version} as --from-version from " - "the config." + LOGGER.info("No ACA-Py version found in wallet storage.") + + if "upgrade.from_version" in settings: + upgrade_from_version_setting = settings.get("upgrade.from_version") + LOGGER.info( + ( + f"Selecting {upgrade_from_version_setting} as " + "--from-version from the config." ) + ) + + force_upgrade_flag = root_profile.settings.get("upgrade.force_upgrade") or False + if upgrade_from_version_config and upgrade_from_version_setting: + if ( + package_version.parse(upgrade_from_version_config) + > package_version.parse(upgrade_from_version_setting) + ) and force_upgrade_flag: + upgrade_from_version = upgrade_from_version_setting + else: + upgrade_from_version = upgrade_from_version_config + if ( + not upgrade_from_version + and not upgrade_from_version_config + and upgrade_from_version_setting + ): + upgrade_from_version = upgrade_from_version_setting + if ( + not upgrade_from_version + and upgrade_from_version_config + and not upgrade_from_version_setting + ): + upgrade_from_version = upgrade_from_version_config + upgrade_version_in_config = get_upgrade_version_list( sorted_versions_found_in_config, upgrade_from_version ) - force_upgrade_flag = root_profile.settings.get("upgrade.force_upgrade") or False - if upgrade_from_version == upgrade_to_version and not force_upgrade_flag: - print( - f"Version {upgrade_from_version} to upgrade from and " - f"current version to upgrade to {upgrade_to_version} " - "are same. If you still wish to run upgrade then plese " - " run ACA-Py with --force-upgrade argument." + to_update_flag = False + if upgrade_from_version == upgrade_to_version: + LOGGER.info( + ( + f"Version {upgrade_from_version} to upgrade from and " + f"current version to upgrade to {upgrade_to_version} " + "are same. If you still wish to run upgrade then please " + " run ACA-Py with --force-upgrade argument." + ) ) else: resave_record_path_sets = set() - executables_called = set() + executables_call_set = set() for config_from_version in upgrade_version_in_config: - print(f"Running upgrade process for {config_from_version}") + LOGGER.info(f"Running upgrade process for {config_from_version}") upgrade_config = upgrade_configs.get(config_from_version) # Step 1 re-saving all BaseRecord and BaseExchangeRecord if "resave_records" in upgrade_config: @@ -194,18 +218,13 @@ async def upgrade(settings: dict): # Step 2 Update existing records, if required config_key_set = set(upgrade_config.keys()) config_key_set.remove("resave_records") - for executable in list(config_key_set): - if ( - upgrade_config.get(executable) is False - or executable in executables_called - ): + for callable_name in list(config_key_set): + if upgrade_config.get(callable_name) is False: continue + executables_call_set.add(callable_name) - _callable = version_upgrade_config_inst.get_callable(executable) - if not _callable: - raise UpgradeError(f"No function specified for {executable}") - executables_called.add(executable) - await _callable(root_profile) + if len(resave_record_path_sets) >= 1 or len(executables_call_set) >= 1: + to_update_flag = True for record_path in resave_record_path_sets: try: rec_type = ClassLoader.load_class(record_path) @@ -223,23 +242,34 @@ async def upgrade(settings: dict): reason="re-saving record during the upgrade process", ) if len(all_records) == 0: - print(f"No records of {str(rec_type)} found") + LOGGER.info(f"No records of {str(rec_type)} found") else: - print(f"All recs of {str(rec_type)} successfully re-saved") + LOGGER.info( + f"All recs of {str(rec_type)} successfully re-saved" + ) + for callable_name in executables_call_set: + _callable = version_upgrade_config_inst.get_callable(callable_name) + if not _callable: + raise UpgradeError(f"No function specified for {callable_name}") + await _callable(root_profile) # Update storage version - async with root_profile.session() as session: - storage = session.inject(BaseStorage) - if not version_storage_record: - await storage.add_record( - StorageRecord( - RECORD_TYPE_ACAPY_VERSION, - upgrade_to_version, + if to_update_flag: + async with root_profile.session() as session: + storage = session.inject(BaseStorage) + if not version_storage_record: + await storage.add_record( + StorageRecord( + RECORD_TYPE_ACAPY_VERSION, + upgrade_to_version, + ) ) - ) - else: - await storage.update_record( - version_storage_record, upgrade_to_version, {} + else: + await storage.update_record( + version_storage_record, upgrade_to_version, {} + ) + LOGGER.info( + f"{RECORD_TYPE_ACAPY_VERSION} storage record set to {upgrade_to_version}" ) await root_profile.close() except BaseError as e: diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index 0b1f20b5e4..ff26a55071 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -2057,7 +2057,10 @@ def add_arguments(self, parser: ArgumentParser): "--force-upgrade", action="store_true", env_var="ACAPY_UPGRADE_FORCE_UPGRADE", - help="Brute force the upgrade process.", + help=( + "Forces the '—from-version' argument to override the version retrieved from " + "secure storage when calculating upgrades to be run." + ), ) def get_settings(self, args: Namespace) -> dict: diff --git a/aries_cloudagent/core/conductor.py b/aries_cloudagent/core/conductor.py index 1e50a83f6e..79033974ca 100644 --- a/aries_cloudagent/core/conductor.py +++ b/aries_cloudagent/core/conductor.py @@ -323,7 +323,7 @@ async def start(self) -> None: ) from_version = record.value except StorageNotFoundError: - LOGGER.exception(("Wallet version storage record not found.")) + LOGGER.warning(("Wallet version storage record not found.")) from_version = from_version or self.root_profile.settings.get( "upgrade.config_path" ) @@ -338,15 +338,17 @@ async def start(self) -> None: ): await upgrade(self.root_profile.settings) else: - LOGGER.exception( + LOGGER.warning( ( - "Wallet storage version not found. " - "Run aca-py upgrade command with " - "--from-version to fix this." + "No upgrade from version was found from wallet or via" + " --from-version startup argument. " + f"{RECORD_TYPE_ACAPY_VERSION} storage record will be " + f"set to {agent_version}. You can run run the upgrade " + "command with --from-version and --force-upgrade to " + "upgrade later." ) ) - raise - await add_version_record(self.root_profile, agent_version) + await add_version_record(self.root_profile, agent_version) # Create a static connection for use by the test-suite if context.settings.get("debug.test_suite_endpoint"): From 7ce4ce2719e371fd987c350e5432bd3376d4ef6f Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Wed, 29 Mar 2023 13:40:22 -0700 Subject: [PATCH 710/872] correct logging statement Signed-off-by: Shaanjot Gill --- aries_cloudagent/commands/upgrade.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/commands/upgrade.py b/aries_cloudagent/commands/upgrade.py index 1ceec9564a..61e4c672c3 100644 --- a/aries_cloudagent/commands/upgrade.py +++ b/aries_cloudagent/commands/upgrade.py @@ -199,8 +199,10 @@ async def upgrade(settings: dict): ( f"Version {upgrade_from_version} to upgrade from and " f"current version to upgrade to {upgrade_to_version} " - "are same. If you still wish to run upgrade then please " - " run ACA-Py with --force-upgrade argument." + "are same. You can apply upgrade from a lower " + "version by running the upgrade command with " + f"--from-version [< {upgrade_to_version}] and " + "--force-upgrade" ) ) else: From 539dfc0dc10d199c20d890732f4e82e11014a13e Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Mon, 27 Feb 2023 04:32:50 -0800 Subject: [PATCH 711/872] check impl Signed-off-by: Shaanjot Gill Signed-off-by: JSyro --- aries_cloudagent/admin/server.py | 4 +- aries_cloudagent/core/dispatcher.py | 49 ++++- .../core/tests/test_dispatcher.py | 199 +++++++++++++++++- aries_cloudagent/messaging/responder.py | 92 +++++++- .../protocols/connections/v1_0/routes.py | 4 + .../connections/v1_0/tests/test_routes.py | 22 ++ 6 files changed, 358 insertions(+), 12 deletions(-) diff --git a/aries_cloudagent/admin/server.py b/aries_cloudagent/admin/server.py index def29588eb..544bc8f585 100644 --- a/aries_cloudagent/admin/server.py +++ b/aries_cloudagent/admin/server.py @@ -124,7 +124,9 @@ def __init__( self._profile = weakref.ref(profile) self._send = send - async def send_outbound(self, message: OutboundMessage) -> OutboundSendStatus: + async def send_outbound( + self, message: OutboundMessage, **kwargs + ) -> OutboundSendStatus: """ Send outbound message. diff --git a/aries_cloudagent/core/dispatcher.py b/aries_cloudagent/core/dispatcher.py index bfc6f95e4a..ca371806b9 100644 --- a/aries_cloudagent/core/dispatcher.py +++ b/aries_cloudagent/core/dispatcher.py @@ -7,6 +7,7 @@ import asyncio import logging +import json import os import warnings @@ -15,7 +16,6 @@ from aiohttp.web import HTTPException - from ..connections.models.conn_record import ConnRecord from ..core.profile import Profile from ..messaging.agent_message import AgentMessage @@ -377,7 +377,28 @@ async def create_outbound( return await super().create_outbound(message, **kwargs) - async def send_outbound(self, message: OutboundMessage) -> OutboundSendStatus: + async def _get_msg_type_from_enc_payload( + self, profile: Profile, parsed_msg: dict + ) -> Optional[Tuple[str, str]]: + """Get message type and id tuple from enc_payload.""" + try: + if not isinstance(parsed_msg, dict): + return None + message_type = parsed_msg.get("@type") + if not message_type: + return None + registry: ProtocolRegistry = profile.inject(ProtocolRegistry) + message_cls = registry.resolve_message_class(message_type) + if not message_cls: + return None + instance = message_cls.deserialize(parsed_msg) + return instance._message_type, instance._id + except (ProtocolMinorVersionNotSupported, BaseModelError, AttributeError): + return None + + async def send_outbound( + self, message: OutboundMessage, **kwargs + ) -> OutboundSendStatus: """ Send outbound message. @@ -388,6 +409,30 @@ async def send_outbound(self, message: OutboundMessage) -> OutboundSendStatus: if not context: raise RuntimeError("weakref to context has expired") + msg_type = kwargs.get("message_type") + msg_id = kwargs.get("message_id") + if not msg_type and not msg_id and (message.enc_payload or message.payload): + msg_dict = json.loads(message.enc_payload or message.payload) + msg_type_id_tuple = await self._get_msg_type_from_enc_payload( + context.profile, msg_dict + ) + if msg_type_id_tuple: + msg_type, msg_id = msg_type_id_tuple + + if ( + message.connection_id + and msg_type + and not await super().conn_rec_active_state_check( + profile=context.profile, + connection_id=message.connection_id, + msg_type=msg_type, + ) + ): + raise RuntimeError( + f"Connection {message.connection_id} is not ready" + " which is required for sending outbound" + f" message {msg_id} of type {msg_type}." + ) return await self._send(context.profile, message, self._inbound_message) async def send_webhook(self, topic: str, payload: dict): diff --git a/aries_cloudagent/core/tests/test_dispatcher.py b/aries_cloudagent/core/tests/test_dispatcher.py index ffca07ce14..ec18696731 100644 --- a/aries_cloudagent/core/tests/test_dispatcher.py +++ b/aries_cloudagent/core/tests/test_dispatcher.py @@ -6,6 +6,8 @@ from marshmallow import EXCLUDE +from ...cache.base import BaseCache +from ...cache.in_memory import InMemoryCache from ...config.injection_context import InjectionContext from ...core.event_bus import EventBus from ...core.in_memory import InMemoryProfile @@ -413,12 +415,129 @@ async def test_create_send_outbound(self): profile, settings={"timing.enabled": True}, ) + registry = profile.inject(ProtocolRegistry) + registry.register_message_types( + { + pfx.qualify(StubAgentMessage.Meta.message_type): StubAgentMessage + for pfx in DIDCommPrefix + } + ) message = StubAgentMessage() responder = test_module.DispatcherResponder(context, message, None) - outbound_message = await responder.create_outbound(message) - with async_mock.patch.object(responder, "_send", async_mock.AsyncMock()): + outbound_message = await responder.create_outbound( + json.dumps(message.serialize()) + ) + with async_mock.patch.object( + responder, "_send", async_mock.AsyncMock() + ), async_mock.patch.object( + test_module.BaseResponder, + "conn_rec_active_state_check", + async_mock.AsyncMock(return_value=True), + ): await responder.send_outbound(outbound_message) + async def test_create_send_outbound_with_msg_attrs(self): + profile = make_profile() + context = RequestContext( + profile, + settings={"timing.enabled": True}, + ) + registry = profile.inject(ProtocolRegistry) + registry.register_message_types( + { + pfx.qualify(StubAgentMessage.Meta.message_type): StubAgentMessage + for pfx in DIDCommPrefix + } + ) + message = StubAgentMessage() + responder = test_module.DispatcherResponder(context, message, None) + outbound_message = await responder.create_outbound(message) + with async_mock.patch.object( + responder, "_send", async_mock.AsyncMock() + ), async_mock.patch.object( + test_module.BaseResponder, + "conn_rec_active_state_check", + async_mock.AsyncMock(return_value=True), + ): + await responder.send_outbound( + message=outbound_message, + message_type=message._message_type, + message_id=message._id, + ) + + async def test_create_send_outbound_with_msg_attrs_x(self): + profile = make_profile() + context = RequestContext( + profile, + settings={"timing.enabled": True}, + ) + registry = profile.inject(ProtocolRegistry) + registry.register_message_types( + { + pfx.qualify(StubAgentMessage.Meta.message_type): StubAgentMessage + for pfx in DIDCommPrefix + } + ) + message = StubAgentMessage() + responder = test_module.DispatcherResponder(context, message, None) + outbound_message = await responder.create_outbound(message) + outbound_message.connection_id = "123" + with async_mock.patch.object( + test_module.BaseResponder, + "conn_rec_active_state_check", + async_mock.AsyncMock(return_value=False), + ): + with self.assertRaises(RuntimeError): + await responder.send_outbound( + message=outbound_message, + message_type=message._message_type, + message_id=message._id, + ) + + async def test_get_msg_type_from_enc_payload(self): + profile = make_profile() + context = RequestContext( + profile, + settings={"timing.enabled": True}, + ) + message = StubAgentMessage() + responder = test_module.DispatcherResponder(context, message, None) + assert not await responder._get_msg_type_from_enc_payload( + profile, ["...", "..."] + ) + assert not await responder._get_msg_type_from_enc_payload( + profile, {"...": "..."} + ) + msg_dict = message.serialize() + not await responder._get_msg_type_from_enc_payload(profile, msg_dict) + registry = profile.inject(ProtocolRegistry) + registry.register_message_types( + { + pfx.qualify(StubAgentMessage.Meta.message_type): StubAgentMessage + for pfx in DIDCommPrefix + } + ) + message = StubAgentMessage() + responder = test_module.DispatcherResponder(context, message, None) + message._id = "test123" + msg_dict = message.serialize() + msg_type, msg_id = await responder._get_msg_type_from_enc_payload( + profile, msg_dict + ) + assert msg_type == StubAgentMessage.Meta.message_type + assert msg_id == "test123" + + msg_dict["@type"] = "proto-name/3.0/message-type" + with async_mock.patch.object( + registry, "resolve_message_class", async_mock.MagicMock() + ) as mock_resolve: + mock_resolve.return_value = async_mock.MagicMock( + deserialize=async_mock.MagicMock( + side_effect=test_module.ProtocolMinorVersionNotSupported() + ) + ) + assert not await responder._get_msg_type_from_enc_payload(profile, msg_dict) + async def test_create_send_webhook(self): profile = make_profile() context = RequestContext(profile) @@ -427,10 +546,65 @@ async def test_create_send_webhook(self): with pytest.deprecated_call(): await responder.send_webhook("topic", {"pay": "load"}) + async def test_conn_rec_active_state_check_a(self): + profile = make_profile() + profile.context.injector.bind_instance(BaseCache, InMemoryCache()) + context = RequestContext(profile) + message = StubAgentMessage() + responder = test_module.DispatcherResponder(context, message, None) + check_flag = await responder.conn_rec_active_state_check( + profile, + "conn-id", + "didexchange/1.0/request", + ) + assert check_flag + with async_mock.patch.object( + test_module.ConnRecord, "retrieve_by_id", async_mock.AsyncMock() + ) as mock_conn_ret_by_id: + conn_rec = test_module.ConnRecord() + conn_rec.state = test_module.ConnRecord.State.COMPLETED + mock_conn_ret_by_id.return_value = conn_rec + check_flag = await responder.conn_rec_active_state_check( + profile, + "conn-id", + "proto-name/1.1/message-type", + ) + assert check_flag + check_flag = await responder.conn_rec_active_state_check( + profile, + "conn-id", + "proto-name/2.1/message-type", + ) + assert check_flag + + async def test_conn_rec_active_state_check_b(self): + profile = make_profile() + profile.context.injector.bind_instance(BaseCache, InMemoryCache()) + profile.context.injector.bind_instance( + EventBus, async_mock.MagicMock(notify=async_mock.AsyncMock()) + ) + context = RequestContext(profile) + message = StubAgentMessage() + responder = test_module.DispatcherResponder(context, message, None) + with async_mock.patch.object( + test_module.ConnRecord, "retrieve_by_id", async_mock.AsyncMock() + ) as mock_conn_ret_by_id: + conn_rec_a = test_module.ConnRecord() + conn_rec_a.state = test_module.ConnRecord.State.REQUEST + conn_rec_b = test_module.ConnRecord() + conn_rec_b.state = test_module.ConnRecord.State.COMPLETED + mock_conn_ret_by_id.side_effect = [conn_rec_a, conn_rec_b] + check_flag = await responder.conn_rec_active_state_check( + profile, + "conn-id", + "proto-name/1.1/message-type", + ) + assert check_flag + async def test_create_enc_outbound(self): profile = make_profile() context = RequestContext(profile) - message = b"abc123xyz7890000" + message = StubAgentMessage() responder = test_module.DispatcherResponder(context, message, None) with async_mock.patch.object( responder, "send_outbound", async_mock.AsyncMock() @@ -438,6 +612,25 @@ async def test_create_enc_outbound(self): await responder.send(message) assert mock_send_outbound.called_once() + message = b"abc123xyz7890000" + with async_mock.patch.object( + responder, "send_outbound", async_mock.AsyncMock() + ) as mock_send_outbound: + await responder.send(message) + + message = StubAgentMessage() + with async_mock.patch.object( + responder, "send_outbound", async_mock.AsyncMock() + ) as mock_send_outbound: + await responder.send_reply(message) + assert mock_send_outbound.called_once() + + message = b"abc123xyz7890000" + with async_mock.patch.object( + responder, "send_outbound", async_mock.AsyncMock() + ) as mock_send_outbound: + await responder.send_reply(message) + async def test_expired_context_x(self): def _smaller_scope(): profile = make_profile() diff --git a/aries_cloudagent/messaging/responder.py b/aries_cloudagent/messaging/responder.py index 32c5af641a..d7e7de0513 100644 --- a/aries_cloudagent/messaging/responder.py +++ b/aries_cloudagent/messaging/responder.py @@ -4,13 +4,19 @@ The responder is provided to message handlers to enable them to send a new message in response to the message being handled. """ +import asyncio +import json +import re from abc import ABC, abstractmethod -import json -from typing import Sequence, Union +from typing import Sequence, Union, Optional, Tuple +from ..cache.base import BaseCache from ..connections.models.connection_target import ConnectionTarget +from ..connections.models.conn_record import ConnRecord from ..core.error import BaseError +from ..core.event_bus import EventBus +from ..core.profile import Profile from ..transport.outbound.message import OutboundMessage from .base_message import BaseMessage @@ -79,7 +85,14 @@ async def send( ) -> OutboundSendStatus: """Convert a message to an OutboundMessage and send it.""" outbound = await self.create_outbound(message, **kwargs) - return await self.send_outbound(outbound) + try: + return await self.send_outbound( + message=outbound, + message_type=message._message_type, + message_id=message._id, + ) + except AttributeError: + return await self.send_outbound(message=outbound) async def send_reply( self, @@ -109,10 +122,75 @@ async def send_reply( target=target, target_list=target_list, ) - return await self.send_outbound(outbound) + try: + return await self.send_outbound( + outbound, message_type=message._message_type, message_id=message._id + ) + except AttributeError: + return await self.send_outbound(outbound) + + async def conn_rec_active_state_check( + self, profile: Profile, connection_id: str, msg_type: str, timeout: int = 7 + ) -> bool: + """Check if the connection record is ready for sending outbound message.""" + CONNECTION_READY_EVENT = re.compile( + "^acapy::record::connections::(active|completed|response)$" + ) + WHITELIST_MSG_TYPE = [ + "didexchange/1.0/request", + "didexchange/1.0/response", + "connections/1.0/invitation", + "connections/1.0/request", + "connections/1.0/response", + ] + if msg_type in WHITELIST_MSG_TYPE: + return True + + async def _wait_for_state() -> Tuple[bool, Optional[str]]: + async with profile.session() as session: + conn_record = await ConnRecord.retrieve_by_id(session, connection_id) + if conn_record.is_ready: + return (True, conn_record.state) + event = profile.inject(EventBus) + with event.wait_for_event( + profile, + CONNECTION_READY_EVENT, + lambda event: event.payload.get("connection_id") == connection_id, + ) as await_event: + async with profile.session() as session: + conn_record = await ConnRecord.retrieve_by_id( + session, connection_id + ) + if conn_record.is_ready: + return (True, conn_record.state) + event = await await_event + conn_record = ConnRecord.deserialize(event.payload) + return (True, conn_record.state) + + try: + cache_key = f"conn_rec_state::{connection_id}" + connection_state = None + cache = profile.inject_or(BaseCache) + if cache: + connection_state = await cache.get(cache_key) + if connection_state and ConnRecord.State.get(connection_state) in ( + ConnRecord.State.COMPLETED, + ConnRecord.State.RESPONSE, + ): + return True + check_flag, connection_state = await asyncio.wait_for( + _wait_for_state(), timeout + ) + if cache and connection_state: + await cache.set(cache_key, connection_state) + return check_flag + except asyncio.TimeoutError: + return False @abstractmethod - async def send_outbound(self, message: OutboundMessage) -> OutboundSendStatus: + async def send_outbound( + self, message: OutboundMessage, **kwargs + ) -> OutboundSendStatus: """ Send an outbound message. @@ -152,7 +230,9 @@ async def send_reply( self.messages.append((message, kwargs)) return OutboundSendStatus.QUEUED_FOR_DELIVERY - async def send_outbound(self, message: OutboundMessage) -> OutboundSendStatus: + async def send_outbound( + self, message: OutboundMessage, **kwargs + ) -> OutboundSendStatus: """Send an outbound message.""" self.messages.append((message, None)) return OutboundSendStatus.QUEUED_FOR_DELIVERY diff --git a/aries_cloudagent/protocols/connections/v1_0/routes.py b/aries_cloudagent/protocols/connections/v1_0/routes.py index 3da5e71f95..11d8ba5651 100644 --- a/aries_cloudagent/protocols/connections/v1_0/routes.py +++ b/aries_cloudagent/protocols/connections/v1_0/routes.py @@ -15,6 +15,7 @@ from ....admin.request_context import AdminRequestContext from ....connections.models.conn_record import ConnRecord, ConnRecordSchema +from ....cache.base import BaseCache from ....messaging.models.base import BaseModelError from ....messaging.models.openapi import OpenAPISchema from ....messaging.valid import ( @@ -739,6 +740,9 @@ async def connections_remove(request: web.BaseRequest): async with profile.session() as session: connection = await ConnRecord.retrieve_by_id(session, connection_id) await connection.delete_record(session) + cache = session.inject_or(BaseCache) + if cache: + await cache.clear(f"conn_rec_state::{connection_id}") except StorageNotFoundError as err: raise web.HTTPNotFound(reason=err.roll_up) from err except StorageError as err: diff --git a/aries_cloudagent/protocols/connections/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/connections/v1_0/tests/test_routes.py index 274ef8b6e6..adc23cf242 100644 --- a/aries_cloudagent/protocols/connections/v1_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/connections/v1_0/tests/test_routes.py @@ -5,6 +5,8 @@ from asynctest import mock as async_mock from .....admin.request_context import AdminRequestContext +from .....cache.base import BaseCache +from .....cache.in_memory import InMemoryCache from .....connections.models.conn_record import ConnRecord from .....storage.error import StorageNotFoundError @@ -705,6 +707,26 @@ async def test_connections_remove(self): await test_module.connections_remove(self.request) mock_response.assert_called_once_with({}) + async def test_connections_remove_cache_key(self): + cache = InMemoryCache() + profile = self.context.profile + await cache.set("conn_rec_state::dummy", "active") + profile.context.injector.bind_instance(BaseCache, cache) + self.request.match_info = {"conn_id": "dummy"} + mock_conn_rec = async_mock.MagicMock() + mock_conn_rec.delete_record = async_mock.CoroutineMock() + assert (await cache.get("conn_rec_state::dummy")) == "active" + with async_mock.patch.object( + test_module.ConnRecord, "retrieve_by_id", async_mock.CoroutineMock() + ) as mock_conn_rec_retrieve_by_id, async_mock.patch.object( + test_module.web, "json_response" + ) as mock_response: + mock_conn_rec_retrieve_by_id.return_value = mock_conn_rec + + await test_module.connections_remove(self.request) + mock_response.assert_called_once_with({}) + assert not (await cache.get("conn_rec_state::dummy")) + async def test_connections_remove_not_found(self): self.request.match_info = {"conn_id": "dummy"} From 553b1280e546c8bf8cbb40a7f9049c65a0a54cce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Humbert?= Date: Mon, 6 Mar 2023 11:56:47 +0100 Subject: [PATCH 712/872] fix: askar exception message always displaying null DID MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Clément Humbert Signed-off-by: JSyro --- aries_cloudagent/wallet/askar.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/aries_cloudagent/wallet/askar.py b/aries_cloudagent/wallet/askar.py index d104d32d36..dac9a4cf5d 100644 --- a/aries_cloudagent/wallet/askar.py +++ b/aries_cloudagent/wallet/askar.py @@ -267,12 +267,12 @@ async def get_local_did(self, did: str) -> DIDInfo: if not did: raise WalletNotFoundError("No identifier provided") try: - did = await self._session.handle.fetch(CATEGORY_DID, did) + did_entry = await self._session.handle.fetch(CATEGORY_DID, did) except AskarError as err: raise WalletError("Error when fetching local DID") from err - if not did: + if not did_entry: raise WalletNotFoundError("Unknown DID: {}".format(did)) - return self._load_did_entry(did) + return self._load_did_entry(did_entry) async def get_local_did_for_verkey(self, verkey: str) -> DIDInfo: """ From 92e14de8918347ad2d3908035e722f8ddfa10d7f Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Thu, 9 Mar 2023 11:11:48 -0800 Subject: [PATCH 713/872] updates based on feedback Signed-off-by: Shaanjot Gill Signed-off-by: JSyro --- aries_cloudagent/core/dispatcher.py | 31 +------- .../core/tests/test_dispatcher.py | 59 +------------- aries_cloudagent/messaging/responder.py | 78 +++++++++---------- 3 files changed, 41 insertions(+), 127 deletions(-) diff --git a/aries_cloudagent/core/dispatcher.py b/aries_cloudagent/core/dispatcher.py index ca371806b9..062a33f3a1 100644 --- a/aries_cloudagent/core/dispatcher.py +++ b/aries_cloudagent/core/dispatcher.py @@ -7,7 +7,6 @@ import asyncio import logging -import json import os import warnings @@ -23,7 +22,7 @@ from ..messaging.error import MessageParseError from ..messaging.models.base import BaseModelError from ..messaging.request_context import RequestContext -from ..messaging.responder import BaseResponder +from ..messaging.responder import BaseResponder, SKIP_ACTIVE_CONN_CHECK_MSG_TYPES from ..messaging.util import datetime_now from ..protocols.connections.v1_0.manager import ConnectionManager from ..protocols.problem_report.v1_0.message import ProblemReport @@ -377,25 +376,6 @@ async def create_outbound( return await super().create_outbound(message, **kwargs) - async def _get_msg_type_from_enc_payload( - self, profile: Profile, parsed_msg: dict - ) -> Optional[Tuple[str, str]]: - """Get message type and id tuple from enc_payload.""" - try: - if not isinstance(parsed_msg, dict): - return None - message_type = parsed_msg.get("@type") - if not message_type: - return None - registry: ProtocolRegistry = profile.inject(ProtocolRegistry) - message_cls = registry.resolve_message_class(message_type) - if not message_cls: - return None - instance = message_cls.deserialize(parsed_msg) - return instance._message_type, instance._id - except (ProtocolMinorVersionNotSupported, BaseModelError, AttributeError): - return None - async def send_outbound( self, message: OutboundMessage, **kwargs ) -> OutboundSendStatus: @@ -411,21 +391,14 @@ async def send_outbound( msg_type = kwargs.get("message_type") msg_id = kwargs.get("message_id") - if not msg_type and not msg_id and (message.enc_payload or message.payload): - msg_dict = json.loads(message.enc_payload or message.payload) - msg_type_id_tuple = await self._get_msg_type_from_enc_payload( - context.profile, msg_dict - ) - if msg_type_id_tuple: - msg_type, msg_id = msg_type_id_tuple if ( message.connection_id and msg_type + and msg_type not in SKIP_ACTIVE_CONN_CHECK_MSG_TYPES and not await super().conn_rec_active_state_check( profile=context.profile, connection_id=message.connection_id, - msg_type=msg_type, ) ): raise RuntimeError( diff --git a/aries_cloudagent/core/tests/test_dispatcher.py b/aries_cloudagent/core/tests/test_dispatcher.py index ec18696731..450bfb4767 100644 --- a/aries_cloudagent/core/tests/test_dispatcher.py +++ b/aries_cloudagent/core/tests/test_dispatcher.py @@ -494,50 +494,6 @@ async def test_create_send_outbound_with_msg_attrs_x(self): message_id=message._id, ) - async def test_get_msg_type_from_enc_payload(self): - profile = make_profile() - context = RequestContext( - profile, - settings={"timing.enabled": True}, - ) - message = StubAgentMessage() - responder = test_module.DispatcherResponder(context, message, None) - assert not await responder._get_msg_type_from_enc_payload( - profile, ["...", "..."] - ) - assert not await responder._get_msg_type_from_enc_payload( - profile, {"...": "..."} - ) - msg_dict = message.serialize() - not await responder._get_msg_type_from_enc_payload(profile, msg_dict) - registry = profile.inject(ProtocolRegistry) - registry.register_message_types( - { - pfx.qualify(StubAgentMessage.Meta.message_type): StubAgentMessage - for pfx in DIDCommPrefix - } - ) - message = StubAgentMessage() - responder = test_module.DispatcherResponder(context, message, None) - message._id = "test123" - msg_dict = message.serialize() - msg_type, msg_id = await responder._get_msg_type_from_enc_payload( - profile, msg_dict - ) - assert msg_type == StubAgentMessage.Meta.message_type - assert msg_id == "test123" - - msg_dict["@type"] = "proto-name/3.0/message-type" - with async_mock.patch.object( - registry, "resolve_message_class", async_mock.MagicMock() - ) as mock_resolve: - mock_resolve.return_value = async_mock.MagicMock( - deserialize=async_mock.MagicMock( - side_effect=test_module.ProtocolMinorVersionNotSupported() - ) - ) - assert not await responder._get_msg_type_from_enc_payload(profile, msg_dict) - async def test_create_send_webhook(self): profile = make_profile() context = RequestContext(profile) @@ -552,12 +508,6 @@ async def test_conn_rec_active_state_check_a(self): context = RequestContext(profile) message = StubAgentMessage() responder = test_module.DispatcherResponder(context, message, None) - check_flag = await responder.conn_rec_active_state_check( - profile, - "conn-id", - "didexchange/1.0/request", - ) - assert check_flag with async_mock.patch.object( test_module.ConnRecord, "retrieve_by_id", async_mock.AsyncMock() ) as mock_conn_ret_by_id: @@ -567,13 +517,11 @@ async def test_conn_rec_active_state_check_a(self): check_flag = await responder.conn_rec_active_state_check( profile, "conn-id", - "proto-name/1.1/message-type", ) assert check_flag check_flag = await responder.conn_rec_active_state_check( profile, "conn-id", - "proto-name/2.1/message-type", ) assert check_flag @@ -597,7 +545,6 @@ async def test_conn_rec_active_state_check_b(self): check_flag = await responder.conn_rec_active_state_check( profile, "conn-id", - "proto-name/1.1/message-type", ) assert check_flag @@ -611,8 +558,8 @@ async def test_create_enc_outbound(self): ) as mock_send_outbound: await responder.send(message) assert mock_send_outbound.called_once() - - message = b"abc123xyz7890000" + msg_json = json.dumps(StubAgentMessage().serialize()) + message = msg_json.encode("utf-8") with async_mock.patch.object( responder, "send_outbound", async_mock.AsyncMock() ) as mock_send_outbound: @@ -625,7 +572,7 @@ async def test_create_enc_outbound(self): await responder.send_reply(message) assert mock_send_outbound.called_once() - message = b"abc123xyz7890000" + message = json.dumps(StubAgentMessage().serialize()) with async_mock.patch.object( responder, "send_outbound", async_mock.AsyncMock() ) as mock_send_outbound: diff --git a/aries_cloudagent/messaging/responder.py b/aries_cloudagent/messaging/responder.py index d7e7de0513..75913a0645 100644 --- a/aries_cloudagent/messaging/responder.py +++ b/aries_cloudagent/messaging/responder.py @@ -6,7 +6,6 @@ """ import asyncio import json -import re from abc import ABC, abstractmethod from typing import Sequence, Union, Optional, Tuple @@ -15,13 +14,20 @@ from ..connections.models.connection_target import ConnectionTarget from ..connections.models.conn_record import ConnRecord from ..core.error import BaseError -from ..core.event_bus import EventBus from ..core.profile import Profile from ..transport.outbound.message import OutboundMessage from .base_message import BaseMessage from ..transport.outbound.status import OutboundSendStatus +SKIP_ACTIVE_CONN_CHECK_MSG_TYPES = [ + "didexchange/1.0/request", + "didexchange/1.0/response", + "connections/1.0/invitation", + "connections/1.0/request", + "connections/1.0/response", +] + class ResponderError(BaseError): """Responder error.""" @@ -85,14 +91,18 @@ async def send( ) -> OutboundSendStatus: """Convert a message to an OutboundMessage and send it.""" outbound = await self.create_outbound(message, **kwargs) - try: - return await self.send_outbound( - message=outbound, - message_type=message._message_type, - message_id=message._id, - ) - except AttributeError: - return await self.send_outbound(message=outbound) + if isinstance(message, BaseMessage): + msg_type = message._message_type + msg_id = message._id + else: + msg_dict = json.loads(message) + msg_type = msg_dict.get("@type") + msg_id = msg_dict.get("@id") + return await self.send_outbound( + message=outbound, + message_type=msg_type, + message_id=msg_id, + ) async def send_reply( self, @@ -122,50 +132,34 @@ async def send_reply( target=target, target_list=target_list, ) - try: - return await self.send_outbound( - outbound, message_type=message._message_type, message_id=message._id - ) - except AttributeError: - return await self.send_outbound(outbound) + if isinstance(message, BaseMessage): + msg_type = message._message_type + msg_id = message._id + else: + msg_dict = json.loads(message) + msg_type = msg_dict.get("@type") + msg_id = msg_dict.get("@id") + return await self.send_outbound( + message=outbound, message_type=msg_type, message_id=msg_id + ) async def conn_rec_active_state_check( - self, profile: Profile, connection_id: str, msg_type: str, timeout: int = 7 + self, profile: Profile, connection_id: str, timeout: int = 7 ) -> bool: """Check if the connection record is ready for sending outbound message.""" - CONNECTION_READY_EVENT = re.compile( - "^acapy::record::connections::(active|completed|response)$" - ) - WHITELIST_MSG_TYPE = [ - "didexchange/1.0/request", - "didexchange/1.0/response", - "connections/1.0/invitation", - "connections/1.0/request", - "connections/1.0/response", - ] - if msg_type in WHITELIST_MSG_TYPE: - return True async def _wait_for_state() -> Tuple[bool, Optional[str]]: - async with profile.session() as session: - conn_record = await ConnRecord.retrieve_by_id(session, connection_id) - if conn_record.is_ready: - return (True, conn_record.state) - event = profile.inject(EventBus) - with event.wait_for_event( - profile, - CONNECTION_READY_EVENT, - lambda event: event.payload.get("connection_id") == connection_id, - ) as await_event: + while True: async with profile.session() as session: conn_record = await ConnRecord.retrieve_by_id( session, connection_id ) if conn_record.is_ready: + # if ConnRecord.State.get(conn_record.state) in ( + # ConnRecord.State.COMPLETED, + # ): return (True, conn_record.state) - event = await await_event - conn_record = ConnRecord.deserialize(event.payload) - return (True, conn_record.state) + await asyncio.sleep(1) try: cache_key = f"conn_rec_state::{connection_id}" From 0c6a92ead1709f3ed3f0049c0990d7da324e331e Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Mon, 13 Mar 2023 19:38:24 -0700 Subject: [PATCH 714/872] Remove CircleCI Status since we aren't using CircleCI anymore Signed-off-by: Stephen Curran Signed-off-by: Stephen Curran Signed-off-by: Stephen Curran Signed-off-by: JSyro --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index f5fc6cd485..f1f1f18157 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ # Hyperledger Aries Cloud Agent - Python [![pypi releases](https://img.shields.io/pypi/v/aries_cloudagent)](https://pypi.org/project/aries-cloudagent/) -[![CircleCI](https://circleci.com/gh/hyperledger/aries-cloudagent-python.svg?style=shield)](https://circleci.com/gh/hyperledger/aries-cloudagent-python) [![codecov](https://codecov.io/gh/hyperledger/aries-cloudagent-python/branch/main/graph/badge.svg)](https://codecov.io/gh/hyperledger/aries-cloudagent-python) From ccfcaafa0809bb7323f90432a8e909b156341b9f Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Mon, 13 Mar 2023 19:53:14 -0700 Subject: [PATCH 715/872] Delete circleci config file Signed-off-by: Stephen Curran Signed-off-by: Stephen Curran Signed-off-by: Stephen Curran Signed-off-by: JSyro --- .circleci/config.yml | 48 -------------------------------------------- 1 file changed, 48 deletions(-) delete mode 100644 .circleci/config.yml diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index f753ba2685..0000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,48 +0,0 @@ -version: 2 -jobs: - agent-build: - docker: - - image: bcgovimages/von-image:py36-1.15-1 - steps: - - checkout - - restore_cache: - keys: - - v5-pip-dependencies-{{ .Branch }}-{{ checksum "requirements.txt" }}-{{ checksum "requirements.dev.txt" }}-{{ checksum "requirements.bbs.txt" }} - - v5-pip-dependencies-{{ .Branch }}- - - run: - name: Install Python Dependencies - command: | - pip install \ - --user \ - -r requirements.txt \ - -r requirements.askar.txt \ - -r requirements.bbs.txt \ - -r requirements.dev.txt - - - save_cache: - paths: - - /home/indy/.local/lib/python3.6/site-packages - key: v5-pip-dependencies-{{ .Branch }}-{{ checksum "requirements.txt" }}-{{ checksum "requirements.dev.txt" }}-{{ checksum "requirements.bbs.txt" }}-{{ checksum "requirements.askar.txt" }} - - - run: - name: Run Agent Tests - command: | - [ ! -d test-reports ] && mkdir test-reports - python -m pytest - - - run: - name: Push to Codecov.io - command: | - bash <(curl -s https://codecov.io/bash) -f test-reports/coverage.xml - - - store_test_results: - path: test-reports - - - store_artifacts: - path: test-reports - -workflows: - version: 2 - aries_cloudagent: - jobs: - - agent-build From a3d36aadb45a9d910d319a1e60a422a0d0baa810 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Tue, 14 Mar 2023 15:27:31 -0700 Subject: [PATCH 716/872] 0.8.0 release Signed-off-by: Stephen Curran Signed-off-by: JSyro --- CHANGELOG.md | 69 +++++++++--- PUBLISHING.md | 13 ++- aries_cloudagent/version.py | 2 +- docs/README.md | 12 +-- docs/conf.py | 3 + docs/generated/aries_cloudagent.admin.rst | 32 +++--- .../aries_cloudagent.askar.didcomm.rst | 20 ++-- docs/generated/aries_cloudagent.askar.rst | 23 ++-- docs/generated/aries_cloudagent.cache.rst | 20 ++-- docs/generated/aries_cloudagent.commands.rst | 32 +++--- docs/generated/aries_cloudagent.config.rst | 98 ++++++++--------- ...s_cloudagent.connections.models.diddoc.rst | 32 +++--- .../aries_cloudagent.connections.models.rst | 23 ++-- .../aries_cloudagent.connections.rst | 17 +-- ...ries_cloudagent.core.in_memory.didcomm.rst | 20 ++-- .../aries_cloudagent.core.in_memory.rst | 17 +-- docs/generated/aries_cloudagent.core.rst | 71 ++++++------ docs/generated/aries_cloudagent.did.rst | 14 +-- docs/generated/aries_cloudagent.holder.rst | 14 +-- .../generated/aries_cloudagent.indy.credx.rst | 26 ++--- .../aries_cloudagent.indy.models.rst | 92 ++++++++-------- docs/generated/aries_cloudagent.indy.rst | 39 +++---- docs/generated/aries_cloudagent.indy.sdk.rst | 56 +++++----- ...es_cloudagent.ledger.merkel_validation.rst | 44 ++++---- ...ries_cloudagent.ledger.multiple_ledger.rst | 44 ++++---- docs/generated/aries_cloudagent.ledger.rst | 55 +++++----- ...agent.messaging.credential_definitions.rst | 20 ++-- .../aries_cloudagent.messaging.decorators.rst | 74 ++++++------- .../aries_cloudagent.messaging.jsonld.rst | 32 +++--- .../aries_cloudagent.messaging.models.rst | 26 ++--- docs/generated/aries_cloudagent.messaging.rst | 67 ++++++------ .../aries_cloudagent.messaging.schemas.rst | 20 ++-- .../aries_cloudagent.multitenant.admin.rst | 14 +-- .../aries_cloudagent.multitenant.rst | 53 ++++----- .../aries_cloudagent.protocols.actionmenu.rst | 17 +-- ...ent.protocols.actionmenu.v1_0.handlers.rst | 26 ++--- ...ent.protocols.actionmenu.v1_0.messages.rst | 26 ++--- ...agent.protocols.actionmenu.v1_0.models.rst | 26 ++--- ...s_cloudagent.protocols.actionmenu.v1_0.rst | 51 ++++----- ...ries_cloudagent.protocols.basicmessage.rst | 17 +-- ...t.protocols.basicmessage.v1_0.handlers.rst | 14 +-- ...t.protocols.basicmessage.v1_0.messages.rst | 14 +-- ...cloudagent.protocols.basicmessage.v1_0.rst | 25 ++--- ...aries_cloudagent.protocols.connections.rst | 17 +-- ...nt.protocols.connections.v1_0.handlers.rst | 26 ++--- ...nt.protocols.connections.v1_0.messages.rst | 32 +++--- ...gent.protocols.connections.v1_0.models.rst | 14 +-- ..._cloudagent.protocols.connections.v1_0.rst | 33 +++--- ...udagent.protocols.coordinate_mediation.rst | 23 ++-- ...ols.coordinate_mediation.v1_0.handlers.rst | 56 +++++----- ...ordinate_mediation.v1_0.messages.inner.rst | 32 +++--- ...ols.coordinate_mediation.v1_0.messages.rst | 59 +++++----- ...ocols.coordinate_mediation.v1_0.models.rst | 14 +-- ...nt.protocols.coordinate_mediation.v1_0.rst | 57 +++++----- ...aries_cloudagent.protocols.didexchange.rst | 17 +-- ...nt.protocols.didexchange.v1_0.handlers.rst | 32 +++--- ...nt.protocols.didexchange.v1_0.messages.rst | 32 +++--- ..._cloudagent.protocols.didexchange.v1_0.rst | 31 +++--- .../aries_cloudagent.protocols.discovery.rst | 19 ++-- ...gent.protocols.discovery.v1_0.handlers.rst | 20 ++-- ...gent.protocols.discovery.v1_0.messages.rst | 20 ++-- ...dagent.protocols.discovery.v1_0.models.rst | 14 +-- ...es_cloudagent.protocols.discovery.v1_0.rst | 33 +++--- ...gent.protocols.discovery.v2_0.handlers.rst | 20 ++-- ...gent.protocols.discovery.v2_0.messages.rst | 20 ++-- ...dagent.protocols.discovery.v2_0.models.rst | 14 +-- ...es_cloudagent.protocols.discovery.v2_0.rst | 33 +++--- ...oudagent.protocols.endorse_transaction.rst | 17 +-- ...cols.endorse_transaction.v1_0.handlers.rst | 50 ++++----- ...cols.endorse_transaction.v1_0.messages.rst | 56 +++++----- ...tocols.endorse_transaction.v1_0.models.rst | 14 +-- ...ent.protocols.endorse_transaction.v1_0.rst | 51 ++++----- ...ries_cloudagent.protocols.introduction.rst | 17 +-- ...t.protocols.introduction.v0_1.handlers.rst | 26 ++--- ...t.protocols.introduction.v0_1.messages.rst | 26 ++--- ...cloudagent.protocols.introduction.v0_1.rst | 37 +++---- ..._cloudagent.protocols.issue_credential.rst | 19 ++-- ...otocols.issue_credential.v1_0.handlers.rst | 44 ++++---- ...s.issue_credential.v1_0.messages.inner.rst | 14 +-- ...otocols.issue_credential.v1_0.messages.rst | 55 ++++++---- ...protocols.issue_credential.v1_0.models.rst | 14 +-- ...dagent.protocols.issue_credential.v1_0.rst | 39 +++---- ...ols.issue_credential.v2_0.formats.indy.rst | 14 +-- ...redential.v2_0.formats.ld_proof.models.rst | 20 ++-- ...issue_credential.v2_0.formats.ld_proof.rst | 17 +-- ...rotocols.issue_credential.v2_0.formats.rst | 19 ++-- ...otocols.issue_credential.v2_0.handlers.rst | 44 ++++---- ...s.issue_credential.v2_0.messages.inner.rst | 14 +-- ...otocols.issue_credential.v2_0.messages.rst | 61 ++++++----- ...ls.issue_credential.v2_0.models.detail.rst | 20 ++-- ...protocols.issue_credential.v2_0.models.rst | 17 +-- ...dagent.protocols.issue_credential.v2_0.rst | 41 +++---- ...ries_cloudagent.protocols.notification.rst | 17 +-- ...t.protocols.notification.v1_0.handlers.rst | 14 +-- ...t.protocols.notification.v1_0.messages.rst | 14 +-- ...cloudagent.protocols.notification.v1_0.rst | 19 ++-- ...aries_cloudagent.protocols.out_of_band.rst | 17 +-- ...nt.protocols.out_of_band.v1_0.handlers.rst | 26 ++--- ...nt.protocols.out_of_band.v1_0.messages.rst | 38 +++---- ...gent.protocols.out_of_band.v1_0.models.rst | 20 ++-- ..._cloudagent.protocols.out_of_band.v1_0.rst | 39 +++---- ...cloudagent.protocols.present_proof.dif.rst | 38 +++---- ...loudagent.protocols.present_proof.indy.rst | 14 +-- ...ies_cloudagent.protocols.present_proof.rst | 23 ++-- ....protocols.present_proof.v1_0.handlers.rst | 38 +++---- ....protocols.present_proof.v1_0.messages.rst | 46 ++++---- ...nt.protocols.present_proof.v1_0.models.rst | 14 +-- ...loudagent.protocols.present_proof.v1_0.rst | 39 +++---- ...otocols.present_proof.v2_0.formats.dif.rst | 14 +-- ...tocols.present_proof.v2_0.formats.indy.rst | 14 +-- ...t.protocols.present_proof.v2_0.formats.rst | 19 ++-- ....protocols.present_proof.v2_0.handlers.rst | 38 +++---- ....protocols.present_proof.v2_0.messages.rst | 52 +++++---- ...nt.protocols.present_proof.v2_0.models.rst | 14 +-- ...loudagent.protocols.present_proof.v2_0.rst | 41 +++---- ...es_cloudagent.protocols.problem_report.rst | 17 +-- ...oudagent.protocols.problem_report.v1_0.rst | 26 ++--- ...gent.protocols.revocation_notification.rst | 19 ++-- ....revocation_notification.v1_0.handlers.rst | 14 +-- ....revocation_notification.v1_0.messages.rst | 14 +-- ...ls.revocation_notification.v1_0.models.rst | 14 +-- ...protocols.revocation_notification.v1_0.rst | 27 ++--- ....revocation_notification.v2_0.handlers.rst | 14 +-- ....revocation_notification.v2_0.messages.rst | 14 +-- ...ls.revocation_notification.v2_0.models.rst | 14 +-- ...protocols.revocation_notification.v2_0.rst | 27 ++--- .../aries_cloudagent.protocols.routing.rst | 17 +-- ...dagent.protocols.routing.v1_0.handlers.rst | 38 +++---- ...dagent.protocols.routing.v1_0.messages.rst | 38 +++---- ...oudagent.protocols.routing.v1_0.models.rst | 44 ++++---- ...ries_cloudagent.protocols.routing.v1_0.rst | 27 ++--- docs/generated/aries_cloudagent.protocols.rst | 49 ++++----- .../aries_cloudagent.protocols.trustping.rst | 17 +-- ...gent.protocols.trustping.v1_0.handlers.rst | 20 ++-- ...gent.protocols.trustping.v1_0.messages.rst | 20 ++-- ...es_cloudagent.protocols.trustping.v1_0.rst | 25 ++--- .../aries_cloudagent.resolver.default.rst | 32 +++--- docs/generated/aries_cloudagent.resolver.rst | 29 ++--- .../aries_cloudagent.revocation.models.rst | 32 +++--- .../generated/aries_cloudagent.revocation.rst | 47 ++++---- docs/generated/aries_cloudagent.rst | 61 +++++------ docs/generated/aries_cloudagent.storage.rst | 47 ++++---- .../aries_cloudagent.storage.vc_holder.rst | 44 ++++---- docs/generated/aries_cloudagent.tails.rst | 26 ++--- .../aries_cloudagent.transport.inbound.rst | 56 +++++----- .../aries_cloudagent.transport.outbound.rst | 44 ++++---- .../aries_cloudagent.transport.queue.rst | 20 ++-- docs/generated/aries_cloudagent.transport.rst | 39 +++---- docs/generated/aries_cloudagent.utils.rst | 68 ++++++------ .../aries_cloudagent.vc.ld_proofs.crypto.rst | 20 ++-- ...aries_cloudagent.vc.ld_proofs.purposes.rst | 38 +++---- .../aries_cloudagent.vc.ld_proofs.rst | 57 +++++----- .../aries_cloudagent.vc.ld_proofs.suites.rst | 50 ++++----- docs/generated/aries_cloudagent.vc.rst | 12 +-- .../aries_cloudagent.vc.vc_ld.models.rst | 20 ++-- docs/generated/aries_cloudagent.vc.vc_ld.rst | 35 +++--- .../aries_cloudagent.wallet.models.rst | 14 +-- docs/generated/aries_cloudagent.wallet.rst | 101 +++++++++--------- open-api/openapi.json | 2 +- 159 files changed, 2571 insertions(+), 2250 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0af211ba2b..e3fe2756c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,10 @@ -# 0.8.0-rc0 +# 0.8.0 -## February 8, 2023 +## March 14, 2023 0.8.0 is a breaking change that contains all updates since release 0.7.5. It extends the previously tagged `1.0.0-rc1` release because it is not clear when -that release will be finalized. Many of the PRs in this release were previously +the 1.0.0 release will be finalized. Many of the PRs in this release were previously included in the `1.0.0-rc1` release. The categorized list of PRs separates those that are new from those in the `1.0.0-rc1` release candidate. @@ -19,6 +19,12 @@ Endorsers. A new repo has been created that is a pre-configured instance of ACA-Py for use as an Endorser service. +A recently completed feature that is outside of ACA-Py is a script to migrate +existing ACA-Py storage from Indy SDK format to Aries Askar format. This +enables existing deployments to switch to using the newer Aries Askar +components. For details see the converter in the +[aries-acapy-tools](https://github.com/hyperledger/aries-acapy-tools) repository. + ### Container Publishing Updated With this release, a new automated process publishes container images in the @@ -32,19 +38,19 @@ container image publication process can be found in the document [Container Images and Github Actions]. The ACA-Py container images are based on [Python 3.6 and 3.9 `slim-bullseye` -images](https://hub.docker.com/_/python), and are built to support `linux/386 -(x86)`, `linux/amd64 (x64)`, and `linux/arm64`. There are two flavors of image -built for each Python version. One contains only the Indy/Aries Shared Libraries -only ([Aries Askar](https://github.com/hyperledger/aries-askar), [Indy +images](https://hub.docker.com/_/python), and are designed to support `linux/386 +(x86)`, `linux/amd64 (x64)`, and `linux/arm64`. However, for this release, the +publication of multi-architecture containers is disabled. We are working to +enable that through the updating of some dependencies that lack that capability. +There are two flavors of image built for each Python version. One contains only +the Indy/Aries Shared Libraries only ([Aries +Askar](https://github.com/hyperledger/aries-askar), [Indy VDR](https://github.com/hyperledger/indy-vdr) and [Indy Shared RS](https://github.com/hyperledger/indy-shared-rs), supporting only the use of `--wallet-type askar`). The other (labelled `indy`) contains the Indy/Aries shared libraries and the Indy SDK (considered deprecated). For new deployments, we recommend using the Python 3.9 Shared Library images. For existing -deployments, we recommend migrating to those images. For those migrating an Indy -SDK deployment, a new secure storage database migration capability from Indy SDK -to Aries Askar is available--contact the ACA-Py maintainers on Hyperledger -Discord for details. +deployments, we recommend migrating to those images. Those currently using the container images published by [BC Gov on Docker Hub](https://hub.docker.com/r/bcgovimages/aries-cloudagent) should change to use @@ -56,7 +62,6 @@ aries-cloudagent-python]. [publish-indy.yml]: https://github.com/hyperledger/aries-cloudagent-python/blob/main/.github/workflows/publish-indy.yml [Container Images and Github Actions]: https://github.com/hyperledger/aries-cloudagent-python/blob/main/ContainerImagesAndGithubActions.md - ## Breaking Changes ### PR [\#2034](https://github.com/hyperledger/aries-cloudagent-python/pull/2034) -- Implicit connections @@ -79,10 +84,26 @@ that can be checked if for unrevealed attributes. As few implementations of Aries wallets support unrevealed attributes in an AnonCreds presentation, this is unlikely to impact any deployments. +### PR [\#2145](https://github.com/hyperledger/aries-cloudagent-python/pull/2145) - Update webhook message to terse form by default, added startup flag --debug-webhooks for full form + +The default behavior in ACA-Py has been to keep the full text of all messages in +the protocol state object, and include the full protocol state object in the +webhooks sent to the controller. When the messages include an object that is +very large in all the messages, the webhook may become too big to be passed via +HTTP. For example, issuing a credential with a photo as one of the claims may +result in a number of copies of the photo in the protocol state object and +hence, very large webhooks. This change reduces the size of the webhook message +by eliminating redundant data in the protocol state of the "Issue Credential" +message as the default, and adds a new parameter to use the old behavior. + ### Categorized List of Pull Requests - Verifiable credential, presentation and revocation handling updates - - Feature: enabled handling VPs \(request, creation, verification\) with different VCs [\#1956](https://github.com/hyperledger/aries-cloudagent-python/pull/1956) ([teanas](https://github.com/teanas)) + - **BREAKING:** Update webhook message to terse form [default, added startup flag --debug-webhooks for full form [\#2145](https://github.com/hyperledger/aries-cloudagent-python/pull/2145) by [victorlee0505](victorlee0505) + - Add startup flag --light-weight-webhook to trim down outbound webhook payload [\#1941](https://github.com/hyperledger/aries-cloudagent-python/pull/1941) [victorlee0505](https://github.com/victorlee0505) + - feat: add verification method issue-credentials-2.0/send endpoint [\#2135](https://github.com/hyperledger/aries-cloudagent-python/pull/2135) [chumbert](https://github.com/chumbert) + - Respect auto-verify-presentation flag in present proof v1 and v2 [\#2097](https://github.com/hyperledger/aries-cloudagent-python/pull/2097) [dbluhm](https://github.com/dbluhm) + - Feature: enabled handling VPs (request, creation, verification) with different VCs [\#1956](https://github.com/hyperledger/aries-cloudagent-python/pull/1956) ([teanas](https://github.com/teanas)) - fix: update issue-credential endpoint summaries [\#1997](https://github.com/hyperledger/aries-cloudagent-python/pull/1997) ([PeterStrob](https://github.com/PeterStrob)) - fix claim format designation in presentation submission [\#2013](https://github.com/hyperledger/aries-cloudagent-python/pull/2013) ([rmnre](https://github.com/rmnre)) - \#2041 - Issue JSON-LD has invalid Admin API documentation [\#2046](https://github.com/hyperledger/aries-cloudagent-python/pull/2046) ([jfblier-amplitude](https://github.com/jfblier-amplitude)) @@ -97,9 +118,10 @@ is unlikely to impact any deployments. - Send webhooks upon record/credential deletion [\#1906](https://github.com/hyperledger/aries-cloudagent-python/pull/1906) ([frostyfrog](https://github.com/frostyfrog)) - Out of Band (OOB) and DID Exchange / Connection Handling / Mediator + - UPGRADE: Fix multi-use invitation performance [\#2116](https://github.com/hyperledger/aries-cloudagent-python/pull/2116) [reflectivedevelopment](https://github.com/reflectivedevelopment) - fix: public did mediator routing keys as did keys [\#1977](https://github.com/hyperledger/aries-cloudagent-python/pull/1977) ([dbluhm](https://github.com/dbluhm)) - Fix for mediator load testing race condition when scaling horizontally [\#2009](https://github.com/hyperledger/aries-cloudagent-python/pull/2009) ([ianco](https://github.com/ianco)) - - BREAKING: Allow multi-use public invites and public invites with metadata [\#2034](https://github.com/hyperledger/aries-cloudagent-python/pull/2034) ([mepeltier](https://github.com/mepeltier)) + - **BREAKING:** Allow multi-use public invites and public invites with metadata [\#2034](https://github.com/hyperledger/aries-cloudagent-python/pull/2034) ([mepeltier](https://github.com/mepeltier)) - Do not reject OOB invitation with unknown handshake protocol\(s\) [\#2060](https://github.com/hyperledger/aries-cloudagent-python/pull/2060) ([andrewwhitehead](https://github.com/andrewwhitehead)) - fix: fix connection timing bug [\#2099](https://github.com/hyperledger/aries-cloudagent-python/pull/2099) ([reflectivedevelopment](https://github.com/reflectivedevelopment)) - Previously flagged in release 1.0.0-rc1 @@ -112,6 +134,8 @@ is unlikely to impact any deployments. - Feat/public did endpoints for agents behind mediators [\#1899](https://github.com/hyperledger/aries-cloudagent-python/pull/1899) ([cjhowland](https://github.com/cjhowland)) - DID Registration and Resolution related updates + - feat: allow marking non-SOV DIDs as public [\#2144](https://github.com/hyperledger/aries-cloudagent-python/pull/2144) [chumbert](https://github.com/chumbert) + - fix: askar exception message always displaying null DID [\#2155](https://github.com/hyperledger/aries-cloudagent-python/pull/2155) [chumbert](https://github.com/chumbert) - feat: enable creation of DIDs for all registered methods [\#2067](https://github.com/hyperledger/aries-cloudagent-python/pull/2067) ([chumbert](https://github.com/chumbert)) - fix: create local DID return schema [\#2086](https://github.com/hyperledger/aries-cloudagent-python/pull/2086) ([chumbert](https://github.com/chumbert)) - feat: universal resolver - configurable authentication [\#2095](https://github.com/hyperledger/aries-cloudagent-python/pull/2095) ([chumbert](https://github.com/chumbert)) @@ -125,6 +149,7 @@ is unlikely to impact any deployments. - Use did:key for recipient keys [\#1886](https://github.com/hyperledger/aries-cloudagent-python/pull/1886) ([frostyfrog](https://github.com/frostyfrog)) - Hyperledger Indy Endorser/Author Transaction Handling + - Update some of the demo Readme and Endorser instructions [\#2122](https://github.com/hyperledger/aries-cloudagent-python/pull/2122) [swcurran](https://github.com/swcurran) - Special handling for the write ledger [\#2030](https://github.com/hyperledger/aries-cloudagent-python/pull/2030) ([ianco](https://github.com/ianco)) - Previously flagged in release 1.0.0-rc1 - Fix/txn job setting [\#1994](https://github.com/hyperledger/aries-cloudagent-python/pull/1994) ([ianco](https://github.com/ianco)) @@ -132,7 +157,14 @@ is unlikely to impact any deployments. - Endorser write DID transaction [\#1938](https://github.com/hyperledger/aries-cloudagent-python/pull/1938) ([ianco](https://github.com/ianco)) - Endorser doc updates and some bug fixes [\#1926](https://github.com/hyperledger/aries-cloudagent-python/pull/1926) ([ianco](https://github.com/ianco)) +- Admin API Additions + - fix: response type on delete-tails-files endpoint [\#2133](https://github.com/hyperledger/aries-cloudagent-python/pull/2133) [chumbert](https://github.com/chumbert) + - OpenAPI validation fixes [\#2127](https://github.com/hyperledger/aries-cloudagent-python/pull/2127) [loneil](https://github.com/loneil) + - Delete tail files [\#2103](https://github.com/hyperledger/aries-cloudagent-python/pull/2103) [ramreddychalla94](https://github.com/ramreddychalla94) + - Startup Command Line / Environment / YAML Parameter Updates + - Update webhook message to terse form [default, added startup flag --debug-webhooks for full form [\#2145](https://github.com/hyperledger/aries-cloudagent-python/pull/2145) by [victorlee0505](victorlee0505) + - Add startup flag --light-weight-webhook to trim down outbound webhook payload [\#1941](https://github.com/hyperledger/aries-cloudagent-python/pull/1941) [victorlee0505](https://github.com/victorlee0505) - Add missing --mediator-connections-invite cmd arg info to docs [\#2051](https://github.com/hyperledger/aries-cloudagent-python/pull/2051) ([matrixik](https://github.com/matrixik)) - Issue \#2068 boolean flag change to support HEAD requests to default route [\#2077](https://github.com/hyperledger/aries-cloudagent-python/pull/2077) ([johnekent](https://github.com/johnekent)) - Previously flagged in release 1.0.0-rc1 @@ -168,6 +200,11 @@ is unlikely to impact any deployments. - Update pip-audit.yml [\#1944](https://github.com/hyperledger/aries-cloudagent-python/pull/1944) ([ryjones](https://github.com/ryjones)) - Dependency, Python version, GitHub Actions and Container Image Changes + - Remove CircleCI Status since we aren't using CircleCI anymore [\#2163](https://github.com/hyperledger/aries-cloudagent-python/pull/2163) [swcurran](https://github.com/swcurran) + - Update ACA-Py docker files to produce OpenShift compatible images [\#2130](https://github.com/hyperledger/aries-cloudagent-python/pull/2130) [WadeBarnes](https://github.com/WadeBarnes) + - Temporarily disable multi-architecture image builds [\#2125](https://github.com/hyperledger/aries-cloudagent-python/pull/2125) [WadeBarnes](https://github.com/WadeBarnes) + - Fix ACA-py image builds [\#2123](https://github.com/hyperledger/aries-cloudagent-python/pull/2123) [WadeBarnes](https://github.com/WadeBarnes) + - Fix publish workflows [\#2117](https://github.com/hyperledger/aries-cloudagent-python/pull/2117) [WadeBarnes](https://github.com/WadeBarnes) - fix: indy dependency version format [\#2054](https://github.com/hyperledger/aries-cloudagent-python/pull/2054) ([chumbert](https://github.com/chumbert)) - ci: add gha for pr-tests [\#2058](https://github.com/hyperledger/aries-cloudagent-python/pull/2058) ([dbluhm](https://github.com/dbluhm)) - ci: test additional versions of python nightly [\#2059](https://github.com/hyperledger/aries-cloudagent-python/pull/2059) ([dbluhm](https://github.com/dbluhm)) @@ -181,6 +218,10 @@ is unlikely to impact any deployments. - chore: update pydid [\#1915](https://github.com/hyperledger/aries-cloudagent-python/pull/1915) ([dbluhm](https://github.com/dbluhm)) - Demo and Documentation Updates + - [fix] Removes extra comma that prevents swagger from accepting the presentation request [\#2149](https://github.com/hyperledger/aries-cloudagent-python/pull/2149) [swcurran](https://github.com/swcurran) + - Initial plugin docs [\#2138](https://github.com/hyperledger/aries-cloudagent-python/pull/2138) [ianco](https://github.com/ianco) + - Acme workshop [\#2137](https://github.com/hyperledger/aries-cloudagent-python/pull/2137) [ianco](https://github.com/ianco) + - Fix: Performance Demo [no --revocation] [\#2151](https://github.com/ hyperledger/aries-cloudagent-python/pull/2151) [shaangill025](https://github.com/shaangill025) - Fix typos in alice-local.sh & faber-local.sh [\#2010](https://github.com/hyperledger/aries-cloudagent-python/pull/2010) ([naonishijima](https://github.com/naonishijima)) - Added a bit about manually creating a revoc reg tails file [\#2012](https://github.com/hyperledger/aries-cloudagent-python/pull/2012) ([ianco](https://github.com/ianco)) - Add ability to set docker container name [\#2024](https://github.com/hyperledger/aries-cloudagent-python/pull/2024) ([matrixik](https://github.com/matrixik)) diff --git a/PUBLISHING.md b/PUBLISHING.md index 4e561692dc..13f7921055 100644 --- a/PUBLISHING.md +++ b/PUBLISHING.md @@ -53,17 +53,18 @@ s/^/- / release (using in the GitHub UI a filter such as `is:pr is:merged sort:updated merged:>2022-04-07`) and for each page, highlight, and copy the text of only the list of PRs on the page to use in the following step. -- For each page, run the command `sed -e :a -e '$!N;s/\n#/ #/;ta' -e 'P;D' < TODO: Automate this when new tags are applied to the repository. \ No newline at end of file +> TODO: Automate this when new tags are applied to the repository. diff --git a/docs/conf.py b/docs/conf.py index 6ed81e5982..db981d6321 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -47,6 +47,9 @@ "qrcode", "rlp", "nest_asyncio", + "marshmallow", + "typing_extensions", + "async_timeout" ] # "aries_cloudagent.tests.test_conductor", diff --git a/docs/generated/aries_cloudagent.admin.rst b/docs/generated/aries_cloudagent.admin.rst index efcb59a1cb..250d1b3da5 100644 --- a/docs/generated/aries_cloudagent.admin.rst +++ b/docs/generated/aries_cloudagent.admin.rst @@ -2,9 +2,9 @@ aries\_cloudagent.admin package =============================== .. automodule:: aries_cloudagent.admin - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,30 +13,32 @@ aries\_cloudagent.admin.base\_server module ------------------------------------------- .. automodule:: aries_cloudagent.admin.base_server - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.admin.error module ------------------------------------ .. automodule:: aries_cloudagent.admin.error - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.admin.request\_context module ----------------------------------------------- .. automodule:: aries_cloudagent.admin.request_context - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.admin.server module ------------------------------------- .. automodule:: aries_cloudagent.admin.server - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.askar.didcomm.rst b/docs/generated/aries_cloudagent.askar.didcomm.rst index 6e599dcc31..10b52b2b30 100644 --- a/docs/generated/aries_cloudagent.askar.didcomm.rst +++ b/docs/generated/aries_cloudagent.askar.didcomm.rst @@ -2,9 +2,9 @@ aries\_cloudagent.askar.didcomm package ======================================= .. automodule:: aries_cloudagent.askar.didcomm - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,14 +13,16 @@ aries\_cloudagent.askar.didcomm.v1 module ----------------------------------------- .. automodule:: aries_cloudagent.askar.didcomm.v1 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.askar.didcomm.v2 module ----------------------------------------- .. automodule:: aries_cloudagent.askar.didcomm.v2 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.askar.rst b/docs/generated/aries_cloudagent.askar.rst index 2787ae97f8..e9f39149ec 100644 --- a/docs/generated/aries_cloudagent.askar.rst +++ b/docs/generated/aries_cloudagent.askar.rst @@ -2,17 +2,16 @@ aries\_cloudagent.askar package =============================== .. automodule:: aries_cloudagent.askar - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.askar.didcomm + aries_cloudagent.askar.didcomm Submodules ---------- @@ -21,14 +20,16 @@ aries\_cloudagent.askar.profile module -------------------------------------- .. automodule:: aries_cloudagent.askar.profile - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.askar.store module ------------------------------------ .. automodule:: aries_cloudagent.askar.store - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.cache.rst b/docs/generated/aries_cloudagent.cache.rst index 8fa69fb650..c77763aa50 100644 --- a/docs/generated/aries_cloudagent.cache.rst +++ b/docs/generated/aries_cloudagent.cache.rst @@ -2,9 +2,9 @@ aries\_cloudagent.cache package =============================== .. automodule:: aries_cloudagent.cache - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,14 +13,16 @@ aries\_cloudagent.cache.base module ----------------------------------- .. automodule:: aries_cloudagent.cache.base - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.cache.in\_memory module ----------------------------------------- .. automodule:: aries_cloudagent.cache.in_memory - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.commands.rst b/docs/generated/aries_cloudagent.commands.rst index f4353c5984..e04e946ca8 100644 --- a/docs/generated/aries_cloudagent.commands.rst +++ b/docs/generated/aries_cloudagent.commands.rst @@ -2,9 +2,9 @@ aries\_cloudagent.commands package ================================== .. automodule:: aries_cloudagent.commands - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,30 +13,32 @@ aries\_cloudagent.commands.help module -------------------------------------- .. automodule:: aries_cloudagent.commands.help - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.commands.provision module ------------------------------------------- .. automodule:: aries_cloudagent.commands.provision - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.commands.start module --------------------------------------- .. automodule:: aries_cloudagent.commands.start - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.commands.upgrade module ----------------------------------------- .. automodule:: aries_cloudagent.commands.upgrade - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.config.rst b/docs/generated/aries_cloudagent.config.rst index 43b1ef34a5..56ba88a314 100644 --- a/docs/generated/aries_cloudagent.config.rst +++ b/docs/generated/aries_cloudagent.config.rst @@ -2,9 +2,9 @@ aries\_cloudagent.config package ================================ .. automodule:: aries_cloudagent.config - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,118 +13,120 @@ aries\_cloudagent.config.argparse module ---------------------------------------- .. automodule:: aries_cloudagent.config.argparse - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.config.banner module -------------------------------------- .. automodule:: aries_cloudagent.config.banner - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.config.base module ------------------------------------ .. automodule:: aries_cloudagent.config.base - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.config.base\_context module --------------------------------------------- .. automodule:: aries_cloudagent.config.base_context - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.config.default\_context module ------------------------------------------------ .. automodule:: aries_cloudagent.config.default_context - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.config.error module ------------------------------------- .. automodule:: aries_cloudagent.config.error - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.config.injection\_context module -------------------------------------------------- .. automodule:: aries_cloudagent.config.injection_context - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.config.injector module ---------------------------------------- .. automodule:: aries_cloudagent.config.injector - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.config.ledger module -------------------------------------- .. automodule:: aries_cloudagent.config.ledger - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.config.logging module --------------------------------------- .. automodule:: aries_cloudagent.config.logging - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.config.plugin\_settings module ------------------------------------------------ .. automodule:: aries_cloudagent.config.plugin_settings - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.config.provider module ---------------------------------------- .. automodule:: aries_cloudagent.config.provider - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.config.settings module ---------------------------------------- .. automodule:: aries_cloudagent.config.settings - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.config.util module ------------------------------------ .. automodule:: aries_cloudagent.config.util - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.config.wallet module -------------------------------------- .. automodule:: aries_cloudagent.config.wallet - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.connections.models.diddoc.rst b/docs/generated/aries_cloudagent.connections.models.diddoc.rst index 01f1124a60..bed72812ab 100644 --- a/docs/generated/aries_cloudagent.connections.models.diddoc.rst +++ b/docs/generated/aries_cloudagent.connections.models.diddoc.rst @@ -2,9 +2,9 @@ aries\_cloudagent.connections.models.diddoc package =================================================== .. automodule:: aries_cloudagent.connections.models.diddoc - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,30 +13,32 @@ aries\_cloudagent.connections.models.diddoc.diddoc module --------------------------------------------------------- .. automodule:: aries_cloudagent.connections.models.diddoc.diddoc - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.connections.models.diddoc.publickey module ------------------------------------------------------------ .. automodule:: aries_cloudagent.connections.models.diddoc.publickey - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.connections.models.diddoc.service module ---------------------------------------------------------- .. automodule:: aries_cloudagent.connections.models.diddoc.service - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.connections.models.diddoc.util module ------------------------------------------------------- .. automodule:: aries_cloudagent.connections.models.diddoc.util - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.connections.models.rst b/docs/generated/aries_cloudagent.connections.models.rst index 4ee733ae04..0d1ad79dc7 100644 --- a/docs/generated/aries_cloudagent.connections.models.rst +++ b/docs/generated/aries_cloudagent.connections.models.rst @@ -2,17 +2,16 @@ aries\_cloudagent.connections.models package ============================================ .. automodule:: aries_cloudagent.connections.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.connections.models.diddoc + aries_cloudagent.connections.models.diddoc Submodules ---------- @@ -21,14 +20,16 @@ aries\_cloudagent.connections.models.conn\_record module -------------------------------------------------------- .. automodule:: aries_cloudagent.connections.models.conn_record - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.connections.models.connection\_target module -------------------------------------------------------------- .. automodule:: aries_cloudagent.connections.models.connection_target - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.connections.rst b/docs/generated/aries_cloudagent.connections.rst index 90d1f68626..9630382119 100644 --- a/docs/generated/aries_cloudagent.connections.rst +++ b/docs/generated/aries_cloudagent.connections.rst @@ -2,17 +2,16 @@ aries\_cloudagent.connections package ===================================== .. automodule:: aries_cloudagent.connections - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.connections.models + aries_cloudagent.connections.models Submodules ---------- @@ -21,6 +20,8 @@ aries\_cloudagent.connections.base\_manager module -------------------------------------------------- .. automodule:: aries_cloudagent.connections.base_manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.core.in_memory.didcomm.rst b/docs/generated/aries_cloudagent.core.in_memory.didcomm.rst index 1bb9828e02..17b9a3a68a 100644 --- a/docs/generated/aries_cloudagent.core.in_memory.didcomm.rst +++ b/docs/generated/aries_cloudagent.core.in_memory.didcomm.rst @@ -2,9 +2,9 @@ aries\_cloudagent.core.in\_memory.didcomm package ================================================= .. automodule:: aries_cloudagent.core.in_memory.didcomm - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,14 +13,16 @@ aries\_cloudagent.core.in\_memory.didcomm.derive\_1pu module ------------------------------------------------------------ .. automodule:: aries_cloudagent.core.in_memory.didcomm.derive_1pu - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.core.in\_memory.didcomm.derive\_ecdh module ------------------------------------------------------------- .. automodule:: aries_cloudagent.core.in_memory.didcomm.derive_ecdh - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.core.in_memory.rst b/docs/generated/aries_cloudagent.core.in_memory.rst index 20dfaa765f..e7aba5fb2a 100644 --- a/docs/generated/aries_cloudagent.core.in_memory.rst +++ b/docs/generated/aries_cloudagent.core.in_memory.rst @@ -2,17 +2,16 @@ aries\_cloudagent.core.in\_memory package ========================================= .. automodule:: aries_cloudagent.core.in_memory - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.core.in_memory.didcomm + aries_cloudagent.core.in_memory.didcomm Submodules ---------- @@ -21,6 +20,8 @@ aries\_cloudagent.core.in\_memory.profile module ------------------------------------------------ .. automodule:: aries_cloudagent.core.in_memory.profile - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.core.rst b/docs/generated/aries_cloudagent.core.rst index f304ae0aa0..c0ffc0d393 100644 --- a/docs/generated/aries_cloudagent.core.rst +++ b/docs/generated/aries_cloudagent.core.rst @@ -2,17 +2,16 @@ aries\_cloudagent.core package ============================== .. automodule:: aries_cloudagent.core - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.core.in_memory + aries_cloudagent.core.in_memory Submodules ---------- @@ -21,78 +20,80 @@ aries\_cloudagent.core.conductor module --------------------------------------- .. automodule:: aries_cloudagent.core.conductor - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.core.dispatcher module ---------------------------------------- .. automodule:: aries_cloudagent.core.dispatcher - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.core.error module ----------------------------------- .. automodule:: aries_cloudagent.core.error - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.core.event\_bus module ---------------------------------------- .. automodule:: aries_cloudagent.core.event_bus - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.core.goal\_code\_registry module -------------------------------------------------- .. automodule:: aries_cloudagent.core.goal_code_registry - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.core.oob\_processor module -------------------------------------------- .. automodule:: aries_cloudagent.core.oob_processor - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.core.plugin\_registry module ---------------------------------------------- .. automodule:: aries_cloudagent.core.plugin_registry - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.core.profile module ------------------------------------- .. automodule:: aries_cloudagent.core.profile - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.core.protocol\_registry module ------------------------------------------------ .. automodule:: aries_cloudagent.core.protocol_registry - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.core.util module ---------------------------------- .. automodule:: aries_cloudagent.core.util - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.did.rst b/docs/generated/aries_cloudagent.did.rst index 4eb9394a21..696571f08d 100644 --- a/docs/generated/aries_cloudagent.did.rst +++ b/docs/generated/aries_cloudagent.did.rst @@ -2,9 +2,9 @@ aries\_cloudagent.did package ============================= .. automodule:: aries_cloudagent.did - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.did.did\_key module ------------------------------------- .. automodule:: aries_cloudagent.did.did_key - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.holder.rst b/docs/generated/aries_cloudagent.holder.rst index 47287253ae..a3f35fbad8 100644 --- a/docs/generated/aries_cloudagent.holder.rst +++ b/docs/generated/aries_cloudagent.holder.rst @@ -2,9 +2,9 @@ aries\_cloudagent.holder package ================================ .. automodule:: aries_cloudagent.holder - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.holder.routes module -------------------------------------- .. automodule:: aries_cloudagent.holder.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.indy.credx.rst b/docs/generated/aries_cloudagent.indy.credx.rst index b80d4017f7..bbb6a380b3 100644 --- a/docs/generated/aries_cloudagent.indy.credx.rst +++ b/docs/generated/aries_cloudagent.indy.credx.rst @@ -2,9 +2,9 @@ aries\_cloudagent.indy.credx package ==================================== .. automodule:: aries_cloudagent.indy.credx - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,22 +13,24 @@ aries\_cloudagent.indy.credx.holder module ------------------------------------------ .. automodule:: aries_cloudagent.indy.credx.holder - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.credx.issuer module ------------------------------------------ .. automodule:: aries_cloudagent.indy.credx.issuer - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.credx.verifier module -------------------------------------------- .. automodule:: aries_cloudagent.indy.credx.verifier - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.indy.models.rst b/docs/generated/aries_cloudagent.indy.models.rst index ae04af6ddb..487ed7b5f9 100644 --- a/docs/generated/aries_cloudagent.indy.models.rst +++ b/docs/generated/aries_cloudagent.indy.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.indy.models package ===================================== .. automodule:: aries_cloudagent.indy.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,110 +13,112 @@ aries\_cloudagent.indy.models.cred module ----------------------------------------- .. automodule:: aries_cloudagent.indy.models.cred - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.models.cred\_abstract module --------------------------------------------------- .. automodule:: aries_cloudagent.indy.models.cred_abstract - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.models.cred\_def module ---------------------------------------------- .. automodule:: aries_cloudagent.indy.models.cred_def - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.models.cred\_precis module ------------------------------------------------- .. automodule:: aries_cloudagent.indy.models.cred_precis - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.models.cred\_request module -------------------------------------------------- .. automodule:: aries_cloudagent.indy.models.cred_request - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.models.non\_rev\_interval module ------------------------------------------------------- .. automodule:: aries_cloudagent.indy.models.non_rev_interval - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.models.predicate module ---------------------------------------------- .. automodule:: aries_cloudagent.indy.models.predicate - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.models.pres\_preview module -------------------------------------------------- .. automodule:: aries_cloudagent.indy.models.pres_preview - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.models.proof module ------------------------------------------ .. automodule:: aries_cloudagent.indy.models.proof - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.models.proof\_request module --------------------------------------------------- .. automodule:: aries_cloudagent.indy.models.proof_request - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.models.requested\_creds module ----------------------------------------------------- .. automodule:: aries_cloudagent.indy.models.requested_creds - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.models.revocation module ----------------------------------------------- .. automodule:: aries_cloudagent.indy.models.revocation - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.models.schema module ------------------------------------------- .. automodule:: aries_cloudagent.indy.models.schema - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.models.xform module ------------------------------------------ .. automodule:: aries_cloudagent.indy.models.xform - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.indy.rst b/docs/generated/aries_cloudagent.indy.rst index 0e5c50f637..dff7b78f07 100644 --- a/docs/generated/aries_cloudagent.indy.rst +++ b/docs/generated/aries_cloudagent.indy.rst @@ -2,19 +2,18 @@ aries\_cloudagent.indy package ============================== .. automodule:: aries_cloudagent.indy - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.indy.credx - aries_cloudagent.indy.models - aries_cloudagent.indy.sdk + aries_cloudagent.indy.credx + aries_cloudagent.indy.models + aries_cloudagent.indy.sdk Submodules ---------- @@ -23,30 +22,32 @@ aries\_cloudagent.indy.holder module ------------------------------------ .. automodule:: aries_cloudagent.indy.holder - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.issuer module ------------------------------------ .. automodule:: aries_cloudagent.indy.issuer - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.util module ---------------------------------- .. automodule:: aries_cloudagent.indy.util - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.verifier module -------------------------------------- .. automodule:: aries_cloudagent.indy.verifier - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.indy.sdk.rst b/docs/generated/aries_cloudagent.indy.sdk.rst index be5adbf5ae..8de7333f14 100644 --- a/docs/generated/aries_cloudagent.indy.sdk.rst +++ b/docs/generated/aries_cloudagent.indy.sdk.rst @@ -2,9 +2,9 @@ aries\_cloudagent.indy.sdk package ================================== .. automodule:: aries_cloudagent.indy.sdk - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,62 +13,64 @@ aries\_cloudagent.indy.sdk.error module --------------------------------------- .. automodule:: aries_cloudagent.indy.sdk.error - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.sdk.holder module ---------------------------------------- .. automodule:: aries_cloudagent.indy.sdk.holder - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.sdk.issuer module ---------------------------------------- .. automodule:: aries_cloudagent.indy.sdk.issuer - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.sdk.profile module ----------------------------------------- .. automodule:: aries_cloudagent.indy.sdk.profile - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.sdk.util module -------------------------------------- .. automodule:: aries_cloudagent.indy.sdk.util - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.sdk.verifier module ------------------------------------------ .. automodule:: aries_cloudagent.indy.sdk.verifier - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.sdk.wallet\_plugin module ------------------------------------------------ .. automodule:: aries_cloudagent.indy.sdk.wallet_plugin - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.sdk.wallet\_setup module ----------------------------------------------- .. automodule:: aries_cloudagent.indy.sdk.wallet_setup - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.ledger.merkel_validation.rst b/docs/generated/aries_cloudagent.ledger.merkel_validation.rst index 2aba21f5ce..9dbc246bfa 100644 --- a/docs/generated/aries_cloudagent.ledger.merkel_validation.rst +++ b/docs/generated/aries_cloudagent.ledger.merkel_validation.rst @@ -2,9 +2,9 @@ aries\_cloudagent.ledger.merkel\_validation package =================================================== .. automodule:: aries_cloudagent.ledger.merkel_validation - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,46 +13,48 @@ aries\_cloudagent.ledger.merkel\_validation.constants module ------------------------------------------------------------ .. automodule:: aries_cloudagent.ledger.merkel_validation.constants - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.merkel\_validation.domain\_txn\_handler module ----------------------------------------------------------------------- .. automodule:: aries_cloudagent.ledger.merkel_validation.domain_txn_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.merkel\_validation.hasher module --------------------------------------------------------- .. automodule:: aries_cloudagent.ledger.merkel_validation.hasher - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.merkel\_validation.merkel\_verifier module ------------------------------------------------------------------- .. automodule:: aries_cloudagent.ledger.merkel_validation.merkel_verifier - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.merkel\_validation.trie module ------------------------------------------------------- .. automodule:: aries_cloudagent.ledger.merkel_validation.trie - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.merkel\_validation.utils module -------------------------------------------------------- .. automodule:: aries_cloudagent.ledger.merkel_validation.utils - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.ledger.multiple_ledger.rst b/docs/generated/aries_cloudagent.ledger.multiple_ledger.rst index ed257bc7cf..37dd4d2e10 100644 --- a/docs/generated/aries_cloudagent.ledger.multiple_ledger.rst +++ b/docs/generated/aries_cloudagent.ledger.multiple_ledger.rst @@ -2,9 +2,9 @@ aries\_cloudagent.ledger.multiple\_ledger package ================================================= .. automodule:: aries_cloudagent.ledger.multiple_ledger - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,46 +13,48 @@ aries\_cloudagent.ledger.multiple\_ledger.base\_manager module -------------------------------------------------------------- .. automodule:: aries_cloudagent.ledger.multiple_ledger.base_manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.multiple\_ledger.indy\_manager module -------------------------------------------------------------- .. automodule:: aries_cloudagent.ledger.multiple_ledger.indy_manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.multiple\_ledger.indy\_vdr\_manager module ------------------------------------------------------------------- .. automodule:: aries_cloudagent.ledger.multiple_ledger.indy_vdr_manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.multiple\_ledger.ledger\_config\_schema module ----------------------------------------------------------------------- .. automodule:: aries_cloudagent.ledger.multiple_ledger.ledger_config_schema - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.multiple\_ledger.ledger\_requests\_executor module --------------------------------------------------------------------------- .. automodule:: aries_cloudagent.ledger.multiple_ledger.ledger_requests_executor - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.multiple\_ledger.manager\_provider module ------------------------------------------------------------------ .. automodule:: aries_cloudagent.ledger.multiple_ledger.manager_provider - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.ledger.rst b/docs/generated/aries_cloudagent.ledger.rst index aff0574fef..53ee6a0bc5 100644 --- a/docs/generated/aries_cloudagent.ledger.rst +++ b/docs/generated/aries_cloudagent.ledger.rst @@ -2,18 +2,17 @@ aries\_cloudagent.ledger package ================================ .. automodule:: aries_cloudagent.ledger - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.ledger.merkel_validation - aries_cloudagent.ledger.multiple_ledger + aries_cloudagent.ledger.merkel_validation + aries_cloudagent.ledger.multiple_ledger Submodules ---------- @@ -22,54 +21,56 @@ aries\_cloudagent.ledger.base module ------------------------------------ .. automodule:: aries_cloudagent.ledger.base - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.endpoint\_type module ---------------------------------------------- .. automodule:: aries_cloudagent.ledger.endpoint_type - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.error module ------------------------------------- .. automodule:: aries_cloudagent.ledger.error - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.indy module ------------------------------------ .. automodule:: aries_cloudagent.ledger.indy - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.indy\_vdr module ----------------------------------------- .. automodule:: aries_cloudagent.ledger.indy_vdr - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.routes module -------------------------------------- .. automodule:: aries_cloudagent.ledger.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.util module ------------------------------------ .. automodule:: aries_cloudagent.ledger.util - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.messaging.credential_definitions.rst b/docs/generated/aries_cloudagent.messaging.credential_definitions.rst index 0de38530c2..d0fdb8879b 100644 --- a/docs/generated/aries_cloudagent.messaging.credential_definitions.rst +++ b/docs/generated/aries_cloudagent.messaging.credential_definitions.rst @@ -2,9 +2,9 @@ aries\_cloudagent.messaging.credential\_definitions package =========================================================== .. automodule:: aries_cloudagent.messaging.credential_definitions - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,14 +13,16 @@ aries\_cloudagent.messaging.credential\_definitions.routes module ----------------------------------------------------------------- .. automodule:: aries_cloudagent.messaging.credential_definitions.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.credential\_definitions.util module --------------------------------------------------------------- .. automodule:: aries_cloudagent.messaging.credential_definitions.util - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.messaging.decorators.rst b/docs/generated/aries_cloudagent.messaging.decorators.rst index 64fb269fb1..1deab83a09 100644 --- a/docs/generated/aries_cloudagent.messaging.decorators.rst +++ b/docs/generated/aries_cloudagent.messaging.decorators.rst @@ -2,9 +2,9 @@ aries\_cloudagent.messaging.decorators package ============================================== .. automodule:: aries_cloudagent.messaging.decorators - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,86 +13,88 @@ aries\_cloudagent.messaging.decorators.attach\_decorator module --------------------------------------------------------------- .. automodule:: aries_cloudagent.messaging.decorators.attach_decorator - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.decorators.base module -------------------------------------------------- .. automodule:: aries_cloudagent.messaging.decorators.base - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.decorators.default module ----------------------------------------------------- .. automodule:: aries_cloudagent.messaging.decorators.default - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.decorators.localization\_decorator module --------------------------------------------------------------------- .. automodule:: aries_cloudagent.messaging.decorators.localization_decorator - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.decorators.please\_ack\_decorator module -------------------------------------------------------------------- .. automodule:: aries_cloudagent.messaging.decorators.please_ack_decorator - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.decorators.service\_decorator module ---------------------------------------------------------------- .. automodule:: aries_cloudagent.messaging.decorators.service_decorator - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.decorators.signature\_decorator module ------------------------------------------------------------------ .. automodule:: aries_cloudagent.messaging.decorators.signature_decorator - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.decorators.thread\_decorator module --------------------------------------------------------------- .. automodule:: aries_cloudagent.messaging.decorators.thread_decorator - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.decorators.timing\_decorator module --------------------------------------------------------------- .. automodule:: aries_cloudagent.messaging.decorators.timing_decorator - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.decorators.trace\_decorator module -------------------------------------------------------------- .. automodule:: aries_cloudagent.messaging.decorators.trace_decorator - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.decorators.transport\_decorator module ------------------------------------------------------------------ .. automodule:: aries_cloudagent.messaging.decorators.transport_decorator - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.messaging.jsonld.rst b/docs/generated/aries_cloudagent.messaging.jsonld.rst index 02ce783dd1..af0f5bf8e5 100644 --- a/docs/generated/aries_cloudagent.messaging.jsonld.rst +++ b/docs/generated/aries_cloudagent.messaging.jsonld.rst @@ -2,9 +2,9 @@ aries\_cloudagent.messaging.jsonld package ========================================== .. automodule:: aries_cloudagent.messaging.jsonld - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,30 +13,32 @@ aries\_cloudagent.messaging.jsonld.create\_verify\_data module -------------------------------------------------------------- .. automodule:: aries_cloudagent.messaging.jsonld.create_verify_data - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.jsonld.credential module ---------------------------------------------------- .. automodule:: aries_cloudagent.messaging.jsonld.credential - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.jsonld.error module ----------------------------------------------- .. automodule:: aries_cloudagent.messaging.jsonld.error - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.jsonld.routes module ------------------------------------------------ .. automodule:: aries_cloudagent.messaging.jsonld.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.messaging.models.rst b/docs/generated/aries_cloudagent.messaging.models.rst index cc951ac8e0..ecf4281974 100644 --- a/docs/generated/aries_cloudagent.messaging.models.rst +++ b/docs/generated/aries_cloudagent.messaging.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.messaging.models package ========================================== .. automodule:: aries_cloudagent.messaging.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,22 +13,24 @@ aries\_cloudagent.messaging.models.base module ---------------------------------------------- .. automodule:: aries_cloudagent.messaging.models.base - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.models.base\_record module ------------------------------------------------------ .. automodule:: aries_cloudagent.messaging.models.base_record - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.models.openapi module ------------------------------------------------- .. automodule:: aries_cloudagent.messaging.models.openapi - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.messaging.rst b/docs/generated/aries_cloudagent.messaging.rst index 21bd62a4f3..29b5a3c101 100644 --- a/docs/generated/aries_cloudagent.messaging.rst +++ b/docs/generated/aries_cloudagent.messaging.rst @@ -2,21 +2,20 @@ aries\_cloudagent.messaging package =================================== .. automodule:: aries_cloudagent.messaging - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.messaging.credential_definitions - aries_cloudagent.messaging.decorators - aries_cloudagent.messaging.jsonld - aries_cloudagent.messaging.models - aries_cloudagent.messaging.schemas + aries_cloudagent.messaging.credential_definitions + aries_cloudagent.messaging.decorators + aries_cloudagent.messaging.jsonld + aries_cloudagent.messaging.models + aries_cloudagent.messaging.schemas Submodules ---------- @@ -25,62 +24,64 @@ aries\_cloudagent.messaging.agent\_message module ------------------------------------------------- .. automodule:: aries_cloudagent.messaging.agent_message - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.base\_handler module ------------------------------------------------ .. automodule:: aries_cloudagent.messaging.base_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.base\_message module ------------------------------------------------ .. automodule:: aries_cloudagent.messaging.base_message - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.error module ---------------------------------------- .. automodule:: aries_cloudagent.messaging.error - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.request\_context module --------------------------------------------------- .. automodule:: aries_cloudagent.messaging.request_context - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.responder module -------------------------------------------- .. automodule:: aries_cloudagent.messaging.responder - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.util module --------------------------------------- .. automodule:: aries_cloudagent.messaging.util - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.valid module ---------------------------------------- .. automodule:: aries_cloudagent.messaging.valid - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.messaging.schemas.rst b/docs/generated/aries_cloudagent.messaging.schemas.rst index 83deffa2a4..06f847b39a 100644 --- a/docs/generated/aries_cloudagent.messaging.schemas.rst +++ b/docs/generated/aries_cloudagent.messaging.schemas.rst @@ -2,9 +2,9 @@ aries\_cloudagent.messaging.schemas package =========================================== .. automodule:: aries_cloudagent.messaging.schemas - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,14 +13,16 @@ aries\_cloudagent.messaging.schemas.routes module ------------------------------------------------- .. automodule:: aries_cloudagent.messaging.schemas.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.schemas.util module ----------------------------------------------- .. automodule:: aries_cloudagent.messaging.schemas.util - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.multitenant.admin.rst b/docs/generated/aries_cloudagent.multitenant.admin.rst index 6a1a695efe..e1f44a2a9e 100644 --- a/docs/generated/aries_cloudagent.multitenant.admin.rst +++ b/docs/generated/aries_cloudagent.multitenant.admin.rst @@ -2,9 +2,9 @@ aries\_cloudagent.multitenant.admin package =========================================== .. automodule:: aries_cloudagent.multitenant.admin - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.multitenant.admin.routes module ------------------------------------------------- .. automodule:: aries_cloudagent.multitenant.admin.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.multitenant.rst b/docs/generated/aries_cloudagent.multitenant.rst index 913a17a652..44d798d511 100644 --- a/docs/generated/aries_cloudagent.multitenant.rst +++ b/docs/generated/aries_cloudagent.multitenant.rst @@ -2,17 +2,16 @@ aries\_cloudagent.multitenant package ===================================== .. automodule:: aries_cloudagent.multitenant - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.multitenant.admin + aries_cloudagent.multitenant.admin Submodules ---------- @@ -21,54 +20,56 @@ aries\_cloudagent.multitenant.askar\_profile\_manager module ------------------------------------------------------------ .. automodule:: aries_cloudagent.multitenant.askar_profile_manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.multitenant.base module ----------------------------------------- .. automodule:: aries_cloudagent.multitenant.base - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.multitenant.cache module ------------------------------------------ .. automodule:: aries_cloudagent.multitenant.cache - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.multitenant.error module ------------------------------------------ .. automodule:: aries_cloudagent.multitenant.error - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.multitenant.manager module -------------------------------------------- .. automodule:: aries_cloudagent.multitenant.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.multitenant.manager\_provider module ------------------------------------------------------ .. automodule:: aries_cloudagent.multitenant.manager_provider - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.multitenant.route\_manager module --------------------------------------------------- .. automodule:: aries_cloudagent.multitenant.route_manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.actionmenu.rst b/docs/generated/aries_cloudagent.protocols.actionmenu.rst index 3688c5eef5..70d47336ca 100644 --- a/docs/generated/aries_cloudagent.protocols.actionmenu.rst +++ b/docs/generated/aries_cloudagent.protocols.actionmenu.rst @@ -2,17 +2,16 @@ aries\_cloudagent.protocols.actionmenu package ============================================== .. automodule:: aries_cloudagent.protocols.actionmenu - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.actionmenu.v1_0 + aries_cloudagent.protocols.actionmenu.v1_0 Submodules ---------- @@ -21,6 +20,8 @@ aries\_cloudagent.protocols.actionmenu.definition module -------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.actionmenu.definition - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.handlers.rst index c3f66b0796..a230fae21f 100644 --- a/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.actionmenu.v1\_0.handlers package ============================================================= .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,22 +13,24 @@ aries\_cloudagent.protocols.actionmenu.v1\_0.handlers.menu\_handler module -------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.handlers.menu_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.actionmenu.v1\_0.handlers.menu\_request\_handler module ----------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.handlers.menu_request_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.actionmenu.v1\_0.handlers.perform\_handler module ----------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.handlers.perform_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.messages.rst b/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.messages.rst index 98578667f1..e3fe2a1522 100644 --- a/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.actionmenu.v1\_0.messages package ============================================================= .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,22 +13,24 @@ aries\_cloudagent.protocols.actionmenu.v1\_0.messages.menu module ----------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.messages.menu - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.actionmenu.v1\_0.messages.menu\_request module -------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.messages.menu_request - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.actionmenu.v1\_0.messages.perform module -------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.messages.perform - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.models.rst b/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.models.rst index 133a5c5207..2d84e02fac 100644 --- a/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.models.rst +++ b/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.actionmenu.v1\_0.models package =========================================================== .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,22 +13,24 @@ aries\_cloudagent.protocols.actionmenu.v1\_0.models.menu\_form module --------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.models.menu_form - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.actionmenu.v1\_0.models.menu\_form\_param module ---------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.models.menu_form_param - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.actionmenu.v1\_0.models.menu\_option module ----------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.models.menu_option - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.rst b/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.rst index 40a91c0756..a1b1097a49 100644 --- a/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.rst @@ -2,19 +2,18 @@ aries\_cloudagent.protocols.actionmenu.v1\_0 package ==================================================== .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.actionmenu.v1_0.handlers - aries_cloudagent.protocols.actionmenu.v1_0.messages - aries_cloudagent.protocols.actionmenu.v1_0.models + aries_cloudagent.protocols.actionmenu.v1_0.handlers + aries_cloudagent.protocols.actionmenu.v1_0.messages + aries_cloudagent.protocols.actionmenu.v1_0.models Submodules ---------- @@ -23,46 +22,48 @@ aries\_cloudagent.protocols.actionmenu.v1\_0.base\_service module ----------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.base_service - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.actionmenu.v1\_0.controller module -------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.controller - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.actionmenu.v1\_0.driver\_service module ------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.driver_service - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.actionmenu.v1\_0.message\_types module ------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.actionmenu.v1\_0.routes module ---------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.actionmenu.v1\_0.util module -------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.util - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.basicmessage.rst b/docs/generated/aries_cloudagent.protocols.basicmessage.rst index 70ae0a38f0..78bbf25c31 100644 --- a/docs/generated/aries_cloudagent.protocols.basicmessage.rst +++ b/docs/generated/aries_cloudagent.protocols.basicmessage.rst @@ -2,17 +2,16 @@ aries\_cloudagent.protocols.basicmessage package ================================================ .. automodule:: aries_cloudagent.protocols.basicmessage - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.basicmessage.v1_0 + aries_cloudagent.protocols.basicmessage.v1_0 Submodules ---------- @@ -21,6 +20,8 @@ aries\_cloudagent.protocols.basicmessage.definition module ---------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.basicmessage.definition - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.basicmessage.v1_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.basicmessage.v1_0.handlers.rst index 9ba540d3d8..9bc951b81e 100644 --- a/docs/generated/aries_cloudagent.protocols.basicmessage.v1_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.basicmessage.v1_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.basicmessage.v1\_0.handlers package =============================================================== .. automodule:: aries_cloudagent.protocols.basicmessage.v1_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.basicmessage.v1\_0.handlers.basicmessage\_handler mo ------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.basicmessage.v1_0.handlers.basicmessage_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.basicmessage.v1_0.messages.rst b/docs/generated/aries_cloudagent.protocols.basicmessage.v1_0.messages.rst index 069d11564b..edc0a395f5 100644 --- a/docs/generated/aries_cloudagent.protocols.basicmessage.v1_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.basicmessage.v1_0.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.basicmessage.v1\_0.messages package =============================================================== .. automodule:: aries_cloudagent.protocols.basicmessage.v1_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.basicmessage.v1\_0.messages.basicmessage module --------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.basicmessage.v1_0.messages.basicmessage - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.basicmessage.v1_0.rst b/docs/generated/aries_cloudagent.protocols.basicmessage.v1_0.rst index 3899ab9e88..3eae1842a4 100644 --- a/docs/generated/aries_cloudagent.protocols.basicmessage.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.basicmessage.v1_0.rst @@ -2,18 +2,17 @@ aries\_cloudagent.protocols.basicmessage.v1\_0 package ====================================================== .. automodule:: aries_cloudagent.protocols.basicmessage.v1_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.basicmessage.v1_0.handlers - aries_cloudagent.protocols.basicmessage.v1_0.messages + aries_cloudagent.protocols.basicmessage.v1_0.handlers + aries_cloudagent.protocols.basicmessage.v1_0.messages Submodules ---------- @@ -22,14 +21,16 @@ aries\_cloudagent.protocols.basicmessage.v1\_0.message\_types module -------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.basicmessage.v1_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.basicmessage.v1\_0.routes module ------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.basicmessage.v1_0.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.connections.rst b/docs/generated/aries_cloudagent.protocols.connections.rst index 523288cad4..fe31957acb 100644 --- a/docs/generated/aries_cloudagent.protocols.connections.rst +++ b/docs/generated/aries_cloudagent.protocols.connections.rst @@ -2,17 +2,16 @@ aries\_cloudagent.protocols.connections package =============================================== .. automodule:: aries_cloudagent.protocols.connections - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.connections.v1_0 + aries_cloudagent.protocols.connections.v1_0 Submodules ---------- @@ -21,6 +20,8 @@ aries\_cloudagent.protocols.connections.definition module --------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.connections.definition - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.connections.v1_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.connections.v1_0.handlers.rst index 1d74cb8497..3b2e92fe39 100644 --- a/docs/generated/aries_cloudagent.protocols.connections.v1_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.connections.v1_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.connections.v1\_0.handlers package ============================================================== .. automodule:: aries_cloudagent.protocols.connections.v1_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,22 +13,24 @@ aries\_cloudagent.protocols.connections.v1\_0.handlers.connection\_invitation\_h --------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.connections.v1_0.handlers.connection_invitation_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.connections.v1\_0.handlers.connection\_request\_handler module ------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.connections.v1_0.handlers.connection_request_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.connections.v1\_0.handlers.connection\_response\_handler module ------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.connections.v1_0.handlers.connection_response_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.connections.v1_0.messages.rst b/docs/generated/aries_cloudagent.protocols.connections.v1_0.messages.rst index 4714c6e9c9..69ad939486 100644 --- a/docs/generated/aries_cloudagent.protocols.connections.v1_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.connections.v1_0.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.connections.v1\_0.messages package ============================================================== .. automodule:: aries_cloudagent.protocols.connections.v1_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,30 +13,32 @@ aries\_cloudagent.protocols.connections.v1\_0.messages.connection\_invitation mo ------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.connections.v1_0.messages.connection_invitation - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.connections.v1\_0.messages.connection\_request module --------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.connections.v1_0.messages.connection_request - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.connections.v1\_0.messages.connection\_response module ---------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.connections.v1_0.messages.connection_response - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.connections.v1\_0.messages.problem\_report module ----------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.connections.v1_0.messages.problem_report - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.connections.v1_0.models.rst b/docs/generated/aries_cloudagent.protocols.connections.v1_0.models.rst index 33cc83e115..3b45b5c9fe 100644 --- a/docs/generated/aries_cloudagent.protocols.connections.v1_0.models.rst +++ b/docs/generated/aries_cloudagent.protocols.connections.v1_0.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.connections.v1\_0.models package ============================================================ .. automodule:: aries_cloudagent.protocols.connections.v1_0.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.connections.v1\_0.models.connection\_detail module ------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.connections.v1_0.models.connection_detail - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.connections.v1_0.rst b/docs/generated/aries_cloudagent.protocols.connections.v1_0.rst index b328e2772f..6c1f05abf6 100644 --- a/docs/generated/aries_cloudagent.protocols.connections.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.connections.v1_0.rst @@ -2,19 +2,18 @@ aries\_cloudagent.protocols.connections.v1\_0 package ===================================================== .. automodule:: aries_cloudagent.protocols.connections.v1_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.connections.v1_0.handlers - aries_cloudagent.protocols.connections.v1_0.messages - aries_cloudagent.protocols.connections.v1_0.models + aries_cloudagent.protocols.connections.v1_0.handlers + aries_cloudagent.protocols.connections.v1_0.messages + aries_cloudagent.protocols.connections.v1_0.models Submodules ---------- @@ -23,22 +22,24 @@ aries\_cloudagent.protocols.connections.v1\_0.manager module ------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.connections.v1_0.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.connections.v1\_0.message\_types module ------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.connections.v1_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.connections.v1\_0.routes module ----------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.connections.v1_0.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.coordinate_mediation.rst b/docs/generated/aries_cloudagent.protocols.coordinate_mediation.rst index e356c5a0b0..a6fd7ddcae 100644 --- a/docs/generated/aries_cloudagent.protocols.coordinate_mediation.rst +++ b/docs/generated/aries_cloudagent.protocols.coordinate_mediation.rst @@ -2,17 +2,16 @@ aries\_cloudagent.protocols.coordinate\_mediation package ========================================================= .. automodule:: aries_cloudagent.protocols.coordinate_mediation - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.coordinate_mediation.v1_0 + aries_cloudagent.protocols.coordinate_mediation.v1_0 Submodules ---------- @@ -21,14 +20,16 @@ aries\_cloudagent.protocols.coordinate\_mediation.definition module ------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.definition - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.mediation\_invite\_store module --------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.mediation_invite_store - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.handlers.rst index fe7d289bf5..596449dc6c 100644 --- a/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.handlers package ======================================================================== .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,62 +13,64 @@ aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.handlers.keylist\_handle ---------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.handlers.keylist_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.handlers.keylist\_query\_handler module ----------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.handlers.keylist_query_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.handlers.keylist\_update\_handler module ------------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.handlers.keylist_update_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.handlers.keylist\_update\_response\_handler module ---------------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.handlers.keylist_update_response_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.handlers.mediation\_deny\_handler module ------------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.handlers.mediation_deny_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.handlers.mediation\_grant\_handler module ------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.handlers.mediation_grant_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.handlers.mediation\_request\_handler module --------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.handlers.mediation_request_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.handlers.problem\_report\_handler module ------------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.handlers.problem_report_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.inner.rst b/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.inner.rst index 431a1485d5..c8a16d75da 100644 --- a/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.inner.rst +++ b/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.inner.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.messages.inner package ============================================================================== .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.inner - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,30 +13,32 @@ aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.messages.inner.keylist\_ ------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.inner.keylist_key - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.messages.inner.keylist\_query\_paginate module ------------------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.inner.keylist_query_paginate - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.messages.inner.keylist\_update\_rule module --------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.inner.keylist_update_rule - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.messages.inner.keylist\_updated module ---------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.inner.keylist_updated - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.rst b/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.rst index dff68b4d21..44ce3b3b26 100644 --- a/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.rst @@ -2,17 +2,16 @@ aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.messages package ======================================================================== .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.inner + aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.inner Submodules ---------- @@ -21,62 +20,64 @@ aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.messages.keylist module ------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.keylist - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.messages.keylist\_query module -------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.keylist_query - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.messages.keylist\_update module --------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.keylist_update - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.messages.keylist\_update\_response module ------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.keylist_update_response - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.messages.mediate\_deny module ------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.mediate_deny - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.messages.mediate\_grant module -------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.mediate_grant - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.messages.mediate\_request module ---------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.mediate_request - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.messages.problem\_report module --------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.problem_report - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.models.rst b/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.models.rst index ad6bf8060d..defcdf0d48 100644 --- a/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.models.rst +++ b/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.models package ====================================================================== .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.models.mediation\_record --------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.models.mediation_record - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.rst b/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.rst index fb05fd04d8..6dca2451a0 100644 --- a/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.rst @@ -2,19 +2,18 @@ aries\_cloudagent.protocols.coordinate\_mediation.v1\_0 package =============================================================== .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.coordinate_mediation.v1_0.handlers - aries_cloudagent.protocols.coordinate_mediation.v1_0.messages - aries_cloudagent.protocols.coordinate_mediation.v1_0.models + aries_cloudagent.protocols.coordinate_mediation.v1_0.handlers + aries_cloudagent.protocols.coordinate_mediation.v1_0.messages + aries_cloudagent.protocols.coordinate_mediation.v1_0.models Submodules ---------- @@ -23,54 +22,56 @@ aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.controller module ------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.controller - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.manager module ---------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.message\_types module ----------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.normalization module ---------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.normalization - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.route\_manager module ----------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.route_manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.route\_manager\_provider module --------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.route_manager_provider - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.routes module --------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.didexchange.rst b/docs/generated/aries_cloudagent.protocols.didexchange.rst index 84c84dd7df..9d591e9af8 100644 --- a/docs/generated/aries_cloudagent.protocols.didexchange.rst +++ b/docs/generated/aries_cloudagent.protocols.didexchange.rst @@ -2,17 +2,16 @@ aries\_cloudagent.protocols.didexchange package =============================================== .. automodule:: aries_cloudagent.protocols.didexchange - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.didexchange.v1_0 + aries_cloudagent.protocols.didexchange.v1_0 Submodules ---------- @@ -21,6 +20,8 @@ aries\_cloudagent.protocols.didexchange.definition module --------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.didexchange.definition - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.didexchange.v1_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.didexchange.v1_0.handlers.rst index 999485395d..352fd0e8dc 100644 --- a/docs/generated/aries_cloudagent.protocols.didexchange.v1_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.didexchange.v1_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.didexchange.v1\_0.handlers package ============================================================== .. automodule:: aries_cloudagent.protocols.didexchange.v1_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,30 +13,32 @@ aries\_cloudagent.protocols.didexchange.v1\_0.handlers.complete\_handler module ------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.didexchange.v1_0.handlers.complete_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.didexchange.v1\_0.handlers.invitation\_handler module --------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.didexchange.v1_0.handlers.invitation_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.didexchange.v1\_0.handlers.request\_handler module ------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.didexchange.v1_0.handlers.request_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.didexchange.v1\_0.handlers.response\_handler module ------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.didexchange.v1_0.handlers.response_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.didexchange.v1_0.messages.rst b/docs/generated/aries_cloudagent.protocols.didexchange.v1_0.messages.rst index bcd9ffdf7a..eecc1e878a 100644 --- a/docs/generated/aries_cloudagent.protocols.didexchange.v1_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.didexchange.v1_0.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.didexchange.v1\_0.messages package ============================================================== .. automodule:: aries_cloudagent.protocols.didexchange.v1_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,30 +13,32 @@ aries\_cloudagent.protocols.didexchange.v1\_0.messages.complete module ---------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.didexchange.v1_0.messages.complete - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.didexchange.v1\_0.messages.problem\_report\_reason module ------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.didexchange.v1_0.messages.problem_report_reason - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.didexchange.v1\_0.messages.request module --------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.didexchange.v1_0.messages.request - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.didexchange.v1\_0.messages.response module ---------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.didexchange.v1_0.messages.response - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.didexchange.v1_0.rst b/docs/generated/aries_cloudagent.protocols.didexchange.v1_0.rst index 5d74e58b7a..e63c3ec552 100644 --- a/docs/generated/aries_cloudagent.protocols.didexchange.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.didexchange.v1_0.rst @@ -2,18 +2,17 @@ aries\_cloudagent.protocols.didexchange.v1\_0 package ===================================================== .. automodule:: aries_cloudagent.protocols.didexchange.v1_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.didexchange.v1_0.handlers - aries_cloudagent.protocols.didexchange.v1_0.messages + aries_cloudagent.protocols.didexchange.v1_0.handlers + aries_cloudagent.protocols.didexchange.v1_0.messages Submodules ---------- @@ -22,22 +21,24 @@ aries\_cloudagent.protocols.didexchange.v1\_0.manager module ------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.didexchange.v1_0.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.didexchange.v1\_0.message\_types module ------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.didexchange.v1_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.didexchange.v1\_0.routes module ----------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.didexchange.v1_0.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.discovery.rst b/docs/generated/aries_cloudagent.protocols.discovery.rst index 4c736183c7..92383fe5fd 100644 --- a/docs/generated/aries_cloudagent.protocols.discovery.rst +++ b/docs/generated/aries_cloudagent.protocols.discovery.rst @@ -2,18 +2,17 @@ aries\_cloudagent.protocols.discovery package ============================================= .. automodule:: aries_cloudagent.protocols.discovery - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.discovery.v1_0 - aries_cloudagent.protocols.discovery.v2_0 + aries_cloudagent.protocols.discovery.v1_0 + aries_cloudagent.protocols.discovery.v2_0 Submodules ---------- @@ -22,6 +21,8 @@ aries\_cloudagent.protocols.discovery.definition module ------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.definition - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.discovery.v1_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.discovery.v1_0.handlers.rst index 3c81111991..260e5a7f44 100644 --- a/docs/generated/aries_cloudagent.protocols.discovery.v1_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.discovery.v1_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.discovery.v1\_0.handlers package ============================================================ .. automodule:: aries_cloudagent.protocols.discovery.v1_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,14 +13,16 @@ aries\_cloudagent.protocols.discovery.v1\_0.handlers.disclose\_handler module ----------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v1_0.handlers.disclose_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.discovery.v1\_0.handlers.query\_handler module -------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v1_0.handlers.query_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.discovery.v1_0.messages.rst b/docs/generated/aries_cloudagent.protocols.discovery.v1_0.messages.rst index 35a9e49889..0c489c0845 100644 --- a/docs/generated/aries_cloudagent.protocols.discovery.v1_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.discovery.v1_0.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.discovery.v1\_0.messages package ============================================================ .. automodule:: aries_cloudagent.protocols.discovery.v1_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,14 +13,16 @@ aries\_cloudagent.protocols.discovery.v1\_0.messages.disclose module -------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v1_0.messages.disclose - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.discovery.v1\_0.messages.query module ----------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v1_0.messages.query - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.discovery.v1_0.models.rst b/docs/generated/aries_cloudagent.protocols.discovery.v1_0.models.rst index baec81e6d7..e080f6bf52 100644 --- a/docs/generated/aries_cloudagent.protocols.discovery.v1_0.models.rst +++ b/docs/generated/aries_cloudagent.protocols.discovery.v1_0.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.discovery.v1\_0.models package ========================================================== .. automodule:: aries_cloudagent.protocols.discovery.v1_0.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.discovery.v1\_0.models.discovery\_record module --------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v1_0.models.discovery_record - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.discovery.v1_0.rst b/docs/generated/aries_cloudagent.protocols.discovery.v1_0.rst index f221130077..410eef398f 100644 --- a/docs/generated/aries_cloudagent.protocols.discovery.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.discovery.v1_0.rst @@ -2,19 +2,18 @@ aries\_cloudagent.protocols.discovery.v1\_0 package =================================================== .. automodule:: aries_cloudagent.protocols.discovery.v1_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.discovery.v1_0.handlers - aries_cloudagent.protocols.discovery.v1_0.messages - aries_cloudagent.protocols.discovery.v1_0.models + aries_cloudagent.protocols.discovery.v1_0.handlers + aries_cloudagent.protocols.discovery.v1_0.messages + aries_cloudagent.protocols.discovery.v1_0.models Submodules ---------- @@ -23,22 +22,24 @@ aries\_cloudagent.protocols.discovery.v1\_0.manager module ---------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v1_0.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.discovery.v1\_0.message\_types module ----------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v1_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.discovery.v1\_0.routes module --------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v1_0.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.discovery.v2_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.discovery.v2_0.handlers.rst index b79d280002..3c17b21340 100644 --- a/docs/generated/aries_cloudagent.protocols.discovery.v2_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.discovery.v2_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.discovery.v2\_0.handlers package ============================================================ .. automodule:: aries_cloudagent.protocols.discovery.v2_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,14 +13,16 @@ aries\_cloudagent.protocols.discovery.v2\_0.handlers.disclosures\_handler module -------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v2_0.handlers.disclosures_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.discovery.v2\_0.handlers.queries\_handler module ---------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v2_0.handlers.queries_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.discovery.v2_0.messages.rst b/docs/generated/aries_cloudagent.protocols.discovery.v2_0.messages.rst index 99fdbc332b..e180e31f23 100644 --- a/docs/generated/aries_cloudagent.protocols.discovery.v2_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.discovery.v2_0.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.discovery.v2\_0.messages package ============================================================ .. automodule:: aries_cloudagent.protocols.discovery.v2_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,14 +13,16 @@ aries\_cloudagent.protocols.discovery.v2\_0.messages.disclosures module ----------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v2_0.messages.disclosures - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.discovery.v2\_0.messages.queries module ------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v2_0.messages.queries - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.discovery.v2_0.models.rst b/docs/generated/aries_cloudagent.protocols.discovery.v2_0.models.rst index 767db8e92e..19ea873489 100644 --- a/docs/generated/aries_cloudagent.protocols.discovery.v2_0.models.rst +++ b/docs/generated/aries_cloudagent.protocols.discovery.v2_0.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.discovery.v2\_0.models package ========================================================== .. automodule:: aries_cloudagent.protocols.discovery.v2_0.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.discovery.v2\_0.models.discovery\_record module --------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v2_0.models.discovery_record - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.discovery.v2_0.rst b/docs/generated/aries_cloudagent.protocols.discovery.v2_0.rst index d12fef9348..07d09fdb73 100644 --- a/docs/generated/aries_cloudagent.protocols.discovery.v2_0.rst +++ b/docs/generated/aries_cloudagent.protocols.discovery.v2_0.rst @@ -2,19 +2,18 @@ aries\_cloudagent.protocols.discovery.v2\_0 package =================================================== .. automodule:: aries_cloudagent.protocols.discovery.v2_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.discovery.v2_0.handlers - aries_cloudagent.protocols.discovery.v2_0.messages - aries_cloudagent.protocols.discovery.v2_0.models + aries_cloudagent.protocols.discovery.v2_0.handlers + aries_cloudagent.protocols.discovery.v2_0.messages + aries_cloudagent.protocols.discovery.v2_0.models Submodules ---------- @@ -23,22 +22,24 @@ aries\_cloudagent.protocols.discovery.v2\_0.manager module ---------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v2_0.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.discovery.v2\_0.message\_types module ----------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v2_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.discovery.v2\_0.routes module --------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v2_0.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.endorse_transaction.rst b/docs/generated/aries_cloudagent.protocols.endorse_transaction.rst index d940678a45..6cb027abe4 100644 --- a/docs/generated/aries_cloudagent.protocols.endorse_transaction.rst +++ b/docs/generated/aries_cloudagent.protocols.endorse_transaction.rst @@ -2,17 +2,16 @@ aries\_cloudagent.protocols.endorse\_transaction package ======================================================== .. automodule:: aries_cloudagent.protocols.endorse_transaction - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.endorse_transaction.v1_0 + aries_cloudagent.protocols.endorse_transaction.v1_0 Submodules ---------- @@ -21,6 +20,8 @@ aries\_cloudagent.protocols.endorse\_transaction.definition module ------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.endorse_transaction.definition - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.handlers.rst index e76ffa3ddc..055c2142c6 100644 --- a/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.endorse\_transaction.v1\_0.handlers package ======================================================================= .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,54 +13,56 @@ aries\_cloudagent.protocols.endorse\_transaction.v1\_0.handlers.endorsed\_transa --------------------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.handlers.endorsed_transaction_response_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.handlers.refused\_transaction\_response\_handler module -------------------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.handlers.refused_transaction_response_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.handlers.transaction\_acknowledgement\_handler module ------------------------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.handlers.transaction_acknowledgement_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.handlers.transaction\_cancel\_handler module --------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.handlers.transaction_cancel_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.handlers.transaction\_job\_to\_send\_handler module ---------------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.handlers.transaction_job_to_send_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.handlers.transaction\_request\_handler module ---------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.handlers.transaction_request_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.handlers.transaction\_resend\_handler module --------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.handlers.transaction_resend_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.messages.rst b/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.messages.rst index 3e14c4194c..c8809f0993 100644 --- a/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.endorse\_transaction.v1\_0.messages package ======================================================================= .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,62 +13,64 @@ aries\_cloudagent.protocols.endorse\_transaction.v1\_0.messages.cancel\_transact ------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.messages.cancel_transaction - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.messages.endorsed\_transaction\_response module ------------------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.messages.endorsed_transaction_response - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.messages.messages\_attach module --------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.messages.messages_attach - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.messages.refused\_transaction\_response module ----------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.messages.refused_transaction_response - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.messages.transaction\_acknowledgement module --------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.messages.transaction_acknowledgement - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.messages.transaction\_job\_to\_send module ------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.messages.transaction_job_to_send - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.messages.transaction\_request module ------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.messages.transaction_request - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.messages.transaction\_resend module ------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.messages.transaction_resend - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.models.rst b/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.models.rst index a120e79ce3..c2f7f4cc89 100644 --- a/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.models.rst +++ b/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.endorse\_transaction.v1\_0.models package ===================================================================== .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.endorse\_transaction.v1\_0.models.transaction\_recor ---------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.models.transaction_record - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.rst b/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.rst index eaf4c9eb32..24862f2880 100644 --- a/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.rst @@ -2,19 +2,18 @@ aries\_cloudagent.protocols.endorse\_transaction.v1\_0 package ============================================================== .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.endorse_transaction.v1_0.handlers - aries_cloudagent.protocols.endorse_transaction.v1_0.messages - aries_cloudagent.protocols.endorse_transaction.v1_0.models + aries_cloudagent.protocols.endorse_transaction.v1_0.handlers + aries_cloudagent.protocols.endorse_transaction.v1_0.messages + aries_cloudagent.protocols.endorse_transaction.v1_0.models Submodules ---------- @@ -23,46 +22,48 @@ aries\_cloudagent.protocols.endorse\_transaction.v1\_0.controller module ------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.controller - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.manager module --------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.message\_types module ---------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.routes module -------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.transaction\_jobs module ------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.transaction_jobs - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.util module ------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.util - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.introduction.rst b/docs/generated/aries_cloudagent.protocols.introduction.rst index ee89b06b48..2c331fb06b 100644 --- a/docs/generated/aries_cloudagent.protocols.introduction.rst +++ b/docs/generated/aries_cloudagent.protocols.introduction.rst @@ -2,17 +2,16 @@ aries\_cloudagent.protocols.introduction package ================================================ .. automodule:: aries_cloudagent.protocols.introduction - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.introduction.v0_1 + aries_cloudagent.protocols.introduction.v0_1 Submodules ---------- @@ -21,6 +20,8 @@ aries\_cloudagent.protocols.introduction.definition module ---------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.introduction.definition - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.introduction.v0_1.handlers.rst b/docs/generated/aries_cloudagent.protocols.introduction.v0_1.handlers.rst index ded1f15cd0..f9334b202d 100644 --- a/docs/generated/aries_cloudagent.protocols.introduction.v0_1.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.introduction.v0_1.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.introduction.v0\_1.handlers package =============================================================== .. automodule:: aries_cloudagent.protocols.introduction.v0_1.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,22 +13,24 @@ aries\_cloudagent.protocols.introduction.v0\_1.handlers.forward\_invitation\_han ------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.introduction.v0_1.handlers.forward_invitation_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.introduction.v0\_1.handlers.invitation\_handler module ---------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.introduction.v0_1.handlers.invitation_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.introduction.v0\_1.handlers.invitation\_request\_handler module ------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.introduction.v0_1.handlers.invitation_request_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.introduction.v0_1.messages.rst b/docs/generated/aries_cloudagent.protocols.introduction.v0_1.messages.rst index 841d3da69c..c469049dfe 100644 --- a/docs/generated/aries_cloudagent.protocols.introduction.v0_1.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.introduction.v0_1.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.introduction.v0\_1.messages package =============================================================== .. automodule:: aries_cloudagent.protocols.introduction.v0_1.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,22 +13,24 @@ aries\_cloudagent.protocols.introduction.v0\_1.messages.forward\_invitation modu ---------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.introduction.v0_1.messages.forward_invitation - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.introduction.v0\_1.messages.invitation module ------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.introduction.v0_1.messages.invitation - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.introduction.v0\_1.messages.invitation\_request module ---------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.introduction.v0_1.messages.invitation_request - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.introduction.v0_1.rst b/docs/generated/aries_cloudagent.protocols.introduction.v0_1.rst index 0ceece45e2..8f8b63d65f 100644 --- a/docs/generated/aries_cloudagent.protocols.introduction.v0_1.rst +++ b/docs/generated/aries_cloudagent.protocols.introduction.v0_1.rst @@ -2,18 +2,17 @@ aries\_cloudagent.protocols.introduction.v0\_1 package ====================================================== .. automodule:: aries_cloudagent.protocols.introduction.v0_1 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.introduction.v0_1.handlers - aries_cloudagent.protocols.introduction.v0_1.messages + aries_cloudagent.protocols.introduction.v0_1.handlers + aries_cloudagent.protocols.introduction.v0_1.messages Submodules ---------- @@ -22,30 +21,32 @@ aries\_cloudagent.protocols.introduction.v0\_1.base\_service module ------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.introduction.v0_1.base_service - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.introduction.v0\_1.demo\_service module ------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.introduction.v0_1.demo_service - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.introduction.v0\_1.message\_types module -------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.introduction.v0_1.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.introduction.v0\_1.routes module ------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.introduction.v0_1.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.rst index 9ad3c8172e..6edd7cb25f 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.rst @@ -2,18 +2,17 @@ aries\_cloudagent.protocols.issue\_credential package ===================================================== .. automodule:: aries_cloudagent.protocols.issue_credential - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.issue_credential.v1_0 - aries_cloudagent.protocols.issue_credential.v2_0 + aries_cloudagent.protocols.issue_credential.v1_0 + aries_cloudagent.protocols.issue_credential.v2_0 Submodules ---------- @@ -22,6 +21,8 @@ aries\_cloudagent.protocols.issue\_credential.definition module --------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.definition - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.handlers.rst index 860b89508a..24e308ca87 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.issue\_credential.v1\_0.handlers package ==================================================================== .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,46 +13,48 @@ aries\_cloudagent.protocols.issue\_credential.v1\_0.handlers.credential\_ack\_ha -------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.handlers.credential_ack_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v1\_0.handlers.credential\_issue\_handler module ---------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.handlers.credential_issue_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v1\_0.handlers.credential\_offer\_handler module ---------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.handlers.credential_offer_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v1\_0.handlers.credential\_problem\_report\_handler module -------------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.handlers.credential_problem_report_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v1\_0.handlers.credential\_proposal\_handler module ------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.handlers.credential_proposal_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v1\_0.handlers.credential\_request\_handler module ------------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.handlers.credential_request_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.messages.inner.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.messages.inner.rst index ebe4cc6072..17ac8e199e 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.messages.inner.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.messages.inner.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.issue\_credential.v1\_0.messages.inner package ========================================================================== .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.messages.inner - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.issue\_credential.v1\_0.messages.inner.credential\_p --------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.messages.inner.credential_preview - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.messages.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.messages.rst index 8279cea992..460cc32ac4 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.messages.rst @@ -2,17 +2,16 @@ aries\_cloudagent.protocols.issue\_credential.v1\_0.messages package ==================================================================== .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.issue_credential.v1_0.messages.inner + aries_cloudagent.protocols.issue_credential.v1_0.messages.inner Submodules ---------- @@ -21,46 +20,56 @@ aries\_cloudagent.protocols.issue\_credential.v1\_0.messages.credential\_ack mod ----------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.messages.credential_ack - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + +aries\_cloudagent.protocols.issue\_credential.v1\_0.messages.credential\_exchange\_webhook module +------------------------------------------------------------------------------------------------- + +.. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.messages.credential_exchange_webhook + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v1\_0.messages.credential\_issue module ------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.messages.credential_issue - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v1\_0.messages.credential\_offer module ------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.messages.credential_offer - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v1\_0.messages.credential\_problem\_report module ----------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.messages.credential_problem_report - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v1\_0.messages.credential\_proposal module ---------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.messages.credential_proposal - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v1\_0.messages.credential\_request module --------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.messages.credential_request - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.models.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.models.rst index 3fa2cfdd78..1624debb7d 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.models.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.issue\_credential.v1\_0.models package ================================================================== .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.issue\_credential.v1\_0.models.credential\_exchange -------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.models.credential_exchange - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.rst index 6221c61954..d1359b1d2a 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.rst @@ -2,19 +2,18 @@ aries\_cloudagent.protocols.issue\_credential.v1\_0 package =========================================================== .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.issue_credential.v1_0.handlers - aries_cloudagent.protocols.issue_credential.v1_0.messages - aries_cloudagent.protocols.issue_credential.v1_0.models + aries_cloudagent.protocols.issue_credential.v1_0.handlers + aries_cloudagent.protocols.issue_credential.v1_0.messages + aries_cloudagent.protocols.issue_credential.v1_0.models Submodules ---------- @@ -23,30 +22,32 @@ aries\_cloudagent.protocols.issue\_credential.v1\_0.controller module --------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.controller - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v1\_0.manager module ------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v1\_0.message\_types module ------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v1\_0.routes module ----------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.indy.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.indy.rst index 34fdbaf253..d4324b778e 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.indy.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.indy.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.formats.indy package ======================================================================== .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.formats.indy - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.formats.indy.handler module ------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.formats.indy.handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.models.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.models.rst index 771f548b4e..96d8eedb71 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.models.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.formats.ld\_proof.models pac ==================================================================================== .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,14 +13,16 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.formats.ld\_proof.models.cre ------------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.models.cred_detail - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.formats.ld\_proof.models.cred\_detail\_options module --------------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.models.cred_detail_options - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.rst index 594e20f4d8..89c7256d13 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.rst @@ -2,17 +2,16 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.formats.ld\_proof package ============================================================================= .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.models + aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.models Submodules ---------- @@ -21,6 +20,8 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.formats.ld\_proof.handler mo ------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.rst index 31bc906fed..9fefa18078 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.rst @@ -2,18 +2,17 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.formats package =================================================================== .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.formats - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.issue_credential.v2_0.formats.indy - aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof + aries_cloudagent.protocols.issue_credential.v2_0.formats.indy + aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof Submodules ---------- @@ -22,6 +21,8 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.formats.handler module -------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.formats.handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.handlers.rst index d64924b2b4..6a16a66f88 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.handlers package ==================================================================== .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,46 +13,48 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.handlers.cred\_ack\_handler -------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.handlers.cred_ack_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.handlers.cred\_issue\_handler module ---------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.handlers.cred_issue_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.handlers.cred\_offer\_handler module ---------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.handlers.cred_offer_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.handlers.cred\_problem\_report\_handler module -------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.handlers.cred_problem_report_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.handlers.cred\_proposal\_handler module ------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.handlers.cred_proposal_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.handlers.cred\_request\_handler module ------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.handlers.cred_request_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.messages.inner.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.messages.inner.rst index 5591934834..631445edbb 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.messages.inner.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.messages.inner.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.messages.inner package ========================================================================== .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.messages.inner - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.messages.inner.cred\_preview --------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.messages.inner.cred_preview - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.messages.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.messages.rst index c4d72caf1d..41a2d6e82b 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.messages.rst @@ -2,17 +2,16 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.messages package ==================================================================== .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.issue_credential.v2_0.messages.inner + aries_cloudagent.protocols.issue_credential.v2_0.messages.inner Submodules ---------- @@ -21,54 +20,64 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.messages.cred\_ack module ----------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.messages.cred_ack - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + +aries\_cloudagent.protocols.issue\_credential.v2\_0.messages.cred\_ex\_record\_webhook module +--------------------------------------------------------------------------------------------- + +.. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.messages.cred_ex_record_webhook + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.messages.cred\_format module -------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.messages.cred_format - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.messages.cred\_issue module ------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.messages.cred_issue - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.messages.cred\_offer module ------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.messages.cred_offer - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.messages.cred\_problem\_report module ----------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.messages.cred_problem_report - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.messages.cred\_proposal module ---------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.messages.cred_proposal - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.messages.cred\_request module --------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.messages.cred_request - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.models.detail.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.models.detail.rst index c677e91e09..b055fb04ea 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.models.detail.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.models.detail.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.models.detail package ========================================================================= .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.models.detail - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,14 +13,16 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.models.detail.indy module ----------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.models.detail.indy - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.models.detail.ld\_proof module ---------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.models.detail.ld_proof - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.models.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.models.rst index 5e77c64a12..65f0f10156 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.models.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.models.rst @@ -2,17 +2,16 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.models package ================================================================== .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.issue_credential.v2_0.models.detail + aries_cloudagent.protocols.issue_credential.v2_0.models.detail Submodules ---------- @@ -21,6 +20,8 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.models.cred\_ex\_record modu ---------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.models.cred_ex_record - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.rst index daf64871a1..461ce7a789 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.rst @@ -2,20 +2,19 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0 package =========================================================== .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.issue_credential.v2_0.formats - aries_cloudagent.protocols.issue_credential.v2_0.handlers - aries_cloudagent.protocols.issue_credential.v2_0.messages - aries_cloudagent.protocols.issue_credential.v2_0.models + aries_cloudagent.protocols.issue_credential.v2_0.formats + aries_cloudagent.protocols.issue_credential.v2_0.handlers + aries_cloudagent.protocols.issue_credential.v2_0.messages + aries_cloudagent.protocols.issue_credential.v2_0.models Submodules ---------- @@ -24,30 +23,32 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.controller module --------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.controller - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.manager module ------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.message\_types module ------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.routes module ----------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.notification.rst b/docs/generated/aries_cloudagent.protocols.notification.rst index e7c192af55..ecded9877d 100644 --- a/docs/generated/aries_cloudagent.protocols.notification.rst +++ b/docs/generated/aries_cloudagent.protocols.notification.rst @@ -2,17 +2,16 @@ aries\_cloudagent.protocols.notification package ================================================ .. automodule:: aries_cloudagent.protocols.notification - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.notification.v1_0 + aries_cloudagent.protocols.notification.v1_0 Submodules ---------- @@ -21,6 +20,8 @@ aries\_cloudagent.protocols.notification.definition module ---------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.notification.definition - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.notification.v1_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.notification.v1_0.handlers.rst index 83fb827146..5d3124b4ad 100644 --- a/docs/generated/aries_cloudagent.protocols.notification.v1_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.notification.v1_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.notification.v1\_0.handlers package =============================================================== .. automodule:: aries_cloudagent.protocols.notification.v1_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.notification.v1\_0.handlers.ack\_handler module --------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.notification.v1_0.handlers.ack_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.notification.v1_0.messages.rst b/docs/generated/aries_cloudagent.protocols.notification.v1_0.messages.rst index 05f005efc2..d3c6dfbf96 100644 --- a/docs/generated/aries_cloudagent.protocols.notification.v1_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.notification.v1_0.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.notification.v1\_0.messages package =============================================================== .. automodule:: aries_cloudagent.protocols.notification.v1_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.notification.v1\_0.messages.ack module ------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.notification.v1_0.messages.ack - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.notification.v1_0.rst b/docs/generated/aries_cloudagent.protocols.notification.v1_0.rst index d18146087d..130125346b 100644 --- a/docs/generated/aries_cloudagent.protocols.notification.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.notification.v1_0.rst @@ -2,18 +2,17 @@ aries\_cloudagent.protocols.notification.v1\_0 package ====================================================== .. automodule:: aries_cloudagent.protocols.notification.v1_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.notification.v1_0.handlers - aries_cloudagent.protocols.notification.v1_0.messages + aries_cloudagent.protocols.notification.v1_0.handlers + aries_cloudagent.protocols.notification.v1_0.messages Submodules ---------- @@ -22,6 +21,8 @@ aries\_cloudagent.protocols.notification.v1\_0.message\_types module -------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.notification.v1_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.out_of_band.rst b/docs/generated/aries_cloudagent.protocols.out_of_band.rst index 8d68b3f53c..0d243c0bd7 100644 --- a/docs/generated/aries_cloudagent.protocols.out_of_band.rst +++ b/docs/generated/aries_cloudagent.protocols.out_of_band.rst @@ -2,17 +2,16 @@ aries\_cloudagent.protocols.out\_of\_band package ================================================= .. automodule:: aries_cloudagent.protocols.out_of_band - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.out_of_band.v1_0 + aries_cloudagent.protocols.out_of_band.v1_0 Submodules ---------- @@ -21,6 +20,8 @@ aries\_cloudagent.protocols.out\_of\_band.definition module ----------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.out_of_band.definition - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.handlers.rst index 354688b2b8..390979fa65 100644 --- a/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.out\_of\_band.v1\_0.handlers package ================================================================ .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,22 +13,24 @@ aries\_cloudagent.protocols.out\_of\_band.v1\_0.handlers.problem\_report\_handle ---------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.handlers.problem_report_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.out\_of\_band.v1\_0.handlers.reuse\_accept\_handler module -------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.handlers.reuse_accept_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.out\_of\_band.v1\_0.handlers.reuse\_handler module ------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.handlers.reuse_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.messages.rst b/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.messages.rst index d01723be08..79183ffd32 100644 --- a/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.out\_of\_band.v1\_0.messages package ================================================================ .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,38 +13,40 @@ aries\_cloudagent.protocols.out\_of\_band.v1\_0.messages.invitation module -------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.messages.invitation - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.out\_of\_band.v1\_0.messages.problem\_report module ------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.messages.problem_report - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.out\_of\_band.v1\_0.messages.reuse module --------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.messages.reuse - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.out\_of\_band.v1\_0.messages.reuse\_accept module ----------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.messages.reuse_accept - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.out\_of\_band.v1\_0.messages.service module ----------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.messages.service - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.models.rst b/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.models.rst index b1b7bfd33c..9ae0effc91 100644 --- a/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.models.rst +++ b/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.out\_of\_band.v1\_0.models package ============================================================== .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,14 +13,16 @@ aries\_cloudagent.protocols.out\_of\_band.v1\_0.models.invitation module ------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.models.invitation - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.out\_of\_band.v1\_0.models.oob\_record module ------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.models.oob_record - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.rst b/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.rst index 80443da521..8462b38004 100644 --- a/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.rst @@ -2,19 +2,18 @@ aries\_cloudagent.protocols.out\_of\_band.v1\_0 package ======================================================= .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.out_of_band.v1_0.handlers - aries_cloudagent.protocols.out_of_band.v1_0.messages - aries_cloudagent.protocols.out_of_band.v1_0.models + aries_cloudagent.protocols.out_of_band.v1_0.handlers + aries_cloudagent.protocols.out_of_band.v1_0.messages + aries_cloudagent.protocols.out_of_band.v1_0.models Submodules ---------- @@ -23,30 +22,32 @@ aries\_cloudagent.protocols.out\_of\_band.v1\_0.controller module ----------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.controller - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.out\_of\_band.v1\_0.manager module -------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.out\_of\_band.v1\_0.message\_types module --------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.out\_of\_band.v1\_0.routes module ------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.dif.rst b/docs/generated/aries_cloudagent.protocols.present_proof.dif.rst index 5d83cee955..adad4e0b16 100644 --- a/docs/generated/aries_cloudagent.protocols.present_proof.dif.rst +++ b/docs/generated/aries_cloudagent.protocols.present_proof.dif.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.present\_proof.dif package ====================================================== .. automodule:: aries_cloudagent.protocols.present_proof.dif - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,38 +13,40 @@ aries\_cloudagent.protocols.present\_proof.dif.pres\_exch module ---------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.dif.pres_exch - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.dif.pres\_exch\_handler module ------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.dif.pres_exch_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.dif.pres\_proposal\_schema module ---------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.dif.pres_proposal_schema - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.dif.pres\_request\_schema module --------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.dif.pres_request_schema - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.dif.pres\_schema module ------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.present_proof.dif.pres_schema - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.indy.rst b/docs/generated/aries_cloudagent.protocols.present_proof.indy.rst index a1954d2b86..ca2879b241 100644 --- a/docs/generated/aries_cloudagent.protocols.present_proof.indy.rst +++ b/docs/generated/aries_cloudagent.protocols.present_proof.indy.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.present\_proof.indy package ======================================================= .. automodule:: aries_cloudagent.protocols.present_proof.indy - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.present\_proof.indy.pres\_exch\_handler module -------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.indy.pres_exch_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.rst b/docs/generated/aries_cloudagent.protocols.present_proof.rst index e9b69e7de6..9318dbf8dc 100644 --- a/docs/generated/aries_cloudagent.protocols.present_proof.rst +++ b/docs/generated/aries_cloudagent.protocols.present_proof.rst @@ -2,20 +2,19 @@ aries\_cloudagent.protocols.present\_proof package ================================================== .. automodule:: aries_cloudagent.protocols.present_proof - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.present_proof.dif - aries_cloudagent.protocols.present_proof.indy - aries_cloudagent.protocols.present_proof.v1_0 - aries_cloudagent.protocols.present_proof.v2_0 + aries_cloudagent.protocols.present_proof.dif + aries_cloudagent.protocols.present_proof.indy + aries_cloudagent.protocols.present_proof.v1_0 + aries_cloudagent.protocols.present_proof.v2_0 Submodules ---------- @@ -24,6 +23,8 @@ aries\_cloudagent.protocols.present\_proof.definition module ------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.present_proof.definition - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.handlers.rst index 24c8341e2e..ef97bbb127 100644 --- a/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.present\_proof.v1\_0.handlers package ================================================================= .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,38 +13,40 @@ aries\_cloudagent.protocols.present\_proof.v1\_0.handlers.presentation\_ack\_han ------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.handlers.presentation_ack_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v1\_0.handlers.presentation\_handler module -------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.handlers.presentation_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v1\_0.handlers.presentation\_problem\_report\_handler module ------------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.handlers.presentation_problem_report_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v1\_0.handlers.presentation\_proposal\_handler module ------------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.handlers.presentation_proposal_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v1\_0.handlers.presentation\_request\_handler module ----------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.handlers.presentation_request_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.messages.rst b/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.messages.rst index a00cb2d278..e98676833d 100644 --- a/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.present\_proof.v1\_0.messages package ================================================================= .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,38 +13,48 @@ aries\_cloudagent.protocols.present\_proof.v1\_0.messages.presentation module ----------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.messages.presentation - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v1\_0.messages.presentation\_ack module ---------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.messages.presentation_ack - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v1\_0.messages.presentation\_problem\_report module ---------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.messages.presentation_problem_report - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v1\_0.messages.presentation\_proposal module --------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.messages.presentation_proposal - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v1\_0.messages.presentation\_request module -------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.messages.presentation_request - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + +aries\_cloudagent.protocols.present\_proof.v1\_0.messages.presentation\_webhook module +-------------------------------------------------------------------------------------- + +.. automodule:: aries_cloudagent.protocols.present_proof.v1_0.messages.presentation_webhook + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.models.rst b/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.models.rst index 6e97752c6a..2fbd454913 100644 --- a/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.models.rst +++ b/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.present\_proof.v1\_0.models package =============================================================== .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.present\_proof.v1\_0.models.presentation\_exchange m ------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.models.presentation_exchange - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.rst b/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.rst index 1a18103b2d..cffc7c119f 100644 --- a/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.rst @@ -2,19 +2,18 @@ aries\_cloudagent.protocols.present\_proof.v1\_0 package ======================================================== .. automodule:: aries_cloudagent.protocols.present_proof.v1_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.present_proof.v1_0.handlers - aries_cloudagent.protocols.present_proof.v1_0.messages - aries_cloudagent.protocols.present_proof.v1_0.models + aries_cloudagent.protocols.present_proof.v1_0.handlers + aries_cloudagent.protocols.present_proof.v1_0.messages + aries_cloudagent.protocols.present_proof.v1_0.models Submodules ---------- @@ -23,30 +22,32 @@ aries\_cloudagent.protocols.present\_proof.v1\_0.controller module ------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.controller - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v1\_0.manager module --------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v1\_0.message\_types module ---------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v1\_0.routes module -------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.formats.dif.rst b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.formats.dif.rst index 06b74fc41a..fd3ada060b 100644 --- a/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.formats.dif.rst +++ b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.formats.dif.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.present\_proof.v2\_0.formats.dif package ==================================================================== .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.formats.dif - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.present\_proof.v2\_0.formats.dif.handler module --------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.formats.dif.handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.formats.indy.rst b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.formats.indy.rst index fb215abc4e..2f54cdef9f 100644 --- a/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.formats.indy.rst +++ b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.formats.indy.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.present\_proof.v2\_0.formats.indy package ===================================================================== .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.formats.indy - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.present\_proof.v2\_0.formats.indy.handler module ---------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.formats.indy.handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.formats.rst b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.formats.rst index 5d44e018a1..8602dd4411 100644 --- a/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.formats.rst +++ b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.formats.rst @@ -2,18 +2,17 @@ aries\_cloudagent.protocols.present\_proof.v2\_0.formats package ================================================================ .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.formats - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.present_proof.v2_0.formats.dif - aries_cloudagent.protocols.present_proof.v2_0.formats.indy + aries_cloudagent.protocols.present_proof.v2_0.formats.dif + aries_cloudagent.protocols.present_proof.v2_0.formats.indy Submodules ---------- @@ -22,6 +21,8 @@ aries\_cloudagent.protocols.present\_proof.v2\_0.formats.handler module ----------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.formats.handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.handlers.rst index 6be81bcd53..8074e740f1 100644 --- a/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.present\_proof.v2\_0.handlers package ================================================================= .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,38 +13,40 @@ aries\_cloudagent.protocols.present\_proof.v2\_0.handlers.pres\_ack\_handler mod ----------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.handlers.pres_ack_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v2\_0.handlers.pres\_handler module ------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.handlers.pres_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v2\_0.handlers.pres\_problem\_report\_handler module ----------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.handlers.pres_problem_report_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v2\_0.handlers.pres\_proposal\_handler module ---------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.handlers.pres_proposal_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v2\_0.handlers.pres\_request\_handler module --------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.handlers.pres_request_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.messages.rst b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.messages.rst index bcaf052ad7..68139f57cd 100644 --- a/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.present\_proof.v2\_0.messages package ================================================================= .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,46 +13,56 @@ aries\_cloudagent.protocols.present\_proof.v2\_0.messages.pres module --------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.messages.pres - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v2\_0.messages.pres\_ack module -------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.messages.pres_ack - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v2\_0.messages.pres\_format module ----------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.messages.pres_format - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v2\_0.messages.pres\_problem\_report module -------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.messages.pres_problem_report - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v2\_0.messages.pres\_proposal module ------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.messages.pres_proposal - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v2\_0.messages.pres\_request module ------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.messages.pres_request - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + +aries\_cloudagent.protocols.present\_proof.v2\_0.messages.pres\_webhook module +------------------------------------------------------------------------------ + +.. automodule:: aries_cloudagent.protocols.present_proof.v2_0.messages.pres_webhook + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.models.rst b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.models.rst index 342773342d..a4b3da6f5e 100644 --- a/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.models.rst +++ b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.present\_proof.v2\_0.models package =============================================================== .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.present\_proof.v2\_0.models.pres\_exchange module ----------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.models.pres_exchange - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.rst b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.rst index ad4e408035..f372e30353 100644 --- a/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.rst +++ b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.rst @@ -2,20 +2,19 @@ aries\_cloudagent.protocols.present\_proof.v2\_0 package ======================================================== .. automodule:: aries_cloudagent.protocols.present_proof.v2_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.present_proof.v2_0.formats - aries_cloudagent.protocols.present_proof.v2_0.handlers - aries_cloudagent.protocols.present_proof.v2_0.messages - aries_cloudagent.protocols.present_proof.v2_0.models + aries_cloudagent.protocols.present_proof.v2_0.formats + aries_cloudagent.protocols.present_proof.v2_0.handlers + aries_cloudagent.protocols.present_proof.v2_0.messages + aries_cloudagent.protocols.present_proof.v2_0.models Submodules ---------- @@ -24,30 +23,32 @@ aries\_cloudagent.protocols.present\_proof.v2\_0.controller module ------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.controller - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v2\_0.manager module --------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v2\_0.message\_types module ---------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v2\_0.routes module -------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.problem_report.rst b/docs/generated/aries_cloudagent.protocols.problem_report.rst index 431b7f1387..8b80c721ac 100644 --- a/docs/generated/aries_cloudagent.protocols.problem_report.rst +++ b/docs/generated/aries_cloudagent.protocols.problem_report.rst @@ -2,17 +2,16 @@ aries\_cloudagent.protocols.problem\_report package =================================================== .. automodule:: aries_cloudagent.protocols.problem_report - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.problem_report.v1_0 + aries_cloudagent.protocols.problem_report.v1_0 Submodules ---------- @@ -21,6 +20,8 @@ aries\_cloudagent.protocols.problem\_report.definition module ------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.problem_report.definition - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.problem_report.v1_0.rst b/docs/generated/aries_cloudagent.protocols.problem_report.v1_0.rst index 7ac574e4c5..3ff5f8972f 100644 --- a/docs/generated/aries_cloudagent.protocols.problem_report.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.problem_report.v1_0.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.problem\_report.v1\_0 package ========================================================= .. automodule:: aries_cloudagent.protocols.problem_report.v1_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,22 +13,24 @@ aries\_cloudagent.protocols.problem\_report.v1\_0.handler module ---------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.problem_report.v1_0.handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.problem\_report.v1\_0.message module ---------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.problem_report.v1_0.message - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.problem\_report.v1\_0.message\_types module ----------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.problem_report.v1_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.revocation_notification.rst b/docs/generated/aries_cloudagent.protocols.revocation_notification.rst index c88d26085d..86337dc76c 100644 --- a/docs/generated/aries_cloudagent.protocols.revocation_notification.rst +++ b/docs/generated/aries_cloudagent.protocols.revocation_notification.rst @@ -2,18 +2,17 @@ aries\_cloudagent.protocols.revocation\_notification package ============================================================ .. automodule:: aries_cloudagent.protocols.revocation_notification - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.revocation_notification.v1_0 - aries_cloudagent.protocols.revocation_notification.v2_0 + aries_cloudagent.protocols.revocation_notification.v1_0 + aries_cloudagent.protocols.revocation_notification.v2_0 Submodules ---------- @@ -22,6 +21,8 @@ aries\_cloudagent.protocols.revocation\_notification.definition module ---------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.revocation_notification.definition - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.handlers.rst index 205b069dc4..98dddbe6f9 100644 --- a/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.revocation\_notification.v1\_0.handlers package =========================================================================== .. automodule:: aries_cloudagent.protocols.revocation_notification.v1_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.revocation\_notification.v1\_0.handlers.revoke\_hand ------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.revocation_notification.v1_0.handlers.revoke_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.messages.rst b/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.messages.rst index d623d66dc8..993f373e2a 100644 --- a/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.revocation\_notification.v1\_0.messages package =========================================================================== .. automodule:: aries_cloudagent.protocols.revocation_notification.v1_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.revocation\_notification.v1\_0.messages.revoke modul --------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.revocation_notification.v1_0.messages.revoke - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.models.rst b/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.models.rst index 48164ca2bf..aae0c83a81 100644 --- a/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.models.rst +++ b/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.revocation\_notification.v1\_0.models package ========================================================================= .. automodule:: aries_cloudagent.protocols.revocation_notification.v1_0.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.revocation\_notification.v1\_0.models.rev\_notificat -------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.revocation_notification.v1_0.models.rev_notification_record - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.rst b/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.rst index 06a3b4ace0..77a1d172ec 100644 --- a/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.rst @@ -2,19 +2,18 @@ aries\_cloudagent.protocols.revocation\_notification.v1\_0 package ================================================================== .. automodule:: aries_cloudagent.protocols.revocation_notification.v1_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.revocation_notification.v1_0.handlers - aries_cloudagent.protocols.revocation_notification.v1_0.messages - aries_cloudagent.protocols.revocation_notification.v1_0.models + aries_cloudagent.protocols.revocation_notification.v1_0.handlers + aries_cloudagent.protocols.revocation_notification.v1_0.messages + aries_cloudagent.protocols.revocation_notification.v1_0.models Submodules ---------- @@ -23,14 +22,16 @@ aries\_cloudagent.protocols.revocation\_notification.v1\_0.message\_types module -------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.revocation_notification.v1_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.revocation\_notification.v1\_0.routes module ------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.revocation_notification.v1_0.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.handlers.rst index 1fa7a93885..86a419dc3f 100644 --- a/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.revocation\_notification.v2\_0.handlers package =========================================================================== .. automodule:: aries_cloudagent.protocols.revocation_notification.v2_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.revocation\_notification.v2\_0.handlers.revoke\_hand ------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.revocation_notification.v2_0.handlers.revoke_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.messages.rst b/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.messages.rst index db465ff9e3..bdd311136b 100644 --- a/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.revocation\_notification.v2\_0.messages package =========================================================================== .. automodule:: aries_cloudagent.protocols.revocation_notification.v2_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.revocation\_notification.v2\_0.messages.revoke modul --------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.revocation_notification.v2_0.messages.revoke - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.models.rst b/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.models.rst index cffa7e42ae..fdc74b0332 100644 --- a/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.models.rst +++ b/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.revocation\_notification.v2\_0.models package ========================================================================= .. automodule:: aries_cloudagent.protocols.revocation_notification.v2_0.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.protocols.revocation\_notification.v2\_0.models.rev\_notificat -------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.revocation_notification.v2_0.models.rev_notification_record - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.rst b/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.rst index 8247c6853a..9a616e6c44 100644 --- a/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.rst +++ b/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.rst @@ -2,19 +2,18 @@ aries\_cloudagent.protocols.revocation\_notification.v2\_0 package ================================================================== .. automodule:: aries_cloudagent.protocols.revocation_notification.v2_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.revocation_notification.v2_0.handlers - aries_cloudagent.protocols.revocation_notification.v2_0.messages - aries_cloudagent.protocols.revocation_notification.v2_0.models + aries_cloudagent.protocols.revocation_notification.v2_0.handlers + aries_cloudagent.protocols.revocation_notification.v2_0.messages + aries_cloudagent.protocols.revocation_notification.v2_0.models Submodules ---------- @@ -23,14 +22,16 @@ aries\_cloudagent.protocols.revocation\_notification.v2\_0.message\_types module -------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.revocation_notification.v2_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.revocation\_notification.v2\_0.routes module ------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.revocation_notification.v2_0.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.routing.rst b/docs/generated/aries_cloudagent.protocols.routing.rst index 8aa2a0a91f..3dab5f14a3 100644 --- a/docs/generated/aries_cloudagent.protocols.routing.rst +++ b/docs/generated/aries_cloudagent.protocols.routing.rst @@ -2,17 +2,16 @@ aries\_cloudagent.protocols.routing package =========================================== .. automodule:: aries_cloudagent.protocols.routing - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.routing.v1_0 + aries_cloudagent.protocols.routing.v1_0 Submodules ---------- @@ -21,6 +20,8 @@ aries\_cloudagent.protocols.routing.definition module ----------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.definition - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.routing.v1_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.routing.v1_0.handlers.rst index dd2b61d077..5535c2a49f 100644 --- a/docs/generated/aries_cloudagent.protocols.routing.v1_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.routing.v1_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.routing.v1\_0.handlers package ========================================================== .. automodule:: aries_cloudagent.protocols.routing.v1_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,38 +13,40 @@ aries\_cloudagent.protocols.routing.v1\_0.handlers.forward\_handler module -------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.handlers.forward_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.routing.v1\_0.handlers.route\_query\_request\_handler module ---------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.handlers.route_query_request_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.routing.v1\_0.handlers.route\_query\_response\_handler module ----------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.handlers.route_query_response_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.routing.v1\_0.handlers.route\_update\_request\_handler module ----------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.handlers.route_update_request_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.routing.v1\_0.handlers.route\_update\_response\_handler module ------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.routing.v1_0.handlers.route_update_response_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.routing.v1_0.messages.rst b/docs/generated/aries_cloudagent.protocols.routing.v1_0.messages.rst index 640e6318fd..84a5e5d143 100644 --- a/docs/generated/aries_cloudagent.protocols.routing.v1_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.routing.v1_0.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.routing.v1\_0.messages package ========================================================== .. automodule:: aries_cloudagent.protocols.routing.v1_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,38 +13,40 @@ aries\_cloudagent.protocols.routing.v1\_0.messages.forward module ----------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.messages.forward - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.routing.v1\_0.messages.route\_query\_request module ------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.messages.route_query_request - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.routing.v1\_0.messages.route\_query\_response module -------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.messages.route_query_response - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.routing.v1\_0.messages.route\_update\_request module -------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.messages.route_update_request - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.routing.v1\_0.messages.route\_update\_response module --------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.messages.route_update_response - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.routing.v1_0.models.rst b/docs/generated/aries_cloudagent.protocols.routing.v1_0.models.rst index 53d1cc867a..1a373f1022 100644 --- a/docs/generated/aries_cloudagent.protocols.routing.v1_0.models.rst +++ b/docs/generated/aries_cloudagent.protocols.routing.v1_0.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.routing.v1\_0.models package ======================================================== .. automodule:: aries_cloudagent.protocols.routing.v1_0.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,46 +13,48 @@ aries\_cloudagent.protocols.routing.v1\_0.models.paginate module ---------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.models.paginate - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.routing.v1\_0.models.paginated module ----------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.models.paginated - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.routing.v1\_0.models.route\_query\_result module ---------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.models.route_query_result - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.routing.v1\_0.models.route\_record module --------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.models.route_record - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.routing.v1\_0.models.route\_update module --------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.models.route_update - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.routing.v1\_0.models.route\_updated module ---------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.models.route_updated - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.routing.v1_0.rst b/docs/generated/aries_cloudagent.protocols.routing.v1_0.rst index ee671a27d2..353b5c393d 100644 --- a/docs/generated/aries_cloudagent.protocols.routing.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.routing.v1_0.rst @@ -2,19 +2,18 @@ aries\_cloudagent.protocols.routing.v1\_0 package ================================================= .. automodule:: aries_cloudagent.protocols.routing.v1_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.routing.v1_0.handlers - aries_cloudagent.protocols.routing.v1_0.messages - aries_cloudagent.protocols.routing.v1_0.models + aries_cloudagent.protocols.routing.v1_0.handlers + aries_cloudagent.protocols.routing.v1_0.messages + aries_cloudagent.protocols.routing.v1_0.models Submodules ---------- @@ -23,14 +22,16 @@ aries\_cloudagent.protocols.routing.v1\_0.manager module -------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.routing.v1\_0.message\_types module --------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.rst b/docs/generated/aries_cloudagent.protocols.rst index c569e1d81b..3af0fbcff1 100644 --- a/docs/generated/aries_cloudagent.protocols.rst +++ b/docs/generated/aries_cloudagent.protocols.rst @@ -2,32 +2,31 @@ aries\_cloudagent.protocols package =================================== .. automodule:: aries_cloudagent.protocols - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - - aries_cloudagent.protocols.actionmenu - aries_cloudagent.protocols.basicmessage - aries_cloudagent.protocols.connections - aries_cloudagent.protocols.coordinate_mediation - aries_cloudagent.protocols.didexchange - aries_cloudagent.protocols.discovery - aries_cloudagent.protocols.endorse_transaction - aries_cloudagent.protocols.introduction - aries_cloudagent.protocols.issue_credential - aries_cloudagent.protocols.notification - aries_cloudagent.protocols.out_of_band - aries_cloudagent.protocols.present_proof - aries_cloudagent.protocols.problem_report - aries_cloudagent.protocols.revocation_notification - aries_cloudagent.protocols.routing - aries_cloudagent.protocols.trustping + + aries_cloudagent.protocols.actionmenu + aries_cloudagent.protocols.basicmessage + aries_cloudagent.protocols.connections + aries_cloudagent.protocols.coordinate_mediation + aries_cloudagent.protocols.didexchange + aries_cloudagent.protocols.discovery + aries_cloudagent.protocols.endorse_transaction + aries_cloudagent.protocols.introduction + aries_cloudagent.protocols.issue_credential + aries_cloudagent.protocols.notification + aries_cloudagent.protocols.out_of_band + aries_cloudagent.protocols.present_proof + aries_cloudagent.protocols.problem_report + aries_cloudagent.protocols.revocation_notification + aries_cloudagent.protocols.routing + aries_cloudagent.protocols.trustping Submodules ---------- @@ -36,6 +35,8 @@ aries\_cloudagent.protocols.didcomm\_prefix module -------------------------------------------------- .. automodule:: aries_cloudagent.protocols.didcomm_prefix - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.trustping.rst b/docs/generated/aries_cloudagent.protocols.trustping.rst index c2df3c688e..01cc9e6332 100644 --- a/docs/generated/aries_cloudagent.protocols.trustping.rst +++ b/docs/generated/aries_cloudagent.protocols.trustping.rst @@ -2,17 +2,16 @@ aries\_cloudagent.protocols.trustping package ============================================= .. automodule:: aries_cloudagent.protocols.trustping - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.trustping.v1_0 + aries_cloudagent.protocols.trustping.v1_0 Submodules ---------- @@ -21,6 +20,8 @@ aries\_cloudagent.protocols.trustping.definition module ------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.trustping.definition - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.trustping.v1_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.trustping.v1_0.handlers.rst index d8dd03ff35..19f404be0e 100644 --- a/docs/generated/aries_cloudagent.protocols.trustping.v1_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.trustping.v1_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.trustping.v1\_0.handlers package ============================================================ .. automodule:: aries_cloudagent.protocols.trustping.v1_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,14 +13,16 @@ aries\_cloudagent.protocols.trustping.v1\_0.handlers.ping\_handler module ------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.trustping.v1_0.handlers.ping_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.trustping.v1\_0.handlers.ping\_response\_handler module ----------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.trustping.v1_0.handlers.ping_response_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.trustping.v1_0.messages.rst b/docs/generated/aries_cloudagent.protocols.trustping.v1_0.messages.rst index 1e1b83a5a0..c7d0fb38f3 100644 --- a/docs/generated/aries_cloudagent.protocols.trustping.v1_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.trustping.v1_0.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.trustping.v1\_0.messages package ============================================================ .. automodule:: aries_cloudagent.protocols.trustping.v1_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,14 +13,16 @@ aries\_cloudagent.protocols.trustping.v1\_0.messages.ping module ---------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.trustping.v1_0.messages.ping - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.trustping.v1\_0.messages.ping\_response module -------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.trustping.v1_0.messages.ping_response - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.protocols.trustping.v1_0.rst b/docs/generated/aries_cloudagent.protocols.trustping.v1_0.rst index cceadfedfc..add52eafbf 100644 --- a/docs/generated/aries_cloudagent.protocols.trustping.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.trustping.v1_0.rst @@ -2,18 +2,17 @@ aries\_cloudagent.protocols.trustping.v1\_0 package =================================================== .. automodule:: aries_cloudagent.protocols.trustping.v1_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.protocols.trustping.v1_0.handlers - aries_cloudagent.protocols.trustping.v1_0.messages + aries_cloudagent.protocols.trustping.v1_0.handlers + aries_cloudagent.protocols.trustping.v1_0.messages Submodules ---------- @@ -22,14 +21,16 @@ aries\_cloudagent.protocols.trustping.v1\_0.message\_types module ----------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.trustping.v1_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.trustping.v1\_0.routes module --------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.trustping.v1_0.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.resolver.default.rst b/docs/generated/aries_cloudagent.resolver.default.rst index b7dd8eac96..00b2bff98d 100644 --- a/docs/generated/aries_cloudagent.resolver.default.rst +++ b/docs/generated/aries_cloudagent.resolver.default.rst @@ -2,9 +2,9 @@ aries\_cloudagent.resolver.default package ========================================== .. automodule:: aries_cloudagent.resolver.default - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,30 +13,32 @@ aries\_cloudagent.resolver.default.indy module ---------------------------------------------- .. automodule:: aries_cloudagent.resolver.default.indy - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.resolver.default.key module --------------------------------------------- .. automodule:: aries_cloudagent.resolver.default.key - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.resolver.default.universal module --------------------------------------------------- .. automodule:: aries_cloudagent.resolver.default.universal - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.resolver.default.web module --------------------------------------------- .. automodule:: aries_cloudagent.resolver.default.web - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.resolver.rst b/docs/generated/aries_cloudagent.resolver.rst index 7732543e90..1b181f1fae 100644 --- a/docs/generated/aries_cloudagent.resolver.rst +++ b/docs/generated/aries_cloudagent.resolver.rst @@ -2,17 +2,16 @@ aries\_cloudagent.resolver package ================================== .. automodule:: aries_cloudagent.resolver - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.resolver.default + aries_cloudagent.resolver.default Submodules ---------- @@ -21,22 +20,24 @@ aries\_cloudagent.resolver.base module -------------------------------------- .. automodule:: aries_cloudagent.resolver.base - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.resolver.did\_resolver module ----------------------------------------------- .. automodule:: aries_cloudagent.resolver.did_resolver - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.resolver.routes module ---------------------------------------- .. automodule:: aries_cloudagent.resolver.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.revocation.models.rst b/docs/generated/aries_cloudagent.revocation.models.rst index 67f5c3ffb1..89a45d9248 100644 --- a/docs/generated/aries_cloudagent.revocation.models.rst +++ b/docs/generated/aries_cloudagent.revocation.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.revocation.models package =========================================== .. automodule:: aries_cloudagent.revocation.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,30 +13,32 @@ aries\_cloudagent.revocation.models.indy module ----------------------------------------------- .. automodule:: aries_cloudagent.revocation.models.indy - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.revocation.models.issuer\_cred\_rev\_record module -------------------------------------------------------------------- .. automodule:: aries_cloudagent.revocation.models.issuer_cred_rev_record - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.revocation.models.issuer\_rev\_reg\_record module ------------------------------------------------------------------- .. automodule:: aries_cloudagent.revocation.models.issuer_rev_reg_record - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.revocation.models.revocation\_registry module --------------------------------------------------------------- .. automodule:: aries_cloudagent.revocation.models.revocation_registry - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.revocation.rst b/docs/generated/aries_cloudagent.revocation.rst index 7263263c71..7d28a08b90 100644 --- a/docs/generated/aries_cloudagent.revocation.rst +++ b/docs/generated/aries_cloudagent.revocation.rst @@ -2,17 +2,16 @@ aries\_cloudagent.revocation package ==================================== .. automodule:: aries_cloudagent.revocation - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.revocation.models + aries_cloudagent.revocation.models Submodules ---------- @@ -21,46 +20,48 @@ aries\_cloudagent.revocation.error module ----------------------------------------- .. automodule:: aries_cloudagent.revocation.error - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.revocation.indy module ---------------------------------------- .. automodule:: aries_cloudagent.revocation.indy - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.revocation.manager module ------------------------------------------- .. automodule:: aries_cloudagent.revocation.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.revocation.recover module ------------------------------------------- .. automodule:: aries_cloudagent.revocation.recover - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.revocation.routes module ------------------------------------------ .. automodule:: aries_cloudagent.revocation.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.revocation.util module ---------------------------------------- .. automodule:: aries_cloudagent.revocation.util - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.rst b/docs/generated/aries_cloudagent.rst index ef01b45059..9bcb0112f2 100644 --- a/docs/generated/aries_cloudagent.rst +++ b/docs/generated/aries_cloudagent.rst @@ -2,38 +2,37 @@ aries\_cloudagent package ========================= .. automodule:: aries_cloudagent - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - - aries_cloudagent.admin - aries_cloudagent.askar - aries_cloudagent.cache - aries_cloudagent.commands - aries_cloudagent.config - aries_cloudagent.connections - aries_cloudagent.core - aries_cloudagent.did - aries_cloudagent.holder - aries_cloudagent.indy - aries_cloudagent.ledger - aries_cloudagent.messaging - aries_cloudagent.multitenant - aries_cloudagent.protocols - aries_cloudagent.resolver - aries_cloudagent.revocation - aries_cloudagent.storage - aries_cloudagent.tails - aries_cloudagent.transport - aries_cloudagent.utils - aries_cloudagent.vc - aries_cloudagent.wallet + + aries_cloudagent.admin + aries_cloudagent.askar + aries_cloudagent.cache + aries_cloudagent.commands + aries_cloudagent.config + aries_cloudagent.connections + aries_cloudagent.core + aries_cloudagent.did + aries_cloudagent.holder + aries_cloudagent.indy + aries_cloudagent.ledger + aries_cloudagent.messaging + aries_cloudagent.multitenant + aries_cloudagent.protocols + aries_cloudagent.resolver + aries_cloudagent.revocation + aries_cloudagent.storage + aries_cloudagent.tails + aries_cloudagent.transport + aries_cloudagent.utils + aries_cloudagent.vc + aries_cloudagent.wallet Submodules ---------- @@ -42,6 +41,8 @@ aries\_cloudagent.version module -------------------------------- .. automodule:: aries_cloudagent.version - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.storage.rst b/docs/generated/aries_cloudagent.storage.rst index bd06d4d460..c62c2e21f5 100644 --- a/docs/generated/aries_cloudagent.storage.rst +++ b/docs/generated/aries_cloudagent.storage.rst @@ -2,17 +2,16 @@ aries\_cloudagent.storage package ================================= .. automodule:: aries_cloudagent.storage - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.storage.vc_holder + aries_cloudagent.storage.vc_holder Submodules ---------- @@ -21,46 +20,48 @@ aries\_cloudagent.storage.askar module -------------------------------------- .. automodule:: aries_cloudagent.storage.askar - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.storage.base module ------------------------------------- .. automodule:: aries_cloudagent.storage.base - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.storage.error module -------------------------------------- .. automodule:: aries_cloudagent.storage.error - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.storage.in\_memory module ------------------------------------------- .. automodule:: aries_cloudagent.storage.in_memory - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.storage.indy module ------------------------------------- .. automodule:: aries_cloudagent.storage.indy - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.storage.record module --------------------------------------- .. automodule:: aries_cloudagent.storage.record - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.storage.vc_holder.rst b/docs/generated/aries_cloudagent.storage.vc_holder.rst index 7bd5098aff..329a262303 100644 --- a/docs/generated/aries_cloudagent.storage.vc_holder.rst +++ b/docs/generated/aries_cloudagent.storage.vc_holder.rst @@ -2,9 +2,9 @@ aries\_cloudagent.storage.vc\_holder package ============================================ .. automodule:: aries_cloudagent.storage.vc_holder - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,46 +13,48 @@ aries\_cloudagent.storage.vc\_holder.askar module ------------------------------------------------- .. automodule:: aries_cloudagent.storage.vc_holder.askar - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.storage.vc\_holder.base module ------------------------------------------------ .. automodule:: aries_cloudagent.storage.vc_holder.base - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.storage.vc\_holder.in\_memory module ------------------------------------------------------ .. automodule:: aries_cloudagent.storage.vc_holder.in_memory - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.storage.vc\_holder.indy module ------------------------------------------------ .. automodule:: aries_cloudagent.storage.vc_holder.indy - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.storage.vc\_holder.vc\_record module ------------------------------------------------------ .. automodule:: aries_cloudagent.storage.vc_holder.vc_record - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.storage.vc\_holder.xform module ------------------------------------------------- .. automodule:: aries_cloudagent.storage.vc_holder.xform - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.tails.rst b/docs/generated/aries_cloudagent.tails.rst index 51e1746aee..5aabc0cb4e 100644 --- a/docs/generated/aries_cloudagent.tails.rst +++ b/docs/generated/aries_cloudagent.tails.rst @@ -2,9 +2,9 @@ aries\_cloudagent.tails package =============================== .. automodule:: aries_cloudagent.tails - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,22 +13,24 @@ aries\_cloudagent.tails.base module ----------------------------------- .. automodule:: aries_cloudagent.tails.base - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.tails.error module ------------------------------------ .. automodule:: aries_cloudagent.tails.error - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.tails.indy\_tails\_server module -------------------------------------------------- .. automodule:: aries_cloudagent.tails.indy_tails_server - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.transport.inbound.rst b/docs/generated/aries_cloudagent.transport.inbound.rst index 879581b75e..6a32abd804 100644 --- a/docs/generated/aries_cloudagent.transport.inbound.rst +++ b/docs/generated/aries_cloudagent.transport.inbound.rst @@ -2,9 +2,9 @@ aries\_cloudagent.transport.inbound package =========================================== .. automodule:: aries_cloudagent.transport.inbound - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,62 +13,64 @@ aries\_cloudagent.transport.inbound.base module ----------------------------------------------- .. automodule:: aries_cloudagent.transport.inbound.base - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.inbound.delivery\_queue module ---------------------------------------------------------- .. automodule:: aries_cloudagent.transport.inbound.delivery_queue - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.inbound.http module ----------------------------------------------- .. automodule:: aries_cloudagent.transport.inbound.http - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.inbound.manager module -------------------------------------------------- .. automodule:: aries_cloudagent.transport.inbound.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.inbound.message module -------------------------------------------------- .. automodule:: aries_cloudagent.transport.inbound.message - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.inbound.receipt module -------------------------------------------------- .. automodule:: aries_cloudagent.transport.inbound.receipt - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.inbound.session module -------------------------------------------------- .. automodule:: aries_cloudagent.transport.inbound.session - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.inbound.ws module --------------------------------------------- .. automodule:: aries_cloudagent.transport.inbound.ws - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.transport.outbound.rst b/docs/generated/aries_cloudagent.transport.outbound.rst index 1fde7f0379..1be069a0c7 100644 --- a/docs/generated/aries_cloudagent.transport.outbound.rst +++ b/docs/generated/aries_cloudagent.transport.outbound.rst @@ -2,9 +2,9 @@ aries\_cloudagent.transport.outbound package ============================================ .. automodule:: aries_cloudagent.transport.outbound - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,46 +13,48 @@ aries\_cloudagent.transport.outbound.base module ------------------------------------------------ .. automodule:: aries_cloudagent.transport.outbound.base - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.outbound.http module ------------------------------------------------ .. automodule:: aries_cloudagent.transport.outbound.http - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.outbound.manager module --------------------------------------------------- .. automodule:: aries_cloudagent.transport.outbound.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.outbound.message module --------------------------------------------------- .. automodule:: aries_cloudagent.transport.outbound.message - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.outbound.status module -------------------------------------------------- .. automodule:: aries_cloudagent.transport.outbound.status - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.outbound.ws module ---------------------------------------------- .. automodule:: aries_cloudagent.transport.outbound.ws - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.transport.queue.rst b/docs/generated/aries_cloudagent.transport.queue.rst index f6e9920d94..577fdf58c2 100644 --- a/docs/generated/aries_cloudagent.transport.queue.rst +++ b/docs/generated/aries_cloudagent.transport.queue.rst @@ -2,9 +2,9 @@ aries\_cloudagent.transport.queue package ========================================= .. automodule:: aries_cloudagent.transport.queue - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,14 +13,16 @@ aries\_cloudagent.transport.queue.base module --------------------------------------------- .. automodule:: aries_cloudagent.transport.queue.base - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.queue.basic module ---------------------------------------------- .. automodule:: aries_cloudagent.transport.queue.basic - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.transport.rst b/docs/generated/aries_cloudagent.transport.rst index 33aed946ad..d10d781102 100644 --- a/docs/generated/aries_cloudagent.transport.rst +++ b/docs/generated/aries_cloudagent.transport.rst @@ -2,19 +2,18 @@ aries\_cloudagent.transport package =================================== .. automodule:: aries_cloudagent.transport - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.transport.inbound - aries_cloudagent.transport.outbound - aries_cloudagent.transport.queue + aries_cloudagent.transport.inbound + aries_cloudagent.transport.outbound + aries_cloudagent.transport.queue Submodules ---------- @@ -23,30 +22,32 @@ aries\_cloudagent.transport.error module ---------------------------------------- .. automodule:: aries_cloudagent.transport.error - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.pack\_format module ----------------------------------------------- .. automodule:: aries_cloudagent.transport.pack_format - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.stats module ---------------------------------------- .. automodule:: aries_cloudagent.transport.stats - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.wire\_format module ----------------------------------------------- .. automodule:: aries_cloudagent.transport.wire_format - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.utils.rst b/docs/generated/aries_cloudagent.utils.rst index f5241f39d8..9d968756b8 100644 --- a/docs/generated/aries_cloudagent.utils.rst +++ b/docs/generated/aries_cloudagent.utils.rst @@ -2,9 +2,9 @@ aries\_cloudagent.utils package =============================== .. automodule:: aries_cloudagent.utils - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,78 +13,80 @@ aries\_cloudagent.utils.classloader module ------------------------------------------ .. automodule:: aries_cloudagent.utils.classloader - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.utils.dependencies module ------------------------------------------- .. automodule:: aries_cloudagent.utils.dependencies - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.utils.env module ---------------------------------- .. automodule:: aries_cloudagent.utils.env - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.utils.http module ----------------------------------- .. automodule:: aries_cloudagent.utils.http - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.utils.jwe module ---------------------------------- .. automodule:: aries_cloudagent.utils.jwe - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.utils.outofband module ---------------------------------------- .. automodule:: aries_cloudagent.utils.outofband - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.utils.repeat module ------------------------------------- .. automodule:: aries_cloudagent.utils.repeat - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.utils.stats module ------------------------------------ .. automodule:: aries_cloudagent.utils.stats - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.utils.task\_queue module ------------------------------------------ .. automodule:: aries_cloudagent.utils.task_queue - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.utils.tracing module -------------------------------------- .. automodule:: aries_cloudagent.utils.tracing - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.vc.ld_proofs.crypto.rst b/docs/generated/aries_cloudagent.vc.ld_proofs.crypto.rst index 66c04359cd..f84983368f 100644 --- a/docs/generated/aries_cloudagent.vc.ld_proofs.crypto.rst +++ b/docs/generated/aries_cloudagent.vc.ld_proofs.crypto.rst @@ -2,9 +2,9 @@ aries\_cloudagent.vc.ld\_proofs.crypto package ============================================== .. automodule:: aries_cloudagent.vc.ld_proofs.crypto - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,14 +13,16 @@ aries\_cloudagent.vc.ld\_proofs.crypto.key\_pair module ------------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.crypto.key_pair - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.crypto.wallet\_key\_pair module --------------------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.crypto.wallet_key_pair - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.vc.ld_proofs.purposes.rst b/docs/generated/aries_cloudagent.vc.ld_proofs.purposes.rst index 169a889c5b..6693036472 100644 --- a/docs/generated/aries_cloudagent.vc.ld_proofs.purposes.rst +++ b/docs/generated/aries_cloudagent.vc.ld_proofs.purposes.rst @@ -2,9 +2,9 @@ aries\_cloudagent.vc.ld\_proofs.purposes package ================================================ .. automodule:: aries_cloudagent.vc.ld_proofs.purposes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,38 +13,40 @@ aries\_cloudagent.vc.ld\_proofs.purposes.assertion\_proof\_purpose module ------------------------------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.purposes.assertion_proof_purpose - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.purposes.authentication\_proof\_purpose module ------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.vc.ld_proofs.purposes.authentication_proof_purpose - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.purposes.controller\_proof\_purpose module -------------------------------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.purposes.controller_proof_purpose - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.purposes.credential\_issuance\_purpose module ----------------------------------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.purposes.credential_issuance_purpose - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.purposes.proof\_purpose module -------------------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.purposes.proof_purpose - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.vc.ld_proofs.rst b/docs/generated/aries_cloudagent.vc.ld_proofs.rst index ca7276b1fc..4727ab22fc 100644 --- a/docs/generated/aries_cloudagent.vc.ld_proofs.rst +++ b/docs/generated/aries_cloudagent.vc.ld_proofs.rst @@ -2,19 +2,18 @@ aries\_cloudagent.vc.ld\_proofs package ======================================= .. automodule:: aries_cloudagent.vc.ld_proofs - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.vc.ld_proofs.crypto - aries_cloudagent.vc.ld_proofs.purposes - aries_cloudagent.vc.ld_proofs.suites + aries_cloudagent.vc.ld_proofs.crypto + aries_cloudagent.vc.ld_proofs.purposes + aries_cloudagent.vc.ld_proofs.suites Submodules ---------- @@ -23,54 +22,56 @@ aries\_cloudagent.vc.ld\_proofs.check module -------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.check - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.constants module ------------------------------------------------ .. automodule:: aries_cloudagent.vc.ld_proofs.constants - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.document\_loader module ------------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.document_loader - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.error module -------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.error - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.ld\_proofs module ------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.ld_proofs - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.proof\_set module ------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.proof_set - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.validation\_result module --------------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.validation_result - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.vc.ld_proofs.suites.rst b/docs/generated/aries_cloudagent.vc.ld_proofs.suites.rst index cbda566bca..3ba6e87d41 100644 --- a/docs/generated/aries_cloudagent.vc.ld_proofs.suites.rst +++ b/docs/generated/aries_cloudagent.vc.ld_proofs.suites.rst @@ -2,9 +2,9 @@ aries\_cloudagent.vc.ld\_proofs.suites package ============================================== .. automodule:: aries_cloudagent.vc.ld_proofs.suites - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,54 +13,56 @@ aries\_cloudagent.vc.ld\_proofs.suites.bbs\_bls\_signature\_2020 module ----------------------------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.suites.bbs_bls_signature_2020 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.suites.bbs\_bls\_signature\_2020\_base module ----------------------------------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.suites.bbs_bls_signature_2020_base - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.suites.bbs\_bls\_signature\_proof\_2020 module ------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.vc.ld_proofs.suites.bbs_bls_signature_proof_2020 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.suites.ed25519\_signature\_2018 module ---------------------------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.suites.ed25519_signature_2018 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.suites.jws\_linked\_data\_signature module -------------------------------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.suites.jws_linked_data_signature - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.suites.linked\_data\_proof module ----------------------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.suites.linked_data_proof - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.suites.linked\_data\_signature module --------------------------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.suites.linked_data_signature - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.vc.rst b/docs/generated/aries_cloudagent.vc.rst index f3c1e09d88..a0a9a0ba2e 100644 --- a/docs/generated/aries_cloudagent.vc.rst +++ b/docs/generated/aries_cloudagent.vc.rst @@ -2,15 +2,15 @@ aries\_cloudagent.vc package ============================ .. automodule:: aries_cloudagent.vc - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.vc.ld_proofs - aries_cloudagent.vc.vc_ld + aries_cloudagent.vc.ld_proofs + aries_cloudagent.vc.vc_ld + diff --git a/docs/generated/aries_cloudagent.vc.vc_ld.models.rst b/docs/generated/aries_cloudagent.vc.vc_ld.models.rst index 83bcd876d2..745e577a62 100644 --- a/docs/generated/aries_cloudagent.vc.vc_ld.models.rst +++ b/docs/generated/aries_cloudagent.vc.vc_ld.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.vc.vc\_ld.models package ========================================== .. automodule:: aries_cloudagent.vc.vc_ld.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,14 +13,16 @@ aries\_cloudagent.vc.vc\_ld.models.credential module ---------------------------------------------------- .. automodule:: aries_cloudagent.vc.vc_ld.models.credential - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.vc\_ld.models.linked\_data\_proof module ------------------------------------------------------------- .. automodule:: aries_cloudagent.vc.vc_ld.models.linked_data_proof - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.vc.vc_ld.rst b/docs/generated/aries_cloudagent.vc.vc_ld.rst index a89699f5cc..00c9235f89 100644 --- a/docs/generated/aries_cloudagent.vc.vc_ld.rst +++ b/docs/generated/aries_cloudagent.vc.vc_ld.rst @@ -2,17 +2,16 @@ aries\_cloudagent.vc.vc\_ld package =================================== .. automodule:: aries_cloudagent.vc.vc_ld - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.vc.vc_ld.models + aries_cloudagent.vc.vc_ld.models Submodules ---------- @@ -21,30 +20,32 @@ aries\_cloudagent.vc.vc\_ld.issue module ---------------------------------------- .. automodule:: aries_cloudagent.vc.vc_ld.issue - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.vc\_ld.prove module ---------------------------------------- .. automodule:: aries_cloudagent.vc.vc_ld.prove - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.vc\_ld.validation\_result module ----------------------------------------------------- .. automodule:: aries_cloudagent.vc.vc_ld.validation_result - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.vc\_ld.verify module ----------------------------------------- .. automodule:: aries_cloudagent.vc.vc_ld.verify - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.wallet.models.rst b/docs/generated/aries_cloudagent.wallet.models.rst index d59bc609c4..741b5a2246 100644 --- a/docs/generated/aries_cloudagent.wallet.models.rst +++ b/docs/generated/aries_cloudagent.wallet.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.wallet.models package ======================================= .. automodule:: aries_cloudagent.wallet.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,6 +13,8 @@ aries\_cloudagent.wallet.models.wallet\_record module ----------------------------------------------------- .. automodule:: aries_cloudagent.wallet.models.wallet_record - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/generated/aries_cloudagent.wallet.rst b/docs/generated/aries_cloudagent.wallet.rst index d7ef3b5efa..e7eaffbce5 100644 --- a/docs/generated/aries_cloudagent.wallet.rst +++ b/docs/generated/aries_cloudagent.wallet.rst @@ -2,17 +2,16 @@ aries\_cloudagent.wallet package ================================ .. automodule:: aries_cloudagent.wallet - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - :maxdepth: 4 - aries_cloudagent.wallet.models + aries_cloudagent.wallet.models Submodules ---------- @@ -21,118 +20,120 @@ aries\_cloudagent.wallet.askar module ------------------------------------- .. automodule:: aries_cloudagent.wallet.askar - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.wallet.base module ------------------------------------ .. automodule:: aries_cloudagent.wallet.base - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.wallet.bbs module ----------------------------------- .. automodule:: aries_cloudagent.wallet.bbs - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.wallet.crypto module -------------------------------------- .. automodule:: aries_cloudagent.wallet.crypto - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.wallet.did\_info module ----------------------------------------- .. automodule:: aries_cloudagent.wallet.did_info - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.wallet.did\_method module ------------------------------------------- .. automodule:: aries_cloudagent.wallet.did_method - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.wallet.did\_parameters\_validation module ----------------------------------------------------------- .. automodule:: aries_cloudagent.wallet.did_parameters_validation - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.wallet.did\_posture module -------------------------------------------- .. automodule:: aries_cloudagent.wallet.did_posture - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.wallet.error module ------------------------------------- .. automodule:: aries_cloudagent.wallet.error - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.wallet.in\_memory module ------------------------------------------ .. automodule:: aries_cloudagent.wallet.in_memory - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.wallet.indy module ------------------------------------ .. automodule:: aries_cloudagent.wallet.indy - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.wallet.key\_pair module ----------------------------------------- .. automodule:: aries_cloudagent.wallet.key_pair - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.wallet.key\_type module ----------------------------------------- .. automodule:: aries_cloudagent.wallet.key_type - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.wallet.routes module -------------------------------------- .. automodule:: aries_cloudagent.wallet.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.wallet.util module ------------------------------------ .. automodule:: aries_cloudagent.wallet.util - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: + + diff --git a/open-api/openapi.json b/open-api/openapi.json index 463eaf3bff..75d4f33e0e 100644 --- a/open-api/openapi.json +++ b/open-api/openapi.json @@ -1,7 +1,7 @@ { "swagger" : "2.0", "info" : { - "version" : "v0.8.0-rc0", + "version" : "v0.8.0", "title" : "Aries Cloud Agent" }, "tags" : [ { From 82da3864cb0fb72ebd464dc5b01f85a3859735f9 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Tue, 14 Mar 2023 15:29:28 -0700 Subject: [PATCH 717/872] Add this PR to the Changelog Signed-off-by: Stephen Curran Signed-off-by: JSyro --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e3fe2756c1..cb5f41982f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -235,6 +235,7 @@ message as the default, and adds a new parameter to use the old behavior. - Redis Plugins \[redis\_cache & redis\_queue\] related updates [\#1937](https://github.com/hyperledger/aries-cloudagent-python/pull/1937) ([shaangill025](https://github.com/shaangill025)) - Release management pull requests + - 0.8.0 release [\#2169](https://github.com/hyperledger/aries-cloudagent-python/pull/2169) ([swcurran](https://github.com/swcurran)) - 0.8.0-rc0 release updates [\#2115](https://github.com/hyperledger/aries-cloudagent-python/pull/2115) ([swcurran](https://github.com/swcurran)) - Previously flagged in release 1.0.0-rc1 - Release 1.0.0-rc0 [\#1904](https://github.com/hyperledger/aries-cloudagent-python/pull/1904) ([swcurran](https://github.com/swcurran)) From f35d070d58ecb3bb8d97f5b8b96c4b558cae1305 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Tue, 14 Mar 2023 15:34:24 -0700 Subject: [PATCH 718/872] Fix lint warning on sphinx config Signed-off-by: Stephen Curran Signed-off-by: JSyro --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index db981d6321..55f83f6ec5 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -49,7 +49,7 @@ "nest_asyncio", "marshmallow", "typing_extensions", - "async_timeout" + "async_timeout", ] # "aries_cloudagent.tests.test_conductor", From c725adac2cecec4930a96646825cd9a0e920cf9f Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Wed, 15 Mar 2023 06:42:50 -0700 Subject: [PATCH 719/872] Change upgrade exception to be a print Signed-off-by: Stephen Curran Signed-off-by: JSyro --- aries_cloudagent/commands/upgrade.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aries_cloudagent/commands/upgrade.py b/aries_cloudagent/commands/upgrade.py index 4b2409e20e..13840f1e2d 100644 --- a/aries_cloudagent/commands/upgrade.py +++ b/aries_cloudagent/commands/upgrade.py @@ -137,7 +137,7 @@ async def upgrade(settings: dict): "the config." ) if upgrade_from_version == upgrade_to_version: - raise UpgradeError( + print( f"Version {upgrade_from_version} to upgrade from and " f"current version to upgrade to {upgrade_to_version} " "are same." From 6eb074ef904e5b7b29ad0697bf319ff1e81962c9 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Wed, 15 Mar 2023 06:58:18 -0700 Subject: [PATCH 720/872] Updated Changelog, removed change from RaiseError to print Signed-off-by: Stephen Curran Signed-off-by: JSyro --- CHANGELOG.md | 16 +++++++++++++++- aries_cloudagent/commands/upgrade.py | 2 +- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb5f41982f..ba21b9c7ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -62,7 +62,7 @@ aries-cloudagent-python]. [publish-indy.yml]: https://github.com/hyperledger/aries-cloudagent-python/blob/main/.github/workflows/publish-indy.yml [Container Images and Github Actions]: https://github.com/hyperledger/aries-cloudagent-python/blob/main/ContainerImagesAndGithubActions.md -## Breaking Changes +## Breaking Changes and Upgrades ### PR [\#2034](https://github.com/hyperledger/aries-cloudagent-python/pull/2034) -- Implicit connections @@ -96,6 +96,20 @@ hence, very large webhooks. This change reduces the size of the webhook message by eliminating redundant data in the protocol state of the "Issue Credential" message as the default, and adds a new parameter to use the old behavior. +### UPGRADE PR [\#2116](https://github.com/hyperledger/aries-cloudagent-python/pull/2116) - UPGRADE: Fix multi-use invitation performance + +The way that multiuse invitations in previous versions of ACA-Py caused +performance to degrade over time. An update was made to add state into the tag +names that eliminated the need to scan the tags when querying storage for the +invitation. + +If you are using multiuse invitations in your existing (pre-`0.8.0` deployment +of ACA-Py, you can run an `upgrade` to apply this change. To run upgrade from +previous versions, use the following command using the `0.8.0` version of +ACA-Py, adding you wallet settings: + +`aca-py upgrade --from-version=v0.7.5 --upgrade-config-path ./upgrade.yml` + ### Categorized List of Pull Requests - Verifiable credential, presentation and revocation handling updates diff --git a/aries_cloudagent/commands/upgrade.py b/aries_cloudagent/commands/upgrade.py index 13840f1e2d..4b2409e20e 100644 --- a/aries_cloudagent/commands/upgrade.py +++ b/aries_cloudagent/commands/upgrade.py @@ -137,7 +137,7 @@ async def upgrade(settings: dict): "the config." ) if upgrade_from_version == upgrade_to_version: - print( + raise UpgradeError( f"Version {upgrade_from_version} to upgrade from and " f"current version to upgrade to {upgrade_to_version} " "are same." From fda3faf79fc243a20821dd9b53052ed9f61cfafe Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Wed, 15 Mar 2023 07:39:50 -0700 Subject: [PATCH 721/872] Exit if upgrde version is the same as current Signed-off-by: Ian Costanzo Signed-off-by: JSyro --- aries_cloudagent/commands/upgrade.py | 108 ++++++++++++++------------- 1 file changed, 55 insertions(+), 53 deletions(-) diff --git a/aries_cloudagent/commands/upgrade.py b/aries_cloudagent/commands/upgrade.py index 4b2409e20e..938797ed54 100644 --- a/aries_cloudagent/commands/upgrade.py +++ b/aries_cloudagent/commands/upgrade.py @@ -137,66 +137,68 @@ async def upgrade(settings: dict): "the config." ) if upgrade_from_version == upgrade_to_version: - raise UpgradeError( + print( f"Version {upgrade_from_version} to upgrade from and " f"current version to upgrade to {upgrade_to_version} " "are same." ) - if upgrade_from_version not in sorted_versions_found_in_config: - raise UpgradeError( - f"No upgrade configuration found for {upgrade_from_version}" + else: + if upgrade_from_version not in sorted_versions_found_in_config: + raise UpgradeError( + f"No upgrade configuration found for {upgrade_from_version}" + ) + upgrade_from_version_index = sorted_versions_found_in_config.index( + upgrade_from_version ) - upgrade_from_version_index = sorted_versions_found_in_config.index( - upgrade_from_version - ) - for config_from_version in sorted_versions_found_in_config[ - upgrade_from_version_index: - ]: - print(f"Running upgrade process for {config_from_version}") - upgrade_config = upgrade_configs.get(config_from_version) - # Step 1 re-saving all BaseRecord and BaseExchangeRecord - if "resave_records" in upgrade_config: - resave_record_paths = upgrade_config.get("resave_records") - for record_path in resave_record_paths: - try: - record_type = ClassLoader.load_class(record_path) - except ClassNotFoundError as err: - raise UpgradeError( - f"Unknown Record type {record_path}" - ) from err - if not issubclass(record_type, BaseRecord): - raise UpgradeError( - f"Only BaseRecord can be resaved, found: {str(record_type)}" - ) - async with root_profile.session() as session: - all_records = await record_type.query(session) - for record in all_records: - await record.save( - session, - reason="re-saving record during ACA-Py upgrade process", - ) - if len(all_records) == 0: - print(f"No records of {str(record_type)} found") - else: - print( - f"All records of {str(record_type)} successfully re-saved" + for config_from_version in sorted_versions_found_in_config[ + upgrade_from_version_index: + ]: + print(f"Running upgrade process for {config_from_version}") + upgrade_config = upgrade_configs.get(config_from_version) + # Step 1 re-saving all BaseRecord and BaseExchangeRecord + if "resave_records" in upgrade_config: + resave_record_paths = upgrade_config.get("resave_records") + for record_path in resave_record_paths: + try: + record_type = ClassLoader.load_class(record_path) + except ClassNotFoundError as err: + raise UpgradeError( + f"Unknown Record type {record_path}" + ) from err + if not issubclass(record_type, BaseRecord): + raise UpgradeError( + f"Only BaseRecord can be resaved, found: {str(record_type)}" ) - # Step 2 Update existing records, if required - if ( - "update_existing_records" in upgrade_config - and upgrade_config.get("update_existing_records") is True - ): - update_existing_recs_callable = ( - version_upgrade_config_inst.get_update_existing_func( - config_from_version - ) - ) - if not update_existing_recs_callable: - raise UpgradeError( - "No update_existing_records function " - f"specified for {config_from_version}" + async with root_profile.session() as session: + all_records = await record_type.query(session) + for record in all_records: + await record.save( + session, + reason="re-saving record during ACA-Py upgrade process", + ) + if len(all_records) == 0: + print(f"No records of {str(record_type)} found") + else: + print( + f"All records of {str(record_type)} successfully re-saved" + ) + # Step 2 Update existing records, if required + if ( + "update_existing_records" in upgrade_config + and upgrade_config.get("update_existing_records") is True + ): + update_existing_recs_callable = ( + version_upgrade_config_inst.get_update_existing_func( + config_from_version + ) ) - await update_existing_recs_callable(root_profile) + if not update_existing_recs_callable: + raise UpgradeError( + "No update_existing_records function " + f"specified for {config_from_version}" + ) + await update_existing_recs_callable(root_profile) + # Update storage version async with root_profile.session() as session: storage = session.inject(BaseStorage) From c78e4f3c48126f41fa68b862dcf7fa63718ea72c Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Wed, 15 Mar 2023 07:50:57 -0700 Subject: [PATCH 722/872] Formatting issues Signed-off-by: Ian Costanzo Signed-off-by: JSyro --- aries_cloudagent/commands/tests/test_upgrade.py | 11 +++++------ aries_cloudagent/commands/upgrade.py | 14 +++++++------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/aries_cloudagent/commands/tests/test_upgrade.py b/aries_cloudagent/commands/tests/test_upgrade.py index a44f020b90..704f4cb43b 100644 --- a/aries_cloudagent/commands/tests/test_upgrade.py +++ b/aries_cloudagent/commands/tests/test_upgrade.py @@ -144,12 +144,11 @@ async def test_upgrade_x_same_version(self): ) ), ): - with self.assertRaises(UpgradeError): - await test_module.upgrade( - { - "upgrade.config_path": "./aries_cloudagent/commands/default_version_upgrade_config.yml", - } - ) + await test_module.upgrade( + { + "upgrade.config_path": "./aries_cloudagent/commands/default_version_upgrade_config.yml", + } + ) async def test_upgrade_missing_from_version(self): with async_mock.patch.object( diff --git a/aries_cloudagent/commands/upgrade.py b/aries_cloudagent/commands/upgrade.py index 938797ed54..fb037c6b2e 100644 --- a/aries_cloudagent/commands/upgrade.py +++ b/aries_cloudagent/commands/upgrade.py @@ -160,27 +160,27 @@ async def upgrade(settings: dict): resave_record_paths = upgrade_config.get("resave_records") for record_path in resave_record_paths: try: - record_type = ClassLoader.load_class(record_path) + rec_type = ClassLoader.load_class(record_path) except ClassNotFoundError as err: raise UpgradeError( f"Unknown Record type {record_path}" ) from err - if not issubclass(record_type, BaseRecord): + if not issubclass(rec_type, BaseRecord): raise UpgradeError( - f"Only BaseRecord can be resaved, found: {str(record_type)}" + f"Only BaseRecord can be resaved, found: {str(rec_type)}" ) async with root_profile.session() as session: - all_records = await record_type.query(session) + all_records = await rec_type.query(session) for record in all_records: await record.save( session, - reason="re-saving record during ACA-Py upgrade process", + reason="re-saving record during the upgrade process", ) if len(all_records) == 0: - print(f"No records of {str(record_type)} found") + print(f"No records of {str(rec_type)} found") else: print( - f"All records of {str(record_type)} successfully re-saved" + f"All recs of {str(rec_type)} successfully re-saved" ) # Step 2 Update existing records, if required if ( From 3362d9a23d5c6898dcea5600a42f71f8e3841dc7 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Thu, 23 Mar 2023 12:58:21 -0700 Subject: [PATCH 723/872] Adds the upgrade command YML file to the PyPi Release Signed-off-by: Stephen Curran Signed-off-by: Stephen Curran Signed-off-by: Stephen Curran Signed-off-by: JSyro --- MANIFEST.in | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST.in b/MANIFEST.in index 494b5def3a..ef5132a345 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,5 @@ include aries_cloudagent/config/default_logging_config.ini +include aries_cloudagent/commands/default_version_upgrade_config.yml include requirements.txt include requirements.dev.txt include requirements.indy.txt From 71768a39fbf229ef12f18883186fbab274694cb4 Mon Sep 17 00:00:00 2001 From: JSyro Date: Wed, 29 Mar 2023 10:14:33 -0700 Subject: [PATCH 724/872] update pytest to resolve the `lineno` issue on test collection Signed-off-by: JSyro Signed-off-by: JSyro --- requirements.dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.dev.txt b/requirements.dev.txt index e9c5d55595..5b4139ea3e 100644 --- a/requirements.dev.txt +++ b/requirements.dev.txt @@ -1,6 +1,6 @@ asynctest==0.13.0 async-case~=10.1 -pytest~=5.4.0 +pytest~=6.2 pytest-asyncio==0.14.0 pytest-cov==2.10.1 pytest-flake8==1.0.6 From 90dafb3762583cea85eb96d72050844b3b30aaa7 Mon Sep 17 00:00:00 2001 From: JSyro Date: Wed, 29 Mar 2023 10:32:02 -0700 Subject: [PATCH 725/872] remove unused import. Signed-off-by: JSyro --- .../protocols/issue_credential/v1_0/tests/test_manager.py | 1 - .../issue_credential/v2_0/formats/indy/tests/test_handler.py | 1 - 2 files changed, 2 deletions(-) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py index 838f5abdf4..6053bd81f0 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py @@ -5,7 +5,6 @@ from time import time from asynctest import mock as async_mock, TestCase as AsyncTestCase -from more_itertools import side_effect from .....core.in_memory import InMemoryProfile from .....cache.base import BaseCache diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/formats/indy/tests/test_handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/formats/indy/tests/test_handler.py index 06028fa00e..7366437304 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/formats/indy/tests/test_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/formats/indy/tests/test_handler.py @@ -5,7 +5,6 @@ from asynctest import TestCase as AsyncTestCase from asynctest import mock as async_mock from marshmallow import ValidationError -from more_itertools import side_effect from .. import handler as test_module From 6ab2f5d08943a3ac4a9b1c817b5c14065fd6537a Mon Sep 17 00:00:00 2001 From: JSyro Date: Wed, 29 Mar 2023 10:58:34 -0700 Subject: [PATCH 726/872] update rlp package. Signed-off-by: JSyro --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index fe33a1021c..20b8cf9652 100644 --- a/requirements.txt +++ b/requirements.txt @@ -24,6 +24,6 @@ pydid~=0.3.6 jsonpath_ng==1.5.2 pytz~=2021.1 python-dateutil~=2.8.1 -rlp==0.5.1 +rlp==1.2.0 unflatten~=0.1 qrcode[pil]~=6.1 From 18b3427001e6913311e1791a2bc8f4a89d36430c Mon Sep 17 00:00:00 2001 From: JSyro Date: Wed, 29 Mar 2023 13:43:51 -0700 Subject: [PATCH 727/872] disable for now. Signed-off-by: JSyro --- aries_cloudagent/admin/tests/test_admin_server.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/aries_cloudagent/admin/tests/test_admin_server.py b/aries_cloudagent/admin/tests/test_admin_server.py index 94f3642edc..049d70c42d 100644 --- a/aries_cloudagent/admin/tests/test_admin_server.py +++ b/aries_cloudagent/admin/tests/test_admin_server.py @@ -334,6 +334,7 @@ async def test_visit_insecure_mode(self): await server.stop() + @pytest.mark.skip(reason="async_case library not compatible with python 3.10") async def test_visit_secure_mode(self): settings = { "admin.admin_insecure_mode": False, @@ -386,6 +387,7 @@ async def test_visit_secure_mode(self): await server.stop() + @pytest.mark.skip(reason="async_case library not compatible with python 3.10") async def test_query_config(self): settings = { "admin.admin_insecure_mode": False, From 7c3959a8b5ca29b7b48fc1793c75a232f4c3871d Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Wed, 29 Mar 2023 16:13:31 -0700 Subject: [PATCH 728/872] fixes Signed-off-by: Shaanjot Gill --- .../commands/tests/test_upgrade.py | 35 ++++------------ aries_cloudagent/commands/upgrade.py | 24 +++++++---- aries_cloudagent/core/conductor.py | 8 +++- aries_cloudagent/core/tests/test_conductor.py | 41 ++----------------- 4 files changed, 34 insertions(+), 74 deletions(-) diff --git a/aries_cloudagent/commands/tests/test_upgrade.py b/aries_cloudagent/commands/tests/test_upgrade.py index 704f4cb43b..258fe8b1bf 100644 --- a/aries_cloudagent/commands/tests/test_upgrade.py +++ b/aries_cloudagent/commands/tests/test_upgrade.py @@ -167,11 +167,13 @@ async def test_upgrade_missing_from_version(self): ), async_mock.patch.object( ConnRecord, "save", async_mock.CoroutineMock() ): - await test_module.upgrade( - { - "upgrade.config_path": "./aries_cloudagent/commands/default_version_upgrade_config.yml", - } - ) + with self.assertRaises(UpgradeError) as ctx: + await test_module.upgrade( + { + "upgrade.config_path": "./aries_cloudagent/commands/default_version_upgrade_config.yml", + } + ) + assert "No upgrade from version found in wallet or" in str(ctx.exception) async def test_upgrade_x_callable_not_set(self): with async_mock.patch.object( @@ -196,7 +198,7 @@ async def test_upgrade_x_callable_not_set(self): }, "update_existing_records": True, }, - "v0.6.0": {"update_existing_records": True}, + "v0.6.0": {"update_existing_records_b": True}, } ), ): @@ -206,7 +208,7 @@ async def test_upgrade_x_callable_not_set(self): "upgrade.from_version": "v0.6.0", } ) - assert "No update_existing_records function specified" in str(ctx.exception) + assert "No function specified for" in str(ctx.exception) async def test_upgrade_x_class_not_found(self): with async_mock.patch.object( @@ -315,25 +317,6 @@ async def test_upgrade_x_invalid_config(self): await test_module.upgrade({}) assert "No version configs found in" in str(ctx.exception) - async def test_upgrade_x_from_version_not_in_config(self): - with async_mock.patch.object( - test_module, - "wallet_config", - async_mock.CoroutineMock( - return_value=( - self.profile, - async_mock.CoroutineMock(did="public DID", verkey="verkey"), - ) - ), - ): - with self.assertRaises(UpgradeError) as ctx: - await test_module.upgrade( - { - "upgrade.from_version": "v1.2.3", - } - ) - assert "No upgrade configuration found for" in str(ctx.exception) - def test_main(self): with async_mock.patch.object( test_module, "__name__", "__main__" diff --git a/aries_cloudagent/commands/upgrade.py b/aries_cloudagent/commands/upgrade.py index 61e4c672c3..22fd3a6ac8 100644 --- a/aries_cloudagent/commands/upgrade.py +++ b/aries_cloudagent/commands/upgrade.py @@ -65,8 +65,11 @@ def setup_version_upgrade_config(self, path: str): "resave_records" ).get("base_exch_record_path") version_config_dict[version]["resave_records"] = recs_list - config_key_set = set(version_config_dict.get(version).keys()) - config_key_set.remove("resave_records") + config_key_set = set(provided_config.keys()) + try: + config_key_set.remove("resave_records") + except KeyError: + pass for executable in config_key_set: version_config_dict[version][executable] = ( provided_config.get(executable) or False @@ -93,10 +96,6 @@ def get_upgrade_version_list( sorted_version_list: Optional[List] = None, config_path: Optional[str] = None, ) -> List: - if not sorted_version_list and not config_path: - raise UpgradeError( - f"No sorted version list from config or path to config provided." - ) if not sorted_version_list: version_upgrade_config_inst = VersionUpgradeConfig(config_path) upgrade_configs = version_upgrade_config_inst.upgrade_configs @@ -189,9 +188,13 @@ async def upgrade(settings: dict): and not upgrade_from_version_setting ): upgrade_from_version = upgrade_from_version_config - + if not upgrade_from_version: + raise UpgradeError( + "No upgrade from version found in wallet or settings [--from-version]" + ) upgrade_version_in_config = get_upgrade_version_list( - sorted_versions_found_in_config, upgrade_from_version + sorted_version_list=sorted_versions_found_in_config, + from_version=upgrade_from_version, ) to_update_flag = False if upgrade_from_version == upgrade_to_version: @@ -219,7 +222,10 @@ async def upgrade(settings: dict): # Step 2 Update existing records, if required config_key_set = set(upgrade_config.keys()) - config_key_set.remove("resave_records") + try: + config_key_set.remove("resave_records") + except KeyError: + pass for callable_name in list(config_key_set): if upgrade_config.get(callable_name) is False: continue diff --git a/aries_cloudagent/core/conductor.py b/aries_cloudagent/core/conductor.py index 79033974ca..3f3816b996 100644 --- a/aries_cloudagent/core/conductor.py +++ b/aries_cloudagent/core/conductor.py @@ -26,7 +26,11 @@ from ..config.logging import LoggingConfigurator from ..config.provider import ClassProvider from ..config.wallet import wallet_config -from ..commands.upgrade import get_upgrade_version_list, add_version_record, upgrade +from ..commands.upgrade import ( + get_upgrade_version_list, + add_version_record, + upgrade, +) from ..core.profile import Profile from ..indy.verifier import IndyVerifier @@ -348,7 +352,7 @@ async def start(self) -> None: "upgrade later." ) ) - await add_version_record(self.root_profile, agent_version) + await add_version_record(profile=self.root_profile, version=agent_version) # Create a static connection for use by the test-suite if context.settings.get("debug.test_suite_endpoint"): diff --git a/aries_cloudagent/core/tests/test_conductor.py b/aries_cloudagent/core/tests/test_conductor.py index d4e73b4a8f..4d373745e0 100644 --- a/aries_cloudagent/core/tests/test_conductor.py +++ b/aries_cloudagent/core/tests/test_conductor.py @@ -1370,43 +1370,6 @@ async def test_setup_ledger_only_base(self): await conductor.setup() mock_genesis_load.assert_called_once() - async def test_startup_x_version_mismatch(self): - builder: ContextBuilder = StubContextBuilder(self.test_settings) - conductor = test_module.Conductor(builder) - - with async_mock.patch.object( - test_module, "InboundTransportManager", autospec=True - ) as mock_inbound_mgr, async_mock.patch.object( - test_module, "OutboundTransportManager", autospec=True - ) as mock_outbound_mgr, async_mock.patch.object( - test_module, "LOGGER" - ) as mock_logger, async_mock.patch.object( - BaseStorage, - "find_record", - async_mock.AsyncMock(return_value=async_mock.MagicMock(value=f"v0.6.0")), - ): - mock_outbound_mgr.return_value.registered_transports = { - "test": async_mock.MagicMock(schemes=["http"]) - } - await conductor.setup() - - session = await conductor.root_profile.session() - - wallet = session.inject(BaseWallet) - await wallet.create_public_did( - SOV, - ED25519, - ) - - mock_inbound_mgr.return_value.setup.assert_awaited_once() - mock_outbound_mgr.return_value.setup.assert_awaited_once() - - mock_inbound_mgr.return_value.registered_transports = {} - mock_outbound_mgr.return_value.registered_transports = {} - with self.assertRaises(RuntimeError): - await conductor.start() - mock_logger.exception.assert_called_once() - async def test_startup_x_no_storage_version(self): builder: ContextBuilder = StubContextBuilder(self.test_settings) conductor = test_module.Conductor(builder) @@ -1421,6 +1384,10 @@ async def test_startup_x_no_storage_version(self): BaseStorage, "find_record", async_mock.AsyncMock(side_effect=StorageNotFoundError()), + ), async_mock.patch.object( + test_module, + "add_version_record", + async_mock.AsyncMock(), ): mock_outbound_mgr.return_value.registered_transports = { "test": async_mock.MagicMock(schemes=["http"]) From d305f538d454c5a24de6e8bbed294ca763cb211f Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Thu, 30 Mar 2023 06:03:07 -0700 Subject: [PATCH 729/872] fixes Signed-off-by: Shaanjot Gill --- aries_cloudagent/commands/upgrade.py | 16 +++++++++++----- aries_cloudagent/config/argparse.py | 5 +++-- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/aries_cloudagent/commands/upgrade.py b/aries_cloudagent/commands/upgrade.py index 22fd3a6ac8..1d7dec5d61 100644 --- a/aries_cloudagent/commands/upgrade.py +++ b/aries_cloudagent/commands/upgrade.py @@ -96,6 +96,7 @@ def get_upgrade_version_list( sorted_version_list: Optional[List] = None, config_path: Optional[str] = None, ) -> List: + """Get available versions from the upgrade config.""" if not sorted_version_list: version_upgrade_config_inst = VersionUpgradeConfig(config_path) upgrade_configs = version_upgrade_config_inst.upgrade_configs @@ -112,11 +113,15 @@ def get_upgrade_version_list( async def add_version_record(profile: Profile, version: str): + """Add an acapy_version storage record for provided version.""" async with profile.session() as session: storage = session.inject(BaseStorage) - version_storage_record = await storage.find_record( - type_filter=RECORD_TYPE_ACAPY_VERSION, tag_query={} - ) + try: + version_storage_record = await storage.find_record( + type_filter=RECORD_TYPE_ACAPY_VERSION, tag_query={} + ) + except StorageNotFoundError: + version_storage_record = None if not version_storage_record: await storage.add_record( StorageRecord( @@ -139,7 +144,6 @@ async def upgrade(settings: dict): ) upgrade_configs = version_upgrade_config_inst.upgrade_configs root_profile, _ = await wallet_config(context) - version_storage_record = None upgrade_to_version = f"v{__version__}" versions_found_in_config = upgrade_configs.keys() sorted_versions_found_in_config = sorted( @@ -157,6 +161,7 @@ async def upgrade(settings: dict): upgrade_from_version_config = version_storage_record.value except StorageNotFoundError: LOGGER.info("No ACA-Py version found in wallet storage.") + version_storage_record = None if "upgrade.from_version" in settings: upgrade_from_version_setting = settings.get("upgrade.from_version") @@ -277,7 +282,8 @@ async def upgrade(settings: dict): version_storage_record, upgrade_to_version, {} ) LOGGER.info( - f"{RECORD_TYPE_ACAPY_VERSION} storage record set to {upgrade_to_version}" + f"{RECORD_TYPE_ACAPY_VERSION} storage record " + f"set to {upgrade_to_version}" ) await root_profile.close() except BaseError as e: diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index ff26a55071..f91f99f592 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -2058,8 +2058,9 @@ def add_arguments(self, parser: ArgumentParser): action="store_true", env_var="ACAPY_UPGRADE_FORCE_UPGRADE", help=( - "Forces the '—from-version' argument to override the version retrieved from " - "secure storage when calculating upgrades to be run." + "Forces the '—from-version' argument to override the version " + "retrieved from secure storage when calculating upgrades to " + "be run." ), ) From ccce11f2d588e062c6067852575185145a88958b Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Thu, 30 Mar 2023 08:09:39 -0700 Subject: [PATCH 730/872] unit test coverage Signed-off-by: Shaanjot Gill --- .../commands/tests/test_upgrade.py | 83 +++++++++++++++++-- aries_cloudagent/commands/upgrade.py | 2 +- .../config/tests/test_argparse.py | 2 + aries_cloudagent/core/tests/test_conductor.py | 64 +++++++++++++- 4 files changed, 139 insertions(+), 12 deletions(-) diff --git a/aries_cloudagent/commands/tests/test_upgrade.py b/aries_cloudagent/commands/tests/test_upgrade.py index 258fe8b1bf..f57a77b1cc 100644 --- a/aries_cloudagent/commands/tests/test_upgrade.py +++ b/aries_cloudagent/commands/tests/test_upgrade.py @@ -17,10 +17,7 @@ class TestUpgrade(AsyncTestCase): async def setUp(self): self.session = InMemoryProfile.test_session() self.profile = self.session.profile - - self.session_storage = InMemoryProfile.test_session() - self.profile_storage = self.session_storage.profile - self.storage = self.session_storage.inject(BaseStorage) + self.storage = self.session.inject(BaseStorage) record = StorageRecord( "acapy_version", "v0.7.2", @@ -37,7 +34,7 @@ async def test_upgrade_storage_from_version_included(self): "wallet_config", async_mock.CoroutineMock( return_value=( - self.profile_storage, + self.profile, async_mock.CoroutineMock(did="public DID", verkey="verkey"), ) ), @@ -61,7 +58,7 @@ async def test_upgrade_storage_missing_from_version(self): "wallet_config", async_mock.CoroutineMock( return_value=( - self.profile_storage, + self.profile, async_mock.CoroutineMock(did="public DID", verkey="verkey"), ) ), @@ -98,6 +95,10 @@ async def test_upgrade_from_version(self): ) async def test_upgrade_callable(self): + version_storage_record = await self.storage.find_record( + type_filter="acapy_version", tag_query={} + ) + await self.storage.delete_record(version_storage_record) with async_mock.patch.object( test_module, "wallet_config", @@ -139,7 +140,7 @@ async def test_upgrade_x_same_version(self): "wallet_config", async_mock.CoroutineMock( return_value=( - self.profile_storage, + self.profile, async_mock.CoroutineMock(did="public DID", verkey="verkey"), ) ), @@ -151,6 +152,10 @@ async def test_upgrade_x_same_version(self): ) async def test_upgrade_missing_from_version(self): + version_storage_record = await self.storage.find_record( + type_filter="acapy_version", tag_query={} + ) + await self.storage.delete_record(version_storage_record) with async_mock.patch.object( test_module, "wallet_config", @@ -176,6 +181,10 @@ async def test_upgrade_missing_from_version(self): assert "No upgrade from version found in wallet or" in str(ctx.exception) async def test_upgrade_x_callable_not_set(self): + version_storage_record = await self.storage.find_record( + type_filter="acapy_version", tag_query={} + ) + await self.storage.delete_record(version_storage_record) with async_mock.patch.object( test_module, "wallet_config", @@ -270,7 +279,8 @@ async def test_execute(self): "--upgrade-config", "./aries_cloudagent/config/tests/test-acapy-upgrade-config.yaml", "--from-version", - "v0.7.2", + "v0.7.0", + "--force-upgrade", ] ) @@ -307,6 +317,63 @@ async def test_upgrade_x_invalid_record_type(self): ) assert "Only BaseRecord can be resaved" in str(ctx.exception) + async def test_upgrade_force(self): + with async_mock.patch.object( + test_module, + "wallet_config", + async_mock.CoroutineMock( + return_value=( + self.profile, + async_mock.CoroutineMock(did="public DID", verkey="verkey"), + ) + ), + ), async_mock.patch.object( + test_module.yaml, + "safe_load", + async_mock.MagicMock( + return_value={ + "v0.7.2": { + "resave_records": { + "base_record_path": [ + "aries_cloudagent.connections.models.conn_record.ConnRecord" + ], + }, + "update_existing_records": True, + }, + "v0.7.3": { + "update_existing_records": True, + }, + "v0.7.1": { + "update_existing_records": False, + }, + } + ), + ): + await test_module.upgrade( + { + "upgrade.from_version": "v0.7.0", + "upgrade.force_upgrade": True, + } + ) + + async def test_get_upgrade_version_list(self): + assert len(test_module.get_upgrade_version_list(from_version="v0.7.2")) >= 1 + + async def test_add_version_record(self): + await test_module.add_version_record(self.profile, "v0.7.4") + version_storage_record = await self.storage.find_record( + type_filter="acapy_version", tag_query={} + ) + assert version_storage_record.value == "v0.7.4" + await self.storage.delete_record(version_storage_record) + with self.assertRaises(test_module.StorageNotFoundError): + await self.storage.find_record(type_filter="acapy_version", tag_query={}) + await test_module.add_version_record(self.profile, "v0.7.5") + version_storage_record = await self.storage.find_record( + type_filter="acapy_version", tag_query={} + ) + assert version_storage_record.value == "v0.7.5" + async def test_upgrade_x_invalid_config(self): with async_mock.patch.object( test_module.yaml, diff --git a/aries_cloudagent/commands/upgrade.py b/aries_cloudagent/commands/upgrade.py index 1d7dec5d61..be98d043ab 100644 --- a/aries_cloudagent/commands/upgrade.py +++ b/aries_cloudagent/commands/upgrade.py @@ -172,7 +172,7 @@ async def upgrade(settings: dict): ) ) - force_upgrade_flag = root_profile.settings.get("upgrade.force_upgrade") or False + force_upgrade_flag = settings.get("upgrade.force_upgrade") or False if upgrade_from_version_config and upgrade_from_version_setting: if ( package_version.parse(upgrade_from_version_config) diff --git a/aries_cloudagent/config/tests/test_argparse.py b/aries_cloudagent/config/tests/test_argparse.py index e9303b8987..044bb217ad 100644 --- a/aries_cloudagent/config/tests/test_argparse.py +++ b/aries_cloudagent/config/tests/test_argparse.py @@ -111,6 +111,7 @@ async def test_upgrade_config(self): "./aries_cloudagent/config/tests/test-acapy-upgrade-config.yml", "--from-version", "v0.7.2", + "--force-upgrade", ] ) @@ -118,6 +119,7 @@ async def test_upgrade_config(self): result.upgrade_config_path == "./aries_cloudagent/config/tests/test-acapy-upgrade-config.yml" ) + assert result.force_upgrade is True settings = group.get_settings(result) diff --git a/aries_cloudagent/core/tests/test_conductor.py b/aries_cloudagent/core/tests/test_conductor.py index 4d373745e0..5ce750cb4a 100644 --- a/aries_cloudagent/core/tests/test_conductor.py +++ b/aries_cloudagent/core/tests/test_conductor.py @@ -101,7 +101,7 @@ async def build_context(self) -> InjectionContext: class TestConductor(IsolatedAsyncioTestCase, Config, TestDIDs): - async def test_startup(self): + async def test_startup_version_record_exists(self): builder: ContextBuilder = StubContextBuilder(self.test_settings) conductor = test_module.Conductor(builder) @@ -114,9 +114,67 @@ async def test_startup(self): ) as mock_logger, async_mock.patch.object( BaseStorage, "find_record", - async_mock.AsyncMock( - return_value=async_mock.MagicMock(value=f"v{__version__}") + async_mock.AsyncMock(return_value=async_mock.MagicMock(value="v0.7.3")), + ), async_mock.patch.object( + test_module, + "get_upgrade_version_list", + async_mock.MagicMock( + return_value=["v0.7.4", "0.7.5", "v0.8.0rc1", "v8.0.0"] ), + ), async_mock.patch.object( + test_module, + "upgrade", + async_mock.AsyncMock(), + ): + mock_outbound_mgr.return_value.registered_transports = { + "test": async_mock.MagicMock(schemes=["http"]) + } + await conductor.setup() + + session = await conductor.root_profile.session() + + wallet = session.inject(BaseWallet) + await wallet.create_public_did( + SOV, + ED25519, + ) + + mock_inbound_mgr.return_value.setup.assert_awaited_once() + mock_outbound_mgr.return_value.setup.assert_awaited_once() + + mock_inbound_mgr.return_value.registered_transports = {} + mock_outbound_mgr.return_value.registered_transports = {} + + await conductor.start() + + mock_inbound_mgr.return_value.start.assert_awaited_once_with() + mock_outbound_mgr.return_value.start.assert_awaited_once_with() + + mock_logger.print_banner.assert_called_once() + + await conductor.stop() + + mock_inbound_mgr.return_value.stop.assert_awaited_once_with() + mock_outbound_mgr.return_value.stop.assert_awaited_once_with() + + async def test_startup_version_record_not_exists(self): + builder: ContextBuilder = StubContextBuilder(self.test_settings) + conductor = test_module.Conductor(builder) + + with async_mock.patch.object( + test_module, "InboundTransportManager", autospec=True + ) as mock_inbound_mgr, async_mock.patch.object( + test_module, "OutboundTransportManager", autospec=True + ) as mock_outbound_mgr, async_mock.patch.object( + test_module, "LoggingConfigurator", autospec=True + ) as mock_logger, async_mock.patch.object( + BaseStorage, + "find_record", + async_mock.AsyncMock(side_effect=StorageNotFoundError()), + ), async_mock.patch.object( + test_module, + "add_version_record", + async_mock.AsyncMock(), ): mock_outbound_mgr.return_value.registered_transports = { "test": async_mock.MagicMock(schemes=["http"]) From 930535287a579138daf2f05d1ac19a4d1a0b68cc Mon Sep 17 00:00:00 2001 From: JSyro Date: Thu, 30 Mar 2023 13:02:32 -0700 Subject: [PATCH 731/872] change flake8 version. Signed-off-by: JSyro --- requirements.dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.dev.txt b/requirements.dev.txt index 5b4139ea3e..f2b6a056d4 100644 --- a/requirements.dev.txt +++ b/requirements.dev.txt @@ -6,7 +6,7 @@ pytest-cov==2.10.1 pytest-flake8==1.0.6 mock~=4.0 -flake8==3.9.0 +flake8==5.0.4 # flake8-rst-docstrings==0.0.8 flake8-docstrings==1.5.0 flake8-rst==0.7.2 From 8600d59d9c816c18dbbafd4111c9c2667643650c Mon Sep 17 00:00:00 2001 From: JSyro Date: Thu, 30 Mar 2023 13:30:27 -0700 Subject: [PATCH 732/872] too far. try this Signed-off-by: JSyro --- requirements.dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.dev.txt b/requirements.dev.txt index f2b6a056d4..4f7b1de964 100644 --- a/requirements.dev.txt +++ b/requirements.dev.txt @@ -6,7 +6,7 @@ pytest-cov==2.10.1 pytest-flake8==1.0.6 mock~=4.0 -flake8==5.0.4 +flake8==4.0.1 # flake8-rst-docstrings==0.0.8 flake8-docstrings==1.5.0 flake8-rst==0.7.2 From 47d5d51c6b3ed29488ffb2cc4aba62dba555e2ae Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Thu, 30 Mar 2023 14:29:43 -0700 Subject: [PATCH 733/872] Doc update and some test scripts Signed-off-by: Ian Costanzo --- demo/docker-test/README.md | 39 ++++++++++++ demo/docker-test/db/Dockerfile | 3 + demo/docker-test/db/init-postgres-role.sh | 6 ++ demo/docker-test/docker-compose-agent.yml | 47 ++++++++++++++ demo/docker-test/docker-compose-wallet.yml | 21 +++++++ docs/GettingStartedAriesDev/PlugIns.md | 73 ++++++++++++---------- 6 files changed, 157 insertions(+), 32 deletions(-) create mode 100644 demo/docker-test/README.md create mode 100644 demo/docker-test/db/Dockerfile create mode 100644 demo/docker-test/db/init-postgres-role.sh create mode 100644 demo/docker-test/docker-compose-agent.yml create mode 100644 demo/docker-test/docker-compose-wallet.yml diff --git a/demo/docker-test/README.md b/demo/docker-test/README.md new file mode 100644 index 0000000000..e415637cf4 --- /dev/null +++ b/demo/docker-test/README.md @@ -0,0 +1,39 @@ +# Aca-Py Docker Test Scripts + +These docker compose files allow you to run an aca-py instance against a postgres database. + +There are separate scripts for starting the database and agent to allow you to restart the agent against the same postgres database. + +This is useful for - for example - initializing a database with on older aca-py version, and then running an upgrade to a newer version. + +To start the database: + +```bash +docker-compose -f docker-compose-wallet.yml up +``` + +To start the agent: + +```bash +docker-compose -f docker-compose-agent.yml up +``` + +Note you can edit the docker-compose file to change the aca-py version. + +To stop the agent: + +```bash +docker-compose -f docker-compose-agent.yml rm +``` + +To stop the database + +```bash +docker-compose -f docker-compose-wallet.yml rm +``` + +To remove the database volume: + +```bash +docker volume rm docker-test_wallet-db-data +``` diff --git a/demo/docker-test/db/Dockerfile b/demo/docker-test/db/Dockerfile new file mode 100644 index 0000000000..5d4e896508 --- /dev/null +++ b/demo/docker-test/db/Dockerfile @@ -0,0 +1,3 @@ +FROM postgres:14 +COPY ./init-postgres-role.sh /docker-entrypoint-initdb.d/init-postgres-role.sh +CMD ["docker-entrypoint.sh", "postgres"] \ No newline at end of file diff --git a/demo/docker-test/db/init-postgres-role.sh b/demo/docker-test/db/init-postgres-role.sh new file mode 100644 index 0000000000..3178a06c97 --- /dev/null +++ b/demo/docker-test/db/init-postgres-role.sh @@ -0,0 +1,6 @@ +#!/bin/bash +set -e + +psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" --set username=$POSTGRES_ADMIN_USER --set password="'$POSTGRES_ADMIN_PASSWORD'"<<-EOSQL + CREATE ROLE :username WITH LOGIN SUPERUSER INHERIT CREATEDB CREATEROLE REPLICATION ENCRYPTED PASSWORD :password; +EOSQL \ No newline at end of file diff --git a/demo/docker-test/docker-compose-agent.yml b/demo/docker-test/docker-compose-agent.yml new file mode 100644 index 0000000000..b76c8e93f9 --- /dev/null +++ b/demo/docker-test/docker-compose-agent.yml @@ -0,0 +1,47 @@ +version: "3" +services: + vcr-agent: + #image: bcgovimages/aries-cloudagent:py36-1.16-1_0.7.5 + build: + context: ../../ + dockerfile: docker/Dockerfile.run + ports: + - 8010:8010 + - 8001:8001 + networks: + - wallet-net + entrypoint: /bin/bash + command: [ + "-c", + "sleep 5; \ + aca-py start \ + --auto-provision \ + --seed '00000000o_faber_secondary_school' \ + --inbound-transport http '0.0.0.0' 8001 \ + --endpoint 'http://host.docker.internal:8001' \ + --outbound-transport http \ + --genesis-url 'http://test.bcovrin.vonx.io/genesis' \ + --auto-accept-invites \ + --auto-accept-requests \ + --auto-ping-connection \ + --auto-respond-messages \ + --auto-respond-credential-proposal \ + --auto-respond-credential-offer \ + --auto-respond-credential-request \ + --auto-verify-presentation \ + --wallet-type 'askar' \ + --wallet-name 'acapy_agent_wallet' \ + --wallet-key 'key' \ + --wallet-storage-type 'postgres_storage' \ + --wallet-storage-config '{\"url\":\"wallet-db:5432\",\"max_connections\":5}' \ + --wallet-storage-creds '{\"account\":\"DB_USER\",\"password\":\"DB_PASSWORD\",\"admin_account\":\"postgres\",\"admin_password\":\"mysecretpassword\"}' \ + --admin '0.0.0.0' 8010 \ + --admin-insecure-mode \ + --label 'tester_agent' \ + --log-level 'info' ", + ] + +networks: + wallet-net: + external: + name: docker-test_wallet-net diff --git a/demo/docker-test/docker-compose-wallet.yml b/demo/docker-test/docker-compose-wallet.yml new file mode 100644 index 0000000000..c000ef7a14 --- /dev/null +++ b/demo/docker-test/docker-compose-wallet.yml @@ -0,0 +1,21 @@ +version: "3" +services: + wallet-db: + build: ./db + environment: + - POSTGRES_USER=DB_USER + - POSTGRES_PASSWORD=DB_PASSWORD + - POSTGRES_ADMIN_USER=postgres + - POSTGRES_ADMIN_PASSWORD=mysecretpassword + networks: + - wallet-net + ports: + - 5432:5432 + volumes: + - wallet-db-data:/var/lib/pgsql/data + +volumes: + wallet-db-data: + +networks: + wallet-net: diff --git a/docs/GettingStartedAriesDev/PlugIns.md b/docs/GettingStartedAriesDev/PlugIns.md index 8f742bd57d..0ea508d235 100644 --- a/docs/GettingStartedAriesDev/PlugIns.md +++ b/docs/GettingStartedAriesDev/PlugIns.md @@ -78,50 +78,59 @@ The load sequence for a plug-in (the "Startup" class depends on how Aca-Py is ru ```mermaid sequenceDiagram - participant Startup - Note right of Startup: Configuration is loaded on startup
from aca-py config params + participant Startup + Note right of Startup: Configuration is loaded on startup
from aca-py config params Startup->>+ArgParse: configure ArgParse->>settings: ["external_plugins"] ArgParse->>settings: ["blocked_plugins"] - Note right of Startup: Each configured plug-in is validated and loaded - Startup->>+DefaultContext: build_context() + Startup->>+Conductor: setup() + Note right of Conductor: Each configured plug-in is validated and loaded + Conductor->>DefaultContext: build_context() DefaultContext->>DefaultContext: load_plugins() DefaultContext->>+PluginRegistry: register_package() (for built-in protocols) PluginRegistry->>PluginRegistry: register_plugin() (for each sub-package) DefaultContext->>PluginRegistry: register_plugin() (for non-protocol built-ins) loop for each external plug-in - DefaultContext->>PluginRegistry: register_plugin() - alt if a setup method is provided - PluginRegistry->>ExternalPlugIn: has setup - else if routes and/or message_types are provided - PluginRegistry->>ExternalPlugIn: has routes - PluginRegistry->>ExternalPlugIn: has message_types - end - opt if definition is provided - PluginRegistry->>ExternalPlugIn: definition() - end + DefaultContext->>PluginRegistry: register_plugin() + alt if a setup method is provided + PluginRegistry->>ExternalPlugIn: has setup + else if routes and/or message_types are provided + PluginRegistry->>ExternalPlugIn: has routes + PluginRegistry->>ExternalPlugIn: has message_types + end + opt if definition is provided + PluginRegistry->>ExternalPlugIn: definition() + end end DefaultContext->>PluginRegistry: init_context() loop for each external plug-in - alt if a setup method is provided - PluginRegistry->>ExternalPlugIn: setup() - else if a setup method is NOT provided - PluginRegistry->>PluginRegistry: load_protocols() - PluginRegistry->>PluginRegistry: load_protocol_version() - PluginRegistry->>ProtocolRegistry: register_message_types() - PluginRegistry->>ProtocolRegistry: register_controllers() - end - PluginRegistry->>PluginRegistry: register_protocol_events() - end - - Note right of Startup: If the admin server is enabled, plug-in routes are added - Startup->>AdminServer: create admin server if enabled - Startup->>AdminServer: setup_context() (called on each request) - AdminServer->>PluginRegistry: register_admin_routes() - loop for each external plug-in - PluginRegistry->>ExternalPlugIn: routes.register() (to register endpoints) - end + alt if a setup method is provided + PluginRegistry->>ExternalPlugIn: setup() + else if a setup method is NOT provided + PluginRegistry->>PluginRegistry: load_protocols() + PluginRegistry->>PluginRegistry: load_protocol_version() + PluginRegistry->>ProtocolRegistry: register_message_types() + PluginRegistry->>ProtocolRegistry: register_controllers() + end + PluginRegistry->>PluginRegistry: register_protocol_events() + end + + Conductor->>Conductor: load_transports() + + Note right of Conductor: If the admin server is enabled, plug-in routes are added + Conductor->>AdminServer: create admin server if enabled + + Startup->>Conductor: start() + Conductor->>Conductor: start_transports() + Conductor->>AdminServer: start() + + Note right of Startup: the following represents an
admin server api request + Startup->>AdminServer: setup_context() (called on each request) + AdminServer->>PluginRegistry: register_admin_routes() + loop for each external plug-in + PluginRegistry->>ExternalPlugIn: routes.register() (to register endpoints) + end ``` ## Developing a New Plug-In From 2c1f39a9a4a833846c25f5a7d82dfcb18603587d Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Thu, 30 Mar 2023 19:06:57 -0700 Subject: [PATCH 734/872] 0.8.1-rc0 Signed-off-by: Stephen Curran --- CHANGELOG.md | 23 ++++ aries_cloudagent/core/tests/test_conductor.py | 2 +- aries_cloudagent/version.py | 2 +- docs/generated/aries_cloudagent.admin.rst | 32 +++--- .../aries_cloudagent.askar.didcomm.rst | 20 ++-- docs/generated/aries_cloudagent.askar.rst | 23 ++-- docs/generated/aries_cloudagent.cache.rst | 20 ++-- docs/generated/aries_cloudagent.commands.rst | 32 +++--- docs/generated/aries_cloudagent.config.rst | 98 +++++++++-------- ...s_cloudagent.connections.models.diddoc.rst | 32 +++--- .../aries_cloudagent.connections.models.rst | 23 ++-- .../aries_cloudagent.connections.rst | 17 ++- ...ries_cloudagent.core.in_memory.didcomm.rst | 20 ++-- .../aries_cloudagent.core.in_memory.rst | 17 ++- docs/generated/aries_cloudagent.core.rst | 71 ++++++------ docs/generated/aries_cloudagent.did.rst | 14 ++- docs/generated/aries_cloudagent.holder.rst | 14 ++- .../generated/aries_cloudagent.indy.credx.rst | 26 +++-- .../aries_cloudagent.indy.models.rst | 92 ++++++++-------- docs/generated/aries_cloudagent.indy.rst | 39 ++++--- docs/generated/aries_cloudagent.indy.sdk.rst | 56 +++++----- ...es_cloudagent.ledger.merkel_validation.rst | 44 ++++---- ...ries_cloudagent.ledger.multiple_ledger.rst | 44 ++++---- docs/generated/aries_cloudagent.ledger.rst | 55 +++++----- ...agent.messaging.credential_definitions.rst | 20 ++-- .../aries_cloudagent.messaging.decorators.rst | 74 +++++++------ .../aries_cloudagent.messaging.jsonld.rst | 32 +++--- .../aries_cloudagent.messaging.models.rst | 26 +++-- docs/generated/aries_cloudagent.messaging.rst | 67 ++++++------ .../aries_cloudagent.messaging.schemas.rst | 20 ++-- .../aries_cloudagent.multitenant.admin.rst | 14 ++- .../aries_cloudagent.multitenant.rst | 53 +++++---- .../aries_cloudagent.protocols.actionmenu.rst | 17 ++- ...ent.protocols.actionmenu.v1_0.handlers.rst | 26 +++-- ...ent.protocols.actionmenu.v1_0.messages.rst | 26 +++-- ...agent.protocols.actionmenu.v1_0.models.rst | 26 +++-- ...s_cloudagent.protocols.actionmenu.v1_0.rst | 51 +++++---- ...ries_cloudagent.protocols.basicmessage.rst | 17 ++- ...t.protocols.basicmessage.v1_0.handlers.rst | 14 ++- ...t.protocols.basicmessage.v1_0.messages.rst | 14 ++- ...cloudagent.protocols.basicmessage.v1_0.rst | 25 +++-- ...aries_cloudagent.protocols.connections.rst | 17 ++- ...nt.protocols.connections.v1_0.handlers.rst | 26 +++-- ...nt.protocols.connections.v1_0.messages.rst | 32 +++--- ...gent.protocols.connections.v1_0.models.rst | 14 ++- ..._cloudagent.protocols.connections.v1_0.rst | 33 +++--- ...udagent.protocols.coordinate_mediation.rst | 23 ++-- ...ols.coordinate_mediation.v1_0.handlers.rst | 56 +++++----- ...ordinate_mediation.v1_0.messages.inner.rst | 32 +++--- ...ols.coordinate_mediation.v1_0.messages.rst | 59 +++++----- ...ocols.coordinate_mediation.v1_0.models.rst | 14 ++- ...nt.protocols.coordinate_mediation.v1_0.rst | 57 +++++----- ...aries_cloudagent.protocols.didexchange.rst | 17 ++- ...nt.protocols.didexchange.v1_0.handlers.rst | 32 +++--- ...nt.protocols.didexchange.v1_0.messages.rst | 32 +++--- ..._cloudagent.protocols.didexchange.v1_0.rst | 31 +++--- .../aries_cloudagent.protocols.discovery.rst | 19 ++-- ...gent.protocols.discovery.v1_0.handlers.rst | 20 ++-- ...gent.protocols.discovery.v1_0.messages.rst | 20 ++-- ...dagent.protocols.discovery.v1_0.models.rst | 14 ++- ...es_cloudagent.protocols.discovery.v1_0.rst | 33 +++--- ...gent.protocols.discovery.v2_0.handlers.rst | 20 ++-- ...gent.protocols.discovery.v2_0.messages.rst | 20 ++-- ...dagent.protocols.discovery.v2_0.models.rst | 14 ++- ...es_cloudagent.protocols.discovery.v2_0.rst | 33 +++--- ...oudagent.protocols.endorse_transaction.rst | 17 ++- ...cols.endorse_transaction.v1_0.handlers.rst | 50 +++++---- ...cols.endorse_transaction.v1_0.messages.rst | 56 +++++----- ...tocols.endorse_transaction.v1_0.models.rst | 14 ++- ...ent.protocols.endorse_transaction.v1_0.rst | 51 +++++---- ...ries_cloudagent.protocols.introduction.rst | 17 ++- ...t.protocols.introduction.v0_1.handlers.rst | 26 +++-- ...t.protocols.introduction.v0_1.messages.rst | 26 +++-- ...cloudagent.protocols.introduction.v0_1.rst | 37 ++++--- ..._cloudagent.protocols.issue_credential.rst | 19 ++-- ...otocols.issue_credential.v1_0.handlers.rst | 44 ++++---- ...s.issue_credential.v1_0.messages.inner.rst | 14 ++- ...otocols.issue_credential.v1_0.messages.rst | 53 +++++---- ...protocols.issue_credential.v1_0.models.rst | 14 ++- ...dagent.protocols.issue_credential.v1_0.rst | 39 ++++--- ...ols.issue_credential.v2_0.formats.indy.rst | 14 ++- ...redential.v2_0.formats.ld_proof.models.rst | 20 ++-- ...issue_credential.v2_0.formats.ld_proof.rst | 17 ++- ...rotocols.issue_credential.v2_0.formats.rst | 19 ++-- ...otocols.issue_credential.v2_0.handlers.rst | 44 ++++---- ...s.issue_credential.v2_0.messages.inner.rst | 14 ++- ...otocols.issue_credential.v2_0.messages.rst | 59 +++++----- ...ls.issue_credential.v2_0.models.detail.rst | 20 ++-- ...protocols.issue_credential.v2_0.models.rst | 17 ++- ...dagent.protocols.issue_credential.v2_0.rst | 41 ++++--- ...ries_cloudagent.protocols.notification.rst | 17 ++- ...t.protocols.notification.v1_0.handlers.rst | 14 ++- ...t.protocols.notification.v1_0.messages.rst | 14 ++- ...cloudagent.protocols.notification.v1_0.rst | 19 ++-- ...aries_cloudagent.protocols.out_of_band.rst | 17 ++- ...nt.protocols.out_of_band.v1_0.handlers.rst | 26 +++-- ...nt.protocols.out_of_band.v1_0.messages.rst | 38 ++++--- ...gent.protocols.out_of_band.v1_0.models.rst | 20 ++-- ..._cloudagent.protocols.out_of_band.v1_0.rst | 39 ++++--- ...cloudagent.protocols.present_proof.dif.rst | 38 ++++--- ...loudagent.protocols.present_proof.indy.rst | 14 ++- ...ies_cloudagent.protocols.present_proof.rst | 23 ++-- ....protocols.present_proof.v1_0.handlers.rst | 38 ++++--- ....protocols.present_proof.v1_0.messages.rst | 44 ++++---- ...nt.protocols.present_proof.v1_0.models.rst | 14 ++- ...loudagent.protocols.present_proof.v1_0.rst | 39 ++++--- ...otocols.present_proof.v2_0.formats.dif.rst | 14 ++- ...tocols.present_proof.v2_0.formats.indy.rst | 14 ++- ...t.protocols.present_proof.v2_0.formats.rst | 19 ++-- ....protocols.present_proof.v2_0.handlers.rst | 38 ++++--- ....protocols.present_proof.v2_0.messages.rst | 50 +++++---- ...nt.protocols.present_proof.v2_0.models.rst | 14 ++- ...loudagent.protocols.present_proof.v2_0.rst | 41 ++++--- ...es_cloudagent.protocols.problem_report.rst | 17 ++- ...oudagent.protocols.problem_report.v1_0.rst | 26 +++-- ...gent.protocols.revocation_notification.rst | 19 ++-- ....revocation_notification.v1_0.handlers.rst | 14 ++- ....revocation_notification.v1_0.messages.rst | 14 ++- ...ls.revocation_notification.v1_0.models.rst | 14 ++- ...protocols.revocation_notification.v1_0.rst | 27 +++-- ....revocation_notification.v2_0.handlers.rst | 14 ++- ....revocation_notification.v2_0.messages.rst | 14 ++- ...ls.revocation_notification.v2_0.models.rst | 14 ++- ...protocols.revocation_notification.v2_0.rst | 27 +++-- .../aries_cloudagent.protocols.routing.rst | 17 ++- ...dagent.protocols.routing.v1_0.handlers.rst | 38 ++++--- ...dagent.protocols.routing.v1_0.messages.rst | 38 ++++--- ...oudagent.protocols.routing.v1_0.models.rst | 44 ++++---- ...ries_cloudagent.protocols.routing.v1_0.rst | 27 +++-- docs/generated/aries_cloudagent.protocols.rst | 49 +++++---- .../aries_cloudagent.protocols.trustping.rst | 17 ++- ...gent.protocols.trustping.v1_0.handlers.rst | 20 ++-- ...gent.protocols.trustping.v1_0.messages.rst | 20 ++-- ...es_cloudagent.protocols.trustping.v1_0.rst | 25 +++-- .../aries_cloudagent.resolver.default.rst | 32 +++--- docs/generated/aries_cloudagent.resolver.rst | 29 +++-- .../aries_cloudagent.revocation.models.rst | 32 +++--- .../generated/aries_cloudagent.revocation.rst | 47 ++++---- docs/generated/aries_cloudagent.rst | 61 ++++++----- docs/generated/aries_cloudagent.storage.rst | 47 ++++---- .../aries_cloudagent.storage.vc_holder.rst | 44 ++++---- docs/generated/aries_cloudagent.tails.rst | 26 +++-- .../aries_cloudagent.transport.inbound.rst | 56 +++++----- .../aries_cloudagent.transport.outbound.rst | 44 ++++---- .../aries_cloudagent.transport.queue.rst | 20 ++-- docs/generated/aries_cloudagent.transport.rst | 39 ++++--- docs/generated/aries_cloudagent.utils.rst | 68 ++++++------ .../aries_cloudagent.vc.ld_proofs.crypto.rst | 20 ++-- ...aries_cloudagent.vc.ld_proofs.purposes.rst | 38 ++++--- .../aries_cloudagent.vc.ld_proofs.rst | 57 +++++----- .../aries_cloudagent.vc.ld_proofs.suites.rst | 50 +++++---- docs/generated/aries_cloudagent.vc.rst | 12 +-- .../aries_cloudagent.vc.vc_ld.models.rst | 20 ++-- docs/generated/aries_cloudagent.vc.vc_ld.rst | 35 +++--- .../aries_cloudagent.wallet.models.rst | 14 ++- docs/generated/aries_cloudagent.wallet.rst | 101 +++++++++--------- open-api/openapi.json | 2 +- 157 files changed, 2260 insertions(+), 2481 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba21b9c7ad..a65d01a12b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,26 @@ +# 0.8.1-rc0 + +## March 31, 2023 + +An urgent update to Release 0.8.1 to address an inability to execute the +`upgrade` command. The `upgrade` command is needed for Pull Request [\#2116] - +"UPGRADE: Fix multi-use invitation performance", which is necessary for (at +least) deployments of ACA-Py as a mediator. The important update is Pull Request +[\#2185] (listed below). + +[\#2116]: https://github.com/hyperledger/aries-cloudagent-python/pull/2116 +[\#2185]: https://github.com/hyperledger/aries-cloudagent-python/pull/2185 + +### Categorized List of Pull Requests + +- Fixes for the `upgrade` Command + - Update and automate ACA-Py upgrade process [\#2185](https://github.com/hyperledger/aries-cloudagent-python/pull/2185) [shaangill025](https://github.com/shaangill025) + - Adds the upgrade command YML file to the PyPi Release [\#2179](https://github.com/hyperledger/aries-cloudagent-python/pull/2179) [swcurran](https://github.com/swcurran) +- Test and Documentation + - Create UnitTests.md [\#2183](https://github.com/hyperledger/aries-cloudagent-python/pull/2183) [swcurran](https://github.com/swcurran) + - Add link to recorded session about the ACA-Py Integration tests [\#2184](https://github.com/hyperledger/aries-cloudagent-python/pull/2184) [swcurran](https://github.com/swcurran) +- Release management pull requests + # 0.8.0 ## March 14, 2023 diff --git a/aries_cloudagent/core/tests/test_conductor.py b/aries_cloudagent/core/tests/test_conductor.py index 5ce750cb4a..b7be39d4a6 100644 --- a/aries_cloudagent/core/tests/test_conductor.py +++ b/aries_cloudagent/core/tests/test_conductor.py @@ -119,7 +119,7 @@ async def test_startup_version_record_exists(self): test_module, "get_upgrade_version_list", async_mock.MagicMock( - return_value=["v0.7.4", "0.7.5", "v0.8.0rc1", "v8.0.0"] + return_value=["v0.7.4", "0.7.5", "v0.8.0-rc1", "v8.0.0", "v0.8.1-rc0"] ), ), async_mock.patch.object( test_module, diff --git a/aries_cloudagent/version.py b/aries_cloudagent/version.py index 553f22bb5b..bde2b40d5e 100644 --- a/aries_cloudagent/version.py +++ b/aries_cloudagent/version.py @@ -1,4 +1,4 @@ """Library version information.""" -__version__ = "0.8.0" +__version__ = "0.8.1-rc0" RECORD_TYPE_ACAPY_VERSION = "acapy_version" diff --git a/docs/generated/aries_cloudagent.admin.rst b/docs/generated/aries_cloudagent.admin.rst index 250d1b3da5..efcb59a1cb 100644 --- a/docs/generated/aries_cloudagent.admin.rst +++ b/docs/generated/aries_cloudagent.admin.rst @@ -2,9 +2,9 @@ aries\_cloudagent.admin package =============================== .. automodule:: aries_cloudagent.admin - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,32 +13,30 @@ aries\_cloudagent.admin.base\_server module ------------------------------------------- .. automodule:: aries_cloudagent.admin.base_server - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.admin.error module ------------------------------------ .. automodule:: aries_cloudagent.admin.error - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.admin.request\_context module ----------------------------------------------- .. automodule:: aries_cloudagent.admin.request_context - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.admin.server module ------------------------------------- .. automodule:: aries_cloudagent.admin.server - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.askar.didcomm.rst b/docs/generated/aries_cloudagent.askar.didcomm.rst index 10b52b2b30..6e599dcc31 100644 --- a/docs/generated/aries_cloudagent.askar.didcomm.rst +++ b/docs/generated/aries_cloudagent.askar.didcomm.rst @@ -2,9 +2,9 @@ aries\_cloudagent.askar.didcomm package ======================================= .. automodule:: aries_cloudagent.askar.didcomm - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,16 +13,14 @@ aries\_cloudagent.askar.didcomm.v1 module ----------------------------------------- .. automodule:: aries_cloudagent.askar.didcomm.v1 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.askar.didcomm.v2 module ----------------------------------------- .. automodule:: aries_cloudagent.askar.didcomm.v2 - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.askar.rst b/docs/generated/aries_cloudagent.askar.rst index e9f39149ec..2787ae97f8 100644 --- a/docs/generated/aries_cloudagent.askar.rst +++ b/docs/generated/aries_cloudagent.askar.rst @@ -2,16 +2,17 @@ aries\_cloudagent.askar package =============================== .. automodule:: aries_cloudagent.askar - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.askar.didcomm + aries_cloudagent.askar.didcomm Submodules ---------- @@ -20,16 +21,14 @@ aries\_cloudagent.askar.profile module -------------------------------------- .. automodule:: aries_cloudagent.askar.profile - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.askar.store module ------------------------------------ .. automodule:: aries_cloudagent.askar.store - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.cache.rst b/docs/generated/aries_cloudagent.cache.rst index c77763aa50..8fa69fb650 100644 --- a/docs/generated/aries_cloudagent.cache.rst +++ b/docs/generated/aries_cloudagent.cache.rst @@ -2,9 +2,9 @@ aries\_cloudagent.cache package =============================== .. automodule:: aries_cloudagent.cache - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,16 +13,14 @@ aries\_cloudagent.cache.base module ----------------------------------- .. automodule:: aries_cloudagent.cache.base - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.cache.in\_memory module ----------------------------------------- .. automodule:: aries_cloudagent.cache.in_memory - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.commands.rst b/docs/generated/aries_cloudagent.commands.rst index e04e946ca8..f4353c5984 100644 --- a/docs/generated/aries_cloudagent.commands.rst +++ b/docs/generated/aries_cloudagent.commands.rst @@ -2,9 +2,9 @@ aries\_cloudagent.commands package ================================== .. automodule:: aries_cloudagent.commands - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,32 +13,30 @@ aries\_cloudagent.commands.help module -------------------------------------- .. automodule:: aries_cloudagent.commands.help - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.commands.provision module ------------------------------------------- .. automodule:: aries_cloudagent.commands.provision - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.commands.start module --------------------------------------- .. automodule:: aries_cloudagent.commands.start - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.commands.upgrade module ----------------------------------------- .. automodule:: aries_cloudagent.commands.upgrade - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.config.rst b/docs/generated/aries_cloudagent.config.rst index 56ba88a314..43b1ef34a5 100644 --- a/docs/generated/aries_cloudagent.config.rst +++ b/docs/generated/aries_cloudagent.config.rst @@ -2,9 +2,9 @@ aries\_cloudagent.config package ================================ .. automodule:: aries_cloudagent.config - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,120 +13,118 @@ aries\_cloudagent.config.argparse module ---------------------------------------- .. automodule:: aries_cloudagent.config.argparse - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.config.banner module -------------------------------------- .. automodule:: aries_cloudagent.config.banner - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.config.base module ------------------------------------ .. automodule:: aries_cloudagent.config.base - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.config.base\_context module --------------------------------------------- .. automodule:: aries_cloudagent.config.base_context - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.config.default\_context module ------------------------------------------------ .. automodule:: aries_cloudagent.config.default_context - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.config.error module ------------------------------------- .. automodule:: aries_cloudagent.config.error - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.config.injection\_context module -------------------------------------------------- .. automodule:: aries_cloudagent.config.injection_context - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.config.injector module ---------------------------------------- .. automodule:: aries_cloudagent.config.injector - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.config.ledger module -------------------------------------- .. automodule:: aries_cloudagent.config.ledger - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.config.logging module --------------------------------------- .. automodule:: aries_cloudagent.config.logging - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.config.plugin\_settings module ------------------------------------------------ .. automodule:: aries_cloudagent.config.plugin_settings - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.config.provider module ---------------------------------------- .. automodule:: aries_cloudagent.config.provider - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.config.settings module ---------------------------------------- .. automodule:: aries_cloudagent.config.settings - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.config.util module ------------------------------------ .. automodule:: aries_cloudagent.config.util - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.config.wallet module -------------------------------------- .. automodule:: aries_cloudagent.config.wallet - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.connections.models.diddoc.rst b/docs/generated/aries_cloudagent.connections.models.diddoc.rst index bed72812ab..01f1124a60 100644 --- a/docs/generated/aries_cloudagent.connections.models.diddoc.rst +++ b/docs/generated/aries_cloudagent.connections.models.diddoc.rst @@ -2,9 +2,9 @@ aries\_cloudagent.connections.models.diddoc package =================================================== .. automodule:: aries_cloudagent.connections.models.diddoc - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,32 +13,30 @@ aries\_cloudagent.connections.models.diddoc.diddoc module --------------------------------------------------------- .. automodule:: aries_cloudagent.connections.models.diddoc.diddoc - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.connections.models.diddoc.publickey module ------------------------------------------------------------ .. automodule:: aries_cloudagent.connections.models.diddoc.publickey - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.connections.models.diddoc.service module ---------------------------------------------------------- .. automodule:: aries_cloudagent.connections.models.diddoc.service - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.connections.models.diddoc.util module ------------------------------------------------------- .. automodule:: aries_cloudagent.connections.models.diddoc.util - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.connections.models.rst b/docs/generated/aries_cloudagent.connections.models.rst index 0d1ad79dc7..4ee733ae04 100644 --- a/docs/generated/aries_cloudagent.connections.models.rst +++ b/docs/generated/aries_cloudagent.connections.models.rst @@ -2,16 +2,17 @@ aries\_cloudagent.connections.models package ============================================ .. automodule:: aries_cloudagent.connections.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.connections.models.diddoc + aries_cloudagent.connections.models.diddoc Submodules ---------- @@ -20,16 +21,14 @@ aries\_cloudagent.connections.models.conn\_record module -------------------------------------------------------- .. automodule:: aries_cloudagent.connections.models.conn_record - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.connections.models.connection\_target module -------------------------------------------------------------- .. automodule:: aries_cloudagent.connections.models.connection_target - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.connections.rst b/docs/generated/aries_cloudagent.connections.rst index 9630382119..90d1f68626 100644 --- a/docs/generated/aries_cloudagent.connections.rst +++ b/docs/generated/aries_cloudagent.connections.rst @@ -2,16 +2,17 @@ aries\_cloudagent.connections package ===================================== .. automodule:: aries_cloudagent.connections - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.connections.models + aries_cloudagent.connections.models Submodules ---------- @@ -20,8 +21,6 @@ aries\_cloudagent.connections.base\_manager module -------------------------------------------------- .. automodule:: aries_cloudagent.connections.base_manager - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.core.in_memory.didcomm.rst b/docs/generated/aries_cloudagent.core.in_memory.didcomm.rst index 17b9a3a68a..1bb9828e02 100644 --- a/docs/generated/aries_cloudagent.core.in_memory.didcomm.rst +++ b/docs/generated/aries_cloudagent.core.in_memory.didcomm.rst @@ -2,9 +2,9 @@ aries\_cloudagent.core.in\_memory.didcomm package ================================================= .. automodule:: aries_cloudagent.core.in_memory.didcomm - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,16 +13,14 @@ aries\_cloudagent.core.in\_memory.didcomm.derive\_1pu module ------------------------------------------------------------ .. automodule:: aries_cloudagent.core.in_memory.didcomm.derive_1pu - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.core.in\_memory.didcomm.derive\_ecdh module ------------------------------------------------------------- .. automodule:: aries_cloudagent.core.in_memory.didcomm.derive_ecdh - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.core.in_memory.rst b/docs/generated/aries_cloudagent.core.in_memory.rst index e7aba5fb2a..20dfaa765f 100644 --- a/docs/generated/aries_cloudagent.core.in_memory.rst +++ b/docs/generated/aries_cloudagent.core.in_memory.rst @@ -2,16 +2,17 @@ aries\_cloudagent.core.in\_memory package ========================================= .. automodule:: aries_cloudagent.core.in_memory - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.core.in_memory.didcomm + aries_cloudagent.core.in_memory.didcomm Submodules ---------- @@ -20,8 +21,6 @@ aries\_cloudagent.core.in\_memory.profile module ------------------------------------------------ .. automodule:: aries_cloudagent.core.in_memory.profile - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.core.rst b/docs/generated/aries_cloudagent.core.rst index c0ffc0d393..f304ae0aa0 100644 --- a/docs/generated/aries_cloudagent.core.rst +++ b/docs/generated/aries_cloudagent.core.rst @@ -2,16 +2,17 @@ aries\_cloudagent.core package ============================== .. automodule:: aries_cloudagent.core - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.core.in_memory + aries_cloudagent.core.in_memory Submodules ---------- @@ -20,80 +21,78 @@ aries\_cloudagent.core.conductor module --------------------------------------- .. automodule:: aries_cloudagent.core.conductor - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.core.dispatcher module ---------------------------------------- .. automodule:: aries_cloudagent.core.dispatcher - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.core.error module ----------------------------------- .. automodule:: aries_cloudagent.core.error - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.core.event\_bus module ---------------------------------------- .. automodule:: aries_cloudagent.core.event_bus - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.core.goal\_code\_registry module -------------------------------------------------- .. automodule:: aries_cloudagent.core.goal_code_registry - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.core.oob\_processor module -------------------------------------------- .. automodule:: aries_cloudagent.core.oob_processor - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.core.plugin\_registry module ---------------------------------------------- .. automodule:: aries_cloudagent.core.plugin_registry - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.core.profile module ------------------------------------- .. automodule:: aries_cloudagent.core.profile - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.core.protocol\_registry module ------------------------------------------------ .. automodule:: aries_cloudagent.core.protocol_registry - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.core.util module ---------------------------------- .. automodule:: aries_cloudagent.core.util - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.did.rst b/docs/generated/aries_cloudagent.did.rst index 696571f08d..4eb9394a21 100644 --- a/docs/generated/aries_cloudagent.did.rst +++ b/docs/generated/aries_cloudagent.did.rst @@ -2,9 +2,9 @@ aries\_cloudagent.did package ============================= .. automodule:: aries_cloudagent.did - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,8 +13,6 @@ aries\_cloudagent.did.did\_key module ------------------------------------- .. automodule:: aries_cloudagent.did.did_key - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.holder.rst b/docs/generated/aries_cloudagent.holder.rst index a3f35fbad8..47287253ae 100644 --- a/docs/generated/aries_cloudagent.holder.rst +++ b/docs/generated/aries_cloudagent.holder.rst @@ -2,9 +2,9 @@ aries\_cloudagent.holder package ================================ .. automodule:: aries_cloudagent.holder - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,8 +13,6 @@ aries\_cloudagent.holder.routes module -------------------------------------- .. automodule:: aries_cloudagent.holder.routes - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.indy.credx.rst b/docs/generated/aries_cloudagent.indy.credx.rst index bbb6a380b3..b80d4017f7 100644 --- a/docs/generated/aries_cloudagent.indy.credx.rst +++ b/docs/generated/aries_cloudagent.indy.credx.rst @@ -2,9 +2,9 @@ aries\_cloudagent.indy.credx package ==================================== .. automodule:: aries_cloudagent.indy.credx - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,24 +13,22 @@ aries\_cloudagent.indy.credx.holder module ------------------------------------------ .. automodule:: aries_cloudagent.indy.credx.holder - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.credx.issuer module ------------------------------------------ .. automodule:: aries_cloudagent.indy.credx.issuer - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.credx.verifier module -------------------------------------------- .. automodule:: aries_cloudagent.indy.credx.verifier - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.indy.models.rst b/docs/generated/aries_cloudagent.indy.models.rst index 487ed7b5f9..ae04af6ddb 100644 --- a/docs/generated/aries_cloudagent.indy.models.rst +++ b/docs/generated/aries_cloudagent.indy.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.indy.models package ===================================== .. automodule:: aries_cloudagent.indy.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,112 +13,110 @@ aries\_cloudagent.indy.models.cred module ----------------------------------------- .. automodule:: aries_cloudagent.indy.models.cred - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.models.cred\_abstract module --------------------------------------------------- .. automodule:: aries_cloudagent.indy.models.cred_abstract - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.models.cred\_def module ---------------------------------------------- .. automodule:: aries_cloudagent.indy.models.cred_def - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.models.cred\_precis module ------------------------------------------------- .. automodule:: aries_cloudagent.indy.models.cred_precis - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.models.cred\_request module -------------------------------------------------- .. automodule:: aries_cloudagent.indy.models.cred_request - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.models.non\_rev\_interval module ------------------------------------------------------- .. automodule:: aries_cloudagent.indy.models.non_rev_interval - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.models.predicate module ---------------------------------------------- .. automodule:: aries_cloudagent.indy.models.predicate - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.models.pres\_preview module -------------------------------------------------- .. automodule:: aries_cloudagent.indy.models.pres_preview - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.models.proof module ------------------------------------------ .. automodule:: aries_cloudagent.indy.models.proof - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.models.proof\_request module --------------------------------------------------- .. automodule:: aries_cloudagent.indy.models.proof_request - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.models.requested\_creds module ----------------------------------------------------- .. automodule:: aries_cloudagent.indy.models.requested_creds - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.models.revocation module ----------------------------------------------- .. automodule:: aries_cloudagent.indy.models.revocation - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.models.schema module ------------------------------------------- .. automodule:: aries_cloudagent.indy.models.schema - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.models.xform module ------------------------------------------ .. automodule:: aries_cloudagent.indy.models.xform - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.indy.rst b/docs/generated/aries_cloudagent.indy.rst index dff7b78f07..0e5c50f637 100644 --- a/docs/generated/aries_cloudagent.indy.rst +++ b/docs/generated/aries_cloudagent.indy.rst @@ -2,18 +2,19 @@ aries\_cloudagent.indy package ============================== .. automodule:: aries_cloudagent.indy - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.indy.credx - aries_cloudagent.indy.models - aries_cloudagent.indy.sdk + aries_cloudagent.indy.credx + aries_cloudagent.indy.models + aries_cloudagent.indy.sdk Submodules ---------- @@ -22,32 +23,30 @@ aries\_cloudagent.indy.holder module ------------------------------------ .. automodule:: aries_cloudagent.indy.holder - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.issuer module ------------------------------------ .. automodule:: aries_cloudagent.indy.issuer - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.util module ---------------------------------- .. automodule:: aries_cloudagent.indy.util - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.verifier module -------------------------------------- .. automodule:: aries_cloudagent.indy.verifier - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.indy.sdk.rst b/docs/generated/aries_cloudagent.indy.sdk.rst index 8de7333f14..be5adbf5ae 100644 --- a/docs/generated/aries_cloudagent.indy.sdk.rst +++ b/docs/generated/aries_cloudagent.indy.sdk.rst @@ -2,9 +2,9 @@ aries\_cloudagent.indy.sdk package ================================== .. automodule:: aries_cloudagent.indy.sdk - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,64 +13,62 @@ aries\_cloudagent.indy.sdk.error module --------------------------------------- .. automodule:: aries_cloudagent.indy.sdk.error - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.sdk.holder module ---------------------------------------- .. automodule:: aries_cloudagent.indy.sdk.holder - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.sdk.issuer module ---------------------------------------- .. automodule:: aries_cloudagent.indy.sdk.issuer - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.sdk.profile module ----------------------------------------- .. automodule:: aries_cloudagent.indy.sdk.profile - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.sdk.util module -------------------------------------- .. automodule:: aries_cloudagent.indy.sdk.util - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.sdk.verifier module ------------------------------------------ .. automodule:: aries_cloudagent.indy.sdk.verifier - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.sdk.wallet\_plugin module ------------------------------------------------ .. automodule:: aries_cloudagent.indy.sdk.wallet_plugin - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.indy.sdk.wallet\_setup module ----------------------------------------------- .. automodule:: aries_cloudagent.indy.sdk.wallet_setup - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.ledger.merkel_validation.rst b/docs/generated/aries_cloudagent.ledger.merkel_validation.rst index 9dbc246bfa..2aba21f5ce 100644 --- a/docs/generated/aries_cloudagent.ledger.merkel_validation.rst +++ b/docs/generated/aries_cloudagent.ledger.merkel_validation.rst @@ -2,9 +2,9 @@ aries\_cloudagent.ledger.merkel\_validation package =================================================== .. automodule:: aries_cloudagent.ledger.merkel_validation - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,48 +13,46 @@ aries\_cloudagent.ledger.merkel\_validation.constants module ------------------------------------------------------------ .. automodule:: aries_cloudagent.ledger.merkel_validation.constants - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.merkel\_validation.domain\_txn\_handler module ----------------------------------------------------------------------- .. automodule:: aries_cloudagent.ledger.merkel_validation.domain_txn_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.merkel\_validation.hasher module --------------------------------------------------------- .. automodule:: aries_cloudagent.ledger.merkel_validation.hasher - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.merkel\_validation.merkel\_verifier module ------------------------------------------------------------------- .. automodule:: aries_cloudagent.ledger.merkel_validation.merkel_verifier - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.merkel\_validation.trie module ------------------------------------------------------- .. automodule:: aries_cloudagent.ledger.merkel_validation.trie - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.merkel\_validation.utils module -------------------------------------------------------- .. automodule:: aries_cloudagent.ledger.merkel_validation.utils - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.ledger.multiple_ledger.rst b/docs/generated/aries_cloudagent.ledger.multiple_ledger.rst index 37dd4d2e10..ed257bc7cf 100644 --- a/docs/generated/aries_cloudagent.ledger.multiple_ledger.rst +++ b/docs/generated/aries_cloudagent.ledger.multiple_ledger.rst @@ -2,9 +2,9 @@ aries\_cloudagent.ledger.multiple\_ledger package ================================================= .. automodule:: aries_cloudagent.ledger.multiple_ledger - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,48 +13,46 @@ aries\_cloudagent.ledger.multiple\_ledger.base\_manager module -------------------------------------------------------------- .. automodule:: aries_cloudagent.ledger.multiple_ledger.base_manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.multiple\_ledger.indy\_manager module -------------------------------------------------------------- .. automodule:: aries_cloudagent.ledger.multiple_ledger.indy_manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.multiple\_ledger.indy\_vdr\_manager module ------------------------------------------------------------------- .. automodule:: aries_cloudagent.ledger.multiple_ledger.indy_vdr_manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.multiple\_ledger.ledger\_config\_schema module ----------------------------------------------------------------------- .. automodule:: aries_cloudagent.ledger.multiple_ledger.ledger_config_schema - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.multiple\_ledger.ledger\_requests\_executor module --------------------------------------------------------------------------- .. automodule:: aries_cloudagent.ledger.multiple_ledger.ledger_requests_executor - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.multiple\_ledger.manager\_provider module ------------------------------------------------------------------ .. automodule:: aries_cloudagent.ledger.multiple_ledger.manager_provider - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.ledger.rst b/docs/generated/aries_cloudagent.ledger.rst index 53ee6a0bc5..aff0574fef 100644 --- a/docs/generated/aries_cloudagent.ledger.rst +++ b/docs/generated/aries_cloudagent.ledger.rst @@ -2,17 +2,18 @@ aries\_cloudagent.ledger package ================================ .. automodule:: aries_cloudagent.ledger - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.ledger.merkel_validation - aries_cloudagent.ledger.multiple_ledger + aries_cloudagent.ledger.merkel_validation + aries_cloudagent.ledger.multiple_ledger Submodules ---------- @@ -21,56 +22,54 @@ aries\_cloudagent.ledger.base module ------------------------------------ .. automodule:: aries_cloudagent.ledger.base - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.endpoint\_type module ---------------------------------------------- .. automodule:: aries_cloudagent.ledger.endpoint_type - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.error module ------------------------------------- .. automodule:: aries_cloudagent.ledger.error - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.indy module ------------------------------------ .. automodule:: aries_cloudagent.ledger.indy - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.indy\_vdr module ----------------------------------------- .. automodule:: aries_cloudagent.ledger.indy_vdr - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.routes module -------------------------------------- .. automodule:: aries_cloudagent.ledger.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.ledger.util module ------------------------------------ .. automodule:: aries_cloudagent.ledger.util - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.messaging.credential_definitions.rst b/docs/generated/aries_cloudagent.messaging.credential_definitions.rst index d0fdb8879b..0de38530c2 100644 --- a/docs/generated/aries_cloudagent.messaging.credential_definitions.rst +++ b/docs/generated/aries_cloudagent.messaging.credential_definitions.rst @@ -2,9 +2,9 @@ aries\_cloudagent.messaging.credential\_definitions package =========================================================== .. automodule:: aries_cloudagent.messaging.credential_definitions - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,16 +13,14 @@ aries\_cloudagent.messaging.credential\_definitions.routes module ----------------------------------------------------------------- .. automodule:: aries_cloudagent.messaging.credential_definitions.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.credential\_definitions.util module --------------------------------------------------------------- .. automodule:: aries_cloudagent.messaging.credential_definitions.util - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.messaging.decorators.rst b/docs/generated/aries_cloudagent.messaging.decorators.rst index 1deab83a09..64fb269fb1 100644 --- a/docs/generated/aries_cloudagent.messaging.decorators.rst +++ b/docs/generated/aries_cloudagent.messaging.decorators.rst @@ -2,9 +2,9 @@ aries\_cloudagent.messaging.decorators package ============================================== .. automodule:: aries_cloudagent.messaging.decorators - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,88 +13,86 @@ aries\_cloudagent.messaging.decorators.attach\_decorator module --------------------------------------------------------------- .. automodule:: aries_cloudagent.messaging.decorators.attach_decorator - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.decorators.base module -------------------------------------------------- .. automodule:: aries_cloudagent.messaging.decorators.base - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.decorators.default module ----------------------------------------------------- .. automodule:: aries_cloudagent.messaging.decorators.default - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.decorators.localization\_decorator module --------------------------------------------------------------------- .. automodule:: aries_cloudagent.messaging.decorators.localization_decorator - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.decorators.please\_ack\_decorator module -------------------------------------------------------------------- .. automodule:: aries_cloudagent.messaging.decorators.please_ack_decorator - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.decorators.service\_decorator module ---------------------------------------------------------------- .. automodule:: aries_cloudagent.messaging.decorators.service_decorator - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.decorators.signature\_decorator module ------------------------------------------------------------------ .. automodule:: aries_cloudagent.messaging.decorators.signature_decorator - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.decorators.thread\_decorator module --------------------------------------------------------------- .. automodule:: aries_cloudagent.messaging.decorators.thread_decorator - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.decorators.timing\_decorator module --------------------------------------------------------------- .. automodule:: aries_cloudagent.messaging.decorators.timing_decorator - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.decorators.trace\_decorator module -------------------------------------------------------------- .. automodule:: aries_cloudagent.messaging.decorators.trace_decorator - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.decorators.transport\_decorator module ------------------------------------------------------------------ .. automodule:: aries_cloudagent.messaging.decorators.transport_decorator - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.messaging.jsonld.rst b/docs/generated/aries_cloudagent.messaging.jsonld.rst index af0f5bf8e5..02ce783dd1 100644 --- a/docs/generated/aries_cloudagent.messaging.jsonld.rst +++ b/docs/generated/aries_cloudagent.messaging.jsonld.rst @@ -2,9 +2,9 @@ aries\_cloudagent.messaging.jsonld package ========================================== .. automodule:: aries_cloudagent.messaging.jsonld - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,32 +13,30 @@ aries\_cloudagent.messaging.jsonld.create\_verify\_data module -------------------------------------------------------------- .. automodule:: aries_cloudagent.messaging.jsonld.create_verify_data - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.jsonld.credential module ---------------------------------------------------- .. automodule:: aries_cloudagent.messaging.jsonld.credential - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.jsonld.error module ----------------------------------------------- .. automodule:: aries_cloudagent.messaging.jsonld.error - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.jsonld.routes module ------------------------------------------------ .. automodule:: aries_cloudagent.messaging.jsonld.routes - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.messaging.models.rst b/docs/generated/aries_cloudagent.messaging.models.rst index ecf4281974..cc951ac8e0 100644 --- a/docs/generated/aries_cloudagent.messaging.models.rst +++ b/docs/generated/aries_cloudagent.messaging.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.messaging.models package ========================================== .. automodule:: aries_cloudagent.messaging.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,24 +13,22 @@ aries\_cloudagent.messaging.models.base module ---------------------------------------------- .. automodule:: aries_cloudagent.messaging.models.base - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.models.base\_record module ------------------------------------------------------ .. automodule:: aries_cloudagent.messaging.models.base_record - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.models.openapi module ------------------------------------------------- .. automodule:: aries_cloudagent.messaging.models.openapi - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.messaging.rst b/docs/generated/aries_cloudagent.messaging.rst index 29b5a3c101..21bd62a4f3 100644 --- a/docs/generated/aries_cloudagent.messaging.rst +++ b/docs/generated/aries_cloudagent.messaging.rst @@ -2,20 +2,21 @@ aries\_cloudagent.messaging package =================================== .. automodule:: aries_cloudagent.messaging - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.messaging.credential_definitions - aries_cloudagent.messaging.decorators - aries_cloudagent.messaging.jsonld - aries_cloudagent.messaging.models - aries_cloudagent.messaging.schemas + aries_cloudagent.messaging.credential_definitions + aries_cloudagent.messaging.decorators + aries_cloudagent.messaging.jsonld + aries_cloudagent.messaging.models + aries_cloudagent.messaging.schemas Submodules ---------- @@ -24,64 +25,62 @@ aries\_cloudagent.messaging.agent\_message module ------------------------------------------------- .. automodule:: aries_cloudagent.messaging.agent_message - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.base\_handler module ------------------------------------------------ .. automodule:: aries_cloudagent.messaging.base_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.base\_message module ------------------------------------------------ .. automodule:: aries_cloudagent.messaging.base_message - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.error module ---------------------------------------- .. automodule:: aries_cloudagent.messaging.error - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.request\_context module --------------------------------------------------- .. automodule:: aries_cloudagent.messaging.request_context - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.responder module -------------------------------------------- .. automodule:: aries_cloudagent.messaging.responder - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.util module --------------------------------------- .. automodule:: aries_cloudagent.messaging.util - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.valid module ---------------------------------------- .. automodule:: aries_cloudagent.messaging.valid - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.messaging.schemas.rst b/docs/generated/aries_cloudagent.messaging.schemas.rst index 06f847b39a..83deffa2a4 100644 --- a/docs/generated/aries_cloudagent.messaging.schemas.rst +++ b/docs/generated/aries_cloudagent.messaging.schemas.rst @@ -2,9 +2,9 @@ aries\_cloudagent.messaging.schemas package =========================================== .. automodule:: aries_cloudagent.messaging.schemas - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,16 +13,14 @@ aries\_cloudagent.messaging.schemas.routes module ------------------------------------------------- .. automodule:: aries_cloudagent.messaging.schemas.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.messaging.schemas.util module ----------------------------------------------- .. automodule:: aries_cloudagent.messaging.schemas.util - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.multitenant.admin.rst b/docs/generated/aries_cloudagent.multitenant.admin.rst index e1f44a2a9e..6a1a695efe 100644 --- a/docs/generated/aries_cloudagent.multitenant.admin.rst +++ b/docs/generated/aries_cloudagent.multitenant.admin.rst @@ -2,9 +2,9 @@ aries\_cloudagent.multitenant.admin package =========================================== .. automodule:: aries_cloudagent.multitenant.admin - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,8 +13,6 @@ aries\_cloudagent.multitenant.admin.routes module ------------------------------------------------- .. automodule:: aries_cloudagent.multitenant.admin.routes - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.multitenant.rst b/docs/generated/aries_cloudagent.multitenant.rst index 44d798d511..913a17a652 100644 --- a/docs/generated/aries_cloudagent.multitenant.rst +++ b/docs/generated/aries_cloudagent.multitenant.rst @@ -2,16 +2,17 @@ aries\_cloudagent.multitenant package ===================================== .. automodule:: aries_cloudagent.multitenant - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.multitenant.admin + aries_cloudagent.multitenant.admin Submodules ---------- @@ -20,56 +21,54 @@ aries\_cloudagent.multitenant.askar\_profile\_manager module ------------------------------------------------------------ .. automodule:: aries_cloudagent.multitenant.askar_profile_manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.multitenant.base module ----------------------------------------- .. automodule:: aries_cloudagent.multitenant.base - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.multitenant.cache module ------------------------------------------ .. automodule:: aries_cloudagent.multitenant.cache - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.multitenant.error module ------------------------------------------ .. automodule:: aries_cloudagent.multitenant.error - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.multitenant.manager module -------------------------------------------- .. automodule:: aries_cloudagent.multitenant.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.multitenant.manager\_provider module ------------------------------------------------------ .. automodule:: aries_cloudagent.multitenant.manager_provider - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.multitenant.route\_manager module --------------------------------------------------- .. automodule:: aries_cloudagent.multitenant.route_manager - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.actionmenu.rst b/docs/generated/aries_cloudagent.protocols.actionmenu.rst index 70d47336ca..3688c5eef5 100644 --- a/docs/generated/aries_cloudagent.protocols.actionmenu.rst +++ b/docs/generated/aries_cloudagent.protocols.actionmenu.rst @@ -2,16 +2,17 @@ aries\_cloudagent.protocols.actionmenu package ============================================== .. automodule:: aries_cloudagent.protocols.actionmenu - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.protocols.actionmenu.v1_0 + aries_cloudagent.protocols.actionmenu.v1_0 Submodules ---------- @@ -20,8 +21,6 @@ aries\_cloudagent.protocols.actionmenu.definition module -------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.actionmenu.definition - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.handlers.rst index a230fae21f..c3f66b0796 100644 --- a/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.actionmenu.v1\_0.handlers package ============================================================= .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,24 +13,22 @@ aries\_cloudagent.protocols.actionmenu.v1\_0.handlers.menu\_handler module -------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.handlers.menu_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.actionmenu.v1\_0.handlers.menu\_request\_handler module ----------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.handlers.menu_request_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.actionmenu.v1\_0.handlers.perform\_handler module ----------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.handlers.perform_handler - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.messages.rst b/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.messages.rst index e3fe2a1522..98578667f1 100644 --- a/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.actionmenu.v1\_0.messages package ============================================================= .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,24 +13,22 @@ aries\_cloudagent.protocols.actionmenu.v1\_0.messages.menu module ----------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.messages.menu - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.actionmenu.v1\_0.messages.menu\_request module -------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.messages.menu_request - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.actionmenu.v1\_0.messages.perform module -------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.messages.perform - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.models.rst b/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.models.rst index 2d84e02fac..133a5c5207 100644 --- a/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.models.rst +++ b/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.actionmenu.v1\_0.models package =========================================================== .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,24 +13,22 @@ aries\_cloudagent.protocols.actionmenu.v1\_0.models.menu\_form module --------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.models.menu_form - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.actionmenu.v1\_0.models.menu\_form\_param module ---------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.models.menu_form_param - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.actionmenu.v1\_0.models.menu\_option module ----------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.models.menu_option - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.rst b/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.rst index a1b1097a49..40a91c0756 100644 --- a/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.actionmenu.v1_0.rst @@ -2,18 +2,19 @@ aries\_cloudagent.protocols.actionmenu.v1\_0 package ==================================================== .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.protocols.actionmenu.v1_0.handlers - aries_cloudagent.protocols.actionmenu.v1_0.messages - aries_cloudagent.protocols.actionmenu.v1_0.models + aries_cloudagent.protocols.actionmenu.v1_0.handlers + aries_cloudagent.protocols.actionmenu.v1_0.messages + aries_cloudagent.protocols.actionmenu.v1_0.models Submodules ---------- @@ -22,48 +23,46 @@ aries\_cloudagent.protocols.actionmenu.v1\_0.base\_service module ----------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.base_service - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.actionmenu.v1\_0.controller module -------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.controller - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.actionmenu.v1\_0.driver\_service module ------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.driver_service - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.actionmenu.v1\_0.message\_types module ------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.actionmenu.v1\_0.routes module ---------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.actionmenu.v1\_0.util module -------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.actionmenu.v1_0.util - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.basicmessage.rst b/docs/generated/aries_cloudagent.protocols.basicmessage.rst index 78bbf25c31..70ae0a38f0 100644 --- a/docs/generated/aries_cloudagent.protocols.basicmessage.rst +++ b/docs/generated/aries_cloudagent.protocols.basicmessage.rst @@ -2,16 +2,17 @@ aries\_cloudagent.protocols.basicmessage package ================================================ .. automodule:: aries_cloudagent.protocols.basicmessage - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.protocols.basicmessage.v1_0 + aries_cloudagent.protocols.basicmessage.v1_0 Submodules ---------- @@ -20,8 +21,6 @@ aries\_cloudagent.protocols.basicmessage.definition module ---------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.basicmessage.definition - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.basicmessage.v1_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.basicmessage.v1_0.handlers.rst index 9bc951b81e..9ba540d3d8 100644 --- a/docs/generated/aries_cloudagent.protocols.basicmessage.v1_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.basicmessage.v1_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.basicmessage.v1\_0.handlers package =============================================================== .. automodule:: aries_cloudagent.protocols.basicmessage.v1_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,8 +13,6 @@ aries\_cloudagent.protocols.basicmessage.v1\_0.handlers.basicmessage\_handler mo ------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.basicmessage.v1_0.handlers.basicmessage_handler - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.basicmessage.v1_0.messages.rst b/docs/generated/aries_cloudagent.protocols.basicmessage.v1_0.messages.rst index edc0a395f5..069d11564b 100644 --- a/docs/generated/aries_cloudagent.protocols.basicmessage.v1_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.basicmessage.v1_0.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.basicmessage.v1\_0.messages package =============================================================== .. automodule:: aries_cloudagent.protocols.basicmessage.v1_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,8 +13,6 @@ aries\_cloudagent.protocols.basicmessage.v1\_0.messages.basicmessage module --------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.basicmessage.v1_0.messages.basicmessage - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.basicmessage.v1_0.rst b/docs/generated/aries_cloudagent.protocols.basicmessage.v1_0.rst index 3eae1842a4..3899ab9e88 100644 --- a/docs/generated/aries_cloudagent.protocols.basicmessage.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.basicmessage.v1_0.rst @@ -2,17 +2,18 @@ aries\_cloudagent.protocols.basicmessage.v1\_0 package ====================================================== .. automodule:: aries_cloudagent.protocols.basicmessage.v1_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.protocols.basicmessage.v1_0.handlers - aries_cloudagent.protocols.basicmessage.v1_0.messages + aries_cloudagent.protocols.basicmessage.v1_0.handlers + aries_cloudagent.protocols.basicmessage.v1_0.messages Submodules ---------- @@ -21,16 +22,14 @@ aries\_cloudagent.protocols.basicmessage.v1\_0.message\_types module -------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.basicmessage.v1_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.basicmessage.v1\_0.routes module ------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.basicmessage.v1_0.routes - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.connections.rst b/docs/generated/aries_cloudagent.protocols.connections.rst index fe31957acb..523288cad4 100644 --- a/docs/generated/aries_cloudagent.protocols.connections.rst +++ b/docs/generated/aries_cloudagent.protocols.connections.rst @@ -2,16 +2,17 @@ aries\_cloudagent.protocols.connections package =============================================== .. automodule:: aries_cloudagent.protocols.connections - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.protocols.connections.v1_0 + aries_cloudagent.protocols.connections.v1_0 Submodules ---------- @@ -20,8 +21,6 @@ aries\_cloudagent.protocols.connections.definition module --------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.connections.definition - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.connections.v1_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.connections.v1_0.handlers.rst index 3b2e92fe39..1d74cb8497 100644 --- a/docs/generated/aries_cloudagent.protocols.connections.v1_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.connections.v1_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.connections.v1\_0.handlers package ============================================================== .. automodule:: aries_cloudagent.protocols.connections.v1_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,24 +13,22 @@ aries\_cloudagent.protocols.connections.v1\_0.handlers.connection\_invitation\_h --------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.connections.v1_0.handlers.connection_invitation_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.connections.v1\_0.handlers.connection\_request\_handler module ------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.connections.v1_0.handlers.connection_request_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.connections.v1\_0.handlers.connection\_response\_handler module ------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.connections.v1_0.handlers.connection_response_handler - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.connections.v1_0.messages.rst b/docs/generated/aries_cloudagent.protocols.connections.v1_0.messages.rst index 69ad939486..4714c6e9c9 100644 --- a/docs/generated/aries_cloudagent.protocols.connections.v1_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.connections.v1_0.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.connections.v1\_0.messages package ============================================================== .. automodule:: aries_cloudagent.protocols.connections.v1_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,32 +13,30 @@ aries\_cloudagent.protocols.connections.v1\_0.messages.connection\_invitation mo ------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.connections.v1_0.messages.connection_invitation - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.connections.v1\_0.messages.connection\_request module --------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.connections.v1_0.messages.connection_request - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.connections.v1\_0.messages.connection\_response module ---------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.connections.v1_0.messages.connection_response - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.connections.v1\_0.messages.problem\_report module ----------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.connections.v1_0.messages.problem_report - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.connections.v1_0.models.rst b/docs/generated/aries_cloudagent.protocols.connections.v1_0.models.rst index 3b45b5c9fe..33cc83e115 100644 --- a/docs/generated/aries_cloudagent.protocols.connections.v1_0.models.rst +++ b/docs/generated/aries_cloudagent.protocols.connections.v1_0.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.connections.v1\_0.models package ============================================================ .. automodule:: aries_cloudagent.protocols.connections.v1_0.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,8 +13,6 @@ aries\_cloudagent.protocols.connections.v1\_0.models.connection\_detail module ------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.connections.v1_0.models.connection_detail - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.connections.v1_0.rst b/docs/generated/aries_cloudagent.protocols.connections.v1_0.rst index 6c1f05abf6..b328e2772f 100644 --- a/docs/generated/aries_cloudagent.protocols.connections.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.connections.v1_0.rst @@ -2,18 +2,19 @@ aries\_cloudagent.protocols.connections.v1\_0 package ===================================================== .. automodule:: aries_cloudagent.protocols.connections.v1_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.protocols.connections.v1_0.handlers - aries_cloudagent.protocols.connections.v1_0.messages - aries_cloudagent.protocols.connections.v1_0.models + aries_cloudagent.protocols.connections.v1_0.handlers + aries_cloudagent.protocols.connections.v1_0.messages + aries_cloudagent.protocols.connections.v1_0.models Submodules ---------- @@ -22,24 +23,22 @@ aries\_cloudagent.protocols.connections.v1\_0.manager module ------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.connections.v1_0.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.connections.v1\_0.message\_types module ------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.connections.v1_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.connections.v1\_0.routes module ----------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.connections.v1_0.routes - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.coordinate_mediation.rst b/docs/generated/aries_cloudagent.protocols.coordinate_mediation.rst index a6fd7ddcae..e356c5a0b0 100644 --- a/docs/generated/aries_cloudagent.protocols.coordinate_mediation.rst +++ b/docs/generated/aries_cloudagent.protocols.coordinate_mediation.rst @@ -2,16 +2,17 @@ aries\_cloudagent.protocols.coordinate\_mediation package ========================================================= .. automodule:: aries_cloudagent.protocols.coordinate_mediation - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.protocols.coordinate_mediation.v1_0 + aries_cloudagent.protocols.coordinate_mediation.v1_0 Submodules ---------- @@ -20,16 +21,14 @@ aries\_cloudagent.protocols.coordinate\_mediation.definition module ------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.definition - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.mediation\_invite\_store module --------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.mediation_invite_store - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.handlers.rst index 596449dc6c..fe7d289bf5 100644 --- a/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.handlers package ======================================================================== .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,64 +13,62 @@ aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.handlers.keylist\_handle ---------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.handlers.keylist_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.handlers.keylist\_query\_handler module ----------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.handlers.keylist_query_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.handlers.keylist\_update\_handler module ------------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.handlers.keylist_update_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.handlers.keylist\_update\_response\_handler module ---------------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.handlers.keylist_update_response_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.handlers.mediation\_deny\_handler module ------------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.handlers.mediation_deny_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.handlers.mediation\_grant\_handler module ------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.handlers.mediation_grant_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.handlers.mediation\_request\_handler module --------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.handlers.mediation_request_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.handlers.problem\_report\_handler module ------------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.handlers.problem_report_handler - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.inner.rst b/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.inner.rst index c8a16d75da..431a1485d5 100644 --- a/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.inner.rst +++ b/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.inner.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.messages.inner package ============================================================================== .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.inner - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,32 +13,30 @@ aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.messages.inner.keylist\_ ------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.inner.keylist_key - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.messages.inner.keylist\_query\_paginate module ------------------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.inner.keylist_query_paginate - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.messages.inner.keylist\_update\_rule module --------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.inner.keylist_update_rule - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.messages.inner.keylist\_updated module ---------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.inner.keylist_updated - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.rst b/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.rst index 44ce3b3b26..dff68b4d21 100644 --- a/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.rst @@ -2,16 +2,17 @@ aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.messages package ======================================================================== .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.inner + aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.inner Submodules ---------- @@ -20,64 +21,62 @@ aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.messages.keylist module ------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.keylist - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.messages.keylist\_query module -------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.keylist_query - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.messages.keylist\_update module --------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.keylist_update - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.messages.keylist\_update\_response module ------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.keylist_update_response - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.messages.mediate\_deny module ------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.mediate_deny - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.messages.mediate\_grant module -------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.mediate_grant - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.messages.mediate\_request module ---------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.mediate_request - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.messages.problem\_report module --------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.messages.problem_report - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.models.rst b/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.models.rst index defcdf0d48..ad6bf8060d 100644 --- a/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.models.rst +++ b/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.models package ====================================================================== .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,8 +13,6 @@ aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.models.mediation\_record --------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.models.mediation_record - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.rst b/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.rst index 6dca2451a0..fb05fd04d8 100644 --- a/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.coordinate_mediation.v1_0.rst @@ -2,18 +2,19 @@ aries\_cloudagent.protocols.coordinate\_mediation.v1\_0 package =============================================================== .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.protocols.coordinate_mediation.v1_0.handlers - aries_cloudagent.protocols.coordinate_mediation.v1_0.messages - aries_cloudagent.protocols.coordinate_mediation.v1_0.models + aries_cloudagent.protocols.coordinate_mediation.v1_0.handlers + aries_cloudagent.protocols.coordinate_mediation.v1_0.messages + aries_cloudagent.protocols.coordinate_mediation.v1_0.models Submodules ---------- @@ -22,56 +23,54 @@ aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.controller module ------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.controller - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.manager module ---------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.message\_types module ----------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.normalization module ---------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.normalization - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.route\_manager module ----------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.route_manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.route\_manager\_provider module --------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.route_manager_provider - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.routes module --------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.coordinate_mediation.v1_0.routes - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.didexchange.rst b/docs/generated/aries_cloudagent.protocols.didexchange.rst index 9d591e9af8..84c84dd7df 100644 --- a/docs/generated/aries_cloudagent.protocols.didexchange.rst +++ b/docs/generated/aries_cloudagent.protocols.didexchange.rst @@ -2,16 +2,17 @@ aries\_cloudagent.protocols.didexchange package =============================================== .. automodule:: aries_cloudagent.protocols.didexchange - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.protocols.didexchange.v1_0 + aries_cloudagent.protocols.didexchange.v1_0 Submodules ---------- @@ -20,8 +21,6 @@ aries\_cloudagent.protocols.didexchange.definition module --------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.didexchange.definition - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.didexchange.v1_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.didexchange.v1_0.handlers.rst index 352fd0e8dc..999485395d 100644 --- a/docs/generated/aries_cloudagent.protocols.didexchange.v1_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.didexchange.v1_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.didexchange.v1\_0.handlers package ============================================================== .. automodule:: aries_cloudagent.protocols.didexchange.v1_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,32 +13,30 @@ aries\_cloudagent.protocols.didexchange.v1\_0.handlers.complete\_handler module ------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.didexchange.v1_0.handlers.complete_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.didexchange.v1\_0.handlers.invitation\_handler module --------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.didexchange.v1_0.handlers.invitation_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.didexchange.v1\_0.handlers.request\_handler module ------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.didexchange.v1_0.handlers.request_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.didexchange.v1\_0.handlers.response\_handler module ------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.didexchange.v1_0.handlers.response_handler - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.didexchange.v1_0.messages.rst b/docs/generated/aries_cloudagent.protocols.didexchange.v1_0.messages.rst index eecc1e878a..bcd9ffdf7a 100644 --- a/docs/generated/aries_cloudagent.protocols.didexchange.v1_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.didexchange.v1_0.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.didexchange.v1\_0.messages package ============================================================== .. automodule:: aries_cloudagent.protocols.didexchange.v1_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,32 +13,30 @@ aries\_cloudagent.protocols.didexchange.v1\_0.messages.complete module ---------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.didexchange.v1_0.messages.complete - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.didexchange.v1\_0.messages.problem\_report\_reason module ------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.didexchange.v1_0.messages.problem_report_reason - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.didexchange.v1\_0.messages.request module --------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.didexchange.v1_0.messages.request - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.didexchange.v1\_0.messages.response module ---------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.didexchange.v1_0.messages.response - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.didexchange.v1_0.rst b/docs/generated/aries_cloudagent.protocols.didexchange.v1_0.rst index e63c3ec552..5d74e58b7a 100644 --- a/docs/generated/aries_cloudagent.protocols.didexchange.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.didexchange.v1_0.rst @@ -2,17 +2,18 @@ aries\_cloudagent.protocols.didexchange.v1\_0 package ===================================================== .. automodule:: aries_cloudagent.protocols.didexchange.v1_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.protocols.didexchange.v1_0.handlers - aries_cloudagent.protocols.didexchange.v1_0.messages + aries_cloudagent.protocols.didexchange.v1_0.handlers + aries_cloudagent.protocols.didexchange.v1_0.messages Submodules ---------- @@ -21,24 +22,22 @@ aries\_cloudagent.protocols.didexchange.v1\_0.manager module ------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.didexchange.v1_0.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.didexchange.v1\_0.message\_types module ------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.didexchange.v1_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.didexchange.v1\_0.routes module ----------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.didexchange.v1_0.routes - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.discovery.rst b/docs/generated/aries_cloudagent.protocols.discovery.rst index 92383fe5fd..4c736183c7 100644 --- a/docs/generated/aries_cloudagent.protocols.discovery.rst +++ b/docs/generated/aries_cloudagent.protocols.discovery.rst @@ -2,17 +2,18 @@ aries\_cloudagent.protocols.discovery package ============================================= .. automodule:: aries_cloudagent.protocols.discovery - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.protocols.discovery.v1_0 - aries_cloudagent.protocols.discovery.v2_0 + aries_cloudagent.protocols.discovery.v1_0 + aries_cloudagent.protocols.discovery.v2_0 Submodules ---------- @@ -21,8 +22,6 @@ aries\_cloudagent.protocols.discovery.definition module ------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.definition - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.discovery.v1_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.discovery.v1_0.handlers.rst index 260e5a7f44..3c81111991 100644 --- a/docs/generated/aries_cloudagent.protocols.discovery.v1_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.discovery.v1_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.discovery.v1\_0.handlers package ============================================================ .. automodule:: aries_cloudagent.protocols.discovery.v1_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,16 +13,14 @@ aries\_cloudagent.protocols.discovery.v1\_0.handlers.disclose\_handler module ----------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v1_0.handlers.disclose_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.discovery.v1\_0.handlers.query\_handler module -------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v1_0.handlers.query_handler - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.discovery.v1_0.messages.rst b/docs/generated/aries_cloudagent.protocols.discovery.v1_0.messages.rst index 0c489c0845..35a9e49889 100644 --- a/docs/generated/aries_cloudagent.protocols.discovery.v1_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.discovery.v1_0.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.discovery.v1\_0.messages package ============================================================ .. automodule:: aries_cloudagent.protocols.discovery.v1_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,16 +13,14 @@ aries\_cloudagent.protocols.discovery.v1\_0.messages.disclose module -------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v1_0.messages.disclose - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.discovery.v1\_0.messages.query module ----------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v1_0.messages.query - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.discovery.v1_0.models.rst b/docs/generated/aries_cloudagent.protocols.discovery.v1_0.models.rst index e080f6bf52..baec81e6d7 100644 --- a/docs/generated/aries_cloudagent.protocols.discovery.v1_0.models.rst +++ b/docs/generated/aries_cloudagent.protocols.discovery.v1_0.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.discovery.v1\_0.models package ========================================================== .. automodule:: aries_cloudagent.protocols.discovery.v1_0.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,8 +13,6 @@ aries\_cloudagent.protocols.discovery.v1\_0.models.discovery\_record module --------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v1_0.models.discovery_record - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.discovery.v1_0.rst b/docs/generated/aries_cloudagent.protocols.discovery.v1_0.rst index 410eef398f..f221130077 100644 --- a/docs/generated/aries_cloudagent.protocols.discovery.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.discovery.v1_0.rst @@ -2,18 +2,19 @@ aries\_cloudagent.protocols.discovery.v1\_0 package =================================================== .. automodule:: aries_cloudagent.protocols.discovery.v1_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.protocols.discovery.v1_0.handlers - aries_cloudagent.protocols.discovery.v1_0.messages - aries_cloudagent.protocols.discovery.v1_0.models + aries_cloudagent.protocols.discovery.v1_0.handlers + aries_cloudagent.protocols.discovery.v1_0.messages + aries_cloudagent.protocols.discovery.v1_0.models Submodules ---------- @@ -22,24 +23,22 @@ aries\_cloudagent.protocols.discovery.v1\_0.manager module ---------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v1_0.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.discovery.v1\_0.message\_types module ----------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v1_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.discovery.v1\_0.routes module --------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v1_0.routes - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.discovery.v2_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.discovery.v2_0.handlers.rst index 3c17b21340..b79d280002 100644 --- a/docs/generated/aries_cloudagent.protocols.discovery.v2_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.discovery.v2_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.discovery.v2\_0.handlers package ============================================================ .. automodule:: aries_cloudagent.protocols.discovery.v2_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,16 +13,14 @@ aries\_cloudagent.protocols.discovery.v2\_0.handlers.disclosures\_handler module -------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v2_0.handlers.disclosures_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.discovery.v2\_0.handlers.queries\_handler module ---------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v2_0.handlers.queries_handler - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.discovery.v2_0.messages.rst b/docs/generated/aries_cloudagent.protocols.discovery.v2_0.messages.rst index e180e31f23..99fdbc332b 100644 --- a/docs/generated/aries_cloudagent.protocols.discovery.v2_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.discovery.v2_0.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.discovery.v2\_0.messages package ============================================================ .. automodule:: aries_cloudagent.protocols.discovery.v2_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,16 +13,14 @@ aries\_cloudagent.protocols.discovery.v2\_0.messages.disclosures module ----------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v2_0.messages.disclosures - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.discovery.v2\_0.messages.queries module ------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v2_0.messages.queries - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.discovery.v2_0.models.rst b/docs/generated/aries_cloudagent.protocols.discovery.v2_0.models.rst index 19ea873489..767db8e92e 100644 --- a/docs/generated/aries_cloudagent.protocols.discovery.v2_0.models.rst +++ b/docs/generated/aries_cloudagent.protocols.discovery.v2_0.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.discovery.v2\_0.models package ========================================================== .. automodule:: aries_cloudagent.protocols.discovery.v2_0.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,8 +13,6 @@ aries\_cloudagent.protocols.discovery.v2\_0.models.discovery\_record module --------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v2_0.models.discovery_record - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.discovery.v2_0.rst b/docs/generated/aries_cloudagent.protocols.discovery.v2_0.rst index 07d09fdb73..d12fef9348 100644 --- a/docs/generated/aries_cloudagent.protocols.discovery.v2_0.rst +++ b/docs/generated/aries_cloudagent.protocols.discovery.v2_0.rst @@ -2,18 +2,19 @@ aries\_cloudagent.protocols.discovery.v2\_0 package =================================================== .. automodule:: aries_cloudagent.protocols.discovery.v2_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.protocols.discovery.v2_0.handlers - aries_cloudagent.protocols.discovery.v2_0.messages - aries_cloudagent.protocols.discovery.v2_0.models + aries_cloudagent.protocols.discovery.v2_0.handlers + aries_cloudagent.protocols.discovery.v2_0.messages + aries_cloudagent.protocols.discovery.v2_0.models Submodules ---------- @@ -22,24 +23,22 @@ aries\_cloudagent.protocols.discovery.v2\_0.manager module ---------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v2_0.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.discovery.v2\_0.message\_types module ----------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v2_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.discovery.v2\_0.routes module --------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.discovery.v2_0.routes - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.endorse_transaction.rst b/docs/generated/aries_cloudagent.protocols.endorse_transaction.rst index 6cb027abe4..d940678a45 100644 --- a/docs/generated/aries_cloudagent.protocols.endorse_transaction.rst +++ b/docs/generated/aries_cloudagent.protocols.endorse_transaction.rst @@ -2,16 +2,17 @@ aries\_cloudagent.protocols.endorse\_transaction package ======================================================== .. automodule:: aries_cloudagent.protocols.endorse_transaction - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.protocols.endorse_transaction.v1_0 + aries_cloudagent.protocols.endorse_transaction.v1_0 Submodules ---------- @@ -20,8 +21,6 @@ aries\_cloudagent.protocols.endorse\_transaction.definition module ------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.endorse_transaction.definition - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.handlers.rst index 055c2142c6..e76ffa3ddc 100644 --- a/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.endorse\_transaction.v1\_0.handlers package ======================================================================= .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,56 +13,54 @@ aries\_cloudagent.protocols.endorse\_transaction.v1\_0.handlers.endorsed\_transa --------------------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.handlers.endorsed_transaction_response_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.handlers.refused\_transaction\_response\_handler module -------------------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.handlers.refused_transaction_response_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.handlers.transaction\_acknowledgement\_handler module ------------------------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.handlers.transaction_acknowledgement_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.handlers.transaction\_cancel\_handler module --------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.handlers.transaction_cancel_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.handlers.transaction\_job\_to\_send\_handler module ---------------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.handlers.transaction_job_to_send_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.handlers.transaction\_request\_handler module ---------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.handlers.transaction_request_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.handlers.transaction\_resend\_handler module --------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.handlers.transaction_resend_handler - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.messages.rst b/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.messages.rst index c8809f0993..3e14c4194c 100644 --- a/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.endorse\_transaction.v1\_0.messages package ======================================================================= .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,64 +13,62 @@ aries\_cloudagent.protocols.endorse\_transaction.v1\_0.messages.cancel\_transact ------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.messages.cancel_transaction - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.messages.endorsed\_transaction\_response module ------------------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.messages.endorsed_transaction_response - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.messages.messages\_attach module --------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.messages.messages_attach - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.messages.refused\_transaction\_response module ----------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.messages.refused_transaction_response - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.messages.transaction\_acknowledgement module --------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.messages.transaction_acknowledgement - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.messages.transaction\_job\_to\_send module ------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.messages.transaction_job_to_send - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.messages.transaction\_request module ------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.messages.transaction_request - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.messages.transaction\_resend module ------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.messages.transaction_resend - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.models.rst b/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.models.rst index c2f7f4cc89..a120e79ce3 100644 --- a/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.models.rst +++ b/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.endorse\_transaction.v1\_0.models package ===================================================================== .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,8 +13,6 @@ aries\_cloudagent.protocols.endorse\_transaction.v1\_0.models.transaction\_recor ---------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.models.transaction_record - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.rst b/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.rst index 24862f2880..eaf4c9eb32 100644 --- a/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.rst @@ -2,18 +2,19 @@ aries\_cloudagent.protocols.endorse\_transaction.v1\_0 package ============================================================== .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.protocols.endorse_transaction.v1_0.handlers - aries_cloudagent.protocols.endorse_transaction.v1_0.messages - aries_cloudagent.protocols.endorse_transaction.v1_0.models + aries_cloudagent.protocols.endorse_transaction.v1_0.handlers + aries_cloudagent.protocols.endorse_transaction.v1_0.messages + aries_cloudagent.protocols.endorse_transaction.v1_0.models Submodules ---------- @@ -22,48 +23,46 @@ aries\_cloudagent.protocols.endorse\_transaction.v1\_0.controller module ------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.controller - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.manager module --------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.message\_types module ---------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.routes module -------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.transaction\_jobs module ------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.transaction_jobs - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.endorse\_transaction.v1\_0.util module ------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.util - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.introduction.rst b/docs/generated/aries_cloudagent.protocols.introduction.rst index 2c331fb06b..ee89b06b48 100644 --- a/docs/generated/aries_cloudagent.protocols.introduction.rst +++ b/docs/generated/aries_cloudagent.protocols.introduction.rst @@ -2,16 +2,17 @@ aries\_cloudagent.protocols.introduction package ================================================ .. automodule:: aries_cloudagent.protocols.introduction - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.protocols.introduction.v0_1 + aries_cloudagent.protocols.introduction.v0_1 Submodules ---------- @@ -20,8 +21,6 @@ aries\_cloudagent.protocols.introduction.definition module ---------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.introduction.definition - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.introduction.v0_1.handlers.rst b/docs/generated/aries_cloudagent.protocols.introduction.v0_1.handlers.rst index f9334b202d..ded1f15cd0 100644 --- a/docs/generated/aries_cloudagent.protocols.introduction.v0_1.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.introduction.v0_1.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.introduction.v0\_1.handlers package =============================================================== .. automodule:: aries_cloudagent.protocols.introduction.v0_1.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,24 +13,22 @@ aries\_cloudagent.protocols.introduction.v0\_1.handlers.forward\_invitation\_han ------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.introduction.v0_1.handlers.forward_invitation_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.introduction.v0\_1.handlers.invitation\_handler module ---------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.introduction.v0_1.handlers.invitation_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.introduction.v0\_1.handlers.invitation\_request\_handler module ------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.introduction.v0_1.handlers.invitation_request_handler - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.introduction.v0_1.messages.rst b/docs/generated/aries_cloudagent.protocols.introduction.v0_1.messages.rst index c469049dfe..841d3da69c 100644 --- a/docs/generated/aries_cloudagent.protocols.introduction.v0_1.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.introduction.v0_1.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.introduction.v0\_1.messages package =============================================================== .. automodule:: aries_cloudagent.protocols.introduction.v0_1.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,24 +13,22 @@ aries\_cloudagent.protocols.introduction.v0\_1.messages.forward\_invitation modu ---------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.introduction.v0_1.messages.forward_invitation - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.introduction.v0\_1.messages.invitation module ------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.introduction.v0_1.messages.invitation - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.introduction.v0\_1.messages.invitation\_request module ---------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.introduction.v0_1.messages.invitation_request - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.introduction.v0_1.rst b/docs/generated/aries_cloudagent.protocols.introduction.v0_1.rst index 8f8b63d65f..0ceece45e2 100644 --- a/docs/generated/aries_cloudagent.protocols.introduction.v0_1.rst +++ b/docs/generated/aries_cloudagent.protocols.introduction.v0_1.rst @@ -2,17 +2,18 @@ aries\_cloudagent.protocols.introduction.v0\_1 package ====================================================== .. automodule:: aries_cloudagent.protocols.introduction.v0_1 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.protocols.introduction.v0_1.handlers - aries_cloudagent.protocols.introduction.v0_1.messages + aries_cloudagent.protocols.introduction.v0_1.handlers + aries_cloudagent.protocols.introduction.v0_1.messages Submodules ---------- @@ -21,32 +22,30 @@ aries\_cloudagent.protocols.introduction.v0\_1.base\_service module ------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.introduction.v0_1.base_service - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.introduction.v0\_1.demo\_service module ------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.introduction.v0_1.demo_service - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.introduction.v0\_1.message\_types module -------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.introduction.v0_1.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.introduction.v0\_1.routes module ------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.introduction.v0_1.routes - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.rst index 6edd7cb25f..9ad3c8172e 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.rst @@ -2,17 +2,18 @@ aries\_cloudagent.protocols.issue\_credential package ===================================================== .. automodule:: aries_cloudagent.protocols.issue_credential - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.protocols.issue_credential.v1_0 - aries_cloudagent.protocols.issue_credential.v2_0 + aries_cloudagent.protocols.issue_credential.v1_0 + aries_cloudagent.protocols.issue_credential.v2_0 Submodules ---------- @@ -21,8 +22,6 @@ aries\_cloudagent.protocols.issue\_credential.definition module --------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.definition - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.handlers.rst index 24e308ca87..860b89508a 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.issue\_credential.v1\_0.handlers package ==================================================================== .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,48 +13,46 @@ aries\_cloudagent.protocols.issue\_credential.v1\_0.handlers.credential\_ack\_ha -------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.handlers.credential_ack_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v1\_0.handlers.credential\_issue\_handler module ---------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.handlers.credential_issue_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v1\_0.handlers.credential\_offer\_handler module ---------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.handlers.credential_offer_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v1\_0.handlers.credential\_problem\_report\_handler module -------------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.handlers.credential_problem_report_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v1\_0.handlers.credential\_proposal\_handler module ------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.handlers.credential_proposal_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v1\_0.handlers.credential\_request\_handler module ------------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.handlers.credential_request_handler - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.messages.inner.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.messages.inner.rst index 17ac8e199e..ebe4cc6072 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.messages.inner.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.messages.inner.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.issue\_credential.v1\_0.messages.inner package ========================================================================== .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.messages.inner - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,8 +13,6 @@ aries\_cloudagent.protocols.issue\_credential.v1\_0.messages.inner.credential\_p --------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.messages.inner.credential_preview - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.messages.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.messages.rst index 460cc32ac4..db091d4033 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.messages.rst @@ -2,16 +2,17 @@ aries\_cloudagent.protocols.issue\_credential.v1\_0.messages package ==================================================================== .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.protocols.issue_credential.v1_0.messages.inner + aries_cloudagent.protocols.issue_credential.v1_0.messages.inner Submodules ---------- @@ -20,56 +21,54 @@ aries\_cloudagent.protocols.issue\_credential.v1\_0.messages.credential\_ack mod ----------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.messages.credential_ack - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v1\_0.messages.credential\_exchange\_webhook module ------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.messages.credential_exchange_webhook - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v1\_0.messages.credential\_issue module ------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.messages.credential_issue - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v1\_0.messages.credential\_offer module ------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.messages.credential_offer - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v1\_0.messages.credential\_problem\_report module ----------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.messages.credential_problem_report - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v1\_0.messages.credential\_proposal module ---------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.messages.credential_proposal - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v1\_0.messages.credential\_request module --------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.messages.credential_request - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.models.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.models.rst index 1624debb7d..3fa2cfdd78 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.models.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.issue\_credential.v1\_0.models package ================================================================== .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,8 +13,6 @@ aries\_cloudagent.protocols.issue\_credential.v1\_0.models.credential\_exchange -------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.models.credential_exchange - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.rst index d1359b1d2a..6221c61954 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v1_0.rst @@ -2,18 +2,19 @@ aries\_cloudagent.protocols.issue\_credential.v1\_0 package =========================================================== .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.protocols.issue_credential.v1_0.handlers - aries_cloudagent.protocols.issue_credential.v1_0.messages - aries_cloudagent.protocols.issue_credential.v1_0.models + aries_cloudagent.protocols.issue_credential.v1_0.handlers + aries_cloudagent.protocols.issue_credential.v1_0.messages + aries_cloudagent.protocols.issue_credential.v1_0.models Submodules ---------- @@ -22,32 +23,30 @@ aries\_cloudagent.protocols.issue\_credential.v1\_0.controller module --------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.controller - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v1\_0.manager module ------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v1\_0.message\_types module ------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v1\_0.routes module ----------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v1_0.routes - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.indy.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.indy.rst index d4324b778e..34fdbaf253 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.indy.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.indy.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.formats.indy package ======================================================================== .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.formats.indy - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,8 +13,6 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.formats.indy.handler module ------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.formats.indy.handler - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.models.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.models.rst index 96d8eedb71..771f548b4e 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.models.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.formats.ld\_proof.models pac ==================================================================================== .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,16 +13,14 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.formats.ld\_proof.models.cre ------------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.models.cred_detail - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.formats.ld\_proof.models.cred\_detail\_options module --------------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.models.cred_detail_options - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.rst index 89c7256d13..594e20f4d8 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.rst @@ -2,16 +2,17 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.formats.ld\_proof package ============================================================================= .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.models + aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.models Submodules ---------- @@ -20,8 +21,6 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.formats.ld\_proof.handler mo ------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.handler - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.rst index 9fefa18078..31bc906fed 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.rst @@ -2,17 +2,18 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.formats package =================================================================== .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.formats - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.protocols.issue_credential.v2_0.formats.indy - aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof + aries_cloudagent.protocols.issue_credential.v2_0.formats.indy + aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof Submodules ---------- @@ -21,8 +22,6 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.formats.handler module -------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.formats.handler - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.handlers.rst index 6a16a66f88..d64924b2b4 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.handlers package ==================================================================== .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,48 +13,46 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.handlers.cred\_ack\_handler -------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.handlers.cred_ack_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.handlers.cred\_issue\_handler module ---------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.handlers.cred_issue_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.handlers.cred\_offer\_handler module ---------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.handlers.cred_offer_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.handlers.cred\_problem\_report\_handler module -------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.handlers.cred_problem_report_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.handlers.cred\_proposal\_handler module ------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.handlers.cred_proposal_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.handlers.cred\_request\_handler module ------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.handlers.cred_request_handler - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.messages.inner.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.messages.inner.rst index 631445edbb..5591934834 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.messages.inner.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.messages.inner.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.messages.inner package ========================================================================== .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.messages.inner - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,8 +13,6 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.messages.inner.cred\_preview --------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.messages.inner.cred_preview - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.messages.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.messages.rst index 41a2d6e82b..aedce87e4f 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.messages.rst @@ -2,16 +2,17 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.messages package ==================================================================== .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.protocols.issue_credential.v2_0.messages.inner + aries_cloudagent.protocols.issue_credential.v2_0.messages.inner Submodules ---------- @@ -20,64 +21,62 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.messages.cred\_ack module ----------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.messages.cred_ack - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.messages.cred\_ex\_record\_webhook module --------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.messages.cred_ex_record_webhook - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.messages.cred\_format module -------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.messages.cred_format - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.messages.cred\_issue module ------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.messages.cred_issue - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.messages.cred\_offer module ------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.messages.cred_offer - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.messages.cred\_problem\_report module ----------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.messages.cred_problem_report - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.messages.cred\_proposal module ---------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.messages.cred_proposal - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.messages.cred\_request module --------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.messages.cred_request - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.models.detail.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.models.detail.rst index b055fb04ea..c677e91e09 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.models.detail.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.models.detail.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.models.detail package ========================================================================= .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.models.detail - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,16 +13,14 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.models.detail.indy module ----------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.models.detail.indy - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.models.detail.ld\_proof module ---------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.models.detail.ld_proof - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.models.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.models.rst index 65f0f10156..5e77c64a12 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.models.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.models.rst @@ -2,16 +2,17 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.models package ================================================================== .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.protocols.issue_credential.v2_0.models.detail + aries_cloudagent.protocols.issue_credential.v2_0.models.detail Submodules ---------- @@ -20,8 +21,6 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.models.cred\_ex\_record modu ---------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.models.cred_ex_record - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.rst b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.rst index 461ce7a789..daf64871a1 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.rst @@ -2,19 +2,20 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0 package =========================================================== .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.protocols.issue_credential.v2_0.formats - aries_cloudagent.protocols.issue_credential.v2_0.handlers - aries_cloudagent.protocols.issue_credential.v2_0.messages - aries_cloudagent.protocols.issue_credential.v2_0.models + aries_cloudagent.protocols.issue_credential.v2_0.formats + aries_cloudagent.protocols.issue_credential.v2_0.handlers + aries_cloudagent.protocols.issue_credential.v2_0.messages + aries_cloudagent.protocols.issue_credential.v2_0.models Submodules ---------- @@ -23,32 +24,30 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.controller module --------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.controller - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.manager module ------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.message\_types module ------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.issue\_credential.v2\_0.routes module ----------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.routes - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.notification.rst b/docs/generated/aries_cloudagent.protocols.notification.rst index ecded9877d..e7c192af55 100644 --- a/docs/generated/aries_cloudagent.protocols.notification.rst +++ b/docs/generated/aries_cloudagent.protocols.notification.rst @@ -2,16 +2,17 @@ aries\_cloudagent.protocols.notification package ================================================ .. automodule:: aries_cloudagent.protocols.notification - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.protocols.notification.v1_0 + aries_cloudagent.protocols.notification.v1_0 Submodules ---------- @@ -20,8 +21,6 @@ aries\_cloudagent.protocols.notification.definition module ---------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.notification.definition - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.notification.v1_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.notification.v1_0.handlers.rst index 5d3124b4ad..83fb827146 100644 --- a/docs/generated/aries_cloudagent.protocols.notification.v1_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.notification.v1_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.notification.v1\_0.handlers package =============================================================== .. automodule:: aries_cloudagent.protocols.notification.v1_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,8 +13,6 @@ aries\_cloudagent.protocols.notification.v1\_0.handlers.ack\_handler module --------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.notification.v1_0.handlers.ack_handler - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.notification.v1_0.messages.rst b/docs/generated/aries_cloudagent.protocols.notification.v1_0.messages.rst index d3c6dfbf96..05f005efc2 100644 --- a/docs/generated/aries_cloudagent.protocols.notification.v1_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.notification.v1_0.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.notification.v1\_0.messages package =============================================================== .. automodule:: aries_cloudagent.protocols.notification.v1_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,8 +13,6 @@ aries\_cloudagent.protocols.notification.v1\_0.messages.ack module ------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.notification.v1_0.messages.ack - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.notification.v1_0.rst b/docs/generated/aries_cloudagent.protocols.notification.v1_0.rst index 130125346b..d18146087d 100644 --- a/docs/generated/aries_cloudagent.protocols.notification.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.notification.v1_0.rst @@ -2,17 +2,18 @@ aries\_cloudagent.protocols.notification.v1\_0 package ====================================================== .. automodule:: aries_cloudagent.protocols.notification.v1_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.protocols.notification.v1_0.handlers - aries_cloudagent.protocols.notification.v1_0.messages + aries_cloudagent.protocols.notification.v1_0.handlers + aries_cloudagent.protocols.notification.v1_0.messages Submodules ---------- @@ -21,8 +22,6 @@ aries\_cloudagent.protocols.notification.v1\_0.message\_types module -------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.notification.v1_0.message_types - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.out_of_band.rst b/docs/generated/aries_cloudagent.protocols.out_of_band.rst index 0d243c0bd7..8d68b3f53c 100644 --- a/docs/generated/aries_cloudagent.protocols.out_of_band.rst +++ b/docs/generated/aries_cloudagent.protocols.out_of_band.rst @@ -2,16 +2,17 @@ aries\_cloudagent.protocols.out\_of\_band package ================================================= .. automodule:: aries_cloudagent.protocols.out_of_band - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.protocols.out_of_band.v1_0 + aries_cloudagent.protocols.out_of_band.v1_0 Submodules ---------- @@ -20,8 +21,6 @@ aries\_cloudagent.protocols.out\_of\_band.definition module ----------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.out_of_band.definition - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.handlers.rst index 390979fa65..354688b2b8 100644 --- a/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.out\_of\_band.v1\_0.handlers package ================================================================ .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,24 +13,22 @@ aries\_cloudagent.protocols.out\_of\_band.v1\_0.handlers.problem\_report\_handle ---------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.handlers.problem_report_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.out\_of\_band.v1\_0.handlers.reuse\_accept\_handler module -------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.handlers.reuse_accept_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.out\_of\_band.v1\_0.handlers.reuse\_handler module ------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.handlers.reuse_handler - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.messages.rst b/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.messages.rst index 79183ffd32..d01723be08 100644 --- a/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.out\_of\_band.v1\_0.messages package ================================================================ .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,40 +13,38 @@ aries\_cloudagent.protocols.out\_of\_band.v1\_0.messages.invitation module -------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.messages.invitation - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.out\_of\_band.v1\_0.messages.problem\_report module ------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.messages.problem_report - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.out\_of\_band.v1\_0.messages.reuse module --------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.messages.reuse - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.out\_of\_band.v1\_0.messages.reuse\_accept module ----------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.messages.reuse_accept - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.out\_of\_band.v1\_0.messages.service module ----------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.messages.service - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.models.rst b/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.models.rst index 9ae0effc91..b1b7bfd33c 100644 --- a/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.models.rst +++ b/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.out\_of\_band.v1\_0.models package ============================================================== .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,16 +13,14 @@ aries\_cloudagent.protocols.out\_of\_band.v1\_0.models.invitation module ------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.models.invitation - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.out\_of\_band.v1\_0.models.oob\_record module ------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.models.oob_record - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.rst b/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.rst index 8462b38004..80443da521 100644 --- a/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.out_of_band.v1_0.rst @@ -2,18 +2,19 @@ aries\_cloudagent.protocols.out\_of\_band.v1\_0 package ======================================================= .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.protocols.out_of_band.v1_0.handlers - aries_cloudagent.protocols.out_of_band.v1_0.messages - aries_cloudagent.protocols.out_of_band.v1_0.models + aries_cloudagent.protocols.out_of_band.v1_0.handlers + aries_cloudagent.protocols.out_of_band.v1_0.messages + aries_cloudagent.protocols.out_of_band.v1_0.models Submodules ---------- @@ -22,32 +23,30 @@ aries\_cloudagent.protocols.out\_of\_band.v1\_0.controller module ----------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.controller - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.out\_of\_band.v1\_0.manager module -------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.out\_of\_band.v1\_0.message\_types module --------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.out\_of\_band.v1\_0.routes module ------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.out_of_band.v1_0.routes - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.dif.rst b/docs/generated/aries_cloudagent.protocols.present_proof.dif.rst index adad4e0b16..5d83cee955 100644 --- a/docs/generated/aries_cloudagent.protocols.present_proof.dif.rst +++ b/docs/generated/aries_cloudagent.protocols.present_proof.dif.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.present\_proof.dif package ====================================================== .. automodule:: aries_cloudagent.protocols.present_proof.dif - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,40 +13,38 @@ aries\_cloudagent.protocols.present\_proof.dif.pres\_exch module ---------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.dif.pres_exch - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.dif.pres\_exch\_handler module ------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.dif.pres_exch_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.dif.pres\_proposal\_schema module ---------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.dif.pres_proposal_schema - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.dif.pres\_request\_schema module --------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.dif.pres_request_schema - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.dif.pres\_schema module ------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.present_proof.dif.pres_schema - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.indy.rst b/docs/generated/aries_cloudagent.protocols.present_proof.indy.rst index ca2879b241..a1954d2b86 100644 --- a/docs/generated/aries_cloudagent.protocols.present_proof.indy.rst +++ b/docs/generated/aries_cloudagent.protocols.present_proof.indy.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.present\_proof.indy package ======================================================= .. automodule:: aries_cloudagent.protocols.present_proof.indy - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,8 +13,6 @@ aries\_cloudagent.protocols.present\_proof.indy.pres\_exch\_handler module -------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.indy.pres_exch_handler - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.rst b/docs/generated/aries_cloudagent.protocols.present_proof.rst index 9318dbf8dc..e9b69e7de6 100644 --- a/docs/generated/aries_cloudagent.protocols.present_proof.rst +++ b/docs/generated/aries_cloudagent.protocols.present_proof.rst @@ -2,19 +2,20 @@ aries\_cloudagent.protocols.present\_proof package ================================================== .. automodule:: aries_cloudagent.protocols.present_proof - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.protocols.present_proof.dif - aries_cloudagent.protocols.present_proof.indy - aries_cloudagent.protocols.present_proof.v1_0 - aries_cloudagent.protocols.present_proof.v2_0 + aries_cloudagent.protocols.present_proof.dif + aries_cloudagent.protocols.present_proof.indy + aries_cloudagent.protocols.present_proof.v1_0 + aries_cloudagent.protocols.present_proof.v2_0 Submodules ---------- @@ -23,8 +24,6 @@ aries\_cloudagent.protocols.present\_proof.definition module ------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.present_proof.definition - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.handlers.rst index ef97bbb127..24c8341e2e 100644 --- a/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.present\_proof.v1\_0.handlers package ================================================================= .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,40 +13,38 @@ aries\_cloudagent.protocols.present\_proof.v1\_0.handlers.presentation\_ack\_han ------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.handlers.presentation_ack_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v1\_0.handlers.presentation\_handler module -------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.handlers.presentation_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v1\_0.handlers.presentation\_problem\_report\_handler module ------------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.handlers.presentation_problem_report_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v1\_0.handlers.presentation\_proposal\_handler module ------------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.handlers.presentation_proposal_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v1\_0.handlers.presentation\_request\_handler module ----------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.handlers.presentation_request_handler - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.messages.rst b/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.messages.rst index e98676833d..dc6cf13bc5 100644 --- a/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.present\_proof.v1\_0.messages package ================================================================= .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,48 +13,46 @@ aries\_cloudagent.protocols.present\_proof.v1\_0.messages.presentation module ----------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.messages.presentation - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v1\_0.messages.presentation\_ack module ---------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.messages.presentation_ack - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v1\_0.messages.presentation\_problem\_report module ---------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.messages.presentation_problem_report - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v1\_0.messages.presentation\_proposal module --------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.messages.presentation_proposal - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v1\_0.messages.presentation\_request module -------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.messages.presentation_request - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v1\_0.messages.presentation\_webhook module -------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.messages.presentation_webhook - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.models.rst b/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.models.rst index 2fbd454913..6e97752c6a 100644 --- a/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.models.rst +++ b/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.present\_proof.v1\_0.models package =============================================================== .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,8 +13,6 @@ aries\_cloudagent.protocols.present\_proof.v1\_0.models.presentation\_exchange m ------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.models.presentation_exchange - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.rst b/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.rst index cffc7c119f..1a18103b2d 100644 --- a/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.rst @@ -2,18 +2,19 @@ aries\_cloudagent.protocols.present\_proof.v1\_0 package ======================================================== .. automodule:: aries_cloudagent.protocols.present_proof.v1_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.protocols.present_proof.v1_0.handlers - aries_cloudagent.protocols.present_proof.v1_0.messages - aries_cloudagent.protocols.present_proof.v1_0.models + aries_cloudagent.protocols.present_proof.v1_0.handlers + aries_cloudagent.protocols.present_proof.v1_0.messages + aries_cloudagent.protocols.present_proof.v1_0.models Submodules ---------- @@ -22,32 +23,30 @@ aries\_cloudagent.protocols.present\_proof.v1\_0.controller module ------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.controller - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v1\_0.manager module --------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v1\_0.message\_types module ---------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v1\_0.routes module -------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v1_0.routes - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.formats.dif.rst b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.formats.dif.rst index fd3ada060b..06b74fc41a 100644 --- a/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.formats.dif.rst +++ b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.formats.dif.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.present\_proof.v2\_0.formats.dif package ==================================================================== .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.formats.dif - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,8 +13,6 @@ aries\_cloudagent.protocols.present\_proof.v2\_0.formats.dif.handler module --------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.formats.dif.handler - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.formats.indy.rst b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.formats.indy.rst index 2f54cdef9f..fb215abc4e 100644 --- a/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.formats.indy.rst +++ b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.formats.indy.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.present\_proof.v2\_0.formats.indy package ===================================================================== .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.formats.indy - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,8 +13,6 @@ aries\_cloudagent.protocols.present\_proof.v2\_0.formats.indy.handler module ---------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.formats.indy.handler - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.formats.rst b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.formats.rst index 8602dd4411..5d44e018a1 100644 --- a/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.formats.rst +++ b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.formats.rst @@ -2,17 +2,18 @@ aries\_cloudagent.protocols.present\_proof.v2\_0.formats package ================================================================ .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.formats - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.protocols.present_proof.v2_0.formats.dif - aries_cloudagent.protocols.present_proof.v2_0.formats.indy + aries_cloudagent.protocols.present_proof.v2_0.formats.dif + aries_cloudagent.protocols.present_proof.v2_0.formats.indy Submodules ---------- @@ -21,8 +22,6 @@ aries\_cloudagent.protocols.present\_proof.v2\_0.formats.handler module ----------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.formats.handler - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.handlers.rst index 8074e740f1..6be81bcd53 100644 --- a/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.present\_proof.v2\_0.handlers package ================================================================= .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,40 +13,38 @@ aries\_cloudagent.protocols.present\_proof.v2\_0.handlers.pres\_ack\_handler mod ----------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.handlers.pres_ack_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v2\_0.handlers.pres\_handler module ------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.handlers.pres_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v2\_0.handlers.pres\_problem\_report\_handler module ----------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.handlers.pres_problem_report_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v2\_0.handlers.pres\_proposal\_handler module ---------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.handlers.pres_proposal_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v2\_0.handlers.pres\_request\_handler module --------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.handlers.pres_request_handler - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.messages.rst b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.messages.rst index 68139f57cd..907d55c539 100644 --- a/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.present\_proof.v2\_0.messages package ================================================================= .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,56 +13,54 @@ aries\_cloudagent.protocols.present\_proof.v2\_0.messages.pres module --------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.messages.pres - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v2\_0.messages.pres\_ack module -------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.messages.pres_ack - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v2\_0.messages.pres\_format module ----------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.messages.pres_format - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v2\_0.messages.pres\_problem\_report module -------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.messages.pres_problem_report - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v2\_0.messages.pres\_proposal module ------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.messages.pres_proposal - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v2\_0.messages.pres\_request module ------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.messages.pres_request - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v2\_0.messages.pres\_webhook module ------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.messages.pres_webhook - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.models.rst b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.models.rst index a4b3da6f5e..342773342d 100644 --- a/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.models.rst +++ b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.present\_proof.v2\_0.models package =============================================================== .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,8 +13,6 @@ aries\_cloudagent.protocols.present\_proof.v2\_0.models.pres\_exchange module ----------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.models.pres_exchange - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.rst b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.rst index f372e30353..ad4e408035 100644 --- a/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.rst +++ b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.rst @@ -2,19 +2,20 @@ aries\_cloudagent.protocols.present\_proof.v2\_0 package ======================================================== .. automodule:: aries_cloudagent.protocols.present_proof.v2_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.protocols.present_proof.v2_0.formats - aries_cloudagent.protocols.present_proof.v2_0.handlers - aries_cloudagent.protocols.present_proof.v2_0.messages - aries_cloudagent.protocols.present_proof.v2_0.models + aries_cloudagent.protocols.present_proof.v2_0.formats + aries_cloudagent.protocols.present_proof.v2_0.handlers + aries_cloudagent.protocols.present_proof.v2_0.messages + aries_cloudagent.protocols.present_proof.v2_0.models Submodules ---------- @@ -23,32 +24,30 @@ aries\_cloudagent.protocols.present\_proof.v2\_0.controller module ------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.controller - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v2\_0.manager module --------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v2\_0.message\_types module ---------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.present\_proof.v2\_0.routes module -------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.present_proof.v2_0.routes - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.problem_report.rst b/docs/generated/aries_cloudagent.protocols.problem_report.rst index 8b80c721ac..431b7f1387 100644 --- a/docs/generated/aries_cloudagent.protocols.problem_report.rst +++ b/docs/generated/aries_cloudagent.protocols.problem_report.rst @@ -2,16 +2,17 @@ aries\_cloudagent.protocols.problem\_report package =================================================== .. automodule:: aries_cloudagent.protocols.problem_report - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.protocols.problem_report.v1_0 + aries_cloudagent.protocols.problem_report.v1_0 Submodules ---------- @@ -20,8 +21,6 @@ aries\_cloudagent.protocols.problem\_report.definition module ------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.problem_report.definition - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.problem_report.v1_0.rst b/docs/generated/aries_cloudagent.protocols.problem_report.v1_0.rst index 3ff5f8972f..7ac574e4c5 100644 --- a/docs/generated/aries_cloudagent.protocols.problem_report.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.problem_report.v1_0.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.problem\_report.v1\_0 package ========================================================= .. automodule:: aries_cloudagent.protocols.problem_report.v1_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,24 +13,22 @@ aries\_cloudagent.protocols.problem\_report.v1\_0.handler module ---------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.problem_report.v1_0.handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.problem\_report.v1\_0.message module ---------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.problem_report.v1_0.message - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.problem\_report.v1\_0.message\_types module ----------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.problem_report.v1_0.message_types - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.revocation_notification.rst b/docs/generated/aries_cloudagent.protocols.revocation_notification.rst index 86337dc76c..c88d26085d 100644 --- a/docs/generated/aries_cloudagent.protocols.revocation_notification.rst +++ b/docs/generated/aries_cloudagent.protocols.revocation_notification.rst @@ -2,17 +2,18 @@ aries\_cloudagent.protocols.revocation\_notification package ============================================================ .. automodule:: aries_cloudagent.protocols.revocation_notification - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.protocols.revocation_notification.v1_0 - aries_cloudagent.protocols.revocation_notification.v2_0 + aries_cloudagent.protocols.revocation_notification.v1_0 + aries_cloudagent.protocols.revocation_notification.v2_0 Submodules ---------- @@ -21,8 +22,6 @@ aries\_cloudagent.protocols.revocation\_notification.definition module ---------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.revocation_notification.definition - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.handlers.rst index 98dddbe6f9..205b069dc4 100644 --- a/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.revocation\_notification.v1\_0.handlers package =========================================================================== .. automodule:: aries_cloudagent.protocols.revocation_notification.v1_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,8 +13,6 @@ aries\_cloudagent.protocols.revocation\_notification.v1\_0.handlers.revoke\_hand ------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.revocation_notification.v1_0.handlers.revoke_handler - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.messages.rst b/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.messages.rst index 993f373e2a..d623d66dc8 100644 --- a/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.revocation\_notification.v1\_0.messages package =========================================================================== .. automodule:: aries_cloudagent.protocols.revocation_notification.v1_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,8 +13,6 @@ aries\_cloudagent.protocols.revocation\_notification.v1\_0.messages.revoke modul --------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.revocation_notification.v1_0.messages.revoke - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.models.rst b/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.models.rst index aae0c83a81..48164ca2bf 100644 --- a/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.models.rst +++ b/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.revocation\_notification.v1\_0.models package ========================================================================= .. automodule:: aries_cloudagent.protocols.revocation_notification.v1_0.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,8 +13,6 @@ aries\_cloudagent.protocols.revocation\_notification.v1\_0.models.rev\_notificat -------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.revocation_notification.v1_0.models.rev_notification_record - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.rst b/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.rst index 77a1d172ec..06a3b4ace0 100644 --- a/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.revocation_notification.v1_0.rst @@ -2,18 +2,19 @@ aries\_cloudagent.protocols.revocation\_notification.v1\_0 package ================================================================== .. automodule:: aries_cloudagent.protocols.revocation_notification.v1_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.protocols.revocation_notification.v1_0.handlers - aries_cloudagent.protocols.revocation_notification.v1_0.messages - aries_cloudagent.protocols.revocation_notification.v1_0.models + aries_cloudagent.protocols.revocation_notification.v1_0.handlers + aries_cloudagent.protocols.revocation_notification.v1_0.messages + aries_cloudagent.protocols.revocation_notification.v1_0.models Submodules ---------- @@ -22,16 +23,14 @@ aries\_cloudagent.protocols.revocation\_notification.v1\_0.message\_types module -------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.revocation_notification.v1_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.revocation\_notification.v1\_0.routes module ------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.revocation_notification.v1_0.routes - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.handlers.rst index 86a419dc3f..1fa7a93885 100644 --- a/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.revocation\_notification.v2\_0.handlers package =========================================================================== .. automodule:: aries_cloudagent.protocols.revocation_notification.v2_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,8 +13,6 @@ aries\_cloudagent.protocols.revocation\_notification.v2\_0.handlers.revoke\_hand ------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.revocation_notification.v2_0.handlers.revoke_handler - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.messages.rst b/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.messages.rst index bdd311136b..db465ff9e3 100644 --- a/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.revocation\_notification.v2\_0.messages package =========================================================================== .. automodule:: aries_cloudagent.protocols.revocation_notification.v2_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,8 +13,6 @@ aries\_cloudagent.protocols.revocation\_notification.v2\_0.messages.revoke modul --------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.revocation_notification.v2_0.messages.revoke - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.models.rst b/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.models.rst index fdc74b0332..cffa7e42ae 100644 --- a/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.models.rst +++ b/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.revocation\_notification.v2\_0.models package ========================================================================= .. automodule:: aries_cloudagent.protocols.revocation_notification.v2_0.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,8 +13,6 @@ aries\_cloudagent.protocols.revocation\_notification.v2\_0.models.rev\_notificat -------------------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.revocation_notification.v2_0.models.rev_notification_record - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.rst b/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.rst index 9a616e6c44..8247c6853a 100644 --- a/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.rst +++ b/docs/generated/aries_cloudagent.protocols.revocation_notification.v2_0.rst @@ -2,18 +2,19 @@ aries\_cloudagent.protocols.revocation\_notification.v2\_0 package ================================================================== .. automodule:: aries_cloudagent.protocols.revocation_notification.v2_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.protocols.revocation_notification.v2_0.handlers - aries_cloudagent.protocols.revocation_notification.v2_0.messages - aries_cloudagent.protocols.revocation_notification.v2_0.models + aries_cloudagent.protocols.revocation_notification.v2_0.handlers + aries_cloudagent.protocols.revocation_notification.v2_0.messages + aries_cloudagent.protocols.revocation_notification.v2_0.models Submodules ---------- @@ -22,16 +23,14 @@ aries\_cloudagent.protocols.revocation\_notification.v2\_0.message\_types module -------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.revocation_notification.v2_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.revocation\_notification.v2\_0.routes module ------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.revocation_notification.v2_0.routes - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.routing.rst b/docs/generated/aries_cloudagent.protocols.routing.rst index 3dab5f14a3..8aa2a0a91f 100644 --- a/docs/generated/aries_cloudagent.protocols.routing.rst +++ b/docs/generated/aries_cloudagent.protocols.routing.rst @@ -2,16 +2,17 @@ aries\_cloudagent.protocols.routing package =========================================== .. automodule:: aries_cloudagent.protocols.routing - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.protocols.routing.v1_0 + aries_cloudagent.protocols.routing.v1_0 Submodules ---------- @@ -20,8 +21,6 @@ aries\_cloudagent.protocols.routing.definition module ----------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.definition - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.routing.v1_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.routing.v1_0.handlers.rst index 5535c2a49f..dd2b61d077 100644 --- a/docs/generated/aries_cloudagent.protocols.routing.v1_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.routing.v1_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.routing.v1\_0.handlers package ========================================================== .. automodule:: aries_cloudagent.protocols.routing.v1_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,40 +13,38 @@ aries\_cloudagent.protocols.routing.v1\_0.handlers.forward\_handler module -------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.handlers.forward_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.routing.v1\_0.handlers.route\_query\_request\_handler module ---------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.handlers.route_query_request_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.routing.v1\_0.handlers.route\_query\_response\_handler module ----------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.handlers.route_query_response_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.routing.v1\_0.handlers.route\_update\_request\_handler module ----------------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.handlers.route_update_request_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.routing.v1\_0.handlers.route\_update\_response\_handler module ------------------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.protocols.routing.v1_0.handlers.route_update_response_handler - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.routing.v1_0.messages.rst b/docs/generated/aries_cloudagent.protocols.routing.v1_0.messages.rst index 84a5e5d143..640e6318fd 100644 --- a/docs/generated/aries_cloudagent.protocols.routing.v1_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.routing.v1_0.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.routing.v1\_0.messages package ========================================================== .. automodule:: aries_cloudagent.protocols.routing.v1_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,40 +13,38 @@ aries\_cloudagent.protocols.routing.v1\_0.messages.forward module ----------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.messages.forward - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.routing.v1\_0.messages.route\_query\_request module ------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.messages.route_query_request - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.routing.v1\_0.messages.route\_query\_response module -------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.messages.route_query_response - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.routing.v1\_0.messages.route\_update\_request module -------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.messages.route_update_request - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.routing.v1\_0.messages.route\_update\_response module --------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.messages.route_update_response - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.routing.v1_0.models.rst b/docs/generated/aries_cloudagent.protocols.routing.v1_0.models.rst index 1a373f1022..53d1cc867a 100644 --- a/docs/generated/aries_cloudagent.protocols.routing.v1_0.models.rst +++ b/docs/generated/aries_cloudagent.protocols.routing.v1_0.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.routing.v1\_0.models package ======================================================== .. automodule:: aries_cloudagent.protocols.routing.v1_0.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,48 +13,46 @@ aries\_cloudagent.protocols.routing.v1\_0.models.paginate module ---------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.models.paginate - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.routing.v1\_0.models.paginated module ----------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.models.paginated - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.routing.v1\_0.models.route\_query\_result module ---------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.models.route_query_result - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.routing.v1\_0.models.route\_record module --------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.models.route_record - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.routing.v1\_0.models.route\_update module --------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.models.route_update - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.routing.v1\_0.models.route\_updated module ---------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.models.route_updated - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.routing.v1_0.rst b/docs/generated/aries_cloudagent.protocols.routing.v1_0.rst index 353b5c393d..ee671a27d2 100644 --- a/docs/generated/aries_cloudagent.protocols.routing.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.routing.v1_0.rst @@ -2,18 +2,19 @@ aries\_cloudagent.protocols.routing.v1\_0 package ================================================= .. automodule:: aries_cloudagent.protocols.routing.v1_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.protocols.routing.v1_0.handlers - aries_cloudagent.protocols.routing.v1_0.messages - aries_cloudagent.protocols.routing.v1_0.models + aries_cloudagent.protocols.routing.v1_0.handlers + aries_cloudagent.protocols.routing.v1_0.messages + aries_cloudagent.protocols.routing.v1_0.models Submodules ---------- @@ -22,16 +23,14 @@ aries\_cloudagent.protocols.routing.v1\_0.manager module -------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.routing.v1\_0.message\_types module --------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.routing.v1_0.message_types - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.rst b/docs/generated/aries_cloudagent.protocols.rst index 3af0fbcff1..c569e1d81b 100644 --- a/docs/generated/aries_cloudagent.protocols.rst +++ b/docs/generated/aries_cloudagent.protocols.rst @@ -2,31 +2,32 @@ aries\_cloudagent.protocols package =================================== .. automodule:: aries_cloudagent.protocols - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - - aries_cloudagent.protocols.actionmenu - aries_cloudagent.protocols.basicmessage - aries_cloudagent.protocols.connections - aries_cloudagent.protocols.coordinate_mediation - aries_cloudagent.protocols.didexchange - aries_cloudagent.protocols.discovery - aries_cloudagent.protocols.endorse_transaction - aries_cloudagent.protocols.introduction - aries_cloudagent.protocols.issue_credential - aries_cloudagent.protocols.notification - aries_cloudagent.protocols.out_of_band - aries_cloudagent.protocols.present_proof - aries_cloudagent.protocols.problem_report - aries_cloudagent.protocols.revocation_notification - aries_cloudagent.protocols.routing - aries_cloudagent.protocols.trustping + :maxdepth: 4 + + aries_cloudagent.protocols.actionmenu + aries_cloudagent.protocols.basicmessage + aries_cloudagent.protocols.connections + aries_cloudagent.protocols.coordinate_mediation + aries_cloudagent.protocols.didexchange + aries_cloudagent.protocols.discovery + aries_cloudagent.protocols.endorse_transaction + aries_cloudagent.protocols.introduction + aries_cloudagent.protocols.issue_credential + aries_cloudagent.protocols.notification + aries_cloudagent.protocols.out_of_band + aries_cloudagent.protocols.present_proof + aries_cloudagent.protocols.problem_report + aries_cloudagent.protocols.revocation_notification + aries_cloudagent.protocols.routing + aries_cloudagent.protocols.trustping Submodules ---------- @@ -35,8 +36,6 @@ aries\_cloudagent.protocols.didcomm\_prefix module -------------------------------------------------- .. automodule:: aries_cloudagent.protocols.didcomm_prefix - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.trustping.rst b/docs/generated/aries_cloudagent.protocols.trustping.rst index 01cc9e6332..c2df3c688e 100644 --- a/docs/generated/aries_cloudagent.protocols.trustping.rst +++ b/docs/generated/aries_cloudagent.protocols.trustping.rst @@ -2,16 +2,17 @@ aries\_cloudagent.protocols.trustping package ============================================= .. automodule:: aries_cloudagent.protocols.trustping - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.protocols.trustping.v1_0 + aries_cloudagent.protocols.trustping.v1_0 Submodules ---------- @@ -20,8 +21,6 @@ aries\_cloudagent.protocols.trustping.definition module ------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.trustping.definition - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.trustping.v1_0.handlers.rst b/docs/generated/aries_cloudagent.protocols.trustping.v1_0.handlers.rst index 19f404be0e..d8dd03ff35 100644 --- a/docs/generated/aries_cloudagent.protocols.trustping.v1_0.handlers.rst +++ b/docs/generated/aries_cloudagent.protocols.trustping.v1_0.handlers.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.trustping.v1\_0.handlers package ============================================================ .. automodule:: aries_cloudagent.protocols.trustping.v1_0.handlers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,16 +13,14 @@ aries\_cloudagent.protocols.trustping.v1\_0.handlers.ping\_handler module ------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.trustping.v1_0.handlers.ping_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.trustping.v1\_0.handlers.ping\_response\_handler module ----------------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.trustping.v1_0.handlers.ping_response_handler - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.trustping.v1_0.messages.rst b/docs/generated/aries_cloudagent.protocols.trustping.v1_0.messages.rst index c7d0fb38f3..1e1b83a5a0 100644 --- a/docs/generated/aries_cloudagent.protocols.trustping.v1_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.trustping.v1_0.messages.rst @@ -2,9 +2,9 @@ aries\_cloudagent.protocols.trustping.v1\_0.messages package ============================================================ .. automodule:: aries_cloudagent.protocols.trustping.v1_0.messages - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,16 +13,14 @@ aries\_cloudagent.protocols.trustping.v1\_0.messages.ping module ---------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.trustping.v1_0.messages.ping - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.trustping.v1\_0.messages.ping\_response module -------------------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.trustping.v1_0.messages.ping_response - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.trustping.v1_0.rst b/docs/generated/aries_cloudagent.protocols.trustping.v1_0.rst index add52eafbf..cceadfedfc 100644 --- a/docs/generated/aries_cloudagent.protocols.trustping.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.trustping.v1_0.rst @@ -2,17 +2,18 @@ aries\_cloudagent.protocols.trustping.v1\_0 package =================================================== .. automodule:: aries_cloudagent.protocols.trustping.v1_0 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.protocols.trustping.v1_0.handlers - aries_cloudagent.protocols.trustping.v1_0.messages + aries_cloudagent.protocols.trustping.v1_0.handlers + aries_cloudagent.protocols.trustping.v1_0.messages Submodules ---------- @@ -21,16 +22,14 @@ aries\_cloudagent.protocols.trustping.v1\_0.message\_types module ----------------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.trustping.v1_0.message_types - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.protocols.trustping.v1\_0.routes module --------------------------------------------------------- .. automodule:: aries_cloudagent.protocols.trustping.v1_0.routes - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.resolver.default.rst b/docs/generated/aries_cloudagent.resolver.default.rst index 00b2bff98d..b7dd8eac96 100644 --- a/docs/generated/aries_cloudagent.resolver.default.rst +++ b/docs/generated/aries_cloudagent.resolver.default.rst @@ -2,9 +2,9 @@ aries\_cloudagent.resolver.default package ========================================== .. automodule:: aries_cloudagent.resolver.default - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,32 +13,30 @@ aries\_cloudagent.resolver.default.indy module ---------------------------------------------- .. automodule:: aries_cloudagent.resolver.default.indy - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.resolver.default.key module --------------------------------------------- .. automodule:: aries_cloudagent.resolver.default.key - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.resolver.default.universal module --------------------------------------------------- .. automodule:: aries_cloudagent.resolver.default.universal - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.resolver.default.web module --------------------------------------------- .. automodule:: aries_cloudagent.resolver.default.web - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.resolver.rst b/docs/generated/aries_cloudagent.resolver.rst index 1b181f1fae..7732543e90 100644 --- a/docs/generated/aries_cloudagent.resolver.rst +++ b/docs/generated/aries_cloudagent.resolver.rst @@ -2,16 +2,17 @@ aries\_cloudagent.resolver package ================================== .. automodule:: aries_cloudagent.resolver - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.resolver.default + aries_cloudagent.resolver.default Submodules ---------- @@ -20,24 +21,22 @@ aries\_cloudagent.resolver.base module -------------------------------------- .. automodule:: aries_cloudagent.resolver.base - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.resolver.did\_resolver module ----------------------------------------------- .. automodule:: aries_cloudagent.resolver.did_resolver - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.resolver.routes module ---------------------------------------- .. automodule:: aries_cloudagent.resolver.routes - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.revocation.models.rst b/docs/generated/aries_cloudagent.revocation.models.rst index 89a45d9248..67f5c3ffb1 100644 --- a/docs/generated/aries_cloudagent.revocation.models.rst +++ b/docs/generated/aries_cloudagent.revocation.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.revocation.models package =========================================== .. automodule:: aries_cloudagent.revocation.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,32 +13,30 @@ aries\_cloudagent.revocation.models.indy module ----------------------------------------------- .. automodule:: aries_cloudagent.revocation.models.indy - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.revocation.models.issuer\_cred\_rev\_record module -------------------------------------------------------------------- .. automodule:: aries_cloudagent.revocation.models.issuer_cred_rev_record - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.revocation.models.issuer\_rev\_reg\_record module ------------------------------------------------------------------- .. automodule:: aries_cloudagent.revocation.models.issuer_rev_reg_record - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.revocation.models.revocation\_registry module --------------------------------------------------------------- .. automodule:: aries_cloudagent.revocation.models.revocation_registry - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.revocation.rst b/docs/generated/aries_cloudagent.revocation.rst index 7d28a08b90..7263263c71 100644 --- a/docs/generated/aries_cloudagent.revocation.rst +++ b/docs/generated/aries_cloudagent.revocation.rst @@ -2,16 +2,17 @@ aries\_cloudagent.revocation package ==================================== .. automodule:: aries_cloudagent.revocation - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.revocation.models + aries_cloudagent.revocation.models Submodules ---------- @@ -20,48 +21,46 @@ aries\_cloudagent.revocation.error module ----------------------------------------- .. automodule:: aries_cloudagent.revocation.error - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.revocation.indy module ---------------------------------------- .. automodule:: aries_cloudagent.revocation.indy - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.revocation.manager module ------------------------------------------- .. automodule:: aries_cloudagent.revocation.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.revocation.recover module ------------------------------------------- .. automodule:: aries_cloudagent.revocation.recover - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.revocation.routes module ------------------------------------------ .. automodule:: aries_cloudagent.revocation.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.revocation.util module ---------------------------------------- .. automodule:: aries_cloudagent.revocation.util - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.rst b/docs/generated/aries_cloudagent.rst index 9bcb0112f2..ef01b45059 100644 --- a/docs/generated/aries_cloudagent.rst +++ b/docs/generated/aries_cloudagent.rst @@ -2,37 +2,38 @@ aries\_cloudagent package ========================= .. automodule:: aries_cloudagent - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: - - aries_cloudagent.admin - aries_cloudagent.askar - aries_cloudagent.cache - aries_cloudagent.commands - aries_cloudagent.config - aries_cloudagent.connections - aries_cloudagent.core - aries_cloudagent.did - aries_cloudagent.holder - aries_cloudagent.indy - aries_cloudagent.ledger - aries_cloudagent.messaging - aries_cloudagent.multitenant - aries_cloudagent.protocols - aries_cloudagent.resolver - aries_cloudagent.revocation - aries_cloudagent.storage - aries_cloudagent.tails - aries_cloudagent.transport - aries_cloudagent.utils - aries_cloudagent.vc - aries_cloudagent.wallet + :maxdepth: 4 + + aries_cloudagent.admin + aries_cloudagent.askar + aries_cloudagent.cache + aries_cloudagent.commands + aries_cloudagent.config + aries_cloudagent.connections + aries_cloudagent.core + aries_cloudagent.did + aries_cloudagent.holder + aries_cloudagent.indy + aries_cloudagent.ledger + aries_cloudagent.messaging + aries_cloudagent.multitenant + aries_cloudagent.protocols + aries_cloudagent.resolver + aries_cloudagent.revocation + aries_cloudagent.storage + aries_cloudagent.tails + aries_cloudagent.transport + aries_cloudagent.utils + aries_cloudagent.vc + aries_cloudagent.wallet Submodules ---------- @@ -41,8 +42,6 @@ aries\_cloudagent.version module -------------------------------- .. automodule:: aries_cloudagent.version - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.storage.rst b/docs/generated/aries_cloudagent.storage.rst index c62c2e21f5..bd06d4d460 100644 --- a/docs/generated/aries_cloudagent.storage.rst +++ b/docs/generated/aries_cloudagent.storage.rst @@ -2,16 +2,17 @@ aries\_cloudagent.storage package ================================= .. automodule:: aries_cloudagent.storage - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.storage.vc_holder + aries_cloudagent.storage.vc_holder Submodules ---------- @@ -20,48 +21,46 @@ aries\_cloudagent.storage.askar module -------------------------------------- .. automodule:: aries_cloudagent.storage.askar - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.storage.base module ------------------------------------- .. automodule:: aries_cloudagent.storage.base - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.storage.error module -------------------------------------- .. automodule:: aries_cloudagent.storage.error - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.storage.in\_memory module ------------------------------------------- .. automodule:: aries_cloudagent.storage.in_memory - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.storage.indy module ------------------------------------- .. automodule:: aries_cloudagent.storage.indy - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.storage.record module --------------------------------------- .. automodule:: aries_cloudagent.storage.record - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.storage.vc_holder.rst b/docs/generated/aries_cloudagent.storage.vc_holder.rst index 329a262303..7bd5098aff 100644 --- a/docs/generated/aries_cloudagent.storage.vc_holder.rst +++ b/docs/generated/aries_cloudagent.storage.vc_holder.rst @@ -2,9 +2,9 @@ aries\_cloudagent.storage.vc\_holder package ============================================ .. automodule:: aries_cloudagent.storage.vc_holder - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,48 +13,46 @@ aries\_cloudagent.storage.vc\_holder.askar module ------------------------------------------------- .. automodule:: aries_cloudagent.storage.vc_holder.askar - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.storage.vc\_holder.base module ------------------------------------------------ .. automodule:: aries_cloudagent.storage.vc_holder.base - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.storage.vc\_holder.in\_memory module ------------------------------------------------------ .. automodule:: aries_cloudagent.storage.vc_holder.in_memory - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.storage.vc\_holder.indy module ------------------------------------------------ .. automodule:: aries_cloudagent.storage.vc_holder.indy - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.storage.vc\_holder.vc\_record module ------------------------------------------------------ .. automodule:: aries_cloudagent.storage.vc_holder.vc_record - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.storage.vc\_holder.xform module ------------------------------------------------- .. automodule:: aries_cloudagent.storage.vc_holder.xform - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.tails.rst b/docs/generated/aries_cloudagent.tails.rst index 5aabc0cb4e..51e1746aee 100644 --- a/docs/generated/aries_cloudagent.tails.rst +++ b/docs/generated/aries_cloudagent.tails.rst @@ -2,9 +2,9 @@ aries\_cloudagent.tails package =============================== .. automodule:: aries_cloudagent.tails - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,24 +13,22 @@ aries\_cloudagent.tails.base module ----------------------------------- .. automodule:: aries_cloudagent.tails.base - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.tails.error module ------------------------------------ .. automodule:: aries_cloudagent.tails.error - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.tails.indy\_tails\_server module -------------------------------------------------- .. automodule:: aries_cloudagent.tails.indy_tails_server - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.transport.inbound.rst b/docs/generated/aries_cloudagent.transport.inbound.rst index 6a32abd804..879581b75e 100644 --- a/docs/generated/aries_cloudagent.transport.inbound.rst +++ b/docs/generated/aries_cloudagent.transport.inbound.rst @@ -2,9 +2,9 @@ aries\_cloudagent.transport.inbound package =========================================== .. automodule:: aries_cloudagent.transport.inbound - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,64 +13,62 @@ aries\_cloudagent.transport.inbound.base module ----------------------------------------------- .. automodule:: aries_cloudagent.transport.inbound.base - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.inbound.delivery\_queue module ---------------------------------------------------------- .. automodule:: aries_cloudagent.transport.inbound.delivery_queue - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.inbound.http module ----------------------------------------------- .. automodule:: aries_cloudagent.transport.inbound.http - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.inbound.manager module -------------------------------------------------- .. automodule:: aries_cloudagent.transport.inbound.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.inbound.message module -------------------------------------------------- .. automodule:: aries_cloudagent.transport.inbound.message - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.inbound.receipt module -------------------------------------------------- .. automodule:: aries_cloudagent.transport.inbound.receipt - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.inbound.session module -------------------------------------------------- .. automodule:: aries_cloudagent.transport.inbound.session - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.inbound.ws module --------------------------------------------- .. automodule:: aries_cloudagent.transport.inbound.ws - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.transport.outbound.rst b/docs/generated/aries_cloudagent.transport.outbound.rst index 1be069a0c7..1fde7f0379 100644 --- a/docs/generated/aries_cloudagent.transport.outbound.rst +++ b/docs/generated/aries_cloudagent.transport.outbound.rst @@ -2,9 +2,9 @@ aries\_cloudagent.transport.outbound package ============================================ .. automodule:: aries_cloudagent.transport.outbound - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,48 +13,46 @@ aries\_cloudagent.transport.outbound.base module ------------------------------------------------ .. automodule:: aries_cloudagent.transport.outbound.base - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.outbound.http module ------------------------------------------------ .. automodule:: aries_cloudagent.transport.outbound.http - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.outbound.manager module --------------------------------------------------- .. automodule:: aries_cloudagent.transport.outbound.manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.outbound.message module --------------------------------------------------- .. automodule:: aries_cloudagent.transport.outbound.message - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.outbound.status module -------------------------------------------------- .. automodule:: aries_cloudagent.transport.outbound.status - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.outbound.ws module ---------------------------------------------- .. automodule:: aries_cloudagent.transport.outbound.ws - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.transport.queue.rst b/docs/generated/aries_cloudagent.transport.queue.rst index 577fdf58c2..f6e9920d94 100644 --- a/docs/generated/aries_cloudagent.transport.queue.rst +++ b/docs/generated/aries_cloudagent.transport.queue.rst @@ -2,9 +2,9 @@ aries\_cloudagent.transport.queue package ========================================= .. automodule:: aries_cloudagent.transport.queue - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,16 +13,14 @@ aries\_cloudagent.transport.queue.base module --------------------------------------------- .. automodule:: aries_cloudagent.transport.queue.base - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.queue.basic module ---------------------------------------------- .. automodule:: aries_cloudagent.transport.queue.basic - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.transport.rst b/docs/generated/aries_cloudagent.transport.rst index d10d781102..33aed946ad 100644 --- a/docs/generated/aries_cloudagent.transport.rst +++ b/docs/generated/aries_cloudagent.transport.rst @@ -2,18 +2,19 @@ aries\_cloudagent.transport package =================================== .. automodule:: aries_cloudagent.transport - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.transport.inbound - aries_cloudagent.transport.outbound - aries_cloudagent.transport.queue + aries_cloudagent.transport.inbound + aries_cloudagent.transport.outbound + aries_cloudagent.transport.queue Submodules ---------- @@ -22,32 +23,30 @@ aries\_cloudagent.transport.error module ---------------------------------------- .. automodule:: aries_cloudagent.transport.error - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.pack\_format module ----------------------------------------------- .. automodule:: aries_cloudagent.transport.pack_format - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.stats module ---------------------------------------- .. automodule:: aries_cloudagent.transport.stats - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.transport.wire\_format module ----------------------------------------------- .. automodule:: aries_cloudagent.transport.wire_format - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.utils.rst b/docs/generated/aries_cloudagent.utils.rst index 9d968756b8..f5241f39d8 100644 --- a/docs/generated/aries_cloudagent.utils.rst +++ b/docs/generated/aries_cloudagent.utils.rst @@ -2,9 +2,9 @@ aries\_cloudagent.utils package =============================== .. automodule:: aries_cloudagent.utils - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,80 +13,78 @@ aries\_cloudagent.utils.classloader module ------------------------------------------ .. automodule:: aries_cloudagent.utils.classloader - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.utils.dependencies module ------------------------------------------- .. automodule:: aries_cloudagent.utils.dependencies - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.utils.env module ---------------------------------- .. automodule:: aries_cloudagent.utils.env - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.utils.http module ----------------------------------- .. automodule:: aries_cloudagent.utils.http - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.utils.jwe module ---------------------------------- .. automodule:: aries_cloudagent.utils.jwe - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.utils.outofband module ---------------------------------------- .. automodule:: aries_cloudagent.utils.outofband - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.utils.repeat module ------------------------------------- .. automodule:: aries_cloudagent.utils.repeat - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.utils.stats module ------------------------------------ .. automodule:: aries_cloudagent.utils.stats - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.utils.task\_queue module ------------------------------------------ .. automodule:: aries_cloudagent.utils.task_queue - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.utils.tracing module -------------------------------------- .. automodule:: aries_cloudagent.utils.tracing - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.vc.ld_proofs.crypto.rst b/docs/generated/aries_cloudagent.vc.ld_proofs.crypto.rst index f84983368f..66c04359cd 100644 --- a/docs/generated/aries_cloudagent.vc.ld_proofs.crypto.rst +++ b/docs/generated/aries_cloudagent.vc.ld_proofs.crypto.rst @@ -2,9 +2,9 @@ aries\_cloudagent.vc.ld\_proofs.crypto package ============================================== .. automodule:: aries_cloudagent.vc.ld_proofs.crypto - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,16 +13,14 @@ aries\_cloudagent.vc.ld\_proofs.crypto.key\_pair module ------------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.crypto.key_pair - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.crypto.wallet\_key\_pair module --------------------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.crypto.wallet_key_pair - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.vc.ld_proofs.purposes.rst b/docs/generated/aries_cloudagent.vc.ld_proofs.purposes.rst index 6693036472..169a889c5b 100644 --- a/docs/generated/aries_cloudagent.vc.ld_proofs.purposes.rst +++ b/docs/generated/aries_cloudagent.vc.ld_proofs.purposes.rst @@ -2,9 +2,9 @@ aries\_cloudagent.vc.ld\_proofs.purposes package ================================================ .. automodule:: aries_cloudagent.vc.ld_proofs.purposes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,40 +13,38 @@ aries\_cloudagent.vc.ld\_proofs.purposes.assertion\_proof\_purpose module ------------------------------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.purposes.assertion_proof_purpose - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.purposes.authentication\_proof\_purpose module ------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.vc.ld_proofs.purposes.authentication_proof_purpose - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.purposes.controller\_proof\_purpose module -------------------------------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.purposes.controller_proof_purpose - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.purposes.credential\_issuance\_purpose module ----------------------------------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.purposes.credential_issuance_purpose - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.purposes.proof\_purpose module -------------------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.purposes.proof_purpose - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.vc.ld_proofs.rst b/docs/generated/aries_cloudagent.vc.ld_proofs.rst index 4727ab22fc..ca7276b1fc 100644 --- a/docs/generated/aries_cloudagent.vc.ld_proofs.rst +++ b/docs/generated/aries_cloudagent.vc.ld_proofs.rst @@ -2,18 +2,19 @@ aries\_cloudagent.vc.ld\_proofs package ======================================= .. automodule:: aries_cloudagent.vc.ld_proofs - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.vc.ld_proofs.crypto - aries_cloudagent.vc.ld_proofs.purposes - aries_cloudagent.vc.ld_proofs.suites + aries_cloudagent.vc.ld_proofs.crypto + aries_cloudagent.vc.ld_proofs.purposes + aries_cloudagent.vc.ld_proofs.suites Submodules ---------- @@ -22,56 +23,54 @@ aries\_cloudagent.vc.ld\_proofs.check module -------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.check - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.constants module ------------------------------------------------ .. automodule:: aries_cloudagent.vc.ld_proofs.constants - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.document\_loader module ------------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.document_loader - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.error module -------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.error - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.ld\_proofs module ------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.ld_proofs - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.proof\_set module ------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.proof_set - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.validation\_result module --------------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.validation_result - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.vc.ld_proofs.suites.rst b/docs/generated/aries_cloudagent.vc.ld_proofs.suites.rst index 3ba6e87d41..cbda566bca 100644 --- a/docs/generated/aries_cloudagent.vc.ld_proofs.suites.rst +++ b/docs/generated/aries_cloudagent.vc.ld_proofs.suites.rst @@ -2,9 +2,9 @@ aries\_cloudagent.vc.ld\_proofs.suites package ============================================== .. automodule:: aries_cloudagent.vc.ld_proofs.suites - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,56 +13,54 @@ aries\_cloudagent.vc.ld\_proofs.suites.bbs\_bls\_signature\_2020 module ----------------------------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.suites.bbs_bls_signature_2020 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.suites.bbs\_bls\_signature\_2020\_base module ----------------------------------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.suites.bbs_bls_signature_2020_base - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.suites.bbs\_bls\_signature\_proof\_2020 module ------------------------------------------------------------------------------ .. automodule:: aries_cloudagent.vc.ld_proofs.suites.bbs_bls_signature_proof_2020 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.suites.ed25519\_signature\_2018 module ---------------------------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.suites.ed25519_signature_2018 - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.suites.jws\_linked\_data\_signature module -------------------------------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.suites.jws_linked_data_signature - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.suites.linked\_data\_proof module ----------------------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.suites.linked_data_proof - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.ld\_proofs.suites.linked\_data\_signature module --------------------------------------------------------------------- .. automodule:: aries_cloudagent.vc.ld_proofs.suites.linked_data_signature - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.vc.rst b/docs/generated/aries_cloudagent.vc.rst index a0a9a0ba2e..f3c1e09d88 100644 --- a/docs/generated/aries_cloudagent.vc.rst +++ b/docs/generated/aries_cloudagent.vc.rst @@ -2,15 +2,15 @@ aries\_cloudagent.vc package ============================ .. automodule:: aries_cloudagent.vc - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.vc.ld_proofs - aries_cloudagent.vc.vc_ld - + aries_cloudagent.vc.ld_proofs + aries_cloudagent.vc.vc_ld diff --git a/docs/generated/aries_cloudagent.vc.vc_ld.models.rst b/docs/generated/aries_cloudagent.vc.vc_ld.models.rst index 745e577a62..83bcd876d2 100644 --- a/docs/generated/aries_cloudagent.vc.vc_ld.models.rst +++ b/docs/generated/aries_cloudagent.vc.vc_ld.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.vc.vc\_ld.models package ========================================== .. automodule:: aries_cloudagent.vc.vc_ld.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,16 +13,14 @@ aries\_cloudagent.vc.vc\_ld.models.credential module ---------------------------------------------------- .. automodule:: aries_cloudagent.vc.vc_ld.models.credential - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.vc\_ld.models.linked\_data\_proof module ------------------------------------------------------------- .. automodule:: aries_cloudagent.vc.vc_ld.models.linked_data_proof - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.vc.vc_ld.rst b/docs/generated/aries_cloudagent.vc.vc_ld.rst index 00c9235f89..a89699f5cc 100644 --- a/docs/generated/aries_cloudagent.vc.vc_ld.rst +++ b/docs/generated/aries_cloudagent.vc.vc_ld.rst @@ -2,16 +2,17 @@ aries\_cloudagent.vc.vc\_ld package =================================== .. automodule:: aries_cloudagent.vc.vc_ld - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.vc.vc_ld.models + aries_cloudagent.vc.vc_ld.models Submodules ---------- @@ -20,32 +21,30 @@ aries\_cloudagent.vc.vc\_ld.issue module ---------------------------------------- .. automodule:: aries_cloudagent.vc.vc_ld.issue - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.vc\_ld.prove module ---------------------------------------- .. automodule:: aries_cloudagent.vc.vc_ld.prove - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.vc\_ld.validation\_result module ----------------------------------------------------- .. automodule:: aries_cloudagent.vc.vc_ld.validation_result - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.vc.vc\_ld.verify module ----------------------------------------- .. automodule:: aries_cloudagent.vc.vc_ld.verify - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.wallet.models.rst b/docs/generated/aries_cloudagent.wallet.models.rst index 741b5a2246..d59bc609c4 100644 --- a/docs/generated/aries_cloudagent.wallet.models.rst +++ b/docs/generated/aries_cloudagent.wallet.models.rst @@ -2,9 +2,9 @@ aries\_cloudagent.wallet.models package ======================================= .. automodule:: aries_cloudagent.wallet.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- @@ -13,8 +13,6 @@ aries\_cloudagent.wallet.models.wallet\_record module ----------------------------------------------------- .. automodule:: aries_cloudagent.wallet.models.wallet_record - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.wallet.rst b/docs/generated/aries_cloudagent.wallet.rst index e7eaffbce5..d7ef3b5efa 100644 --- a/docs/generated/aries_cloudagent.wallet.rst +++ b/docs/generated/aries_cloudagent.wallet.rst @@ -2,16 +2,17 @@ aries\_cloudagent.wallet package ================================ .. automodule:: aries_cloudagent.wallet - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - aries_cloudagent.wallet.models + aries_cloudagent.wallet.models Submodules ---------- @@ -20,120 +21,118 @@ aries\_cloudagent.wallet.askar module ------------------------------------- .. automodule:: aries_cloudagent.wallet.askar - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.wallet.base module ------------------------------------ .. automodule:: aries_cloudagent.wallet.base - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.wallet.bbs module ----------------------------------- .. automodule:: aries_cloudagent.wallet.bbs - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.wallet.crypto module -------------------------------------- .. automodule:: aries_cloudagent.wallet.crypto - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.wallet.did\_info module ----------------------------------------- .. automodule:: aries_cloudagent.wallet.did_info - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.wallet.did\_method module ------------------------------------------- .. automodule:: aries_cloudagent.wallet.did_method - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.wallet.did\_parameters\_validation module ----------------------------------------------------------- .. automodule:: aries_cloudagent.wallet.did_parameters_validation - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.wallet.did\_posture module -------------------------------------------- .. automodule:: aries_cloudagent.wallet.did_posture - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.wallet.error module ------------------------------------- .. automodule:: aries_cloudagent.wallet.error - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.wallet.in\_memory module ------------------------------------------ .. automodule:: aries_cloudagent.wallet.in_memory - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.wallet.indy module ------------------------------------ .. automodule:: aries_cloudagent.wallet.indy - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.wallet.key\_pair module ----------------------------------------- .. automodule:: aries_cloudagent.wallet.key_pair - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.wallet.key\_type module ----------------------------------------- .. automodule:: aries_cloudagent.wallet.key_type - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.wallet.routes module -------------------------------------- .. automodule:: aries_cloudagent.wallet.routes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: aries\_cloudagent.wallet.util module ------------------------------------ .. automodule:: aries_cloudagent.wallet.util - :members: - :undoc-members: - :show-inheritance: - - + :members: + :undoc-members: + :show-inheritance: diff --git a/open-api/openapi.json b/open-api/openapi.json index 75d4f33e0e..af84cd6fc2 100644 --- a/open-api/openapi.json +++ b/open-api/openapi.json @@ -1,7 +1,7 @@ { "swagger" : "2.0", "info" : { - "version" : "v0.8.0", + "version" : "v0.8.1-rc0", "title" : "Aries Cloud Agent" }, "tags" : [ { From c212bab24d633459c750e842182be99e897e126b Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Thu, 30 Mar 2023 19:19:03 -0700 Subject: [PATCH 735/872] Add this PR to the CHANGELOG.md Signed-off-by: Stephen Curran --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a65d01a12b..3aba00d97b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ least) deployments of ACA-Py as a mediator. The important update is Pull Request - Create UnitTests.md [\#2183](https://github.com/hyperledger/aries-cloudagent-python/pull/2183) [swcurran](https://github.com/swcurran) - Add link to recorded session about the ACA-Py Integration tests [\#2184](https://github.com/hyperledger/aries-cloudagent-python/pull/2184) [swcurran](https://github.com/swcurran) - Release management pull requests + - 0.8.1-rc0 [\#2190](https://github.com/hyperledger/aries-cloudagent-python/pull/2190) [swcurran](https://github.com/swcurran) # 0.8.0 From 458ce4335484ef3214f841e562c2a294f88e7a8d Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Fri, 31 Mar 2023 12:52:03 -0700 Subject: [PATCH 736/872] upgrade config path fix Signed-off-by: Shaanjot Gill --- aries_cloudagent/commands/upgrade.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/aries_cloudagent/commands/upgrade.py b/aries_cloudagent/commands/upgrade.py index be98d043ab..87082d65f5 100644 --- a/aries_cloudagent/commands/upgrade.py +++ b/aries_cloudagent/commands/upgrade.py @@ -2,6 +2,7 @@ import asyncio import logging +import os import yaml from configargparse import ArgumentParser @@ -23,9 +24,7 @@ from . import PROG -DEFAULT_UPGRADE_CONFIG_PATH = ( - "./aries_cloudagent/commands/default_version_upgrade_config.yml" -) +DEFAULT_UPGRADE_CONFIG_FILE_NAME = "default_version_upgrade_config.yml" LOGGER = logging.getLogger(__name__) @@ -43,7 +42,12 @@ def __init__(self, config_path: str = None): if config_path: self.setup_version_upgrade_config(config_path) else: - self.setup_version_upgrade_config(DEFAULT_UPGRADE_CONFIG_PATH) + self.setup_version_upgrade_config( + os.path.join( + os.path.dirname(os.path.realpath(__file__)), + DEFAULT_UPGRADE_CONFIG_FILE_NAME, + ) + ) def setup_version_upgrade_config(self, path: str): """Set ups config dict from the provided YML file.""" From c932a81cb463c0230b50320807b9924d770f805e Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Fri, 31 Mar 2023 13:41:21 -0700 Subject: [PATCH 737/872] 0.8.1-rc1 Signed-off-by: Stephen Curran --- CHANGELOG.md | 4 +++- aries_cloudagent/core/tests/test_conductor.py | 2 +- aries_cloudagent/version.py | 2 +- open-api/openapi.json | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3aba00d97b..ac4aa736d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -# 0.8.1-rc0 +# 0.8.1-rc1 ## March 31, 2023 @@ -14,12 +14,14 @@ least) deployments of ACA-Py as a mediator. The important update is Pull Request ### Categorized List of Pull Requests - Fixes for the `upgrade` Command + - Fix: Resolve Upgrade Config file in Container [\#2193](https://github.com/hyperledger/aries-cloudagent-python/pull/2193) [shaangill025](https://github.com/shaangill025) - Update and automate ACA-Py upgrade process [\#2185](https://github.com/hyperledger/aries-cloudagent-python/pull/2185) [shaangill025](https://github.com/shaangill025) - Adds the upgrade command YML file to the PyPi Release [\#2179](https://github.com/hyperledger/aries-cloudagent-python/pull/2179) [swcurran](https://github.com/swcurran) - Test and Documentation - Create UnitTests.md [\#2183](https://github.com/hyperledger/aries-cloudagent-python/pull/2183) [swcurran](https://github.com/swcurran) - Add link to recorded session about the ACA-Py Integration tests [\#2184](https://github.com/hyperledger/aries-cloudagent-python/pull/2184) [swcurran](https://github.com/swcurran) - Release management pull requests + - 0.8.1-rc1 [\#2194](https://github.com/hyperledger/aries-cloudagent-python/pull/2194) [swcurran](https://github.com/swcurran) - 0.8.1-rc0 [\#2190](https://github.com/hyperledger/aries-cloudagent-python/pull/2190) [swcurran](https://github.com/swcurran) # 0.8.0 diff --git a/aries_cloudagent/core/tests/test_conductor.py b/aries_cloudagent/core/tests/test_conductor.py index b7be39d4a6..c448534631 100644 --- a/aries_cloudagent/core/tests/test_conductor.py +++ b/aries_cloudagent/core/tests/test_conductor.py @@ -119,7 +119,7 @@ async def test_startup_version_record_exists(self): test_module, "get_upgrade_version_list", async_mock.MagicMock( - return_value=["v0.7.4", "0.7.5", "v0.8.0-rc1", "v8.0.0", "v0.8.1-rc0"] + return_value=["v0.7.4", "0.7.5", "v0.8.0-rc1", "v8.0.0", "v0.8.1-rc1"] ), ), async_mock.patch.object( test_module, diff --git a/aries_cloudagent/version.py b/aries_cloudagent/version.py index bde2b40d5e..26775b065d 100644 --- a/aries_cloudagent/version.py +++ b/aries_cloudagent/version.py @@ -1,4 +1,4 @@ """Library version information.""" -__version__ = "0.8.1-rc0" +__version__ = "0.8.1-rc1" RECORD_TYPE_ACAPY_VERSION = "acapy_version" diff --git a/open-api/openapi.json b/open-api/openapi.json index af84cd6fc2..07266ce22b 100644 --- a/open-api/openapi.json +++ b/open-api/openapi.json @@ -1,7 +1,7 @@ { "swagger" : "2.0", "info" : { - "version" : "v0.8.1-rc0", + "version" : "v0.8.1-rc1", "title" : "Aries Cloud Agent" }, "tags" : [ { From b1632aecba0ed78a1a9cd8ba3183514b4cefa19a Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Mon, 3 Apr 2023 08:14:37 -0700 Subject: [PATCH 738/872] indy wallet already open fix Signed-off-by: Shaanjot Gill --- .../commands/tests/test_upgrade.py | 42 ++++++++----------- aries_cloudagent/commands/upgrade.py | 29 +++++++++---- aries_cloudagent/core/conductor.py | 8 +++- 3 files changed, 45 insertions(+), 34 deletions(-) diff --git a/aries_cloudagent/commands/tests/test_upgrade.py b/aries_cloudagent/commands/tests/test_upgrade.py index f57a77b1cc..7fd7be0538 100644 --- a/aries_cloudagent/commands/tests/test_upgrade.py +++ b/aries_cloudagent/commands/tests/test_upgrade.py @@ -46,7 +46,7 @@ async def test_upgrade_storage_from_version_included(self): ConnRecord, "save", async_mock.CoroutineMock() ): await test_module.upgrade( - { + settings={ "upgrade.config_path": "./aries_cloudagent/commands/default_version_upgrade_config.yml", "upgrade.from_version": "v0.7.2", } @@ -69,29 +69,21 @@ async def test_upgrade_storage_missing_from_version(self): ), async_mock.patch.object( ConnRecord, "save", async_mock.CoroutineMock() ): - await test_module.upgrade({}) + await test_module.upgrade(settings={}) async def test_upgrade_from_version(self): + self.profile.settings.extend( + { + "upgrade.from_version": "v0.7.2", + } + ) with async_mock.patch.object( - test_module, - "wallet_config", - async_mock.CoroutineMock( - return_value=( - self.profile, - async_mock.CoroutineMock(did="public DID", verkey="verkey"), - ) - ), - ), async_mock.patch.object( ConnRecord, "query", async_mock.CoroutineMock(return_value=[ConnRecord()]), - ), async_mock.patch.object( - ConnRecord, "save", async_mock.CoroutineMock() - ): + ), async_mock.patch.object(ConnRecord, "save", async_mock.CoroutineMock()): await test_module.upgrade( - { - "upgrade.from_version": "v0.7.2", - } + profile=self.profile, ) async def test_upgrade_callable(self): @@ -125,7 +117,7 @@ async def test_upgrade_callable(self): ), ): await test_module.upgrade( - { + settings={ "upgrade.from_version": "v0.7.2", } ) @@ -146,7 +138,7 @@ async def test_upgrade_x_same_version(self): ), ): await test_module.upgrade( - { + settings={ "upgrade.config_path": "./aries_cloudagent/commands/default_version_upgrade_config.yml", } ) @@ -174,7 +166,7 @@ async def test_upgrade_missing_from_version(self): ): with self.assertRaises(UpgradeError) as ctx: await test_module.upgrade( - { + settings={ "upgrade.config_path": "./aries_cloudagent/commands/default_version_upgrade_config.yml", } ) @@ -213,7 +205,7 @@ async def test_upgrade_x_callable_not_set(self): ): with self.assertRaises(UpgradeError) as ctx: await test_module.upgrade( - { + settings={ "upgrade.from_version": "v0.6.0", } ) @@ -246,7 +238,7 @@ async def test_upgrade_x_class_not_found(self): ): with self.assertRaises(UpgradeError) as ctx: await test_module.upgrade( - { + settings={ "upgrade.from_version": "v0.7.2", } ) @@ -311,7 +303,7 @@ async def test_upgrade_x_invalid_record_type(self): ): with self.assertRaises(UpgradeError) as ctx: await test_module.upgrade( - { + settings={ "upgrade.from_version": "v0.7.2", } ) @@ -350,7 +342,7 @@ async def test_upgrade_force(self): ), ): await test_module.upgrade( - { + settings={ "upgrade.from_version": "v0.7.0", "upgrade.force_upgrade": True, } @@ -381,7 +373,7 @@ async def test_upgrade_x_invalid_config(self): async_mock.MagicMock(return_value={}), ): with self.assertRaises(UpgradeError) as ctx: - await test_module.upgrade({}) + await test_module.upgrade(settings={}) assert "No version configs found in" in str(ctx.exception) def test_main(self): diff --git a/aries_cloudagent/commands/upgrade.py b/aries_cloudagent/commands/upgrade.py index 87082d65f5..9eb3f5cf8e 100644 --- a/aries_cloudagent/commands/upgrade.py +++ b/aries_cloudagent/commands/upgrade.py @@ -7,12 +7,20 @@ from configargparse import ArgumentParser from packaging import version as package_version -from typing import Callable, Sequence, Optional, List +from typing import ( + Callable, + Sequence, + Optional, + List, + Union, + Mapping, + Any, +) from ..core.profile import Profile from ..config import argparse as arg from ..config.default_context import DefaultContextBuilder -from ..config.base import BaseError +from ..config.base import BaseError, BaseSettings from ..config.util import common_config from ..config.wallet import wallet_config from ..messaging.models.base_record import BaseRecord @@ -138,16 +146,23 @@ async def add_version_record(profile: Profile, version: str): LOGGER.info(f"{RECORD_TYPE_ACAPY_VERSION} storage record set to {version}") -async def upgrade(settings: dict): +async def upgrade( + settings: Optional[Union[Mapping[str, Any], BaseSettings]] = None, + profile: Optional[Profile] = None, +): """Perform upgradation steps.""" - context_builder = DefaultContextBuilder(settings) - context = await context_builder.build_context() try: + if settings or settings == {}: + context_builder = DefaultContextBuilder(settings) + context = await context_builder.build_context() + root_profile, _ = await wallet_config(context) + if profile: + root_profile = profile + settings = profile.settings version_upgrade_config_inst = VersionUpgradeConfig( settings.get("upgrade.config_path") ) upgrade_configs = version_upgrade_config_inst.upgrade_configs - root_profile, _ = await wallet_config(context) upgrade_to_version = f"v{__version__}" versions_found_in_config = upgrade_configs.keys() sorted_versions_found_in_config = sorted( @@ -314,7 +329,7 @@ def execute(argv: Sequence[str] = None): settings = get_settings(args) common_config(settings) loop = asyncio.get_event_loop() - loop.run_until_complete(upgrade(settings)) + loop.run_until_complete(upgrade(settings=settings)) def main(): diff --git a/aries_cloudagent/core/conductor.py b/aries_cloudagent/core/conductor.py index 3f3816b996..71359729a7 100644 --- a/aries_cloudagent/core/conductor.py +++ b/aries_cloudagent/core/conductor.py @@ -326,8 +326,12 @@ async def start(self) -> None: tag_query={}, ) from_version = record.value + LOGGER.info( + "Existing acapy_version storage record found, " + f"version set to {from_version}" + ) except StorageNotFoundError: - LOGGER.warning(("Wallet version storage record not found.")) + LOGGER.warning("Wallet version storage record not found.") from_version = from_version or self.root_profile.settings.get( "upgrade.config_path" ) @@ -340,7 +344,7 @@ async def start(self) -> None: from_version != agent_version or self.root_profile.settings.get("upgrade.force_upgrade") ): - await upgrade(self.root_profile.settings) + await upgrade(profile=self.root_profile) else: LOGGER.warning( ( From 58066231fc27ca33dd1034112c078c4af56bebc6 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Mon, 3 Apr 2023 10:33:57 -0700 Subject: [PATCH 739/872] issue#2197 updates and fixes Signed-off-by: Shaanjot Gill --- aries_cloudagent/commands/upgrade.py | 38 ++++----- aries_cloudagent/config/argparse.py | 2 +- aries_cloudagent/core/conductor.py | 47 ++++++----- aries_cloudagent/core/tests/test_conductor.py | 82 ++++++++++++++++++- 4 files changed, 128 insertions(+), 41 deletions(-) diff --git a/aries_cloudagent/commands/upgrade.py b/aries_cloudagent/commands/upgrade.py index 9eb3f5cf8e..3133f21637 100644 --- a/aries_cloudagent/commands/upgrade.py +++ b/aries_cloudagent/commands/upgrade.py @@ -152,13 +152,13 @@ async def upgrade( ): """Perform upgradation steps.""" try: - if settings or settings == {}: - context_builder = DefaultContextBuilder(settings) - context = await context_builder.build_context() - root_profile, _ = await wallet_config(context) if profile: root_profile = profile settings = profile.settings + else: + context_builder = DefaultContextBuilder(settings) + context = await context_builder.build_context() + root_profile, _ = await wallet_config(context) version_upgrade_config_inst = VersionUpgradeConfig( settings.get("upgrade.config_path") ) @@ -168,8 +168,8 @@ async def upgrade( sorted_versions_found_in_config = sorted( versions_found_in_config, key=lambda x: package_version.parse(x) ) + upgrade_from_version_storage = None upgrade_from_version_config = None - upgrade_from_version_setting = None upgrade_from_version = None async with root_profile.session() as session: storage = session.inject(BaseStorage) @@ -177,41 +177,41 @@ async def upgrade( version_storage_record = await storage.find_record( type_filter=RECORD_TYPE_ACAPY_VERSION, tag_query={} ) - upgrade_from_version_config = version_storage_record.value + upgrade_from_version_storage = version_storage_record.value except StorageNotFoundError: LOGGER.info("No ACA-Py version found in wallet storage.") version_storage_record = None if "upgrade.from_version" in settings: - upgrade_from_version_setting = settings.get("upgrade.from_version") + upgrade_from_version_config = settings.get("upgrade.from_version") LOGGER.info( ( - f"Selecting {upgrade_from_version_setting} as " + f"Selecting {upgrade_from_version_config} as " "--from-version from the config." ) ) force_upgrade_flag = settings.get("upgrade.force_upgrade") or False - if upgrade_from_version_config and upgrade_from_version_setting: + if upgrade_from_version_storage and upgrade_from_version_config: if ( - package_version.parse(upgrade_from_version_config) - > package_version.parse(upgrade_from_version_setting) + package_version.parse(upgrade_from_version_storage) + > package_version.parse(upgrade_from_version_config) ) and force_upgrade_flag: - upgrade_from_version = upgrade_from_version_setting - else: upgrade_from_version = upgrade_from_version_config + else: + upgrade_from_version = upgrade_from_version_storage if ( not upgrade_from_version - and not upgrade_from_version_config - and upgrade_from_version_setting + and not upgrade_from_version_storage + and upgrade_from_version_config ): - upgrade_from_version = upgrade_from_version_setting + upgrade_from_version = upgrade_from_version_config if ( not upgrade_from_version - and upgrade_from_version_config - and not upgrade_from_version_setting + and upgrade_from_version_storage + and not upgrade_from_version_config ): - upgrade_from_version = upgrade_from_version_config + upgrade_from_version = upgrade_from_version_storage if not upgrade_from_version: raise UpgradeError( "No upgrade from version found in wallet or settings [--from-version]" diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index f91f99f592..84ee7a99a6 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -2021,7 +2021,7 @@ def get_settings(self, args: Namespace): return settings -@group(CAT_UPGRADE) +@group(CAT_START, CAT_UPGRADE) class UpgradeGroup(ArgumentGroup): """ACA-Py Upgrade process settings.""" diff --git a/aries_cloudagent/core/conductor.py b/aries_cloudagent/core/conductor.py index 71359729a7..20b310a6e5 100644 --- a/aries_cloudagent/core/conductor.py +++ b/aries_cloudagent/core/conductor.py @@ -12,6 +12,7 @@ import json import logging +from packaging import version as package_version from qrcode import QRCode from ..admin.base_server import BaseAdminServer @@ -28,7 +29,6 @@ from ..config.wallet import wallet_config from ..commands.upgrade import ( get_upgrade_version_list, - add_version_record, upgrade, ) from ..core.profile import Profile @@ -78,6 +78,9 @@ from .util import SHUTDOWN_EVENT_TOPIC, STARTUP_EVENT_TOPIC LOGGER = logging.getLogger(__name__) +# Refer ACA-Py issue #2197 +# When the from version is not found +DEFAULT_ACAPY_VERSION = "v0.7.5" class Conductor: @@ -332,31 +335,37 @@ async def start(self) -> None: ) except StorageNotFoundError: LOGGER.warning("Wallet version storage record not found.") - from_version = from_version or self.root_profile.settings.get( - "upgrade.config_path" + from_version_config = self.root_profile.settings.get("upgrade.from_version") + force_upgrade_flag = ( + self.root_profile.settings.get("upgrade.force_upgrade") or False ) - if from_version: - config_available_list = get_upgrade_version_list( - config_path=self.root_profile.settings.get("upgrade.config_path"), - from_version=from_version, - ) - if len(config_available_list) >= 1 and ( - from_version != agent_version - or self.root_profile.settings.get("upgrade.force_upgrade") - ): - await upgrade(profile=self.root_profile) + + if force_upgrade_flag and from_version_config: + if from_version: + if package_version.parse(from_version) > package_version.parse( + from_version_config + ): + from_version = from_version_config + else: + from_version = from_version_config else: + from_version = from_version or from_version_config + if not from_version: LOGGER.warning( ( "No upgrade from version was found from wallet or via" - " --from-version startup argument. " - f"{RECORD_TYPE_ACAPY_VERSION} storage record will be " - f"set to {agent_version}. You can run run the upgrade " - "command with --from-version and --force-upgrade to " - "upgrade later." + " --from-version startup argument. Defaulting to " + f"{DEFAULT_ACAPY_VERSION}." ) ) - await add_version_record(profile=self.root_profile, version=agent_version) + from_version = DEFAULT_ACAPY_VERSION + self.root_profile.settings.set_value("upgrade.from_version", from_version) + config_available_list = get_upgrade_version_list( + config_path=self.root_profile.settings.get("upgrade.config_path"), + from_version=from_version, + ) + if len(config_available_list) >= 1: + await upgrade(profile=self.root_profile) # Create a static connection for use by the test-suite if context.settings.get("debug.test_suite_endpoint"): diff --git a/aries_cloudagent/core/tests/test_conductor.py b/aries_cloudagent/core/tests/test_conductor.py index c448534631..3e0bcccd74 100644 --- a/aries_cloudagent/core/tests/test_conductor.py +++ b/aries_cloudagent/core/tests/test_conductor.py @@ -157,6 +157,80 @@ async def test_startup_version_record_exists(self): mock_inbound_mgr.return_value.stop.assert_awaited_once_with() mock_outbound_mgr.return_value.stop.assert_awaited_once_with() + async def test_startup_version_force_upgrade(self): + test_settings = { + "admin.webhook_urls": ["http://sample.webhook.ca"], + "upgrade.from_version": "v0.7.5", + "upgrade.force_upgrade": True, + } + builder: ContextBuilder = StubContextBuilder(test_settings) + conductor = test_module.Conductor(builder) + with async_mock.patch.object( + test_module, "InboundTransportManager", autospec=True + ) as mock_inbound_mgr, async_mock.patch.object( + test_module, "OutboundTransportManager", autospec=True + ) as mock_outbound_mgr, async_mock.patch.object( + test_module, "LoggingConfigurator", autospec=True + ) as mock_logger, async_mock.patch.object( + BaseStorage, + "find_record", + async_mock.AsyncMock(return_value=async_mock.MagicMock(value="v0.8.0")), + ), async_mock.patch.object( + test_module, + "get_upgrade_version_list", + async_mock.MagicMock(return_value=["v0.8.0-rc1", "v8.0.0", "v0.8.1-rc1"]), + ), async_mock.patch.object( + test_module, + "upgrade", + async_mock.AsyncMock(), + ): + mock_outbound_mgr.return_value.registered_transports = { + "test": async_mock.MagicMock(schemes=["http"]) + } + await conductor.setup() + session = await conductor.root_profile.session() + wallet = session.inject(BaseWallet) + await wallet.create_public_did( + SOV, + ED25519, + ) + mock_inbound_mgr.return_value.registered_transports = {} + mock_outbound_mgr.return_value.registered_transports = {} + await conductor.start() + await conductor.stop() + + with async_mock.patch.object( + test_module, "InboundTransportManager", autospec=True + ) as mock_inbound_mgr, async_mock.patch.object( + test_module, "OutboundTransportManager", autospec=True + ) as mock_outbound_mgr, async_mock.patch.object( + test_module, "LoggingConfigurator", autospec=True + ) as mock_logger, async_mock.patch.object( + BaseStorage, + "find_record", + async_mock.AsyncMock(side_effect=StorageNotFoundError()), + ), async_mock.patch.object( + test_module, + "get_upgrade_version_list", + async_mock.MagicMock(return_value=["v0.8.0-rc1", "v8.0.0", "v0.8.1-rc1"]), + ), async_mock.patch.object( + test_module, + "upgrade", + async_mock.AsyncMock(), + ): + await conductor.setup() + session = await conductor.root_profile.session() + wallet = session.inject(BaseWallet) + await wallet.create_public_did( + SOV, + ED25519, + ) + mock_inbound_mgr.return_value.registered_transports = {} + mock_outbound_mgr.return_value.registered_transports = {} + await conductor.start() + mock_logger.print_banner.assert_called_once() + await conductor.stop() + async def test_startup_version_record_not_exists(self): builder: ContextBuilder = StubContextBuilder(self.test_settings) conductor = test_module.Conductor(builder) @@ -173,7 +247,11 @@ async def test_startup_version_record_not_exists(self): async_mock.AsyncMock(side_effect=StorageNotFoundError()), ), async_mock.patch.object( test_module, - "add_version_record", + "get_upgrade_version_list", + async_mock.MagicMock(return_value=["v0.8.0-rc1", "v8.0.0", "v0.8.1-rc1"]), + ), async_mock.patch.object( + test_module, + "upgrade", async_mock.AsyncMock(), ): mock_outbound_mgr.return_value.registered_transports = { @@ -1444,7 +1522,7 @@ async def test_startup_x_no_storage_version(self): async_mock.AsyncMock(side_effect=StorageNotFoundError()), ), async_mock.patch.object( test_module, - "add_version_record", + "upgrade", async_mock.AsyncMock(), ): mock_outbound_mgr.return_value.registered_transports = { From 164f2aae4e8b574bc0b55984dd5606919addcf95 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Mon, 3 Apr 2023 11:29:11 -0700 Subject: [PATCH 740/872] final updates Signed-off-by: Shaanjot Gill --- aries_cloudagent/core/conductor.py | 16 ++- aries_cloudagent/core/tests/test_conductor.py | 100 ++++++++++++++++++ 2 files changed, 111 insertions(+), 5 deletions(-) diff --git a/aries_cloudagent/core/conductor.py b/aries_cloudagent/core/conductor.py index 20b310a6e5..8952ccb986 100644 --- a/aries_cloudagent/core/conductor.py +++ b/aries_cloudagent/core/conductor.py @@ -29,6 +29,7 @@ from ..config.wallet import wallet_config from ..commands.upgrade import ( get_upgrade_version_list, + add_version_record, upgrade, ) from ..core.profile import Profile @@ -319,6 +320,7 @@ async def start(self) -> None: ) # record ACA-Py version in Wallet, if needed + from_version_storage = None from_version = None agent_version = f"v{__version__}" async with self.root_profile.session() as session: @@ -328,10 +330,10 @@ async def start(self) -> None: type_filter=RECORD_TYPE_ACAPY_VERSION, tag_query={}, ) - from_version = record.value + from_version_storage = record.value LOGGER.info( "Existing acapy_version storage record found, " - f"version set to {from_version}" + f"version set to {from_version_storage}" ) except StorageNotFoundError: LOGGER.warning("Wallet version storage record not found.") @@ -341,15 +343,17 @@ async def start(self) -> None: ) if force_upgrade_flag and from_version_config: - if from_version: - if package_version.parse(from_version) > package_version.parse( + if from_version_storage: + if package_version.parse(from_version_storage) > package_version.parse( from_version_config ): from_version = from_version_config + else: + from_version = from_version_storage else: from_version = from_version_config else: - from_version = from_version or from_version_config + from_version = from_version_storage or from_version_config if not from_version: LOGGER.warning( ( @@ -366,6 +370,8 @@ async def start(self) -> None: ) if len(config_available_list) >= 1: await upgrade(profile=self.root_profile) + elif not (from_version_storage and from_version_storage == agent_version): + await add_version_record(profile=self.root_profile, version=agent_version) # Create a static connection for use by the test-suite if context.settings.get("debug.test_suite_endpoint"): diff --git a/aries_cloudagent/core/tests/test_conductor.py b/aries_cloudagent/core/tests/test_conductor.py index 3e0bcccd74..73f337d85d 100644 --- a/aries_cloudagent/core/tests/test_conductor.py +++ b/aries_cloudagent/core/tests/test_conductor.py @@ -157,6 +157,72 @@ async def test_startup_version_record_exists(self): mock_inbound_mgr.return_value.stop.assert_awaited_once_with() mock_outbound_mgr.return_value.stop.assert_awaited_once_with() + async def test_startup_version_no_upgrade_add_record(self): + builder: ContextBuilder = StubContextBuilder(self.test_settings) + conductor = test_module.Conductor(builder) + + with async_mock.patch.object( + test_module, "InboundTransportManager", autospec=True + ) as mock_inbound_mgr, async_mock.patch.object( + test_module, "OutboundTransportManager", autospec=True + ) as mock_outbound_mgr, async_mock.patch.object( + BaseStorage, + "find_record", + async_mock.AsyncMock(return_value=async_mock.MagicMock(value="v0.8.1")), + ), async_mock.patch.object( + test_module, + "get_upgrade_version_list", + async_mock.MagicMock(return_value=[]), + ), async_mock.patch.object( + test_module, + "add_version_record", + async_mock.AsyncMock(), + ): + mock_outbound_mgr.return_value.registered_transports = { + "test": async_mock.MagicMock(schemes=["http"]) + } + await conductor.setup() + session = await conductor.root_profile.session() + wallet = session.inject(BaseWallet) + await wallet.create_public_did( + SOV, + ED25519, + ) + mock_inbound_mgr.return_value.registered_transports = {} + mock_outbound_mgr.return_value.registered_transports = {} + await conductor.start() + await conductor.stop() + + with async_mock.patch.object( + test_module, "InboundTransportManager", autospec=True + ) as mock_inbound_mgr, async_mock.patch.object( + test_module, "OutboundTransportManager", autospec=True + ) as mock_outbound_mgr, async_mock.patch.object( + BaseStorage, + "find_record", + async_mock.AsyncMock( + return_value=async_mock.MagicMock(value=f"v{__version__}") + ), + ), async_mock.patch.object( + test_module, + "get_upgrade_version_list", + async_mock.MagicMock(return_value=[]), + ): + mock_outbound_mgr.return_value.registered_transports = { + "test": async_mock.MagicMock(schemes=["http"]) + } + await conductor.setup() + session = await conductor.root_profile.session() + wallet = session.inject(BaseWallet) + await wallet.create_public_did( + SOV, + ED25519, + ) + mock_inbound_mgr.return_value.registered_transports = {} + mock_outbound_mgr.return_value.registered_transports = {} + await conductor.start() + await conductor.stop() + async def test_startup_version_force_upgrade(self): test_settings = { "admin.webhook_urls": ["http://sample.webhook.ca"], @@ -199,6 +265,40 @@ async def test_startup_version_force_upgrade(self): await conductor.start() await conductor.stop() + with async_mock.patch.object( + test_module, "InboundTransportManager", autospec=True + ) as mock_inbound_mgr, async_mock.patch.object( + test_module, "OutboundTransportManager", autospec=True + ) as mock_outbound_mgr, async_mock.patch.object( + test_module, "LoggingConfigurator", autospec=True + ) as mock_logger, async_mock.patch.object( + BaseStorage, + "find_record", + async_mock.AsyncMock(return_value=async_mock.MagicMock(value="v0.7.0")), + ), async_mock.patch.object( + test_module, + "get_upgrade_version_list", + async_mock.MagicMock(return_value=["v0.7.2", "v0.7.3", "v0.7.4"]), + ), async_mock.patch.object( + test_module, + "upgrade", + async_mock.AsyncMock(), + ): + mock_outbound_mgr.return_value.registered_transports = { + "test": async_mock.MagicMock(schemes=["http"]) + } + await conductor.setup() + session = await conductor.root_profile.session() + wallet = session.inject(BaseWallet) + await wallet.create_public_did( + SOV, + ED25519, + ) + mock_inbound_mgr.return_value.registered_transports = {} + mock_outbound_mgr.return_value.registered_transports = {} + await conductor.start() + await conductor.stop() + with async_mock.patch.object( test_module, "InboundTransportManager", autospec=True ) as mock_inbound_mgr, async_mock.patch.object( From 159e1c9fdb0ba47c6666a7bc353a1ead6efb8ba4 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Mon, 3 Apr 2023 12:30:53 -0700 Subject: [PATCH 741/872] add exception + profile close check Signed-off-by: Shaanjot Gill --- aries_cloudagent/commands/tests/test_upgrade.py | 8 ++++++++ aries_cloudagent/commands/upgrade.py | 5 ++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/commands/tests/test_upgrade.py b/aries_cloudagent/commands/tests/test_upgrade.py index 7fd7be0538..e7ad5a827b 100644 --- a/aries_cloudagent/commands/tests/test_upgrade.py +++ b/aries_cloudagent/commands/tests/test_upgrade.py @@ -376,6 +376,14 @@ async def test_upgrade_x_invalid_config(self): await test_module.upgrade(settings={}) assert "No version configs found in" in str(ctx.exception) + async def test_upgrade_x_params(self): + with self.assertRaises(UpgradeError) as ctx: + await test_module.upgrade(profile=self.profile, settings={}) + assert "upgrade requires either profile or settings" in str(ctx.exception) + with self.assertRaises(UpgradeError) as ctx: + await test_module.upgrade(profile=self.profile, settings={"...": "..."}) + assert "upgrade requires either profile or settings" in str(ctx.exception) + def test_main(self): with async_mock.patch.object( test_module, "__name__", "__main__" diff --git a/aries_cloudagent/commands/upgrade.py b/aries_cloudagent/commands/upgrade.py index 3133f21637..e40a438409 100644 --- a/aries_cloudagent/commands/upgrade.py +++ b/aries_cloudagent/commands/upgrade.py @@ -152,6 +152,8 @@ async def upgrade( ): """Perform upgradation steps.""" try: + if profile and (settings or settings == {}): + raise UpgradeError("upgrade requires either profile or settings, not both.") if profile: root_profile = profile settings = profile.settings @@ -304,7 +306,8 @@ async def upgrade( f"{RECORD_TYPE_ACAPY_VERSION} storage record " f"set to {upgrade_to_version}" ) - await root_profile.close() + if not profile: + await root_profile.close() except BaseError as e: raise UpgradeError(f"Error during upgrade: {e}") From b7c396a12372b32a19d976fa82593bee835c5d3f Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Mon, 3 Apr 2023 14:56:17 -0700 Subject: [PATCH 742/872] 0.8.1-rc2 Signed-off-by: Stephen Curran --- CHANGELOG.md | 12 ++++++++---- aries_cloudagent/core/tests/test_conductor.py | 2 +- aries_cloudagent/version.py | 2 +- open-api/openapi.json | 2 +- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ac4aa736d7..68aed7d406 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,19 +1,22 @@ -# 0.8.1-rc1 +# 0.8.1-rc2 -## March 31, 2023 +## April 3, 2023 An urgent update to Release 0.8.1 to address an inability to execute the `upgrade` command. The `upgrade` command is needed for Pull Request [\#2116] - "UPGRADE: Fix multi-use invitation performance", which is necessary for (at -least) deployments of ACA-Py as a mediator. The important update is Pull Request -[\#2185] (listed below). +least) deployments of ACA-Py as a mediator. The important updates are Pull +Requests [\#2185] and [\#2196] (listed below). Documentation on upgrades in +ACA-Py be included before the final 0.8.1 release is tagged. [\#2116]: https://github.com/hyperledger/aries-cloudagent-python/pull/2116 [\#2185]: https://github.com/hyperledger/aries-cloudagent-python/pull/2185 +[\#2196]: https://github.com/hyperledger/aries-cloudagent-python/pull/2196 ### Categorized List of Pull Requests - Fixes for the `upgrade` Command + - Fix: Indy WalletAlreadyOpenedError during upgrade process [\#2196](https://github.com/hyperledger/aries-cloudagent-python/pull/2196) [shaangill025](https://github.com/shaangill025) - Fix: Resolve Upgrade Config file in Container [\#2193](https://github.com/hyperledger/aries-cloudagent-python/pull/2193) [shaangill025](https://github.com/shaangill025) - Update and automate ACA-Py upgrade process [\#2185](https://github.com/hyperledger/aries-cloudagent-python/pull/2185) [shaangill025](https://github.com/shaangill025) - Adds the upgrade command YML file to the PyPi Release [\#2179](https://github.com/hyperledger/aries-cloudagent-python/pull/2179) [swcurran](https://github.com/swcurran) @@ -21,6 +24,7 @@ least) deployments of ACA-Py as a mediator. The important update is Pull Request - Create UnitTests.md [\#2183](https://github.com/hyperledger/aries-cloudagent-python/pull/2183) [swcurran](https://github.com/swcurran) - Add link to recorded session about the ACA-Py Integration tests [\#2184](https://github.com/hyperledger/aries-cloudagent-python/pull/2184) [swcurran](https://github.com/swcurran) - Release management pull requests + - 0.8.1-rc2 [\#2197](https://github.com/hyperledger/aries-cloudagent-python/pull/2194) [swcurran](https://github.com/swcurran) - 0.8.1-rc1 [\#2194](https://github.com/hyperledger/aries-cloudagent-python/pull/2194) [swcurran](https://github.com/swcurran) - 0.8.1-rc0 [\#2190](https://github.com/hyperledger/aries-cloudagent-python/pull/2190) [swcurran](https://github.com/swcurran) diff --git a/aries_cloudagent/core/tests/test_conductor.py b/aries_cloudagent/core/tests/test_conductor.py index 73f337d85d..0ed44f8b53 100644 --- a/aries_cloudagent/core/tests/test_conductor.py +++ b/aries_cloudagent/core/tests/test_conductor.py @@ -119,7 +119,7 @@ async def test_startup_version_record_exists(self): test_module, "get_upgrade_version_list", async_mock.MagicMock( - return_value=["v0.7.4", "0.7.5", "v0.8.0-rc1", "v8.0.0", "v0.8.1-rc1"] + return_value=["v0.7.4", "0.7.5", "v0.8.0-rc1", "v8.0.0", "v0.8.1-rc2"] ), ), async_mock.patch.object( test_module, diff --git a/aries_cloudagent/version.py b/aries_cloudagent/version.py index 26775b065d..c2eca1272c 100644 --- a/aries_cloudagent/version.py +++ b/aries_cloudagent/version.py @@ -1,4 +1,4 @@ """Library version information.""" -__version__ = "0.8.1-rc1" +__version__ = "0.8.1-rc2" RECORD_TYPE_ACAPY_VERSION = "acapy_version" diff --git a/open-api/openapi.json b/open-api/openapi.json index 07266ce22b..696904f732 100644 --- a/open-api/openapi.json +++ b/open-api/openapi.json @@ -1,7 +1,7 @@ { "swagger" : "2.0", "info" : { - "version" : "v0.8.1-rc1", + "version" : "v0.8.1-rc2", "title" : "Aries Cloud Agent" }, "tags" : [ { From 2a349b41f3de16697bc2ec59c050542d34a28ea9 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Mon, 3 Apr 2023 14:57:38 -0700 Subject: [PATCH 743/872] Update PR number in changelog Signed-off-by: Stephen Curran --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 68aed7d406..d34bc274c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,7 +24,7 @@ ACA-Py be included before the final 0.8.1 release is tagged. - Create UnitTests.md [\#2183](https://github.com/hyperledger/aries-cloudagent-python/pull/2183) [swcurran](https://github.com/swcurran) - Add link to recorded session about the ACA-Py Integration tests [\#2184](https://github.com/hyperledger/aries-cloudagent-python/pull/2184) [swcurran](https://github.com/swcurran) - Release management pull requests - - 0.8.1-rc2 [\#2197](https://github.com/hyperledger/aries-cloudagent-python/pull/2194) [swcurran](https://github.com/swcurran) + - 0.8.1-rc2 [\#2198](https://github.com/hyperledger/aries-cloudagent-python/pull/2198) [swcurran](https://github.com/swcurran) - 0.8.1-rc1 [\#2194](https://github.com/hyperledger/aries-cloudagent-python/pull/2194) [swcurran](https://github.com/swcurran) - 0.8.1-rc0 [\#2190](https://github.com/hyperledger/aries-cloudagent-python/pull/2190) [swcurran](https://github.com/swcurran) From ff5e61f397b7e57092896ced7e79a5821cdcaa95 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Tue, 4 Apr 2023 12:07:58 -0700 Subject: [PATCH 744/872] Add Upgrading ACA-Py document Signed-off-by: Stephen Curran --- UpgradingACA-Py.md | 88 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 UpgradingACA-Py.md diff --git a/UpgradingACA-Py.md b/UpgradingACA-Py.md new file mode 100644 index 0000000000..3f5e90f7fa --- /dev/null +++ b/UpgradingACA-Py.md @@ -0,0 +1,88 @@ +# Upgrading ACA-Py Data + +Some releases of ACA-Py may be improved by, or even require, an upgrade when +moving to a new version. Such changes are documented in the [CHANGELOG.md], +and those with ACA-Py deployments should take note of those upgrades. This +document summarizes the upgrade system in ACA-Py. + +## Version Information and Automatic Upgrades + +The file [version.py] contains the current version of a running instance of +ACA-Py. In addition, a record is made in the ACA-Py secure storage (database) +about the "most recently upgraded" version. When deploying a new version of +ACA-Py, the [version.py] value will be higher than the version in +secure storage. When that happens, an upgrade is executed, and on successful +completion, the version is updated in secure storage to match what is +in [version.py]. + +Upgrades are defined in the [Upgrade Definition YML file]. For a given +version listed in the follow, the corresponding entry is what actions are +required when upgrading from a previous version. If a version is not listed +in the file, there is no upgrade defined for that version from its immediate +predecessor version. + +Once an upgrade is identified as needed, the process is: + +- Collect (if any) the actions to be taken to get from the version recorded in +secure storage to the current [version.py] +- Execute the actions from oldest to newest. + - If the same action is collected more than once (e.g., "Resave the +Connection Records" is defined for two different versions), perform the action +only once. +- Store the current ACA-Py version (from [version.py]) in the secure storage + database. + +## Forced Offline Upgrades + +In some cases, it may be necessary to do an offline upgrade, where ACA-Py is +taken off line temporarily, the database upgraded explicitly, and then +ACA-Py re-deployed as normal. As yet, we do not know of any cases of this, but +those deploying ACA-Py should be aware of this possibility. For example, +we may at some point need an upgrade that **MUST NOT** be executed by more +than one ACA-Py instance. In that case, a "normal" upgrade could be dangerous +for deployments on container orchestration platforms like Kubernetes. + +If the Maintainers of ACA-Py recognize a case where ACA-Py must be upgraded while +offline, a new Upgrade feature will be added that will prevent the "auto upgrade" +process from executing. See [Issue XXXX] for the status of that feature. + +Those deploying ACA-Py upgrades for production installations should check in +each [CHANGELOG.md] release entry about what upgrades (if any) will be run when +upgrading to that version, and consider how they want those upgrades to run in +their ACA-Py installation. In most cases, simply deploying the new version should +be OK. If the number of records to be upgraded is high (such as a "resave +connections" upgrade to a deployment with many, many connections), you may want +to do a test upgrade offline first, to see if there is likely to be a service +disruption during the upgrade. Plan accordingly! + +## Exceptions + +There are a couple of upgrade exception conditions to consider, as outlined +in the following sections. + +### No version in secure storage + +Versions prior to ACA-Py 0.8.1 did not automatically populate the secure storage +"version" record. That only occurred if an upgrade was explicitly executed. As +of ACA-Py 0.8.1, the version record is added immediately after the secure +storage database is created. If you are upgrading to ACA-Py 0.8.1 or later, and +there is no version record in the secure storage, ACA-Py will assume you are +running version 0.7.5, and execute the upgrade from that version to the current +one. The choice of 0.7.5 as the default is safe because the same upgrades will +be run of any version 0.7.5 and earlier, as can be seen in the [Upgrade +Definition YML file]. Thus, even if you are really upgrading from (for example) +0.6.2, the same upgrades are needed as from 0.7.5. + +### Forcing an upgrade + +If you would like to force an upgrade from a given version, a pair of ACA-Py +configuration arguments can be used together. If you specify "`--from-version +`" and "`--force-upgrade`", the `--from-version` version will override what +is found (or not) in secure storage, and the upgrade will be from that version +to the current one. However, given the few upgrades defined prior to version +0.8.1, and the "[no version in secure storage](#no-version-in-secure-storage)" +handling, it is unlikely this feature will ever be needed. + +[CHANGELOG.md]: https://github.com/hyperledger/aries-cloudagent-python/blob/main/CHANGELOG.md +[version.py]: https://github.com/hyperledger/aries-cloudagent-python/blob/main/aries_cloudagent/version.py +[Upgrade Definition YML file]: https://github.com/hyperledger/aries-cloudagent-python/blob/main/aries_cloudagent/commands/default_version_upgrade_config.yml \ No newline at end of file From a72746af170e382f48f448a005ab8bc9e1d1a427 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Tue, 4 Apr 2023 12:54:42 -0700 Subject: [PATCH 745/872] Change upgrade definition file entry from 0.8.0 to 0.8.1 Signed-off-by: Stephen Curran --- aries_cloudagent/commands/default_version_upgrade_config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aries_cloudagent/commands/default_version_upgrade_config.yml b/aries_cloudagent/commands/default_version_upgrade_config.yml index 67b0b4a022..8c04b9e48f 100644 --- a/aries_cloudagent/commands/default_version_upgrade_config.yml +++ b/aries_cloudagent/commands/default_version_upgrade_config.yml @@ -1,4 +1,4 @@ -v0.8.0: +v0.8.1: resave_records: base_record_path: - "aries_cloudagent.connections.models.conn_record.ConnRecord" From 883259219d2f13bc1d6f37652df5eb927cd27087 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Wed, 5 Apr 2023 13:46:43 -0700 Subject: [PATCH 746/872] Updating the document to clarify, adding issue and PR links Signed-off-by: Stephen Curran --- UpgradingACA-Py.md | 54 +++++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/UpgradingACA-Py.md b/UpgradingACA-Py.md index 3f5e90f7fa..0ad8708564 100644 --- a/UpgradingACA-Py.md +++ b/UpgradingACA-Py.md @@ -36,24 +36,28 @@ only once. In some cases, it may be necessary to do an offline upgrade, where ACA-Py is taken off line temporarily, the database upgraded explicitly, and then -ACA-Py re-deployed as normal. As yet, we do not know of any cases of this, but +ACA-Py re-deployed as normal. As yet, we do not have any use cases for this, but those deploying ACA-Py should be aware of this possibility. For example, we may at some point need an upgrade that **MUST NOT** be executed by more than one ACA-Py instance. In that case, a "normal" upgrade could be dangerous for deployments on container orchestration platforms like Kubernetes. -If the Maintainers of ACA-Py recognize a case where ACA-Py must be upgraded while -offline, a new Upgrade feature will be added that will prevent the "auto upgrade" -process from executing. See [Issue XXXX] for the status of that feature. +If the Maintainers of ACA-Py recognize a case where ACA-Py must be upgraded +while offline, a new Upgrade feature will be added that will prevent the "auto +upgrade" process from executing. See [Issue 2201] and [Pull Request 2204] for +the status of that feature. -Those deploying ACA-Py upgrades for production installations should check in -each [CHANGELOG.md] release entry about what upgrades (if any) will be run when -upgrading to that version, and consider how they want those upgrades to run in -their ACA-Py installation. In most cases, simply deploying the new version should -be OK. If the number of records to be upgraded is high (such as a "resave -connections" upgrade to a deployment with many, many connections), you may want -to do a test upgrade offline first, to see if there is likely to be a service -disruption during the upgrade. Plan accordingly! +[Issue 2201]: https://github.com/hyperledger/aries-cloudagent-python/issues/2201 +[Pull Request 2204]: https://github.com/hyperledger/aries-cloudagent-python/pull/2204 + +Those deploying ACA-Py upgrades for production installations (forced offline or +not) should check in each [CHANGELOG.md] release entry about what upgrades (if +any) will be run when upgrading to that version, and consider how they want +those upgrades to run in their ACA-Py installation. In most cases, simply +deploying the new version should be OK. If the number of records to be upgraded +is high (such as a "resave connections" upgrade to a deployment with many, many +connections), you may want to do a test upgrade offline first, to see if there +is likely to be a service disruption during the upgrade. Plan accordingly! ## Exceptions @@ -67,21 +71,27 @@ Versions prior to ACA-Py 0.8.1 did not automatically populate the secure storage of ACA-Py 0.8.1, the version record is added immediately after the secure storage database is created. If you are upgrading to ACA-Py 0.8.1 or later, and there is no version record in the secure storage, ACA-Py will assume you are -running version 0.7.5, and execute the upgrade from that version to the current -one. The choice of 0.7.5 as the default is safe because the same upgrades will -be run of any version 0.7.5 and earlier, as can be seen in the [Upgrade -Definition YML file]. Thus, even if you are really upgrading from (for example) -0.6.2, the same upgrades are needed as from 0.7.5. +running version 0.7.5, and execute the upgrades from version 0.7.5 to the +current version. The choice of 0.7.5 as the default is safe because the same +upgrades will be run on any version of ACA-Py up to and including 0.7.5, as can +be seen in the [Upgrade Definition YML file]. Thus, even if you are really +upgrading from (for example) 0.6.2, the same upgrades are needed as from 0.7.5 +to a post-0.8.1 version. ### Forcing an upgrade -If you would like to force an upgrade from a given version, a pair of ACA-Py -configuration arguments can be used together. If you specify "`--from-version +If you need to force an upgrade from a given version of ACA-Py, a pair of +configuration options can be used together. If you specify "`--from-version `" and "`--force-upgrade`", the `--from-version` version will override what is found (or not) in secure storage, and the upgrade will be from that version -to the current one. However, given the few upgrades defined prior to version -0.8.1, and the "[no version in secure storage](#no-version-in-secure-storage)" -handling, it is unlikely this feature will ever be needed. +to the current one. For example, if you have "0.8.1" in your "secure storage" +version, and you know that the upgrade for version 0.8.1 has not been executed, +you can use the parameters `--from-version v0.7.5 --force-upgrade` to force the +upgrade on next starting an ACA-Py instance. However, given the few upgrades +defined prior to version 0.8.1, and the "[no version in secure +storage](#no-version-in-secure-storage)" handling, it is unlikely this +capability will ever be needed. We expect to deprecate and remove these +options in future (post-0.8.1) ACA-Py versions. [CHANGELOG.md]: https://github.com/hyperledger/aries-cloudagent-python/blob/main/CHANGELOG.md [version.py]: https://github.com/hyperledger/aries-cloudagent-python/blob/main/aries_cloudagent/version.py From 548256f09b96ecc4da1a7f7d70547c9b9e85ae55 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Wed, 5 Apr 2023 15:39:10 -0700 Subject: [PATCH 747/872] 0.8.1 Signed-off-by: Stephen Curran --- CHANGELOG.md | 64 +++++++++++++++++++++++++++++-------- aries_cloudagent/version.py | 2 +- open-api/openapi.json | 2 +- 3 files changed, 52 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d34bc274c5..822288547b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,29 +1,65 @@ -# 0.8.1-rc2 - -## April 3, 2023 - -An urgent update to Release 0.8.1 to address an inability to execute the -`upgrade` command. The `upgrade` command is needed for Pull Request [\#2116] - -"UPGRADE: Fix multi-use invitation performance", which is necessary for (at -least) deployments of ACA-Py as a mediator. The important updates are Pull -Requests [\#2185] and [\#2196] (listed below). Documentation on upgrades in -ACA-Py be included before the final 0.8.1 release is tagged. - -[\#2116]: https://github.com/hyperledger/aries-cloudagent-python/pull/2116 -[\#2185]: https://github.com/hyperledger/aries-cloudagent-python/pull/2185 -[\#2196]: https://github.com/hyperledger/aries-cloudagent-python/pull/2196 +# 0.8.1 + +## April 5, 2023 + +Version 0.8.1 is an urgent update to Release 0.8.0 to address an inability to +execute the `upgrade` command. The `upgrade` command is needed for 0.8.0 Pull +Request [\#2116] - "UPGRADE: Fix multi-use invitation performance", which is +useful for (at least) deployments of ACA-Py as a mediator. In the release, the +upgrade process is revamped, and documented in [Upgrading ACA-Py]. + +Key points about upgrading for those with production, pre-0.8.1 ACA-Py deployments: + +- Upgrades now happen **automatically** on startup, when needed. +- The version of the last executed upgrade, even if it is a "no change" upgrade, + is put into secure storage and is used to detect when future upgrades are needed. + - Upgrades are needed when the running version is greater than the version is + secure storage. +- If you have an existing, pre-0.8.1 deployment with many connection records, +there may be a delay in starting as an upgrade will be run that loads and saves +every connection record, updating the data in the record in the process. + - A mechanism is to be added (see [Issue #2201]) for preventing an upgrade + running if it should not be run automatically, and requires using the + `upgrade` command. To date, there has been no need for this feature. +- See the [Upgrading ACA-Py] document for more details. + +### Postgres Support with Aries Askar + +Recent changes to [Aries Askar] have resulted in Askar supporting Postgres +version 11 and greater. If you are on Postgres 10 or earlier and want to upgrade +to use Askar, you must migrate your database to Postgres 10. + +We have also noted that in some container orchestration environments such as +[Red Hat's OpenShift] and possibly other [Kubernetes] distributions, Askar using +[Postgres] versions greater than 14 do not install correctly. Please monitor +[Issue \#2199] for an update to this limitation. We have found that Postgres 15 does +install correctly in other environments (such as in `docker compose` setups). + +[\#2116]: https://github.com/hyperledger/aries-cloudagent-python/issues/2116 +[Upgrading ACA-Py]: ./UpgradingACA-Py.md +[Issue #2201]: https://github.com/hyperledger/aries-cloudagent-python/issues/2201 +[Aries Askar]: https://github.com/hyperledger/aries-askar +[Red Hat's OpenShift]: https://www.openshift.com/products/container-platform/ +[Kubernetes]: https://kubernetes.io/ +[Postgres]: https://www.postgresql.org/ +[Issue \#2199]: https://github.com/hyperledger/aries-cloudagent-python/issues/2199 ### Categorized List of Pull Requests - Fixes for the `upgrade` Command + - Change upgrade definition file entry from 0.8.0 to 0.8.1 [\#2203](https://github.com/hyperledger/aries-cloudagent-python/pull/2203) [swcurran](https://github.com/swcurran) + - Add Upgrading ACA-Py document [\#2200](https://github.com/hyperledger/aries-cloudagent-python/pull/2200) [swcurran](https://github.com/swcurran) - Fix: Indy WalletAlreadyOpenedError during upgrade process [\#2196](https://github.com/hyperledger/aries-cloudagent-python/pull/2196) [shaangill025](https://github.com/shaangill025) - Fix: Resolve Upgrade Config file in Container [\#2193](https://github.com/hyperledger/aries-cloudagent-python/pull/2193) [shaangill025](https://github.com/shaangill025) - Update and automate ACA-Py upgrade process [\#2185](https://github.com/hyperledger/aries-cloudagent-python/pull/2185) [shaangill025](https://github.com/shaangill025) - Adds the upgrade command YML file to the PyPi Release [\#2179](https://github.com/hyperledger/aries-cloudagent-python/pull/2179) [swcurran](https://github.com/swcurran) - Test and Documentation + - 3.7 and 3.10 unittests fix [\#2187](https://github.com/hyperledger/aries-cloudagent-python/pull/2187) [Jsyro](https://github.com/Jsyro) + - Doc update and some test scripts [\#2189](https://github.com/hyperledger/aries-cloudagent-python/pull/2189) [ianco](https://github.com/ianco) - Create UnitTests.md [\#2183](https://github.com/hyperledger/aries-cloudagent-python/pull/2183) [swcurran](https://github.com/swcurran) - Add link to recorded session about the ACA-Py Integration tests [\#2184](https://github.com/hyperledger/aries-cloudagent-python/pull/2184) [swcurran](https://github.com/swcurran) - Release management pull requests + - 0.8.1 [\#2207](https://github.com/hyperledger/aries-cloudagent-python/pull/2207) [swcurran](https://github.com/swcurran) - 0.8.1-rc2 [\#2198](https://github.com/hyperledger/aries-cloudagent-python/pull/2198) [swcurran](https://github.com/swcurran) - 0.8.1-rc1 [\#2194](https://github.com/hyperledger/aries-cloudagent-python/pull/2194) [swcurran](https://github.com/swcurran) - 0.8.1-rc0 [\#2190](https://github.com/hyperledger/aries-cloudagent-python/pull/2190) [swcurran](https://github.com/swcurran) diff --git a/aries_cloudagent/version.py b/aries_cloudagent/version.py index c2eca1272c..b745dcd5ec 100644 --- a/aries_cloudagent/version.py +++ b/aries_cloudagent/version.py @@ -1,4 +1,4 @@ """Library version information.""" -__version__ = "0.8.1-rc2" +__version__ = "0.8.1" RECORD_TYPE_ACAPY_VERSION = "acapy_version" diff --git a/open-api/openapi.json b/open-api/openapi.json index 696904f732..2ee691f00d 100644 --- a/open-api/openapi.json +++ b/open-api/openapi.json @@ -1,7 +1,7 @@ { "swagger" : "2.0", "info" : { - "version" : "v0.8.1-rc2", + "version" : "v0.8.1", "title" : "Aries Cloud Agent" }, "tags" : [ { From 9990cc01e2671e8325f86e031bbefce413d62cfb Mon Sep 17 00:00:00 2001 From: Pritam Singh Date: Fri, 23 Jun 2023 00:12:11 +0530 Subject: [PATCH 748/872] feat: healthcheck aca-py server Signed-off-by: Pritam Singh --- deployment/start.sh | 39 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/deployment/start.sh b/deployment/start.sh index 06a0c33bd0..930b2a0c00 100644 --- a/deployment/start.sh +++ b/deployment/start.sh @@ -1,8 +1,43 @@ #!/usr/bin/env bash set -e +nginx -c "/etc/nginx/conf.d/default.conf" & + aca-py start --inbound-transport http 0.0.0.0 11020 --outbound-transport http --admin 0.0.0.0 11021 & -nginx -c "/etc/nginx/conf.d/default.conf" & +ping(){ + url="http://localhost:11021/status/ready" + local resp=$(curl -s --write-out '%{http_code}' --output /dev/null ${url}) + if [ $resp -eq 200 ]; then + return 0 + else + return 1 + fi +} + +wait_for_cloud_agent(){ + COUNT=${WAIT_SECOND:-60} # seconds + printf "waiting for cloud agent" + while ! ping ; do + printf "." + if [ $COUNT -eq 0 ];then + echo "\nThe Cloud agent failed to start within ${duration} seconds.\n" + exit 1 + fi + ((COUNT=COUNT-1)) + sleep 1 + done + printf "\n" +} + +healthcheck(){ + while ping ; do + sleep ${HEALTH_CHECK_PERIOD_SECOND:-300} # second + done + echo "\nAca-py is down" + exit 1 +} + +wait_for_cloud_agent -wait -n \ No newline at end of file +healthcheck \ No newline at end of file From 6c252e2ce9483390224c47401c894ab33fd6e408 Mon Sep 17 00:00:00 2001 From: Pritam Singh Date: Thu, 29 Jun 2023 00:58:34 +0530 Subject: [PATCH 749/872] feat: support oob proof request with connection Signed-off-by: Pritam Singh --- aries_cloudagent/protocols/present_proof/v1_0/manager.py | 1 - aries_cloudagent/protocols/present_proof/v2_0/manager.py | 1 - 2 files changed, 2 deletions(-) diff --git a/aries_cloudagent/protocols/present_proof/v1_0/manager.py b/aries_cloudagent/protocols/present_proof/v1_0/manager.py index 540db13966..a929949fc6 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/manager.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/manager.py @@ -337,7 +337,6 @@ async def receive_presentation( {"thread_id": thread_id}, { "role": V10PresentationExchange.ROLE_VERIFIER, - "connection_id": connection_id, }, ) ) diff --git a/aries_cloudagent/protocols/present_proof/v2_0/manager.py b/aries_cloudagent/protocols/present_proof/v2_0/manager.py index 5b4f2362ab..b6c323d9eb 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/manager.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/manager.py @@ -338,7 +338,6 @@ async def receive_pres( {"thread_id": thread_id}, { "role": V20PresExRecord.ROLE_VERIFIER, - "connection_id": connection_id, }, ) From c4a493923227e1578dc46660ac516d8b0e49d4f1 Mon Sep 17 00:00:00 2001 From: Darko Kulic Date: Tue, 21 Mar 2023 10:35:00 +0100 Subject: [PATCH 750/872] Add support for JsonWebKey2020 for the connection invitations Signed-off-by: Darko Kulic --- aries_cloudagent/connections/base_manager.py | 30 +++++--- .../connections/v1_0/tests/test_manager.py | 69 ++++++++++++++++++- 2 files changed, 88 insertions(+), 11 deletions(-) diff --git a/aries_cloudagent/connections/base_manager.py b/aries_cloudagent/connections/base_manager.py index 815843b4c0..4582b01330 100644 --- a/aries_cloudagent/connections/base_manager.py +++ b/aries_cloudagent/connections/base_manager.py @@ -13,7 +13,7 @@ VerificationMethod, ) import pydid -from pydid.verification_method import Ed25519VerificationKey2018 +from pydid.verification_method import Ed25519VerificationKey2018, JsonWebKey2020 from ..core.error import BaseError from ..core.profile import Profile @@ -37,6 +37,7 @@ from .models.conn_record import ConnRecord from .models.connection_target import ConnectionTarget from .models.diddoc import DIDDoc, PublicKey, PublicKeyType, Service +from ..wallet.util import bytes_to_b58, b64_to_bytes class BaseConnectionManagerError(BaseError): @@ -48,7 +49,6 @@ class BaseConnectionManager: RECORD_TYPE_DID_DOC = "did_doc" RECORD_TYPE_DID_KEY = "did_key" - SUPPORTED_KEY_TYPES = (Ed25519VerificationKey2018,) def __init__(self, profile: Profile): """ @@ -277,18 +277,28 @@ async def resolve_invitation( for url in first_didcomm_service.routing_keys ] - for key in [*recipient_keys, *routing_keys]: - if not isinstance(key, self.SUPPORTED_KEY_TYPES): - raise BaseConnectionManagerError( - f"Key type {type(key).__name__} is not supported" - ) - return ( endpoint, - [key.material for key in recipient_keys], - [key.material for key in routing_keys], + [self._extract_key_material_in_base58_format(key) for key in recipient_keys], + [self._extract_key_material_in_base58_format(key) for key in routing_keys], ) + @staticmethod + def _extract_key_material_in_base58_format(method: VerificationMethod) -> str: + if isinstance(method, Ed25519VerificationKey2018): + return method.material + elif isinstance(method, JsonWebKey2020): + if method.public_key_jwk.get('kty') == 'OKP': + return bytes_to_b58(b64_to_bytes(method.public_key_jwk.get("x"), True)) + else: + raise BaseConnectionManagerError( + f"Key type {type(method).__name__} with kty {method.public_key_jwk.get('kty')} is not supported" + ) + else: + raise BaseConnectionManagerError( + f"Key type {type(method).__name__} is not supported" + ) + async def fetch_connection_targets( self, connection: ConnRecord ) -> Sequence[ConnectionTarget]: diff --git a/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py index 03c7e64647..482c9d7809 100644 --- a/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py @@ -39,6 +39,7 @@ from ..messages.connection_request import ConnectionRequest from ..messages.connection_response import ConnectionResponse from ..models.connection_detail import ConnectionDetail +from .....wallet.util import bytes_to_b64, b58_to_bytes class TestConnectionManager(AsyncTestCase): @@ -2322,13 +2323,79 @@ async def test_fetch_connection_targets_conn_invitation_no_didcomm_services(self with self.assertRaises(BaseConnectionManagerError): await self.manager.fetch_connection_targets(mock_conn) + async def test_fetch_connection_targets_conn_invitation_supported_JsonWebKey2020_key_type(self): + async with self.profile.session() as session: + builder = DIDDocumentBuilder("did:btcr:x705-jznz-q3nl-srs") + vmethod = builder.verification_method.add( + JsonWebKey2020, + ident="1", + public_key_jwk={ + "kty": "OKP", + "crv": "Ed25519", + "x": bytes_to_b64(b58_to_bytes(self.test_target_verkey), True) + }, + ) + builder.service.add_didcomm( + type_="IndyAgent", + service_endpoint=self.test_endpoint, + recipient_keys=[vmethod], + ) + did_doc = builder.build() + self.resolver = async_mock.MagicMock() + self.resolver.get_endpoint_for_did = async_mock.CoroutineMock( + return_value=self.test_endpoint + ) + self.resolver.resolve = async_mock.CoroutineMock(return_value=did_doc) + self.resolver.dereference = async_mock.CoroutineMock( + return_value=did_doc.verification_method[0] + ) + self.context.injector.bind_instance(DIDResolver, self.resolver) + local_did = await session.wallet.create_local_did( + method=SOV, + key_type=ED25519, + seed=self.test_seed, + did=did_doc.id, + metadata=None, + ) + + conn_invite = ConnectionInvitation( + did=did_doc.id, + endpoint=self.test_endpoint, + recipient_keys=[vmethod.public_key_jwk], + routing_keys=[self.test_verkey], + label="label", + ) + mock_conn = async_mock.MagicMock( + my_did=did_doc.id, + their_did=self.test_target_did, + connection_id="dummy", + their_role=ConnRecord.Role.RESPONDER.rfc23, + state=ConnRecord.State.INVITATION.rfc23, + retrieve_invitation=async_mock.CoroutineMock(return_value=conn_invite), + ) + + targets = await self.manager.fetch_connection_targets(mock_conn) + assert len(targets) == 1 + target = targets[0] + assert target.did == mock_conn.their_did + assert target.endpoint == self.test_endpoint + assert target.label == conn_invite.label + assert target.recipient_keys == [self.test_target_verkey] + assert target.routing_keys == [] + assert target.sender_key == local_did.verkey + async def test_fetch_connection_targets_conn_invitation_unsupported_key_type(self): async with self.profile.session() as session: builder = DIDDocumentBuilder("did:btcr:x705-jznz-q3nl-srs") vmethod = builder.verification_method.add( JsonWebKey2020, ident="1", - public_key_jwk={"jwk": "stuff"}, + public_key_jwk={ + "kty": "EC", + "crv": "P-256", + "x": "2syLh57B-dGpa0F8p1JrO6JU7UUSF6j7qL-vfk1eOoY", + "y": "BgsGtI7UPsObMRjdElxLOrgAO9JggNMjOcfzEPox18w", + }, ) builder.service.add_didcomm( type_="IndyAgent", From e36f913e055135f4a71b09d27f1b397aa285b197 Mon Sep 17 00:00:00 2001 From: Darko Kulic Date: Wed, 29 Mar 2023 15:14:40 +0200 Subject: [PATCH 751/872] Fix linter errors Signed-off-by: Darko Kulic --- aries_cloudagent/connections/base_manager.py | 10 +++++++--- .../protocols/connections/v1_0/tests/test_manager.py | 6 ++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/aries_cloudagent/connections/base_manager.py b/aries_cloudagent/connections/base_manager.py index 4582b01330..481c471218 100644 --- a/aries_cloudagent/connections/base_manager.py +++ b/aries_cloudagent/connections/base_manager.py @@ -279,7 +279,10 @@ async def resolve_invitation( return ( endpoint, - [self._extract_key_material_in_base58_format(key) for key in recipient_keys], + [ + self._extract_key_material_in_base58_format(key) + for key in recipient_keys + ], [self._extract_key_material_in_base58_format(key) for key in routing_keys], ) @@ -288,11 +291,12 @@ def _extract_key_material_in_base58_format(method: VerificationMethod) -> str: if isinstance(method, Ed25519VerificationKey2018): return method.material elif isinstance(method, JsonWebKey2020): - if method.public_key_jwk.get('kty') == 'OKP': + if method.public_key_jwk.get("kty") == "OKP": return bytes_to_b58(b64_to_bytes(method.public_key_jwk.get("x"), True)) else: raise BaseConnectionManagerError( - f"Key type {type(method).__name__} with kty {method.public_key_jwk.get('kty')} is not supported" + f"Key type {type(method).__name__}" + f"with kty {method.public_key_jwk.get('kty')} is not supported" ) else: raise BaseConnectionManagerError( diff --git a/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py index 482c9d7809..fb4105def5 100644 --- a/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py @@ -2323,7 +2323,9 @@ async def test_fetch_connection_targets_conn_invitation_no_didcomm_services(self with self.assertRaises(BaseConnectionManagerError): await self.manager.fetch_connection_targets(mock_conn) - async def test_fetch_connection_targets_conn_invitation_supported_JsonWebKey2020_key_type(self): + async def test_fetch_connection_targets_conn_invitation_supported_JsonWebKey2020_key_type( + self, + ): async with self.profile.session() as session: builder = DIDDocumentBuilder("did:btcr:x705-jznz-q3nl-srs") vmethod = builder.verification_method.add( @@ -2332,7 +2334,7 @@ async def test_fetch_connection_targets_conn_invitation_supported_JsonWebKey2020 public_key_jwk={ "kty": "OKP", "crv": "Ed25519", - "x": bytes_to_b64(b58_to_bytes(self.test_target_verkey), True) + "x": bytes_to_b64(b58_to_bytes(self.test_target_verkey), True), }, ) builder.service.add_didcomm( From d6722bd34fe495ce6fb696beffb1733c9e115dda Mon Sep 17 00:00:00 2001 From: Alex Walker Date: Tue, 14 Mar 2023 10:50:47 -0700 Subject: [PATCH 752/872] fix: run only on main, forks ok Signed-off-by: Alex Walker --- .github/workflows/nightly-tests.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/nightly-tests.yml b/.github/workflows/nightly-tests.yml index e9b6d5d77a..d0df76c859 100644 --- a/.github/workflows/nightly-tests.yml +++ b/.github/workflows/nightly-tests.yml @@ -3,10 +3,13 @@ name: Nightly Tests on: schedule: - cron: '0 0 * * *' - workflow_dispatch: + push: + branches: + - main jobs: tests: + if: github.event.ref == 'refs/heads/main' name: Tests strategy: fail-fast: false @@ -22,6 +25,7 @@ jobs: os: ${{ matrix.os }} tests-indy: + if: github.event.ref == 'refs/heads/main' name: Tests (Indy) strategy: fail-fast: false @@ -31,7 +35,6 @@ jobs: include: - os: "ubuntu-20.04" python-version: "3.6" - uses: ./.github/workflows/tests-indy.yml with: python-version: ${{ matrix.python-version }} From a3d2d9c29af3abdec69ea0974f32ecfe59a0b355 Mon Sep 17 00:00:00 2001 From: Alex Walker Date: Mon, 20 Mar 2023 11:45:35 -0700 Subject: [PATCH 753/872] fix: on schedule Signed-off-by: Alex Walker --- .github/workflows/nightly-tests.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/nightly-tests.yml b/.github/workflows/nightly-tests.yml index d0df76c859..e510dd4272 100644 --- a/.github/workflows/nightly-tests.yml +++ b/.github/workflows/nightly-tests.yml @@ -3,9 +3,6 @@ name: Nightly Tests on: schedule: - cron: '0 0 * * *' - push: - branches: - - main jobs: tests: From 309c8281520290f684674353e0c677132320f17f Mon Sep 17 00:00:00 2001 From: Alex Walker Date: Tue, 28 Mar 2023 11:09:15 -0700 Subject: [PATCH 754/872] fix: run on hyperledger's only Signed-off-by: Alex Walker --- .github/workflows/nightly-tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/nightly-tests.yml b/.github/workflows/nightly-tests.yml index e510dd4272..511600f722 100644 --- a/.github/workflows/nightly-tests.yml +++ b/.github/workflows/nightly-tests.yml @@ -6,7 +6,7 @@ on: jobs: tests: - if: github.event.ref == 'refs/heads/main' + if: github.event.ref == 'refs/heads/main' && github.repository == 'hyperledger/aries-cloudagent-python' name: Tests strategy: fail-fast: false @@ -22,7 +22,7 @@ jobs: os: ${{ matrix.os }} tests-indy: - if: github.event.ref == 'refs/heads/main' + if: github.event.ref == 'refs/heads/main' && github.repository == 'hyperledger/aries-cloudagent-python' name: Tests (Indy) strategy: fail-fast: false From eefd0ba1e4695cc2c9f7666fbd458c1461d436ad Mon Sep 17 00:00:00 2001 From: Alex Walker Date: Tue, 28 Mar 2023 11:35:11 -0700 Subject: [PATCH 755/872] feat: add workflow_dispatch parameter to run flows Signed-off-by: Alex Walker --- .github/workflows/nightly-tests.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/nightly-tests.yml b/.github/workflows/nightly-tests.yml index 511600f722..d75704ca80 100644 --- a/.github/workflows/nightly-tests.yml +++ b/.github/workflows/nightly-tests.yml @@ -3,10 +3,16 @@ name: Nightly Tests on: schedule: - cron: '0 0 * * *' + workflow_dispatch: + inputs: + run_flows: + description: '(Bool) Run nightly-tests.yml' + required: false + default: true jobs: tests: - if: github.event.ref == 'refs/heads/main' && github.repository == 'hyperledger/aries-cloudagent-python' + if: (github.event.ref == 'refs/heads/main' && github.repository == 'hyperledger/aries-cloudagent-python') || (github.event.inputs.run_flows) name: Tests strategy: fail-fast: false @@ -22,7 +28,7 @@ jobs: os: ${{ matrix.os }} tests-indy: - if: github.event.ref == 'refs/heads/main' && github.repository == 'hyperledger/aries-cloudagent-python' + if: (github.event.ref == 'refs/heads/main' && github.repository == 'hyperledger/aries-cloudagent-python') || (github.event.inputs.run_flows) name: Tests (Indy) strategy: fail-fast: false From f7783a8a152d09d38f2b524f44686dec8abc7a8e Mon Sep 17 00:00:00 2001 From: Alex Walker Date: Tue, 4 Apr 2023 13:15:43 -0700 Subject: [PATCH 756/872] fix: remove 'main' Signed-off-by: Alex Walker --- .github/workflows/nightly-tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/nightly-tests.yml b/.github/workflows/nightly-tests.yml index d75704ca80..2aabbe90de 100644 --- a/.github/workflows/nightly-tests.yml +++ b/.github/workflows/nightly-tests.yml @@ -12,7 +12,7 @@ on: jobs: tests: - if: (github.event.ref == 'refs/heads/main' && github.repository == 'hyperledger/aries-cloudagent-python') || (github.event.inputs.run_flows) + if: github.repository == 'hyperledger/aries-cloudagent-python' || (github.event.inputs.run_flows) name: Tests strategy: fail-fast: false @@ -28,7 +28,7 @@ jobs: os: ${{ matrix.os }} tests-indy: - if: (github.event.ref == 'refs/heads/main' && github.repository == 'hyperledger/aries-cloudagent-python') || (github.event.inputs.run_flows) + if: github.repository == 'hyperledger/aries-cloudagent-python' || (github.event.inputs.run_flows) name: Tests (Indy) strategy: fail-fast: false From 1111e0bbb7237d20f29917629c556cf4a07e2d71 Mon Sep 17 00:00:00 2001 From: Alex Walker Date: Thu, 6 Apr 2023 08:05:30 -0700 Subject: [PATCH 757/872] fix: repo name variable formatting Signed-off-by: Alex Walker --- .github/workflows/nightly-tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/nightly-tests.yml b/.github/workflows/nightly-tests.yml index 2aabbe90de..8157205ba5 100644 --- a/.github/workflows/nightly-tests.yml +++ b/.github/workflows/nightly-tests.yml @@ -12,7 +12,7 @@ on: jobs: tests: - if: github.repository == 'hyperledger/aries-cloudagent-python' || (github.event.inputs.run_flows) + if: ${{github.event.repository.full_name}} == 'hyperledger/aries-cloudagent-python' || (github.event.inputs.run_flows) name: Tests strategy: fail-fast: false @@ -28,7 +28,7 @@ jobs: os: ${{ matrix.os }} tests-indy: - if: github.repository == 'hyperledger/aries-cloudagent-python' || (github.event.inputs.run_flows) + if: ${{github.event.repository.full_name}} == 'hyperledger/aries-cloudagent-python' || (github.event.inputs.run_flows) name: Tests (Indy) strategy: fail-fast: false From 26ee9780b77db5deee72ea0e5514c4df7a123d97 Mon Sep 17 00:00:00 2001 From: Alex Walker Date: Thu, 6 Apr 2023 17:38:47 +0200 Subject: [PATCH 758/872] Revert "fix: repo name variable formatting" This reverts commit 115a3a05b402f72765359a39d94771cc8659dfe7. Signed-off-by: Alex Walker --- .github/workflows/nightly-tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/nightly-tests.yml b/.github/workflows/nightly-tests.yml index 8157205ba5..2aabbe90de 100644 --- a/.github/workflows/nightly-tests.yml +++ b/.github/workflows/nightly-tests.yml @@ -12,7 +12,7 @@ on: jobs: tests: - if: ${{github.event.repository.full_name}} == 'hyperledger/aries-cloudagent-python' || (github.event.inputs.run_flows) + if: github.repository == 'hyperledger/aries-cloudagent-python' || (github.event.inputs.run_flows) name: Tests strategy: fail-fast: false @@ -28,7 +28,7 @@ jobs: os: ${{ matrix.os }} tests-indy: - if: ${{github.event.repository.full_name}} == 'hyperledger/aries-cloudagent-python' || (github.event.inputs.run_flows) + if: github.repository == 'hyperledger/aries-cloudagent-python' || (github.event.inputs.run_flows) name: Tests (Indy) strategy: fail-fast: false From 406ea4c2f1117f25636a786e283fde92913e51fd Mon Sep 17 00:00:00 2001 From: Alex Walker Date: Thu, 20 Apr 2023 11:25:56 -0700 Subject: [PATCH 759/872] feat: run if workflow triggered Signed-off-by: Alex Walker --- .github/workflows/nightly-tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/nightly-tests.yml b/.github/workflows/nightly-tests.yml index 2aabbe90de..fce1b53969 100644 --- a/.github/workflows/nightly-tests.yml +++ b/.github/workflows/nightly-tests.yml @@ -12,7 +12,7 @@ on: jobs: tests: - if: github.repository == 'hyperledger/aries-cloudagent-python' || (github.event.inputs.run_flows) + if: github.repository == 'hyperledger/aries-cloudagent-python' || github.event_name == 'workflow_dispatch' name: Tests strategy: fail-fast: false @@ -28,7 +28,7 @@ jobs: os: ${{ matrix.os }} tests-indy: - if: github.repository == 'hyperledger/aries-cloudagent-python' || (github.event.inputs.run_flows) + if: github.repository == 'hyperledger/aries-cloudagent-python' || github.event_name == 'workflow_dispatch' name: Tests (Indy) strategy: fail-fast: false From 3d7d955d372f0e3284cb34ae32f4df57bece13b4 Mon Sep 17 00:00:00 2001 From: Alex Walker Date: Mon, 24 Apr 2023 08:49:35 -0700 Subject: [PATCH 760/872] fix: remove env variable run_flows Signed-off-by: Alex Walker --- .github/workflows/nightly-tests.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/nightly-tests.yml b/.github/workflows/nightly-tests.yml index fce1b53969..5c180f8fdd 100644 --- a/.github/workflows/nightly-tests.yml +++ b/.github/workflows/nightly-tests.yml @@ -4,11 +4,6 @@ on: schedule: - cron: '0 0 * * *' workflow_dispatch: - inputs: - run_flows: - description: '(Bool) Run nightly-tests.yml' - required: false - default: true jobs: tests: From 0c642a8016f48374e393b055421af83b638d97a0 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Wed, 26 Apr 2023 14:03:26 -0700 Subject: [PATCH 761/872] Update Alice Wants a JSON-LD Credential to fix invocation Addresses issue #2215 Signed-off-by: Stephen Curran --- demo/AliceWantsAJsonCredential.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/demo/AliceWantsAJsonCredential.md b/demo/AliceWantsAJsonCredential.md index 8243968b64..e5afad3213 100644 --- a/demo/AliceWantsAJsonCredential.md +++ b/demo/AliceWantsAJsonCredential.md @@ -18,16 +18,18 @@ cd aries-cloudagent-python/demo Open up a second shell (so you have 2 shells open in the `demo` directory) and in one shell: ```bash -./run_demo faber --did-exchange --aip 20 --cred-type json-ld +LEDGER_URL=http://dev.greenlight.bcovrin.vonx.io ./run_demo faber --did-exchange --aip 20 --cred-type json-ld ``` ... and in the other: ```bash -./run_demo alice +LEDGER_URL=http://dev.greenlight.bcovrin.vonx.io ./run_demo alice ``` -Note that you start the `faber` agent with AIP2.0 options. (When you specify `--cred-type json-ld` faber will set aip to `20` automatically, so the `--aip` option is not strictly required.) +Note that you start the `faber` agent with AIP2.0 options. (When you specify `--cred-type json-ld` faber will set aip to `20` automatically, +so the `--aip` option is not strictly required). Note as well the use of the `LEDGER_URL`. Technically, that should not be needed if we aren't +doing anything with an Indy ledger-based credentials. However, there must be something in the way that the Faber and Alice controllers are starting up that requires access to a ledger. Also note that the above will only work with the `/issue-credential-2.0/create-offer` endpoint. If you want to use the `/issue-credential-2.0/send` endpoint - which automates each step of the credential exchange - you will need to include the `--no-auto` option when starting each of the alice and faber agents (since the alice and faber controllers *also* automatically respond to each step in the credential exchange). From 957b9a169586ab79ca3483b620ff8e52a396c140 Mon Sep 17 00:00:00 2001 From: ff137 Date: Tue, 2 May 2023 19:42:34 +0200 Subject: [PATCH 762/872] :art: fix formatting and grammar Signed-off-by: ff137 --- AdminAPI.md | 128 ++++++++++++++++++++++++++-------------------------- 1 file changed, 64 insertions(+), 64 deletions(-) diff --git a/AdminAPI.md b/AdminAPI.md index f677daabdb..0441899312 100644 --- a/AdminAPI.md +++ b/AdminAPI.md @@ -2,72 +2,72 @@ ## Using the OpenAPI (Swagger) Interface -ACA-Py provides an OpenAPI-documented REST interface for administering the agent's internal state and sparking communication with connected agents. +ACA-Py provides an OpenAPI-documented REST interface for administering the agent's internal state and initiating communication with connected agents. -To see the specifics of the supported endpoints as well as the expected request and response formats it is recommended to run the `aca-py` agent with the `--admin {HOST} {PORT}` and `--admin-insecure-mode` command line parameters, which exposes the OpenAPI UI on the provided port for interaction via a web browser. Production deployments should run the agent with `--admin-api-key {KEY}` and add the `X-API-Key: {KEY}` header to all requests instead of running the agent with the `--admin-insecure-mode` parameter. +To see the specifics of the supported endpoints, as well as the expected request and response formats, it is recommended to run the `aca-py` agent with the `--admin {HOST} {PORT}` and `--admin-insecure-mode` command line parameters. This exposes the OpenAPI UI on the provided port for interaction via a web browser. For production deployments, run the agent with `--admin-api-key {KEY}` and add the `X-API-Key: {KEY}` header to all requests instead of using the `--admin-insecure-mode` parameter. ![Admin API Screenshot](/docs/assets/adminApi.png) To invoke a specific method: - * scroll to and find that endpoint; - * click on the endpoint name to expand its section of the UI; - * click on the Try it out button; - * fill in any data necessary to run the command; - * click Execute; - * check the response to see if the request worked as expected. +* Scroll to and find that endpoint; +* Click on the endpoint name to expand its section of the UI; +* Click on the Try it out button; +* Fill in any data necessary to run the command; +* Click Execute; +* Check the response to see if the request worked as expected. -The mechanical steps are easy, it’s fourth step from the list above that can be tricky. Supplying the right data and, where JSON is involved, getting the syntax correct - braces and quotes can be a pain. When steps don’t work, start your debugging by looking at your JSON. You may also choose to use a REST client like Postman or Insomnia which will provide syntax highlighting and other features to simplify the process. +The mechanical steps are easy; however, the fourth step from the list above can be tricky. Supplying the right data and, where JSON is involved, getting the syntax correct—braces and quotes can be a pain. When steps don't work, start your debugging by looking at your JSON. You may also choose to use a REST client like Postman or Insomnia, which will provide syntax highlighting and other features to simplify the process. -Because API methods will often kick off asynchronous processes, the JSON response provided by an endpoint is not always sufficient to determine the next action. To handle this situation as well as events triggered due to external inputs (such as new connection requests), it is necessary to implement a webhook processor, as detailed in the next section. +Because API methods often initiate asynchronous processes, the JSON response provided by an endpoint is not always sufficient to determine the next action. To handle this situation, as well as events triggered by external inputs (such as new connection requests), it is necessary to implement a webhook processor, as detailed in the next section. -The combination of an OpenAPI client and webhook processor is referred to as an ACA-Py Controller and is the recommended method to define custom behaviours for your ACA-Py-based agent application. +The combination of an OpenAPI client and webhook processor is referred to as an ACA-Py Controller and is the recommended method to define custom behaviors for your ACA-Py-based agent application. ## Administration API Webhooks When ACA-Py is started with the `--webhook-url {URL}` command line parameter, state-management records are sent to the provided URL via POST requests whenever a record is created or its `state` property is updated. -When a webhook is dispatched, the record `topic` is appended as a path component to the URL, for example: `https://webhook.host.example` becomes `https://webhook.host.example/topic/connections` when a connection record is updated. A POST request is made to the resulting URL with the body of the request comprised by a serialized JSON object. The full set of properties of the current set of webhook payloads are listed below. Note that empty (null-value) properties are omitted. +When a webhook is dispatched, the record `topic` is appended as a path component to the URL. For example, `https://webhook.host.example` becomes `https://webhook.host.example/topic/connections` when a connection record is updated. A POST request is made to the resulting URL with the body of the request comprising a serialized JSON object. The full set of properties of the current set of webhook payloads are listed below. Note that empty (null-value) properties are omitted. -#### Pairwise Connection Record Updated (`/connections`) +### Pairwise Connection Record Updated (`/connections`) - * `connection_id`: the unique connection identifier - * `state`: `init` / `invitation` / `request` / `response` / `active` / `error` / `inactive` - * `my_did`: the DID this agent is using in the connection - * `their_did`: the DID the other agent in the connection is using - * `their_label`: a connection label provided by the other agent - * `their_role`: a role assigned to the other agent in the connection - * `inbound_connection_id`: a connection identifier for the related inbound routing connection - * `initiator`: `self` / `external` / `multiuse` - * `invitation_key`: a verification key used to identify the source connection invitation - * `request_id`: the `@id` property from the connection request message - * `routing_state`: `none` / `request` / `active` / `error` - * `accept`: `manual` / `auto` - * `error_msg`: the most recent error message - * `invitation_mode`: `once` / `multi` - * `alias`: a local alias for the connection record +* `connection_id`: the unique connection identifier +* `state`: `init` / `invitation` / `request` / `response` / `active` / `error` / `inactive` +* `my_did`: the DID this agent is using in the connection +* `their_did`: the DID the other agent in the connection is using +* `their_label`: a connection label provided by the other agent +* `their_role`: a role assigned to the other agent in the connection +* `inbound_connection_id`: a connection identifier for the related inbound routing connection +* `initiator`: `self` / `external` / `multiuse` +* `invitation_key`: a verification key used to identify the source connection invitation +* `request_id`: the `@id` property from the connection request message +* `routing_state`: `none` / `request` / `active` / `error` +* `accept`: `manual` / `auto` +* `error_msg`: the most recent error message +* `invitation_mode`: `once` / `multi` +* `alias`: a local alias for the connection record -#### Basic Message Received (`/basicmessages`) +### Basic Message Received (`/basicmessages`) - * `connection_id`: the identifier of the related pairwise connection - * `message_id`: the `@id` of the incoming agent message - * `content`: the contents of the agent message - * `state`: `received` +* `connection_id`: the identifier of the related pairwise connection +* `message_id`: the `@id` of the incoming agent message +* `content`: the contents of the agent message +* `state`: `received` -#### Forward Message Received (`/forward`) +### Forward Message Received (`/forward`) Enable using `--monitor-forward`. - * `connection_id`: the identifier of the connection associated with the recipient key - * `recipient_key`: the recipient key of the forward message (`to` field of the forward message) - * `status`: The delivery status of the received forward message. Possible values: - * `sent_to_session`: Message is sent directly to the connection over an active transport session - * `sent_to_external_queue`: Message is sent to external queue. No information is known on the delivery of the message - * `queued_for_delivery`: Message is queued for delivery using outbound transport (recipient connection has an endpoint) - * `waiting_for_pickup`: The connection has no reachable endpoint. Need to wait for the recipient to connect with return routing for delivery - * `undeliverable`: The connection has no reachable endpoint, and the internal queue for messages is not enabled (`--enable-undelivered-queue`). +* `connection_id`: the identifier of the connection associated with the recipient key +* `recipient_key`: the recipient key of the forward message (`to` field of the forward message) +* `status`: The delivery status of the received forward message. Possible values: + * `sent_to_session`: Message is sent directly to the connection over an active transport session + * `sent_to_external_queue`: Message is sent to an external queue. No information is known on the delivery of the message + * `queued_for_delivery`: Message is queued for delivery using outbound transport (recipient connection has an endpoint) + * `waiting_for_pickup`: The connection has no reachable endpoint. Need to wait for the recipient to connect with return routing for delivery + * `undeliverable`: The connection has no reachable endpoint, and the internal queue for messages is not enabled (`--enable-undelivered-queue`). -#### Credential Exchange Record Updated (`/issue_credential`) +### Credential Exchange Record Updated (`/issue_credential`) * `credential_exchange_id`: the unique identifier of the credential exchange * `connection_id`: the identifier of the related pairwise connection @@ -90,35 +90,35 @@ Enable using `--monitor-forward`. #### Presentation Exchange Record Updated (`/present_proof`) - * `presentation_exchange_id`: the unique identifier of the presentation exchange - * `connection_id`: the identifier of the related pairwise connection - * `thread_id`: the thread ID of the previously received presentation proposal or offer - * `initiator`: present-proof exchange initiator: `self` / `external` - * `state`: `proposal_sent` / `proposal_received` / `request_sent` / `request_received` / `presentation_sent` / `presentation_received` / `verified` - * `presentation_proposal_dict`: the presentation proposal message - * `presentation_request`: (Indy) presentation request (also known as proof request) - * `presentation`: (Indy) presentation (also known as proof) - * `verified`: (string) whether the presentation is verified: `true` or `false` - * `auto_present`: (boolean) prover choice to auto-present proof as verifier requests - * `error_msg`: the previous error message +* `presentation_exchange_id`: the unique identifier of the presentation exchange +* `connection_id`: the identifier of the related pairwise connection +* `thread_id`: the thread ID of the previously received presentation proposal or offer +* `initiator`: present-proof exchange initiator: `self` / `external` +* `state`: `proposal_sent` / `proposal_received` / `request_sent` / `request_received` / `presentation_sent` / `presentation_received` / `verified` +* `presentation_proposal_dict`: the presentation proposal message +* `presentation_request`: (Indy) presentation request (also known as proof request) +* `presentation`: (Indy) presentation (also known as proof) +* `verified`: (string) whether the presentation is verified: `true` or `false` +* `auto_present`: (boolean) prover choice to auto-present proof as verifier requests +* `error_msg`: the previous error message -## API Standard Behaviour +## API Standard Behavior The best way to develop a new admin API or protocol is to follow one of the existing protocols, such as the Credential Exchange or Presentation Exchange. The `routes.py` file contains the API definitions - API endpoints and payload schemas (note that these are not the Aries message schemas). -The payload schemas are defined using [marshmallow](https://marshmallow.readthedocs.io/) and will be validated automatically when the API is executed (using a middleware). (This raises a status `422` HTTP response with an error message if the schema validation fails.) +The payload schemas are defined using [marshmallow](https://marshmallow.readthedocs.io/) and will be validated automatically when the API is executed (using middleware). (This raises a status `422` HTTP response with an error message if the schema validation fails.) -API endpoints are defined using [aiohttp_apispec](https://github.com/maximdanilchenko/aiohttp-apispec) tags (e.g. `@doc`, `@request_schema`, `@response_schema` etc.) which define the input and output parameters of the endpoint. API url paths are defined in the `register()` method and added to the swagger page in the `post_process_routes()` method. +API endpoints are defined using [aiohttp_apispec](https://github.com/maximdanilchenko/aiohttp-apispec) tags (e.g. `@doc`, `@request_schema`, `@response_schema` etc.) which define the input and output parameters of the endpoint. API URL paths are defined in the `register()` method and added to the Swagger page in the `post_process_routes()` method. -The API's should return the following HTTP status: +The APIs should return the following HTTP status: - * HTTP 200 for successful API completion, with appropriate response - * HTTP 400 (or appropriate 4xx code) (with an error message) for errors on input parameters (i.e. the user can retry with different parameters and potentially get a successful API call) - * HTTP 404 if a record is expected and not found (generally for GET requests that fetch a single record) - * HTTP 500 (or appropriate 5xx code) if there is some other processing error (i.e. won't make any difference what parameters the user tries) with an error message +* HTTP 200 for successful API completion, with an appropriate response +* HTTP 400 (or appropriate 4xx code) (with an error message) for errors on input parameters (i.e., the user can retry with different parameters and potentially get a successful API call) +* HTTP 404 if a record is expected and not found (generally for GET requests that fetch a single record) +* HTTP 500 (or appropriate 5xx code) if there is some other processing error (i.e., it won't make any difference what parameters the user tries) with an error message -.. and should not return: +...and should not return: - * HTTP 500 with a stack trace due to untrapped error (we should handle error conditions with a 400 or 404 response, and catch errors and provide a meaningful error message) +* HTTP 500 with a stack trace due to an untrapped error (we should handle error conditions with a 400 or 404 response and catch errors, providing a meaningful error message) From 1920bcaac97d44b396a457e01da9bb0e872e97c8 Mon Sep 17 00:00:00 2001 From: ff137 Date: Tue, 2 May 2023 19:49:40 +0200 Subject: [PATCH 763/872] :art: fix formatting and grammar Signed-off-by: ff137 --- AnoncredsProofValidation.md | 53 ++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/AnoncredsProofValidation.md b/AnoncredsProofValidation.md index 9114c3a727..aa4a599189 100644 --- a/AnoncredsProofValidation.md +++ b/AnoncredsProofValidation.md @@ -1,10 +1,10 @@ -# Anoncreds Proof Validation in Aca-Py +# Anoncreds Proof Validation in ACA-Py -Aca-Py does some pre-validation when verifying Anoncreds presentations (proofs), some scenarios are rejected (things that are indicative of tampering, for example) and some attributes are removed before running the anoncreds validation (for example removing superfluous non-revocation timestamps). Any Aca-Py validations or presentation modifications are indicated by the "verify_msgs" attribute in the final presentation exchange object +ACA-Py performs pre-validation when verifying Anoncreds presentations (proofs). Some scenarios are rejected (such as those indicative of tampering), while some attributes are removed before running the anoncreds validation (e.g., removing superfluous non-revocation timestamps). Any ACA-Py validations or presentation modifications are indicated by the "verify_msgs" attribute in the final presentation exchange object. -The list of possible verification messages is [here](https://github.com/hyperledger/aries-cloudagent-python/blob/main/aries_cloudagent/indy/verifier.py#L24), and consists of: +The list of possible verification messages can be found [here](https://github.com/hyperledger/aries-cloudagent-python/blob/main/aries_cloudagent/indy/verifier.py#L24), and consists of: -``` +```python class PresVerifyMsg(str, Enum): """Credential verification codes.""" @@ -16,11 +16,11 @@ class PresVerifyMsg(str, Enum): PRES_VERIFY_ERROR = "VERIFY_ERROR" ``` -If there is additional information, it will be included like this: `TS_OUT_NRI::19_uuid` (which means the attribute identified by `19_uuid` contained a timestamp outside of the non-revocation interval (which is just a warning)). +If there is additional information, it will be included like this: `TS_OUT_NRI::19_uuid` (which means the attribute identified by `19_uuid` contained a timestamp outside of the non-revocation interval (this is just a warning)). A presentation verification may include multiple messages, for example: -``` +```python ... "verified": "true", "verified_msgs": [ @@ -33,7 +33,7 @@ A presentation verification may include multiple messages, for example: ... or it may include a single message, for example: -``` +```python ... "verified": "false", "verified_msgs": [ @@ -46,39 +46,38 @@ A presentation verification may include multiple messages, for example: ## Presentation Modifications and Warnings -The following modifications/warnings may be done by Aca-Py which shouldn't affect the verification of the received proof): +The following modifications/warnings may be made by ACA-Py, which shouldn't affect the verification of the received proof: -- "RMV_RFNT_NRI": Referent contains a non-revocation interval for a non-revocable credential (timestamp is removed) -- "RMV_GLB_NRI": Presentation contains a global interval for a non-revocable credential (timestamp is removed) -- "TS_OUT_NRI": Presentation contains a non-revocation timestamp outside of the requested non-revocation interval (warning) -- "UNRVL_ATTR": Presentation contains attributes with unrevealed values (warning) +- "RMV_RFNT_NRI": Referent contains a non-revocation interval for a non-revocable credential (timestamp is removed) +- "RMV_GLB_NRI": Presentation contains a global interval for a non-revocable credential (timestamp is removed) +- "TS_OUT_NRI": Presentation contains a non-revocation timestamp outside of the requested non-revocation interval (warning) +- "UNRVL_ATTR": Presentation contains attributes with unrevealed values (warning) ## Presentation Pre-validation Errors -The following pre-verification checks are done, which will fail the proof (before calling anoncreds) and will result in the following message: +The following pre-verification checks are performed, which will cause the proof to fail (before calling anoncreds) and result in the following message: -``` +```plaintext VALUE_ERROR:: ``` -These validations are all done within the [Indy verifier class](https://github.com/hyperledger/aries-cloudagent-python/blob/main/aries_cloudagent/indy/verifier.py) - to see the detailed validation just look for anywhere a `raise ValueError(...)` appears in the code. +These validations are all performed within the [Indy verifier class](https://github.com/hyperledger/aries-cloudagent-python/blob/main/aries_cloudagent/indy/verifier.py) - to see the detailed validation, look for any occurrences of `raise ValueError(...)` in the code. -A summary of the possible errors is: +A summary of the possible errors includes: -- information missing in presentation exchange record -- timestamp provided for irrevocable credential -- referenced revocation registry not found on ledger -- timestamp outside of reasonable range (future date or pre-dates revocation registry) -- mis-match between provided and requested timestamps for non-revocation -- mis-match between requested and provided attributes or predicates -- self-attested attribute is provided for a requested attribute with restrictions -- encoded value doesn't match raw value +- Information missing in presentation exchange record +- Timestamp provided for irrevocable credential +- Referenced revocation registry not found on ledger +- Timestamp outside of reasonable range (future date or pre-dates revocation registry) +- Mismatch between provided and requested timestamps for non-revocation +- Mismatch between requested and provided attributes or predicates +- Self-attested attribute provided for a requested attribute with restrictions +- Encoded value doesn't match raw value ## Anoncreds Verification Exceptions -Typically when you call the anoncreds `verifier_verify_proof()` method, it will return a `True` or `False` based on whether the presentation cryptographically verifies. However in the case where anoncreds throws an exception, the exception text will be included in a verification message as follows: +Typically, when you call the anoncreds `verifier_verify_proof()` method, it will return a `True` or `False` based on whether the presentation cryptographically verifies. However, in the case where anoncreds throws an exception, the exception text will be included in a verification message as follows: -``` +```plaintext VERIFY_ERROR:: ``` - From 3eeb4d6b938fb3592e8eafc2bf35e64a3e610cfa Mon Sep 17 00:00:00 2001 From: ff137 Date: Tue, 2 May 2023 19:53:49 +0200 Subject: [PATCH 764/872] :art: Signed-off-by: ff137 --- AdminAPI.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AdminAPI.md b/AdminAPI.md index 0441899312..a5f537a7b4 100644 --- a/AdminAPI.md +++ b/AdminAPI.md @@ -88,7 +88,7 @@ Enable using `--monitor-forward`. * `auto_issue`: (boolean) whether to automatically issue the credential * `error_msg`: the previous error message -#### Presentation Exchange Record Updated (`/present_proof`) +### Presentation Exchange Record Updated (`/present_proof`) * `presentation_exchange_id`: the unique identifier of the presentation exchange * `connection_id`: the identifier of the related pairwise connection From 19676fbc7dfb72af5a4f57e0ae817b45e2811f5f Mon Sep 17 00:00:00 2001 From: ff137 Date: Tue, 2 May 2023 20:03:30 +0200 Subject: [PATCH 765/872] :art: fix formatting and grammar Signed-off-by: ff137 --- UsingOpenAPI.md | 42 +++++++++++++++++++----------------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/UsingOpenAPI.md b/UsingOpenAPI.md index 9523f8245e..3a88381932 100644 --- a/UsingOpenAPI.md +++ b/UsingOpenAPI.md @@ -2,47 +2,43 @@ ACA-Py provides an OpenAPI-documented REST interface for administering the agent's internal state and initiating communication with connected agents. -The running agent provides a `Swagger User Interface` that can be browsed and used to test various scenarios manually (see the [Admin API Readme](AdminApi.md) for details). However it is often desirable to produce native language interfaces rather than coding `Controllers` using HTTP primitives. This is possible using several public code generation (codegen) tools. This page provides some suggestions based on experience with these tools when trying to generate `Typescript` wrappers. The information should be useful to those trying to generate other langauages. Updates to this page based on experience are encouraged. +The running agent provides a `Swagger User Interface` that can be browsed and used to test various scenarios manually (see the [Admin API Readme](AdminApi.md) for details). However, it is often desirable to produce native language interfaces rather than coding `Controllers` using HTTP primitives. This is possible using several public code generation (codegen) tools. This page provides some suggestions based on experience with these tools when trying to generate `Typescript` wrappers. The information should be useful to those trying to generate other languages. Updates to this page based on experience are encouraged. -## ACA-py, OpenAPI Raw output characteristics +## ACA-Py, OpenAPI Raw Output Characteristics -ACA-Py uses [aiohttp_apispec](https://github.com/maximdanilchenko/aiohttp-apispec) tags in code to produce the OpenAPI spec file at runtime dependent on what features have been loaded. How these tags are created is documented in the [API Standard Behaviour](https://github.com/hyperledger/aries-cloudagent-python/blob/main/AdminAPI.md#api-standard-behaviour) section of the [Admin API Readme](AdminApi.md). The OpenAPI spec is available in raw, unformated form from a running ACA-py instance using a route of `http:///api/docs/swagger.json` or from the browser `Swagger User Interface` directly. +ACA-Py uses [aiohttp_apispec](https://github.com/maximdanilchenko/aiohttp-apispec) tags in code to produce the OpenAPI spec file at runtime dependent on what features have been loaded. How these tags are created is documented in the [API Standard Behaviour](https://github.com/hyperledger/aries-cloudagent-python/blob/main/AdminAPI.md#api-standard-behaviour) section of the [Admin API Readme](AdminApi.md). The OpenAPI spec is available in raw, unformatted form from a running ACA-Py instance using a route of `http:///api/docs/swagger.json` or from the browser `Swagger User Interface` directly. -To help identify changes in the ACA-py Admin API over releases there is a tool that can be run located at `scripts/generate-open-api-spec`. This tool will start ACA-py, pull the `swagger.json` file, run a codegen tool and specify a language output of `json`. Apart from providing a better format to compare changes (i.e. by comparing this output to the checked in `open-api/openapi.json` version), the tool can be used to identify any non-conformance to the OpenAPI specification. At the moment `validation` is turned off via the `open-api/openAPIJSON.config` file so that warning messages are printed for non-conformance but the `json` is still output. Most of the warnings reported by `generate-open-api-spec` relate to missing `operationId` fields which results in manufactured method names being created by codgen tools. At the moment [aiohttp_apispec](https://github.com/maximdanilchenko/aiohttp-apispec) does not support adding `operationId` anotations via tags. +To help identify changes in the ACA-Py Admin API over releases, there is a tool that can be run located at `scripts/generate-open-api-spec`. This tool will start ACA-Py, pull the `swagger.json` file, run a codegen tool, and specify a language output of `json`. Apart from providing a better format to compare changes (i.e., by comparing this output to the checked-in `open-api/openapi.json` version), the tool can be used to identify any non-conformance to the OpenAPI specification. At the moment, `validation` is turned off via the `open-api/openAPIJSON.config` file, so warning messages are printed for non-conformance, but the `json` is still output. Most of the warnings reported by `generate-open-api-spec` relate to missing `operationId` fields which results in manufactured method names being created by codegen tools. At the moment, [aiohttp_apispec](https://github.com/maximdanilchenko/aiohttp-apispec) does not support adding `operationId` annotations via tags. -The `generate-open-api-spec` tool was initially created to help identify issues with method parameters not being sorted, resulting in somewhat random ordering each time a codegen operation was performed. This is relevent for languages which do not have support for [named parameters](https://en.wikipedia.org/wiki/Named_parameter) such as `Javascript`. It is recomended that the `generate-open-api-spec` is run prior to each release and the resulting `open-api/openapi.json` file checked in to allow tracking of API changes over time. At the moment this process is not automated as part of the release pipeline. +The `generate-open-api-spec` tool was initially created to help identify issues with method parameters not being sorted, resulting in somewhat random ordering each time a codegen operation was performed. This is relevant for languages which do not have support for [named parameters](https://en.wikipedia.org/wiki/Named_parameter) such as `Javascript`. It is recommended that the `generate-open-api-spec` is run prior to each release, and the resulting `open-api/openapi.json` file checked in to allow tracking of API changes over time. At the moment, this process is not automated as part of the release pipeline. -## Generating Language Wrappers for ACA-py +## Generating Language Wrappers for ACA-Py -There are inevitably differences around `best practice` for method naming based on coding language, and indeed organisation standards. +There are inevitably differences around `best practice` for method naming based on coding language and organization standards. -Best practice for generating ACA-Py language wrappers is to obtain the raw OpenAPI file from a configured/running ACA-Py instance and then post-process it with a merge utility to match routes and insert desired `operationId` fields. This allows greatest flexibility in conforming to external naming requirements. +Best practice for generating ACA-Py language wrappers is to obtain the raw OpenAPI file from a configured/running ACA-Py instance and then post-process it with a merge utility to match routes and insert desired `operationId` fields. This allows the greatest flexibility in conforming to external naming requirements. -Two major open source code generation tools are [Swagger](https://github.com/swagger-api/swagger-codegen) and [OpenAPI Tools](https://github.com/OpenAPITools/openapi-generator). Which of these to use can be very dependent on language support required and preference for the style of code generated. +Two major open-source code generation tools are [Swagger](https://github.com/swagger-api/swagger-codegen) and [OpenAPI Tools](https://github.com/OpenAPITools/openapi-generator). Which of these to use can be very dependent on language support required and preference for the style of code generated. - The [OpenAPI Tools](https://github.com/OpenAPITools/openapi-generator) was found to offer some nice features when generating `Typescript`. It creates seperate files for each class and allows use of a `.openapi-generator-ignore` file to override generation if there is a spec file issue that needs to be maintained manually. +The [OpenAPI Tools](https://github.com/OpenAPITools/openapi-generator) was found to offer some nice features when generating `Typescript`. It creates separate files for each class and allows the use of a `.openapi-generator-ignore` file to override generation if there is a spec file issue that needs to be maintained manually. - If generating code for languages that do not support [named parameters](https://en.wikipedia.org/wiki/Named_parameter) it is recommended to specify the `useSingleRequestParameter` or equivalent in your code generator of choice. The reason is that as mentioned previously, there have been instances where parameters were not sorted when output into the raw ACA-Py API spec file and this approach helps remove that risk. +If generating code for languages that do not support [named parameters](https://en.wikipedia.org/wiki/Named_parameter), it is recommended to specify the `useSingleRequestParameter` or equivalent in your code generator of choice. The reason is that, as mentioned previously, there have been instances where parameters were not sorted when output into the raw ACA-Py API spec file, and this approach helps remove that risk. - Another suggestion for code generation is to keep the `modelPropertyNaming` set to `original` when generating code. Although it is tempting to try and enable marshalling into standard naming formats such as `camelCase`, the reality is that the models represent what is sent on the wire and documented in the [Aries Protocol RFCS](https://github.com/hyperledger/aries-rfcs/tree/master/features). It has proven handy to be able to see code references correspond directly with protocol RFCs when debugging. It will also correspond directly with what the `model` shows when looking at the ACA-py `Swagger UI` in a browser if you need to try something out manually before coding. One final point is that on occasions it has been discovered that the code generation tools don't always get the marshalling correct in all circumstances when changing model name format. +Another suggestion for code generation is to keep the `modelPropertyNaming` set to `original` when generating code. Although it is tempting to try and enable marshaling into standard naming formats such as `camelCase`, the reality is that the models represent what is sent on the wire and documented in the [Aries Protocol RFCS](https://github.com/hyperledger/aries-rfcs/tree/master/features). It has proven handy to be able to see code references correspond directly with protocol RFCs when debugging. It will also correspond directly with what the `model` shows when looking at the ACA-Py `Swagger UI` in a browser if you need to try something out manually before coding. One final point is that on occasions, it has been discovered that the code generation tools don't always get the marshaling correct in all circumstances when changing model name format. -## Existing Language Wrappers for ACA-py +## Existing Language Wrappers for ACA-Py ### Python -- https://pypi.org/project/aries-cloudcontroller/ - - https://github.com/didx-xyz/aries-cloudcontroller-python -- https://github.com/bcgov/traction/tree/develop/services/traction/acapy_client -- https://github.com/Indicio-tech/acapy-client +- [aries-cloudcontroller (PyPI)](https://pypi.org/project/aries-cloudcontroller/) + - [aries-cloudcontroller-python (GitHub)](https://github.com/didx-xyz/aries-cloudcontroller-python) +- [traction (GitHub)](https://github.com/bcgov/traction/tree/develop/services/traction/acapy_client) +- [acapy-client (GitHub)](https://github.com/Indicio-tech/acapy-client) ### Go -- https://github.com/ldej/go-acapy-client +- [go-acapy-client (GitHub)](https://github.com/ldej/go-acapy-client) ### Java -- https://github.com/hyperledger-labs/acapy-java-client - - - - +- [acapy-java-client (GitHub)](https://github.com/hyperledger-labs/acapy-java-client) From 4592ef70a577c303cff2694ab1112773dbc65f47 Mon Sep 17 00:00:00 2001 From: ff137 Date: Tue, 2 May 2023 20:08:54 +0200 Subject: [PATCH 766/872] lint code blocks Signed-off-by: ff137 --- UnitTests.md | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/UnitTests.md b/UnitTests.md index f61e23af3d..bbaaa104fe 100644 --- a/UnitTests.md +++ b/UnitTests.md @@ -11,12 +11,11 @@ developer @shaangill025. - `./scripts/run_tests aries_clouadagent/protocols/out_of_band/v1_0/tests` - `./scripts/run_tests_indy` includes Indy specific tests - ## Pytest Example: aries_cloudagent/core/tests/test_event_bus.py -``` +```python @pytest.fixture def event_bus(): yield EventBus() @@ -49,7 +48,7 @@ def processor(): --- -``` +```python def test_sub_unsub(event_bus: EventBus, processor): """Test subscribe and unsubscribe.""" event_bus.subscribe(re.compile(".*"), processor) @@ -61,7 +60,7 @@ def test_sub_unsub(event_bus: EventBus, processor): From aries_cloudagent/core/event_bus.py -``` +```python class EventBus: def __init__(self): self.topic_patterns_to_subscribers: Dict[Pattern, List[Callable]] = {} @@ -84,7 +83,7 @@ def unsubscribe(self, pattern: Pattern, processor: Callable): --- -``` +```python @pytest.mark.asyncio async def test_sub_notify(event_bus: EventBus, profile, event, processor): """Test subscriber receives event.""" @@ -94,7 +93,7 @@ async def test_sub_notify(event_bus: EventBus, profile, event, processor): assert processor.event == event ``` -``` +```python async def notify(self, profile: "Profile", event: Event): partials = [] for pattern, subscribers in self.topic_patterns_to_subscribers.items(): @@ -125,7 +124,7 @@ async def notify(self, profile: "Profile", event: Event): From: aries_cloudagent/protocols/didexchange/v1_0/tests/test.manager.py -``` +```python class TestDidExchangeManager(AsyncTestCase, TestConfig): async def setUp(self): self.responder = MockResponder() @@ -183,7 +182,7 @@ class TestDidExchangeManager(AsyncTestCase, TestConfig): --- -``` +```python async def receive_invitation( self, invitation: OOBInvitationMessage, @@ -242,7 +241,7 @@ async def receive_invitation( - Error catching -``` +```python with self.assertRaises(DIDXManagerError) as ctx: ... assert " ... error ..." in str(ctx.exception) @@ -255,4 +254,4 @@ async def receive_invitation( can be attributed at function or class level. Example, `@pytest.mark.indy` - Code coverage - ![](https://i.imgur.com/VhNYcje.png) + ![Code coverage screenshot](https://i.imgur.com/VhNYcje.png) From 5f37e0df2f7507f254ca7ba03ad02a7af15f3413 Mon Sep 17 00:00:00 2001 From: ff137 Date: Tue, 2 May 2023 20:18:53 +0200 Subject: [PATCH 767/872] :art: lint Signed-off-by: ff137 --- Multitenancy.md | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/Multitenancy.md b/Multitenancy.md index d3d7d9805a..032c43be2a 100644 --- a/Multitenancy.md +++ b/Multitenancy.md @@ -22,6 +22,8 @@ This allows ACA-Py to be used for a wider range of use cases. One use case could - [Identifying the wallet](#identifying-the-wallet) - [Authentication](#authentication) - [Getting a token](#getting-a-token) + - [Method 1: Register new tenant](#method-1-register-new-tenant) + - [Method 2: Get tenant token](#method-2-get-tenant-token) - [JWT Secret](#jwt-secret) - [SwaggerUI](#swaggerui) - [Tenant Management](#tenant-management) @@ -127,7 +129,7 @@ The main tradeoff between option 1. and 2. is redundancy and control. Option 1. A combination of option 1. and 2. is also possible. In this case, two mediators will be used and the sub wallet mediator will forward to the base wallet mediator, which will, in turn, forward to the ACA-Py instance. -``` +```plaintext +---------------------+ +----------------------+ +--------------------+ | Sub wallet mediator | ---> | Base wallet mediator | ---> | Multi-tenant agent | +---------------------+ +----------------------+ +--------------------+ @@ -196,7 +198,7 @@ For sub wallets, an additional authentication method is introduced using JSON We Example -``` +```jsonc GET /connections [headers="Authorization: Bearer {token}] ``` @@ -227,7 +229,7 @@ new_tenant='{ }' ``` -``` +```sh echo $new_tenant | curl -X POST "${ACAPY_ADMIN_URL}/multitenancy/wallet" \ -H "Content-Type: application/json" \ -H "X-Api-Key: $ACAPY_ADMIN_URL_API_KEY" \ @@ -257,14 +259,13 @@ echo $new_tenant | curl -X POST "${ACAPY_ADMIN_URL}/multitenancy/wallet" \ } ``` - #### Method 2: Get tenant token This method allows you to retrieve a tenant `token` for an already registered tenant. To retrieve a token you will need an Admin API key (if your admin is protected with one), `wallet_key` and the `wallet_id` of the tenant. Note that calling the get tenant token endpoint will **invalidate** the old token. This is useful if the old token needs to be revoked, but does mean that you can't have multiple authentication tokens for the same wallet. Only the last generated token will always be valid. Example -``` +```sh curl -X POST "${ACAPY_ADMIN_URL}/multitenancy/wallet/{wallet_id}/token" \ -H "Content-Type: application/json" \ -H "X-Api-Key: $ACAPY_ADMIN_URL_API_KEY" \ @@ -297,9 +298,9 @@ For deterministic JWT creation and verification between restarts and multiple in ### SwaggerUI -When using the SwaggerUI you can click the :lock: icon next to each of the endpoints or the `Authorize` button at the top to set the correct authentication headers. Make sure to also include the `Bearer ` part in the input field. This won't be automatically added. +When using the SwaggerUI you can click the :lock: icon next to each of the endpoints or the `Authorize` button at the top to set the correct authentication headers. Make sure to also include the `Bearer` part in the input field. This won't be automatically added. -![](/docs/assets/adminApiAuthentication.png) +![API Authentication](/docs/assets/adminApiAuthentication.png) ## Tenant Management @@ -321,7 +322,7 @@ update_tenant='{ }' ``` -``` +```sh echo $update_tenant | curl -X PUT "${ACAPY_ADMIN_URL}/multitenancy/wallet/${TENANT_WALLET_ID}" \ -H "Content-Type: application/json" \ -H "x-api-key: $ACAPY_ADMIN_URL_API_KEY" \ @@ -349,18 +350,20 @@ echo $update_tenant | curl -X PUT "${ACAPY_ADMIN_URL}/multitenancy/wallet/${TEN "created_at": "2022-04-01T15:12:35.474975Z" } ``` + > An Admin API Key is all that is ALLOWED to be included in a request header during an update. Inluding the Bearer token header will result in a 404: Unauthorized error -## Remove a tenant +### Remove a tenant + +The following information is required to delete a tenant: -The following information is required to delete a tenant: - wallet_id - wallet_key - {Admin_Api_Key} if admin is protected Example -``` +```sh curl -X POST "${ACAPY_ADMIN_URL}/multitenancy/wallet/{wallet_id}/remove" \ -H "Content-Type: application/json" \ -H "x-api-key: $ACAPY_ADMIN_URL_API_KEY" \ @@ -371,4 +374,4 @@ curl -X POST "${ACAPY_ADMIN_URL}/multitenancy/wallet/{wallet_id}/remove" \ ```jsonc {} -``` \ No newline at end of file +``` From 904cf21e6e4c6b1b87bb4da62daf0963bad74066 Mon Sep 17 00:00:00 2001 From: ff137 Date: Tue, 2 May 2023 20:19:16 +0200 Subject: [PATCH 768/872] :art: lint Signed-off-by: ff137 --- DIDResolution.md | 60 +++++++++++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 26 deletions(-) diff --git a/DIDResolution.md b/DIDResolution.md index cbe00edc1d..82d0b345b6 100644 --- a/DIDResolution.md +++ b/DIDResolution.md @@ -1,25 +1,26 @@ # DID Resolution in ACA-Py + Decentralized Identifiers, or DIDs, are URIs that point to documents that describe cryptographic primitives and protocols used in decentralized identity management. DIDs include methods that describe where and how documents can be retrieved. DID resolution is the process of "resolving" a DID Document from a DID as dictated by the DID method. A DID Resolver is a piece of software that implements the methods for resolving a document from a DID. For example, given the DID `did:example:1234abcd`, a DID Resolver that supports `did:example` might return: -```json= +```json { - "@context": "https://www.w3.org/ns/did/v1", - "id": "did:example:1234abcd", - "verificationMethod": [{ - "id": "did:example:1234abcd#keys-1", - "type": "Ed25519VerificationKey2018", - "controller": "did:example:1234abcd", - "publicKeyBase58": "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV" - }], - "service": [{ - "id": "did:example:1234abcd#did-communication", - "type": "did-communication", - "serviceEndpoint": "https://agent.example.com/8377464" - }] + "@context": "https://www.w3.org/ns/did/v1", + "id": "did:example:1234abcd", + "verificationMethod": [{ + "id": "did:example:1234abcd#keys-1", + "type": "Ed25519VerificationKey2018", + "controller": "did:example:1234abcd", + "publicKeyBase58": "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV" + }], + "service": [{ + "id": "did:example:1234abcd#did-communication", + "type": "did-communication", + "serviceEndpoint": "https://agent.example.com/8377464" + }] } ``` @@ -31,8 +32,9 @@ In practice, DIDs and DID Documents are used for a variety of purposes but espec In ACA-Py, the `DIDResolver` provides the interface to resolve DIDs using registered method resolvers. Method resolver registration happens on startup in a `did_resolvers` list. This registry enables additional resolvers to be loaded via plugin. -#### Example usage: -```python= +### Example usage + +```python class ExampleMessageHandler: async def handle(context: RequestContext, responder: BaseResponder): """Handle example message.""" @@ -87,6 +89,7 @@ async def setup(context: InjectionContext): ``` #### `example_resolver.py` + ```python= import re from typing import Pattern @@ -133,25 +136,26 @@ class ExampleResolver(BaseDIDResolver): ``` #### Errors + There are 3 different errors associated with resolution in ACA-Py that could be used for development purposes. - ResolverError - - Base class for resolver exceptions. + - Base class for resolver exceptions. - DIDNotFound - - Raised when DID is not found using DID method specific algorithm. + - Raised when DID is not found using DID method specific algorithm. - DIDMethodNotSupported - - Raised when no resolver is registered for a given did method. + - Raised when no resolver is registered for a given did method. ### Using Resolver Plugins -In this section, the [Github Resolver Plugin found here](https://github.com/dbluhm/acapy-resolver-github) will be used as an an example plugin to work with. This resolver resolves `did:github` DIDs. +In this section, the [Github Resolver Plugin found here](https://github.com/dbluhm/acapy-resolver-github) will be used as an an example plugin to work with. This resolver resolves `did:github` DIDs. The resolution algorithm is simple: for the github DID `did:github:dbluhm`, the method specific identifier `dbluhm` (a GitHub username) is used to lookup a `index.jsonld` file in the `ghdid` repository in that GitHub users profile. See [GitHub DID Method Specification](http://docs.github-did.com/did-method-spec/) for more details. To use this plugin, first install it into your project's python environment: ```shell -$ pip install git+https://github.com/dbluhm/acapy-resolver-github +pip install git+https://github.com/dbluhm/acapy-resolver-github ``` Then, invoke ACA-Py as you normally do with the addition of: @@ -163,7 +167,8 @@ $ aca-py start \ ``` Or add the following to your configuration file: -```yaml= + +```yaml plugin: - acapy_resolver_github ``` @@ -180,16 +185,19 @@ CMD ["aca-py", "start", "-it", "http", "0.0.0.0", "3000", "-ot", "http", "-e", " ``` To use the above dockerfile: + ```shell -$ docker build -t resolver-example . -$ docker run --rm -it -p 3000:3000 -p 3001:3001 resolver-example +docker build -t resolver-example . +docker run --rm -it -p 3000:3000 -p 3001:3001 resolver-example ``` ### Directory of Resolver Plugins + - [Github Resolver](https://github.com/dbluhm/acapy-resolver-github) - [Universal Resolver](https://github.com/sicpa-dlab/acapy-resolver-universal) - [DIDComm Resolver](https://github.com/sicpa-dlab/acapy-resolver-didcomm) ## References -https://www.w3.org/TR/did-core/ -https://w3c-ccg.github.io/did-resolution/ + + + From 1a8f1ec96cfc23383b180590c57a00eed089bed8 Mon Sep 17 00:00:00 2001 From: ff137 Date: Tue, 2 May 2023 20:20:37 +0200 Subject: [PATCH 769/872] :art: lint Signed-off-by: ff137 --- DIDMethods.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/DIDMethods.md b/DIDMethods.md index 179103aba8..fa21e6ee72 100644 --- a/DIDMethods.md +++ b/DIDMethods.md @@ -1,4 +1,5 @@ -# DID methods in ACA-Py +# DID Methods in ACA-Py + Decentralized Identifiers, or DIDs, are URIs that point to documents that describe cryptographic primitives and protocols used in decentralized identity management. DIDs include methods that describe where and how documents can be retrieved. DID methods support specific types of keys and may or may not require the holder to specify the DID itself. @@ -8,11 +9,12 @@ ACA-Py provides a `DIDMethods` registry holding all the DID methods supported fo > :warning: Askar and InMemory are the only wallets supporting this registry. ## Registering a DID method + By default, ACA-Py supports `did:key` and `did:sov`. -Plugins can register DID additional methods to make them available to holders. +Plugins can register DID additional methods to make them available to holders. Here's a snippet adding support for `did:web` to the registry from a plugin `setup` method. -```python= +```python WEB = DIDMethod( name="web", key_types=[ED25519, BLS12381G2], @@ -30,7 +32,7 @@ async def setup(context: InjectionContext): `POST /wallet/did/create` can be provided with parameters for any registered DID method. Here's a follow-up to the `did:web` method example: -```json= +```json { "method": "web", "options": { From 09d8101ef1bca14088e4c65202f8c930cef33dae Mon Sep 17 00:00:00 2001 From: ff137 Date: Tue, 2 May 2023 18:56:36 +0200 Subject: [PATCH 770/872] fix broken link Signed-off-by: ff137 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f1f1f18157..81b2ab6360 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ ## Overview -Hyperledger Aries Cloud Agent Python (ACA-Py) is a foundation for building Verifiable Credential (VC) ecosystems. It operates in the second and third layers of the [Trust Over IP framework (PDF)](https://trustoverip.org/wp-content/uploads/sites/98/2020/05/toip_050520_primer.pdf) using [DIDComm messaging](https://github.com/hyperledger/aries-rfcs/tree/main/concepts/0005-didcomm) and [Hyperledger Aries](https://www.hyperledger.org/use/aries) protocols. The "cloud" in the name means that ACA-Py runs on servers (cloud, enterprise, IoT devices, and so forth), and is not designed to run on mobile devices. +Hyperledger Aries Cloud Agent Python (ACA-Py) is a foundation for building Verifiable Credential (VC) ecosystems. It operates in the second and third layers of the [Trust Over IP framework (PDF)](https://trustoverip.org/wp-content/uploads/2020/05/toip_050520_primer.pdf) using [DIDComm messaging](https://github.com/hyperledger/aries-rfcs/tree/main/concepts/0005-didcomm) and [Hyperledger Aries](https://www.hyperledger.org/use/aries) protocols. The "cloud" in the name means that ACA-Py runs on servers (cloud, enterprise, IoT devices, and so forth), and is not designed to run on mobile devices. ACA-Py is built on the Aries concepts and features that make up [Aries Interop Profile (AIP) 1.0](https://github.com/hyperledger/aries-rfcs/tree/main/concepts/0302-aries-interop-profile#aries-interop-profile-version-10), and most of the features in [AIP 2.0](https://github.com/hyperledger/aries-rfcs/tree/main/concepts/0302-aries-interop-profile#aries-interop-profile-version-20). [ACA-Py’s supported Aries protocols](https://github.com/hyperledger/aries-cloudagent-python/blob/main/SupportedRFCs.md) include, most importantly, protocols for issuing, verifying, and holding verifiable credentials using both [Hyperledger Indy AnonCreds](https://hyperledger-indy.readthedocs.io/projects/sdk/en/latest/docs/design/002-anoncreds/README.html) verifiable credential format, and the [W3C Standard Verifiable Credential](https://www.w3.org/TR/vc-data-model/) format using JSON-LD with LD-Signatures and BBS+ Signatures. From 8ba34bd6f953f6cfe6dd52dbc8dc4d39a5738734 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emiliano=20Su=C3=B1=C3=A9?= Date: Tue, 2 May 2023 10:31:37 -0700 Subject: [PATCH 771/872] Disable webhook trigger on initial response to multi-use connection invitation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Emiliano Suñé --- aries_cloudagent/protocols/connections/v1_0/manager.py | 1 + 1 file changed, 1 insertion(+) diff --git a/aries_cloudagent/protocols/connections/v1_0/manager.py b/aries_cloudagent/protocols/connections/v1_0/manager.py index 8d69a62821..4c3beea328 100644 --- a/aries_cloudagent/protocols/connections/v1_0/manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/manager.py @@ -490,6 +490,7 @@ async def receive_request( reason=( "Received connection request from multi-use invitation DID" ), + event=False ) # Transfer metadata from multi-use to new connection From a6fc723e57dc86ab362bd76c264e018cade13eca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emiliano=20Su=C3=B1=C3=A9?= Date: Tue, 2 May 2023 11:26:44 -0700 Subject: [PATCH 772/872] Fix formatting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Emiliano Suñé --- aries_cloudagent/protocols/connections/v1_0/manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aries_cloudagent/protocols/connections/v1_0/manager.py b/aries_cloudagent/protocols/connections/v1_0/manager.py index 4c3beea328..6e87aeb96f 100644 --- a/aries_cloudagent/protocols/connections/v1_0/manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/manager.py @@ -490,7 +490,7 @@ async def receive_request( reason=( "Received connection request from multi-use invitation DID" ), - event=False + event=False, ) # Transfer metadata from multi-use to new connection From b8e33b92805d2da46a0b597df42c39198ccf5f47 Mon Sep 17 00:00:00 2001 From: Darko Kulic Date: Wed, 10 May 2023 14:58:45 +0200 Subject: [PATCH 773/872] Connection target should not be limited only to indy dids Signed-off-by: Darko Kulic --- .../connections/models/connection_target.py | 4 ++-- .../models/tests/test_connection_target.py | 16 +++++++++++----- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/aries_cloudagent/connections/models/connection_target.py b/aries_cloudagent/connections/models/connection_target.py index ade3f47392..a62b939af4 100644 --- a/aries_cloudagent/connections/models/connection_target.py +++ b/aries_cloudagent/connections/models/connection_target.py @@ -5,7 +5,7 @@ from marshmallow import EXCLUDE, fields from ...messaging.models.base import BaseModel, BaseModelSchema -from ...messaging.valid import INDY_DID, INDY_RAW_PUBLIC_KEY +from ...messaging.valid import INDY_RAW_PUBLIC_KEY, GENERIC_DID class ConnectionTarget(BaseModel): @@ -53,7 +53,7 @@ class Meta: model_class = ConnectionTarget unknown = EXCLUDE - did = fields.Str(required=False, description="", **INDY_DID) + did = fields.Str(required=False, description="", **GENERIC_DID) endpoint = fields.Str( required=False, description="Connection endpoint", diff --git a/aries_cloudagent/connections/models/tests/test_connection_target.py b/aries_cloudagent/connections/models/tests/test_connection_target.py index 093313924f..c60635e736 100644 --- a/aries_cloudagent/connections/models/tests/test_connection_target.py +++ b/aries_cloudagent/connections/models/tests/test_connection_target.py @@ -1,16 +1,22 @@ -from asynctest import TestCase as AsyncTestCase +import pytest from ..connection_target import ConnectionTarget -TEST_DID = "55GkHamhTU1ZbTbV2ab9DE" +TEST_DID_UNQUALIFIED = "55GkHamhTU1ZbTbV2ab9DE" +TEST_DID_SOV = "did:sov:55GkHamhTU1ZbTbV2ab9DE" +TEST_DID_PEER = "did:peer:WgWxqztrNooG92RXvxSTWv" +TEST_DID_WEB = "did:web:example" TEST_VERKEY = "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx" TEST_ENDPOINT = "http://localhost" -class TestConnectionTarget(AsyncTestCase): - def test_deser(self): +class TestConnectionTarget: + @pytest.mark.parametrize( + "did", [TEST_DID_UNQUALIFIED, TEST_DID_SOV, TEST_DID_PEER, TEST_DID_WEB] + ) + def test_deser(self, did): target = ConnectionTarget( - did=TEST_DID, + did=did, endpoint=TEST_ENDPOINT, label="a label", recipient_keys=[TEST_VERKEY], From 8bce02b768b5ccb0d47482ee6ddbc5e301377d8c Mon Sep 17 00:00:00 2001 From: Jason Sherman Date: Thu, 11 May 2023 12:36:23 -0700 Subject: [PATCH 774/872] stand up multiple agents (single and multi) for local development and testing Signed-off-by: Jason Sherman --- .gitignore | 6 +- demo/playground/.env.sample | 147 ++++++++++ demo/playground/Dockerfile.acapy | 20 ++ demo/playground/README.md | 124 ++++++++ .../configs/multitenant-auto-accept.yml | 10 + .../configs/singletenant-auto-accept.yml | 5 + demo/playground/docker-compose.yml | 265 ++++++++++++++++++ demo/playground/max_conns.sql | 1 + demo/playground/ngrok-acme-multi.yml | 8 + demo/playground/ngrok-faber-alice.yml | 8 + demo/playground/start.sh | 40 +++ 11 files changed, 633 insertions(+), 1 deletion(-) create mode 100644 demo/playground/.env.sample create mode 100644 demo/playground/Dockerfile.acapy create mode 100644 demo/playground/README.md create mode 100644 demo/playground/configs/multitenant-auto-accept.yml create mode 100644 demo/playground/configs/singletenant-auto-accept.yml create mode 100644 demo/playground/docker-compose.yml create mode 100644 demo/playground/max_conns.sql create mode 100644 demo/playground/ngrok-acme-multi.yml create mode 100644 demo/playground/ngrok-faber-alice.yml create mode 100644 demo/playground/start.sh diff --git a/.gitignore b/.gitignore index 044eff6da1..2c4db55712 100644 --- a/.gitignore +++ b/.gitignore @@ -188,4 +188,8 @@ _build/ **/*.iml # Open API build -open-api/.build \ No newline at end of file +open-api/.build + +# poetry +poetry.lock +pyproject.toml \ No newline at end of file diff --git a/demo/playground/.env.sample b/demo/playground/.env.sample new file mode 100644 index 0000000000..e8783006a9 --- /dev/null +++ b/demo/playground/.env.sample @@ -0,0 +1,147 @@ +NGROK_AUTHTOKEN= + +ACAPY_GENESIS_URL=http://test.bcovrin.vonx.io/genesis + +# +# database +# +POSTGRESQL_HOST=wallet-db +POSTGRESQL_PORT=5432 +POSTGRESQL_USER=postgres +POSTGRESQL_PASSWORD=development +POSTGRESQL_ADMIN_USER=postgres +POSTGRESQL_ADMIN_PASSWORD=development + +# +# wallet storage configuration +# +ACAPY_WALLET_STORAGE_CONFIG={"url":"${POSTGRESQL_HOST}:${POSTGRESQL_PORT}","wallet_scheme":"DatabasePerWallet"} +ACAPY_WALLET_STORAGE_CREDS={"account":"${POSTGRESQL_USER}","password":"${POSTGRESQL_PASSWORD}","admin_account":"${POSTGRESQL_ADMIN_USER}","admin_password":"${POSTGRESQL_ADMIN_PASSWORD}"} + +# +# logging +# +ACAPY_LOG_LEVEL=INFO +RUST_LOG=ERROR + +# +# tracing - uncomment in each service if needed +# +ACAPY_TRACE=0 +ACAPY_TRACE_TARGET=http://logstash:9700/ +ACAPY_TRACE_TAG=acapy.events +ACAPY_TRACE_LABEL=agent.trace + +# +# NGROK Tunnels/Hosts : ensure services and tunnel config match if you change service ports +# tunnels: +# : +# proto: http +# addr: : +# +NGROK_FABER_ALICE_HOST=ngrok-faber-alice +NGROK_ACME_MULTI_HOST=ngrok-acme-multi + +# +# Faber : faber-agent 9001/9011 +# +FABER_TUNNEL_HOST=${NGROK_FABER_ALICE_HOST} +FABER_TUNNEL_NAME=faber +FABER_HOST=faber-agent +FABER_AGENT_LABEL=faber +FABER_AGENT_HTTP_IN_PORT=9001 +FABER_AGENT_HTTP_ADMIN_PORT=9011 +FABER_AGENT_ARG_FILE=./configs/singletenant-auto-accept.yml +FABER_ACAPY_ENDPOINT=http://${FABER_HOST}:${FABER_AGENT_HTTP_IN_PORT} +FABER_ACAPY_WALLET_STORAGE_CONFIG=${ACAPY_WALLET_STORAGE_CONFIG} +FABER_ACAPY_WALLET_STORAGE_CREDS=${ACAPY_WALLET_STORAGE_CREDS} +FABER_ACAPY_WALLET_NAME=faber_wallet +FABER_ACAPY_WALLET_KEY=changeme +FABER_ACAPY_GENESIS_URL=${ACAPY_GENESIS_URL} +FABER_ACAPY_LOG_LEVEL=${ACAPY_LOG_LEVEL} +FABER_RUST_LOG=${RUST_LOG} +FABER_POSTGRESQL_HOST=${POSTGRESQL_HOST} +FABER_POSTGRESQL_PORT=${POSTGRESQL_PORT} +# tracing... +FABER_ACAPY_TRACE=${ACAPY_TRACE} +FABER_ACAPY_TRACE_TARGET=${ACAPY_TRACE_TARGET} +FABER_ACAPY_TRACE_TAG=${ACAPY_TRACE_TAG} +FABER_ACAPY_TRACE_LABEL=faber.agent.trace + +# +# Alice : alice-agent - 9002/9012 +# +ALICE_TUNNEL_HOST=${NGROK_FABER_ALICE_HOST} +ALICE_TUNNEL_NAME=alice +ALICE_HOST=alice-agent +ALICE_AGENT_LABEL=alice +ALICE_AGENT_HTTP_IN_PORT=9002 +ALICE_AGENT_HTTP_ADMIN_PORT=9012 +ALICE_AGENT_ARG_FILE=./configs/singletenant-auto-accept.yml +ALICE_ACAPY_ENDPOINT=http://${ALICE_HOST}:${ALICE_AGENT_HTTP_IN_PORT} +ALICE_ACAPY_WALLET_STORAGE_CONFIG=${ACAPY_WALLET_STORAGE_CONFIG} +ALICE_ACAPY_WALLET_STORAGE_CREDS=${ACAPY_WALLET_STORAGE_CREDS} +ALICE_ACAPY_WALLET_NAME=alice_wallet +ALICE_ACAPY_WALLET_KEY=changeme +ALICE_ACAPY_GENESIS_URL=${ACAPY_GENESIS_URL} +ALICE_ACAPY_LOG_LEVEL=${ACAPY_LOG_LEVEL} +ALICE_RUST_LOG=${RUST_LOG} +ALICE_POSTGRESQL_HOST=${POSTGRESQL_HOST} +ALICE_POSTGRESQL_PORT=${POSTGRESQL_PORT} +# tracing... +ALICE_ACAPY_TRACE=${ACAPY_TRACE} +ALICE_ACAPY_TRACE_TARGET=${ACAPY_TRACE_TARGET} +ALICE_ACAPY_TRACE_TAG=${ACAPY_TRACE_TAG} +ALICE_ACAPY_TRACE_LABEL=alice.agent.trace + +# +# Acme : acme-agent - 9003/9013 +# +ACME_TUNNEL_HOST=${NGROK_ACME_MULTI_HOST} +ACME_TUNNEL_NAME=acme +ACME_HOST=acme-agent +ACME_AGENT_LABEL=acme +ACME_AGENT_HTTP_IN_PORT=9003 +ACME_AGENT_HTTP_ADMIN_PORT=9013 +ACME_AGENT_ARG_FILE=./configs/singletenant-auto-accept.yml +ACME_ACAPY_ENDPOINT=http://${ACME_HOST}:${ACME_AGENT_HTTP_IN_PORT} +ACME_ACAPY_WALLET_STORAGE_CONFIG=${ACAPY_WALLET_STORAGE_CONFIG} +ACME_ACAPY_WALLET_STORAGE_CREDS=${ACAPY_WALLET_STORAGE_CREDS} +ACME_ACAPY_WALLET_NAME=acme_wallet +ACME_ACAPY_WALLET_KEY=changeme +ACME_ACAPY_GENESIS_URL=${ACAPY_GENESIS_URL} +ACME_ACAPY_LOG_LEVEL=${ACAPY_LOG_LEVEL} +ACME_RUST_LOG=${RUST_LOG} +ACME_POSTGRESQL_HOST=${POSTGRESQL_HOST} +ACME_POSTGRESQL_PORT=${POSTGRESQL_PORT} +# tracing... +ACME_ACAPY_TRACE=${ACAPY_TRACE} +ACME_ACAPY_TRACE_TARGET=${ACAPY_TRACE_TARGET} +ACME_ACAPY_TRACE_TAG=${ACAPY_TRACE_TAG} +ACME_ACAPY_TRACE_LABEL=acme.agent.trace + +# +# Multi : multi-agent - 9004/9014 +# +MULTI_TUNNEL_HOST=${NGROK_ACME_MULTI_HOST} +MULTI_TUNNEL_NAME=multi +MULTI_HOST=multi-agent +MULTI_AGENT_LABEL=multi +MULTI_AGENT_HTTP_IN_PORT=9004 +MULTI_AGENT_HTTP_ADMIN_PORT=9014 +MULTI_AGENT_ARG_FILE=./configs/multitenant-auto-accept.yml +MULTI_ACAPY_ENDPOINT=http://${MULTI_HOST}:${MULTI_AGENT_HTTP_IN_PORT} +MULTI_ACAPY_WALLET_STORAGE_CONFIG=${ACAPY_WALLET_STORAGE_CONFIG} +MULTI_ACAPY_WALLET_STORAGE_CREDS=${ACAPY_WALLET_STORAGE_CREDS} +MULTI_ACAPY_WALLET_NAME=multi_wallet +MULTI_ACAPY_WALLET_KEY=changeme +MULTI_ACAPY_GENESIS_URL=${ACAPY_GENESIS_URL} +MULTI_ACAPY_LOG_LEVEL=${ACAPY_LOG_LEVEL} +MULTI_RUST_LOG=${RUST_LOG} +MULTI_POSTGRESQL_HOST=${POSTGRESQL_HOST} +MULTI_POSTGRESQL_PORT=${POSTGRESQL_PORT} +# tracing... +MULTI_ACAPY_TRACE=${ACAPY_TRACE} +MULTI_ACAPY_TRACE_TARGET=${ACAPY_TRACE_TARGET} +MULTI_ACAPY_TRACE_TAG=${ACAPY_TRACE_TAG} +MULTI_ACAPY_TRACE_LABEL=multi.agent.trace diff --git a/demo/playground/Dockerfile.acapy b/demo/playground/Dockerfile.acapy new file mode 100644 index 0000000000..c9d5b50f5d --- /dev/null +++ b/demo/playground/Dockerfile.acapy @@ -0,0 +1,20 @@ +FROM ghcr.io/hyperledger/aries-cloudagent-python:py3.9-indy-1.16.0-0.8.1 + +USER root + +RUN mkdir -p /acapy-agent +WORKDIR /acapy-agent + +ADD https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64 /usr/bin/jq +RUN chmod +x /usr/bin/jq + +USER $user + +# Copy the necessary files +COPY ./start.sh start.sh +COPY ./configs configs + +RUN chmod +x start.sh && \ + aca-py --version > ./acapy-version.txt + +ENTRYPOINT ["bash", "./start.sh"] diff --git a/demo/playground/README.md b/demo/playground/README.md new file mode 100644 index 0000000000..6c0e458e77 --- /dev/null +++ b/demo/playground/README.md @@ -0,0 +1,124 @@ +# Aca-Py Playground + +This directory contains scripts to run several Aca-Py agents in various configurations for demonstration, development and testing scenarios. The agents are using Postgres (15) database storage (`askar`, `DatabasePerWallet`) and are running without security (`--admin-insecure-mode`). + +The inspiration for this playground was testing mediation and the differences between single-tenant and multi-tenant modes. These scripts allowed the developer to stand up 3 single-tenant agents and 1 multi-tenant agent and run various scenarios (in other scripts - not included). Running in `--admin-insecure-mode` simplifies creating tenants in multi-tenant mode and eliminates the need for adding headers for calls in single-tenant mode. + +- faber-agent +- alice-agent +- acme-agent +- multi-agent (the multi-tenant agent) + +By default, all the agents share the same Postgres Database Service (version 15) and all use [Ngrok](https://ngrok.com) for publicly accessible URLs. + + +### Dependencies +Docker Compose version v2.17.2 + +## Agent Configuration + +There are two extremely simple configurations provided: + +- [`singletenant-auto-accept.yml`](./configs/singletenant-auto-accept.yml) +- [`multitenant-auto-accept.yml`](./configs/multitenant-auto-accept.yml) + +These configuration files are provided to the Aca-Py start command via the `AGENT_ARG_FILE` environment variable. See [`.env`](./.env.sample) and [`start.sh`](./start.sh). + +Additional (and more complicated) configurations coming soon... maybe. + +### Dockerfile and start.sh + +[`Dockerfile.acapy`](./Dockerfile.acapy) assembles the image to run. Currently based on [Aries Cloudagent Python 0.8.1](ghcr.io/hyperledger/aries-cloudagent-python:py3.9-indy-1.16.0-0.8.1), we need [jq](https://stedolan.github.io/jq/) to setup (or not) the ngrok tunnel and execute the Aca-py start command - see [`start.sh`](./start.sh). You may note that the start command is very sparse, additional configuration is done via environment variables in the [docker compose file](./docker-compose.yml). + +### ngrok +Note that ngrok allows 2 tunnels per instance with an unpaid account. We have broken up the 4 default services into 2 ngrok services and tunnel configurations. If you need to alter port numbers for your agent services, you will have to update the ngrok tunnel files. + +- [ngrok-faber-alice](./ngrok-faber-alice.yml) +- [ngrok-acme-multi](./ngrok-acme-multi.yml) + +If you have a paid account, you can set the `NGROK_AUTHTOKEN` environment variable. See below. + +### .env +Additional configuration (ie. port numbers for the services, `NGROK_AUTHTOKEN`, ...) are done in the [`.env`](./.env.sample) file. Change as needed and ensure ngrok configuration matches. + +```shell +cp .env.sample .env +``` + + +## Running the Playground + +To run the agents in this repo, open a command shell in this directory and run: + +- to build the containers: + +```bash +docker compose build +``` + +- to run the agents: + +```bash +docker compose up +``` + +- to shut down: + +```bash +docker compose stop +docker compose rm -f +``` + +This will leave the agents wallet data, so if you restart the agent it will maintain any created data. + +- to remove the wallet data: + +```bash +docker compose down -v --remove-orphans +``` + +- individual services can be started by specifying the service name(s): + +```bash +docker compose up multi-agent +docker compose up faber-agent alice-agent +``` + +## Run without NGrok +[Ngrok](https://ngrok.com) provides a tunneling service and a way to provide a public IP to your locally running instance of Aca-Py. There are restrictions with Ngrok most notably regarding inbound connections. + +``` +Too many connections! The tunnel session SESSION has violated the rate-limit policy of THRESHOLD connections per minute by initiating COUNT connections in the last SECONDS seconds. Please decrease your inbound connection volume or upgrade to a paid plan for additional capacity. + +ngrok limits the number of inbound connections to your tunnels. Limits are imposed on connections, not requests. If your HTTP clients use persistent connections aka HTTP keep-alive (most modern ones do), you'll likely never hit this limit. ngrok will return a 429 response to HTTP connections that exceed the rate limit. Connections to TCP and TLS tunnels violating the rate limit will be closed without a response. +``` + +If you do not require external access to your instance, consider turning NGrok off. NGrok tunnelling can be disabled by changing an environment variable for each service. Set `TUNNEL_NAME` to null/empty, and no tunnel will be created. See [`.env`](.env.sample), [`docker-compose.yml`](./docker-compose.yml) and [`start.sh`](./start.sh), + +#### .env +``` +FABER_TUNNEL_NAME= +``` + +#### docker-compose.yml +``` + environment: + - TUNNEL_HOST=${FABER_TUNNEL_HOST} +``` + +#### start.sh +``` +# if $TUNNEL_NAME is not empty, grab the service's ngrok route and set our ACAPY_ENDPOINT +if [[ ! -z "$TUNNEL_NAME" ]]; then . . . +``` + +Set service value to empty in `.env` that will set the `TUNNEL_NAME` environment variable to empty which will circumvent the use of the tunnel for the service (`ACAPY_ENDPOINT`). + + +## ELK Stack / Tracing logging +Please see [ELK Stack Readme](../elk-stack/README.md). + +You may notice a series of environment variables for each agent service in [.env](./.env.sample) and commented-out network and agent configuration in the [docker compose file](./docker-compose.yml). Check the environment variables and uncomment as needed if wanting to send trace events to ELK. + + + diff --git a/demo/playground/configs/multitenant-auto-accept.yml b/demo/playground/configs/multitenant-auto-accept.yml new file mode 100644 index 0000000000..ee8a3b61d2 --- /dev/null +++ b/demo/playground/configs/multitenant-auto-accept.yml @@ -0,0 +1,10 @@ +# Connections +debug-connections: true +auto-accept-invites: true +auto-accept-requests: true +auto-ping-connection: true + +# multitenant +multitenant: true +multitenant-admin: true +jwt-secret: changeme \ No newline at end of file diff --git a/demo/playground/configs/singletenant-auto-accept.yml b/demo/playground/configs/singletenant-auto-accept.yml new file mode 100644 index 0000000000..a9434151a4 --- /dev/null +++ b/demo/playground/configs/singletenant-auto-accept.yml @@ -0,0 +1,5 @@ +# Connections +debug-connections: true +auto-accept-invites: true +auto-accept-requests: true +auto-ping-connection: true \ No newline at end of file diff --git a/demo/playground/docker-compose.yml b/demo/playground/docker-compose.yml new file mode 100644 index 0000000000..ef03b667cd --- /dev/null +++ b/demo/playground/docker-compose.yml @@ -0,0 +1,265 @@ +# Sample docker-compose to start a local aca-py multitenancy agent +# To start aca-py and the postgres database, just run `docker compose up` +# To shut down the services run `docker compose rm` - this will retain the postgres database, so you can change aca-py startup parameters +# and restart the docker containers without losing your wallet data +# If you want to delete your wallet data just run `docker volume ls -q | xargs docker volume rm` +# +# If you want to enable tracing, see elk-stack. Ensure elk-stack is running, uncomment the ACAPY_TRACE environement variables and run `docker compose up` +version: "3" + + +networks: + app-network: + name: ${APP_NETWORK_NAME:-playgroundnet} + driver: bridge + # elk-network: + # name: ${ELK_NETWORK_NAME:-elknet} + # driver: bridge + +services: + ngrok-faber-alice: + image: ngrok/ngrok:latest + restart: unless-stopped + hostname: ${NGROK_FABER_ALICE_HOST} + environment: + - NGROK_AUTHTOKEN=${NGROK_AUTHTOKEN:-} + command: + - "start" + - "--all" + - "--config" + - "/etc/ngrok.yml" + volumes: + - ./ngrok-faber-alice.yml:/etc/ngrok.yml + networks: + - app-network + # ports: + # - 4040:4040 + healthcheck: + test: /bin/bash -c " Date: Fri, 12 May 2023 10:35:07 -0700 Subject: [PATCH 775/872] add the documented comment to start.sh Signed-off-by: Jason Sherman --- demo/playground/start.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/demo/playground/start.sh b/demo/playground/start.sh index 653ab569e1..ce41ecb8af 100644 --- a/demo/playground/start.sh +++ b/demo/playground/start.sh @@ -2,6 +2,7 @@ # set -euxo pipefail +# if $TUNNEL_NAME is not empty, grab the service's ngrok route and set our ACAPY_ENDPOINT if [[ ! -z "$TUNNEL_NAME" ]]; then echo "using ngrok tunnel for [$TUNNEL_NAME]" From 6ff5f2d27cf632329fb939248229e0ebb258109f Mon Sep 17 00:00:00 2001 From: Jason Sherman Date: Fri, 12 May 2023 15:19:39 -0700 Subject: [PATCH 776/872] ACA-Py consistency... lint on README... add some example scripts using the agents. Signed-off-by: Jason Sherman --- .gitignore | 1 + demo/playground/README.md | 67 +++-- demo/playground/docker-compose.yml | 8 +- .../scripts/mediator_ping_agents.py | 279 ++++++++++++++++++ demo/playground/scripts/ping_agents.py | 243 +++++++++++++++ demo/playground/scripts/requirements.txt | 24 ++ 6 files changed, 596 insertions(+), 26 deletions(-) create mode 100644 demo/playground/scripts/mediator_ping_agents.py create mode 100644 demo/playground/scripts/ping_agents.py create mode 100644 demo/playground/scripts/requirements.txt diff --git a/.gitignore b/.gitignore index 2c4db55712..c3e7baa1ad 100644 --- a/.gitignore +++ b/.gitignore @@ -154,6 +154,7 @@ Temporary Items ### .idea/* +**/.idea/* ### ### Windows diff --git a/demo/playground/README.md b/demo/playground/README.md index 6c0e458e77..1d3ab700fc 100644 --- a/demo/playground/README.md +++ b/demo/playground/README.md @@ -1,8 +1,8 @@ -# Aca-Py Playground +# ACA-Py Playground -This directory contains scripts to run several Aca-Py agents in various configurations for demonstration, development and testing scenarios. The agents are using Postgres (15) database storage (`askar`, `DatabasePerWallet`) and are running without security (`--admin-insecure-mode`). +This directory contains scripts to run several ACA-Py agents in various configurations for demonstration, development and testing scenarios. The agents are using Postgres (15) database storage (`askar`, `DatabasePerWallet`) and are running without security (`--admin-insecure-mode`). -The inspiration for this playground was testing mediation and the differences between single-tenant and multi-tenant modes. These scripts allowed the developer to stand up 3 single-tenant agents and 1 multi-tenant agent and run various scenarios (in other scripts - not included). Running in `--admin-insecure-mode` simplifies creating tenants in multi-tenant mode and eliminates the need for adding headers for calls in single-tenant mode. +The inspiration for this playground was testing mediation and the differences between single-tenant and multi-tenant modes. These scripts allowed the developer to stand up 3 single-tenant agents and 1 multi-tenant agent and run various scenarios (see [scripts](./scripts) - for some basic examples). Running in `--admin-insecure-mode` simplifies creating tenants in multi-tenant mode and eliminates the need for adding headers for calls in single-tenant mode. - faber-agent - alice-agent @@ -11,26 +11,25 @@ The inspiration for this playground was testing mediation and the differences be By default, all the agents share the same Postgres Database Service (version 15) and all use [Ngrok](https://ngrok.com) for publicly accessible URLs. +## Dependencies -### Dependencies Docker Compose version v2.17.2 ## Agent Configuration -There are two extremely simple configurations provided: +There are two simple configurations provided: - [`singletenant-auto-accept.yml`](./configs/singletenant-auto-accept.yml) - [`multitenant-auto-accept.yml`](./configs/multitenant-auto-accept.yml) -These configuration files are provided to the Aca-Py start command via the `AGENT_ARG_FILE` environment variable. See [`.env`](./.env.sample) and [`start.sh`](./start.sh). - -Additional (and more complicated) configurations coming soon... maybe. +These configuration files are provided to the ACA-Py start command via the `AGENT_ARG_FILE` environment variable. See [`.env`](./.env.sample) and [`start.sh`](./start.sh). ### Dockerfile and start.sh [`Dockerfile.acapy`](./Dockerfile.acapy) assembles the image to run. Currently based on [Aries Cloudagent Python 0.8.1](ghcr.io/hyperledger/aries-cloudagent-python:py3.9-indy-1.16.0-0.8.1), we need [jq](https://stedolan.github.io/jq/) to setup (or not) the ngrok tunnel and execute the Aca-py start command - see [`start.sh`](./start.sh). You may note that the start command is very sparse, additional configuration is done via environment variables in the [docker compose file](./docker-compose.yml). ### ngrok + Note that ngrok allows 2 tunnels per instance with an unpaid account. We have broken up the 4 default services into 2 ngrok services and tunnel configurations. If you need to alter port numbers for your agent services, you will have to update the ngrok tunnel files. - [ngrok-faber-alice](./ngrok-faber-alice.yml) @@ -39,13 +38,13 @@ Note that ngrok allows 2 tunnels per instance with an unpaid account. We have br If you have a paid account, you can set the `NGROK_AUTHTOKEN` environment variable. See below. ### .env + Additional configuration (ie. port numbers for the services, `NGROK_AUTHTOKEN`, ...) are done in the [`.env`](./.env.sample) file. Change as needed and ensure ngrok configuration matches. ```shell cp .env.sample .env ``` - ## Running the Playground To run the agents in this repo, open a command shell in this directory and run: @@ -84,41 +83,65 @@ docker compose up multi-agent docker compose up faber-agent alice-agent ``` -## Run without NGrok -[Ngrok](https://ngrok.com) provides a tunneling service and a way to provide a public IP to your locally running instance of Aca-Py. There are restrictions with Ngrok most notably regarding inbound connections. +You can now access the agent Admin APIs via Swagger at: + +- faber: [http://localhost:9011/api/doc#](http://localhost:9011/api/doc#) +- alice: [http://localhost:9012/api/doc#](http://localhost:9012/api/doc#) +- acme: [http://localhost:9013/api/doc#](http://localhost:9013/api/doc#) +- multi: [http://localhost:9014/api/doc#](http://localhost:9014/api/doc#) + +## Scripts + +While having the Swagger Admin API is excellent, you may need to do something more complex than a single API call. You may need to see how agents with varying capabilities interact or validate that single-tenant and multi-tenant work the same. Jumping around from multiple browser tabs and cutting and pasting ids and JSON blocks can quickly grow tiresome. +A few Python (3.9) [scripts](./scripts) are provided as examples of what you may do once your agents are up and running. + +```shell +cd scripts +pip install -r requirements.txt +python ping_agents.py ``` + +The [`ping_agents`](./scripts/ping_agents.py) script is a trivial example using the ACA-Py API to create tenants in the multi-agent instance and interact between the agents. We create and receive invitations and ping each other. + +The [`mediator_ping_agents`](./scripts/mediator_ping_agents) script requires that you have a mediator service running and have the mediator's invitation URL. See [Aries Mediator Service](https://github.com/hyperledger/aries-mediator-service) for standing up a local instance and how to find the invitation URL. In this script, each agent requests mediation and we can see the mediator forwarding messages between the agents. + +## Run without NGrok + +[Ngrok](https://ngrok.com) provides a tunneling service and a way to provide a public IP to your locally running instance of ACA-Py. There are restrictions with Ngrok most notably regarding inbound connections. + +```shell Too many connections! The tunnel session SESSION has violated the rate-limit policy of THRESHOLD connections per minute by initiating COUNT connections in the last SECONDS seconds. Please decrease your inbound connection volume or upgrade to a paid plan for additional capacity. ngrok limits the number of inbound connections to your tunnels. Limits are imposed on connections, not requests. If your HTTP clients use persistent connections aka HTTP keep-alive (most modern ones do), you'll likely never hit this limit. ngrok will return a 429 response to HTTP connections that exceed the rate limit. Connections to TCP and TLS tunnels violating the rate limit will be closed without a response. ``` -If you do not require external access to your instance, consider turning NGrok off. NGrok tunnelling can be disabled by changing an environment variable for each service. Set `TUNNEL_NAME` to null/empty, and no tunnel will be created. See [`.env`](.env.sample), [`docker-compose.yml`](./docker-compose.yml) and [`start.sh`](./start.sh), +If you do not require external access to your instance, consider turning NGrok off. NGrok tunnelling can be disabled by changing an environment variable for each service. Set `TUNNEL_NAME` to null/empty, and no tunnel will be created. See [`.env`](.env.sample), [`docker-compose.yml`](./docker-compose.yml) and [`start.sh`](./start.sh), -#### .env -``` +### update .env + +```shell FABER_TUNNEL_NAME= ``` -#### docker-compose.yml -``` +### docker-compose.yml + +```shell environment: - TUNNEL_HOST=${FABER_TUNNEL_HOST} ``` -#### start.sh -``` +### start.sh + +```shell # if $TUNNEL_NAME is not empty, grab the service's ngrok route and set our ACAPY_ENDPOINT if [[ ! -z "$TUNNEL_NAME" ]]; then . . . ``` Set service value to empty in `.env` that will set the `TUNNEL_NAME` environment variable to empty which will circumvent the use of the tunnel for the service (`ACAPY_ENDPOINT`). - ## ELK Stack / Tracing logging + Please see [ELK Stack Readme](../elk-stack/README.md). You may notice a series of environment variables for each agent service in [.env](./.env.sample) and commented-out network and agent configuration in the [docker compose file](./docker-compose.yml). Check the environment variables and uncomment as needed if wanting to send trace events to ELK. - - - diff --git a/demo/playground/docker-compose.yml b/demo/playground/docker-compose.yml index ef03b667cd..d74688df62 100644 --- a/demo/playground/docker-compose.yml +++ b/demo/playground/docker-compose.yml @@ -12,9 +12,9 @@ networks: app-network: name: ${APP_NETWORK_NAME:-playgroundnet} driver: bridge - # elk-network: - # name: ${ELK_NETWORK_NAME:-elknet} - # driver: bridge + elk-network: + name: ${ELK_NETWORK_NAME:-elknet} + driver: bridge services: ngrok-faber-alice: @@ -234,7 +234,7 @@ services: - ${MULTI_AGENT_HTTP_IN_PORT}:${MULTI_AGENT_HTTP_IN_PORT} networks: - app-network - # - elk-network + - elk-network healthcheck: test: /bin/bash -c "= "3.6" +aiosignal==1.3.1; python_version >= "3.7" +async-timeout==4.0.2; python_version >= "3.6" +attrs==23.1.0; python_version >= "3.7" +certifi==2023.5.7; python_version >= "3.7" +charset-normalizer==3.1.0; python_full_version >= "3.7.0" and python_version >= "3.7" +colorama==0.4.6; python_version >= "3.7" and python_full_version < "3.0.0" and sys_platform == "win32" or sys_platform == "win32" and python_version >= "3.7" and python_full_version >= "3.7.0" +exceptiongroup==1.1.1; python_version < "3.11" and python_version >= "3.7" +frozenlist==1.3.3; python_version >= "3.7" +idna==3.4; python_version >= "3.7" +iniconfig==2.0.0; python_version >= "3.7" +multidict==6.0.4; python_version >= "3.7" +packaging==23.1; python_version >= "3.7" +pluggy==1.0.0; python_version >= "3.7" +pytest==7.3.1; python_version >= "3.7" +pyyaml==6.0; python_version >= "3.6" +random-word==1.0.11; python_version >= "3" +repoze.lru==0.7 +requests==2.30.0; python_version >= "3.7" +routes==2.5.1 +six==1.16.0; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" +tomli==2.0.1; python_version < "3.11" and python_version >= "3.7" +urllib3==2.0.2; python_version >= "3.7" +yarl==1.9.2; python_version >= "3.7" From d1eae127eecf7c0e3cb3b42c91699d5d20e7fec0 Mon Sep 17 00:00:00 2001 From: Jason Sherman Date: Fri, 12 May 2023 15:26:01 -0700 Subject: [PATCH 777/872] flake8/black Signed-off-by: Jason Sherman --- .../scripts/mediator_ping_agents.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/demo/playground/scripts/mediator_ping_agents.py b/demo/playground/scripts/mediator_ping_agents.py index b30d7230d1..4bb0e1e9c1 100644 --- a/demo/playground/scripts/mediator_ping_agents.py +++ b/demo/playground/scripts/mediator_ping_agents.py @@ -12,7 +12,7 @@ MULTI_ADMIN_URL = "http://localhost:9014" -MEDIATOR_INVITATION_URL = "https://93f7-207-6-152-178.ngrok.io?c_i=eyJAdHlwZSI6ICJodHRwczovL2RpZGNvbW0ub3JnL2Nvbm5lY3Rpb25zLzEuMC9pbnZpdGF0aW9uIiwgIkBpZCI6ICI3NWVmYzhmYy00ZmNkLTRjY2YtYWFkOC0zYTAzNjZkYTczNDYiLCAibGFiZWwiOiAiTWVkaWF0b3IiLCAicmVjaXBpZW50S2V5cyI6IFsiRlhHZnZCb2l1VFIyVzV6QzFZQzVWeERvVXR5UjFTNndpRTJLTG5ab3lpbXoiXSwgInNlcnZpY2VFbmRwb2ludCI6ICJodHRwczovLzkzZjctMjA3LTYtMTUyLTE3OC5uZ3Jvay5pbyJ9" +MEDIATOR_INVITATION_URL = "" def wait_a_bit(secs: int = 1): @@ -201,16 +201,16 @@ def create_tenant(wallet_name, wallet_key, url, headers=None): faber_mediation_connection_id, faber_mediation_id = initialize_mediation( FABER_ADMIN_URL ) - print( - f"faber \nmediation_connection_id={faber_mediation_connection_id}\nmediation_id={faber_mediation_id}\n" - ) + print("faber") + print(f" mediation_connection_id={faber_mediation_connection_id}") + print(f" mediation_id={faber_mediation_id}") alice_mediation_connection_id, alice_mediation_id = initialize_mediation( ALICE_ADMIN_URL ) - print( - f"alice \nmediation_connection_id={alice_mediation_connection_id}\nmediation_id={alice_mediation_id}\n" - ) + print("alice") + print(f" mediation_connection_id={alice_mediation_connection_id}") + print(f" mediation_id={alice_mediation_id}") print("\n... single tenants, connect ...\n") @@ -249,9 +249,9 @@ def create_tenant(wallet_name, wallet_key, url, headers=None): multi_mediation_connection_id, multi_mediation_id = initialize_mediation( MULTI_ADMIN_URL, multi_headers ) - print( - f"multi ({multi_wallet_name}) \nmediation_connection_id={multi_mediation_connection_id}\nmediation_id={multi_mediation_id}\n" - ) + print("multi") + print(f" mediation_connection_id={multi_mediation_connection_id}") + print(f" mediation_id={multi_mediation_id}") print("\n... multitenant, connect ...\n") multi_mediation_id = None From 037c04616d1d7a40bc17cb30e36cd8d2e47a836e Mon Sep 17 00:00:00 2001 From: Jason Sherman Date: Fri, 12 May 2023 15:30:51 -0700 Subject: [PATCH 778/872] check with local black - no line limit Signed-off-by: Jason Sherman --- demo/playground/scripts/mediator_ping_agents.py | 14 ++++++++++---- demo/playground/scripts/ping_agents.py | 8 +++++--- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/demo/playground/scripts/mediator_ping_agents.py b/demo/playground/scripts/mediator_ping_agents.py index 4bb0e1e9c1..32d68a2e80 100644 --- a/demo/playground/scripts/mediator_ping_agents.py +++ b/demo/playground/scripts/mediator_ping_agents.py @@ -215,9 +215,11 @@ def create_tenant(wallet_name, wallet_key, url, headers=None): print("\n... single tenants, connect ...\n") # faber create invitation for alice - faber_invitation, faber_alice_connection_id, faber_recipient_keys = create_invitation( - "faber", "alice", faber_mediation_id, FABER_ADMIN_URL - ) + ( + faber_invitation, + faber_alice_connection_id, + faber_recipient_keys, + ) = create_invitation("faber", "alice", faber_mediation_id, FABER_ADMIN_URL) alice_faber_connection_id = receive_invitation( faber_invitation, "faber", alice_mediation_id, ALICE_ADMIN_URL ) @@ -255,7 +257,11 @@ def create_tenant(wallet_name, wallet_key, url, headers=None): print("\n... multitenant, connect ...\n") multi_mediation_id = None - multi_invitation, multi_alice_connection_id, multi_recipient_keys = create_invitation( + ( + multi_invitation, + multi_alice_connection_id, + multi_recipient_keys, + ) = create_invitation( multi_wallet_name, "alice", multi_mediation_id, MULTI_ADMIN_URL, multi_headers ) diff --git a/demo/playground/scripts/ping_agents.py b/demo/playground/scripts/ping_agents.py index 04ac2af8c1..c8d24196d2 100644 --- a/demo/playground/scripts/ping_agents.py +++ b/demo/playground/scripts/ping_agents.py @@ -124,9 +124,11 @@ def create_tenant(wallet_name, wallet_key, url, headers=None): print("\n... single tenants, connect ...\n") # faber create invitation for alice - faber_invitation, faber_alice_connection_id, faber_recipient_keys = create_invitation( - "faber", "alice", None, FABER_ADMIN_URL - ) + ( + faber_invitation, + faber_alice_connection_id, + faber_recipient_keys, + ) = create_invitation("faber", "alice", None, FABER_ADMIN_URL) alice_faber_connection_id = receive_invitation( faber_invitation, "faber", None, ALICE_ADMIN_URL ) From b10b073232d9131f841b4e2f03b2dcd431f08ab8 Mon Sep 17 00:00:00 2001 From: Jason Sherman Date: Fri, 12 May 2023 11:11:09 -0700 Subject: [PATCH 779/872] Multi-tenant self-managed mediation verkey lookup Signed-off-by: Jason Sherman --- aries_cloudagent/multitenant/route_manager.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/multitenant/route_manager.py b/aries_cloudagent/multitenant/route_manager.py index d430aa0c51..954b3c98f9 100644 --- a/aries_cloudagent/multitenant/route_manager.py +++ b/aries_cloudagent/multitenant/route_manager.py @@ -81,8 +81,17 @@ async def _route_for_key( keylist_updates = await mediation_mgr.remove_key( replace_key, keylist_updates ) - - responder = self.root_profile.inject(BaseResponder) + # in order to locate the correct verkey for message packing we need + # to use the correct profile. + # if we are using default/base mediation then we need + # the root_profile to create the responder. + # if sub-wallets are configuring their own mediation, then + # we need the sub-wallet (profile) to create the responder. + responder = ( + self.root_profile.inject(BaseResponder) + if base_mediation_record + else profile.inject(BaseResponder) + ) await responder.send( keylist_updates, connection_id=mediation_record.connection_id ) From 9be03e4045f009e9a634e4d1c9c0d9458452d485 Mon Sep 17 00:00:00 2001 From: Sacha Kozma Date: Wed, 17 May 2023 17:30:30 +0200 Subject: [PATCH 780/872] refactor: put _get_verification_method in an injectable class Signed-off-by: Sacha Kozma --- aries_cloudagent/config/default_context.py | 4 ++ .../v2_0/formats/ld_proof/handler.py | 27 +++++------- .../formats/ld_proof/tests/test_handler.py | 26 ++++------- .../present_proof/dif/pres_exch_handler.py | 22 ++++------ .../dif/tests/test_pres_exch_handler.py | 13 ------ .../default_verification_key_strategy.py | 43 +++++++++++++++++++ .../test_default_verification_key_strategy.py | 27 ++++++++++++ 7 files changed, 100 insertions(+), 62 deletions(-) create mode 100644 aries_cloudagent/wallet/default_verification_key_strategy.py create mode 100644 aries_cloudagent/wallet/tests/test_default_verification_key_strategy.py diff --git a/aries_cloudagent/config/default_context.py b/aries_cloudagent/config/default_context.py index 360f103fa1..2194c1e077 100644 --- a/aries_cloudagent/config/default_context.py +++ b/aries_cloudagent/config/default_context.py @@ -17,6 +17,7 @@ from ..transport.wire_format import BaseWireFormat from ..utils.dependencies import is_indy_sdk_module_installed from ..utils.stats import Collector +from ..wallet.default_verification_key_strategy import DefaultVerificationKeyStrategy from ..wallet.did_method import DIDMethods from ..wallet.key_type import KeyTypes from .base_context import ContextBuilder @@ -53,6 +54,9 @@ async def build_context(self) -> InjectionContext: context.injector.bind_instance(DIDResolver, DIDResolver([])) context.injector.bind_instance(DIDMethods, DIDMethods()) context.injector.bind_instance(KeyTypes, KeyTypes()) + context.injector.bind_instance( + DefaultVerificationKeyStrategy, DefaultVerificationKeyStrategy() + ) await self.bind_providers(context) await self.load_plugins(context) diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py index c4fccc02d5..81a0f60ab2 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py @@ -12,7 +12,6 @@ from pyld import jsonld from pyld.jsonld import JsonLdProcessor -from ......did.did_key import DIDKey from ......messaging.decorators.attach_decorator import AttachDecorator from ......storage.vc_holder.base import VCHolder from ......storage.vc_holder.vc_record import VCRecord @@ -35,6 +34,9 @@ ) from ......vc.ld_proofs.constants import SECURITY_CONTEXT_BBS_URL from ......wallet.base import BaseWallet, DIDInfo +from ......wallet.default_verification_key_strategy import ( + DefaultVerificationKeyStrategy, +) from ......wallet.error import WalletNotFoundError from ......wallet.key_type import BLS12381G2, ED25519 @@ -270,9 +272,13 @@ async def _get_suite_for_detail( ) did_info = await self._did_info_for_did(issuer_id) - verification_method = verification_method or self._get_verification_method( - issuer_id - ) + verkey_id_strategy = self.profile.context.inject(DefaultVerificationKeyStrategy) + verification_method = verkey_id_strategy.get_verkey_id_for_did(issuer_id) + + if verification_method is None: + raise V20CredFormatError( + f"Unable to get retrieve verification method for did {issuer_id}" + ) suite = await self._get_suite( proof_type=proof_type, @@ -309,19 +315,6 @@ async def _get_suite( ), ) - def _get_verification_method(self, did: str): - """Get the verification method for a did.""" - - if did.startswith("did:key:"): - return DIDKey.from_did(did).key_id - elif did.startswith("did:sov:"): - # key-1 is what the resolver uses for key id - return did + "#key-1" - else: - raise V20CredFormatError( - f"Unable to get retrieve verification method for did {did}" - ) - def _get_proof_purpose( self, *, proof_purpose: str = None, challenge: str = None, domain: str = None ) -> ProofPurpose: diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/tests/test_handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/tests/test_handler.py index b89dad7da5..5384d0d132 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/tests/test_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/tests/test_handler.py @@ -26,6 +26,9 @@ ) from .......vc.ld_proofs.constants import SECURITY_CONTEXT_BBS_URL from .......vc.tests.document_loader import custom_document_loader +from .......wallet.default_verification_key_strategy import ( + DefaultVerificationKeyStrategy, +) from .......wallet.key_type import BLS12381G2, ED25519 from .......wallet.error import WalletNotFoundError from .......wallet.did_method import SOV @@ -124,6 +127,11 @@ async def setUp(self): # Set custom document loader self.context.injector.bind_instance(DocumentLoader, custom_document_loader) + # Set default verkey ID strategy + self.context.injector.bind_instance( + DefaultVerificationKeyStrategy, DefaultVerificationKeyStrategy() + ) + self.handler = LDProofCredFormatHandler(self.profile) self.cred_proposal = V20CredProposal( @@ -318,24 +326,6 @@ async def test_get_suite(self): assert suite.key_pair.key_type == ED25519 assert suite.key_pair.public_key_base58 == did_info.verkey - async def test_get_verification_method(self): - assert ( - self.handler._get_verification_method(TEST_DID_KEY) - == DIDKey.from_did(TEST_DID_KEY).key_id - ) - - assert ( - self.handler._get_verification_method(TEST_DID_SOV) - == TEST_DID_SOV + "#key-1" - ) - - with self.assertRaises(V20CredFormatError) as context: - self.handler._get_verification_method("did:random:not-supported") - - assert "Unable to get retrieve verification method for did" in str( - context.exception - ) - async def test_get_proof_purpose(self): purpose = self.handler._get_proof_purpose() assert type(purpose) == CredentialIssuancePurpose diff --git a/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py b/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py index 76cde3729d..239a9f7aec 100644 --- a/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py +++ b/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py @@ -24,7 +24,6 @@ from ....core.error import BaseError from ....core.profile import Profile -from ....did.did_key import DIDKey from ....storage.vc_holder.vc_record import VCRecord from ....vc.ld_proofs import ( Ed25519Signature2018, @@ -39,6 +38,7 @@ ) from ....vc.vc_ld.prove import sign_presentation, create_presentation, derive_credential from ....wallet.base import BaseWallet, DIDInfo +from ....wallet.default_verification_key_strategy import DefaultVerificationKeyStrategy from ....wallet.error import WalletError, WalletNotFoundError from ....wallet.key_type import BLS12381G2, ED25519 @@ -117,7 +117,13 @@ async def _get_issue_suite( ): """Get signature suite for signing presentation.""" did_info = await self._did_info_for_did(issuer_id) - verification_method = self._get_verification_method(issuer_id) + verkey_id_strategy = self.profile.context.inject(DefaultVerificationKeyStrategy) + verification_method = verkey_id_strategy.get_verkey_id_for_did(issuer_id) + + if verification_method is None: + raise DIFPresExchError( + f"Unable to get retrieve verification method for did {issuer_id}" + ) # Get signature class based on proof type SignatureClass = self.PROOF_TYPE_SIGNATURE_SUITE_MAPPING[self.proof_type] @@ -151,18 +157,6 @@ async def _get_derive_suite( ), ) - def _get_verification_method(self, did: str): - """Get the verification method for a did.""" - if did.startswith("did:key:"): - return DIDKey.from_did(did).key_id - elif did.startswith("did:sov:"): - # key-1 is what uniresolver uses for key id - return did + "#key-1" - else: - raise DIFPresExchError( - f"Unable to get retrieve verification method for did {did}" - ) - async def _did_info_for_did(self, did: str) -> DIDInfo: """Get the did info for specified did. diff --git a/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py b/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py index 87597359cc..6771dfc19c 100644 --- a/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py +++ b/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py @@ -1867,19 +1867,6 @@ def test_cred_schema_match_b(self, profile, setup_tuple): test_cred, "https://example.org/examples/degree.json" ) - def test_verification_method(self, profile): - dif_pres_exch_handler = DIFPresExchHandler(profile) - assert ( - dif_pres_exch_handler._get_verification_method( - "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL" - ) - == DIDKey.from_did( - "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL" - ).key_id - ) - with pytest.raises(DIFPresExchError): - dif_pres_exch_handler._get_verification_method("did:test:test") - @pytest.mark.asyncio @pytest.mark.ursa_bbs_signatures async def test_sign_pres_no_cred_subject_id(self, profile, setup_tuple): diff --git a/aries_cloudagent/wallet/default_verification_key_strategy.py b/aries_cloudagent/wallet/default_verification_key_strategy.py new file mode 100644 index 0000000000..2ca51ab402 --- /dev/null +++ b/aries_cloudagent/wallet/default_verification_key_strategy.py @@ -0,0 +1,43 @@ +"""Utilities for specifying which verification method is in use for a given DID.""" +from abc import ABC, abstractmethod +from typing import Optional + +from aries_cloudagent.did.did_key import DIDKey + + +class DefaultVerificationKeyStrategyBase(ABC): + """Base class for defining which verification method is in use.""" + + @abstractmethod + def get_verkey_id_for_did(self, did) -> Optional[str]: + """Given a DID, returns the verification key ID in use. + + Returns None if no strategy is specified for this DID. + + :params str did: the did + :returns Optional[str]: the current verkey ID + """ + pass + + +class DefaultVerificationKeyStrategy(DefaultVerificationKeyStrategyBase): + """A basic implementation for verkey strategy. + + Supports did:key: and did:sov only. + """ + + def get_verkey_id_for_did(self, did) -> Optional[str]: + """Given a did:key or did:sov, returns the verification key ID in use. + + Returns None if no strategy is specified for this DID. + + :params str did: the did + :returns Optional[str]: the current verkey ID + """ + if did.startswith("did:key:"): + return DIDKey.from_did(did).key_id + elif did.startswith("did:sov:"): + # key-1 is what uniresolver uses for key id + return did + "#key-1" + + return None diff --git a/aries_cloudagent/wallet/tests/test_default_verification_key_strategy.py b/aries_cloudagent/wallet/tests/test_default_verification_key_strategy.py new file mode 100644 index 0000000000..d68ca45653 --- /dev/null +++ b/aries_cloudagent/wallet/tests/test_default_verification_key_strategy.py @@ -0,0 +1,27 @@ +from unittest import TestCase + +from aries_cloudagent.did.did_key import DIDKey + +from aries_cloudagent.wallet.default_verification_key_strategy import ( + DefaultVerificationKeyStrategy, +) + +TEST_DID_SOV = "did:sov:LjgpST2rjsoxYegQDRm7EL" +TEST_DID_KEY = "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL" + + +class TestDefaultVerificationKeyStrategy(TestCase): + def test_with_did_sov(self): + strategy = DefaultVerificationKeyStrategy() + assert strategy.get_verkey_id_for_did(TEST_DID_SOV) == TEST_DID_SOV + "#key-1" + + def test_with_did_key(self): + strategy = DefaultVerificationKeyStrategy() + assert ( + strategy.get_verkey_id_for_did(TEST_DID_KEY) + == DIDKey.from_did(TEST_DID_KEY).key_id + ) + + def test_unsupported_did_method(self): + strategy = DefaultVerificationKeyStrategy() + assert strategy.get_verkey_id_for_did("did:test:test") is None From ba355668f1d17df96d371d3acc1539eeffa84e87 Mon Sep 17 00:00:00 2001 From: Sacha Kozma Date: Mon, 22 May 2023 11:49:15 +0200 Subject: [PATCH 781/872] fix: use given verification_method when given + renaming Signed-off-by: Sacha Kozma --- .../issue_credential/v2_0/formats/ld_proof/handler.py | 5 ++++- .../protocols/present_proof/dif/pres_exch_handler.py | 4 +++- .../wallet/default_verification_key_strategy.py | 4 ++-- .../tests/test_default_verification_key_strategy.py | 9 ++++++--- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py index 81a0f60ab2..0ea8aabf93 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py @@ -273,7 +273,10 @@ async def _get_suite_for_detail( did_info = await self._did_info_for_did(issuer_id) verkey_id_strategy = self.profile.context.inject(DefaultVerificationKeyStrategy) - verification_method = verkey_id_strategy.get_verkey_id_for_did(issuer_id) + verification_method = ( + verification_method + or verkey_id_strategy.get_verification_method_id_for_did(issuer_id) + ) if verification_method is None: raise V20CredFormatError( diff --git a/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py b/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py index 239a9f7aec..191b9676bb 100644 --- a/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py +++ b/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py @@ -118,7 +118,9 @@ async def _get_issue_suite( """Get signature suite for signing presentation.""" did_info = await self._did_info_for_did(issuer_id) verkey_id_strategy = self.profile.context.inject(DefaultVerificationKeyStrategy) - verification_method = verkey_id_strategy.get_verkey_id_for_did(issuer_id) + verification_method = verkey_id_strategy.get_verification_method_id_for_did( + issuer_id + ) if verification_method is None: raise DIFPresExchError( diff --git a/aries_cloudagent/wallet/default_verification_key_strategy.py b/aries_cloudagent/wallet/default_verification_key_strategy.py index 2ca51ab402..cd3e65c59a 100644 --- a/aries_cloudagent/wallet/default_verification_key_strategy.py +++ b/aries_cloudagent/wallet/default_verification_key_strategy.py @@ -9,7 +9,7 @@ class DefaultVerificationKeyStrategyBase(ABC): """Base class for defining which verification method is in use.""" @abstractmethod - def get_verkey_id_for_did(self, did) -> Optional[str]: + def get_verification_method_id_for_did(self, did) -> Optional[str]: """Given a DID, returns the verification key ID in use. Returns None if no strategy is specified for this DID. @@ -26,7 +26,7 @@ class DefaultVerificationKeyStrategy(DefaultVerificationKeyStrategyBase): Supports did:key: and did:sov only. """ - def get_verkey_id_for_did(self, did) -> Optional[str]: + def get_verification_method_id_for_did(self, did) -> Optional[str]: """Given a did:key or did:sov, returns the verification key ID in use. Returns None if no strategy is specified for this DID. diff --git a/aries_cloudagent/wallet/tests/test_default_verification_key_strategy.py b/aries_cloudagent/wallet/tests/test_default_verification_key_strategy.py index d68ca45653..c53207836e 100644 --- a/aries_cloudagent/wallet/tests/test_default_verification_key_strategy.py +++ b/aries_cloudagent/wallet/tests/test_default_verification_key_strategy.py @@ -13,15 +13,18 @@ class TestDefaultVerificationKeyStrategy(TestCase): def test_with_did_sov(self): strategy = DefaultVerificationKeyStrategy() - assert strategy.get_verkey_id_for_did(TEST_DID_SOV) == TEST_DID_SOV + "#key-1" + assert ( + strategy.get_verification_method_id_for_did(TEST_DID_SOV) + == TEST_DID_SOV + "#key-1" + ) def test_with_did_key(self): strategy = DefaultVerificationKeyStrategy() assert ( - strategy.get_verkey_id_for_did(TEST_DID_KEY) + strategy.get_verification_method_id_for_did(TEST_DID_KEY) == DIDKey.from_did(TEST_DID_KEY).key_id ) def test_unsupported_did_method(self): strategy = DefaultVerificationKeyStrategy() - assert strategy.get_verkey_id_for_did("did:test:test") is None + assert strategy.get_verification_method_id_for_did("did:test:test") is None From ec54164a84d0a7b1ae034ca886860869ad1d1d24 Mon Sep 17 00:00:00 2001 From: Sacha Kozma Date: Mon, 22 May 2023 13:49:29 +0200 Subject: [PATCH 782/872] fix: set up context correctly in pres_exch tests Signed-off-by: Sacha Kozma --- .../present_proof/dif/tests/test_pres_exch_handler.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py b/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py index 6771dfc19c..51211e824a 100644 --- a/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py +++ b/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py @@ -15,6 +15,7 @@ from .....storage.vc_holder.vc_record import VCRecord from .....wallet.base import BaseWallet, DIDInfo from .....wallet.crypto import KeyType +from .....wallet.default_verification_key_strategy import DefaultVerificationKeyStrategy from .....wallet.did_method import SOV, KEY, DIDMethods from .....wallet.error import WalletNotFoundError from .....vc.ld_proofs import ( @@ -73,6 +74,9 @@ def profile(): context = profile.context context.injector.bind_instance(DIDResolver, DIDResolver([])) context.injector.bind_instance(DocumentLoader, custom_document_loader) + context.injector.bind_instance( + DefaultVerificationKeyStrategy, DefaultVerificationKeyStrategy() + ) context.settings["debug.auto_respond_presentation_request"] = True return profile From ea5adc86cdd16e48c4edc3e2163422c0e289a11b Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Tue, 10 Jan 2023 11:43:45 -0800 Subject: [PATCH 783/872] WIP fix multitenant/mediation in demo, now getting aca-py error Signed-off-by: Ian Costanzo --- .../protocols/connections/v1_0/manager.py | 4 ++++ .../protocols/connections/v1_0/routes.py | 5 +++++ demo/runners/support/agent.py | 18 +++++++++++++++--- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/aries_cloudagent/protocols/connections/v1_0/manager.py b/aries_cloudagent/protocols/connections/v1_0/manager.py index 6e87aeb96f..b74a258d7f 100644 --- a/aries_cloudagent/protocols/connections/v1_0/manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/manager.py @@ -1082,6 +1082,10 @@ async def get_connection_targets( else: if not connection: async with self.profile.session() as session: + records = await ConnRecord.query( + session, {}, post_filter_positive={}, alt=True + ) + print(" >>> existing connections:", records) connection = await ConnRecord.retrieve_by_id( session, connection_id ) diff --git a/aries_cloudagent/protocols/connections/v1_0/routes.py b/aries_cloudagent/protocols/connections/v1_0/routes.py index 11d8ba5651..4dc153d41b 100644 --- a/aries_cloudagent/protocols/connections/v1_0/routes.py +++ b/aries_cloudagent/protocols/connections/v1_0/routes.py @@ -549,6 +549,11 @@ async def connections_create_invitation(request: web.BaseRequest): else invitation_url, } except (ConnectionManagerError, StorageError, BaseModelError) as err: + async with profile.session() as session: + records = await ConnRecord.query( + session, {}, post_filter_positive={}, alt=True + ) + print(" >>> existing connections:", records) raise web.HTTPBadRequest(reason=err.roll_up) from err if connection and connection.alias: diff --git a/demo/runners/support/agent.py b/demo/runners/support/agent.py index 0aa7f995ed..b01658abae 100644 --- a/demo/runners/support/agent.py +++ b/demo/runners/support/agent.py @@ -341,6 +341,7 @@ def get_agent_args(self): "--preserve-exchange-records", "--auto-provision", "--public-invites", + #("--log-level", "debug"), ] if self.aip == 20: result.append("--emit-new-didcomm-prefix") @@ -844,6 +845,10 @@ async def handle_revocation_registry(self, message): async def handle_mediation(self, message): self.log(f"Received mediation message ...\n") + async def handle_keylist(self, message): + self.log(f"Received handle_keylist message ...\n") + self.log(json.dumps(message)) + async def taa_accept(self): taa_info = await self.admin_GET("/ledger/taa") if taa_info["result"]["taa_required"]: @@ -967,6 +972,7 @@ async def admin_POST( headers["Authorization"] = ( "Bearer " + self.managed_wallet_params["token"] ) + print(" >>> POST with headers:", path, headers) response = await self.admin_request( "POST", path, data, text, params, headers=headers ) @@ -1215,6 +1221,8 @@ async def get_invite( "handshake_protocols": ["rfc23"], "use_public_did": reuse_connections, } + if self.mediation: + payload["mediation_id"] = self.mediator_request_id invi_rec = await self.admin_POST( "/out-of-band/create-invitation", payload, @@ -1225,9 +1233,11 @@ async def get_invite( invi_params = { "auto_accept": json.dumps(auto_accept), } + payload = {"mediation_id": self.mediator_request_id} + print(" >>> Mediation invitation payload:", payload) invi_rec = await self.admin_POST( "/connections/create-invitation", - {"mediation_id": self.mediator_request_id}, + payload, params=invi_params, ) else: @@ -1240,6 +1250,8 @@ async def receive_invite(self, invite, auto_accept: bool = True): params = {"alias": "endorser"} else: params = {} + if self.mediation: + params["mediation_id"] = self.mediator_request_id if "/out-of-band/" in invite.get("@type", ""): # always reuse connections if possible params["use_existing_connection"] = "true" @@ -1337,12 +1349,12 @@ async def connect_wallet_to_mediator(agent, mediator_agent): log_msg("Connected agent to mediator:", agent.ident, mediator_agent.ident) # setup mediation on our connection - log_msg("Request mediation ...") + log_msg(f"Request mediation on connection {agent.mediator_connection_id} ...") mediation_request = await agent.admin_POST( "/mediation/request/" + agent.mediator_connection_id, {} ) agent.mediator_request_id = mediation_request["mediation_id"] - log_msg("Mediation request id:", agent.mediator_request_id) + log_msg(f"Mediation request id: {agent.mediator_request_id}") count = 3 while 0 < count: From 61157759c9144e1d2cec465fde2baa50f1cd7b77 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Mon, 15 May 2023 07:08:58 -0700 Subject: [PATCH 784/872] Cleanup logging Signed-off-by: Ian Costanzo --- aries_cloudagent/protocols/connections/v1_0/manager.py | 1 - aries_cloudagent/protocols/connections/v1_0/routes.py | 1 - demo/runners/support/agent.py | 2 -- 3 files changed, 4 deletions(-) diff --git a/aries_cloudagent/protocols/connections/v1_0/manager.py b/aries_cloudagent/protocols/connections/v1_0/manager.py index b74a258d7f..1fb7e51e45 100644 --- a/aries_cloudagent/protocols/connections/v1_0/manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/manager.py @@ -1085,7 +1085,6 @@ async def get_connection_targets( records = await ConnRecord.query( session, {}, post_filter_positive={}, alt=True ) - print(" >>> existing connections:", records) connection = await ConnRecord.retrieve_by_id( session, connection_id ) diff --git a/aries_cloudagent/protocols/connections/v1_0/routes.py b/aries_cloudagent/protocols/connections/v1_0/routes.py index 4dc153d41b..7709c5477a 100644 --- a/aries_cloudagent/protocols/connections/v1_0/routes.py +++ b/aries_cloudagent/protocols/connections/v1_0/routes.py @@ -553,7 +553,6 @@ async def connections_create_invitation(request: web.BaseRequest): records = await ConnRecord.query( session, {}, post_filter_positive={}, alt=True ) - print(" >>> existing connections:", records) raise web.HTTPBadRequest(reason=err.roll_up) from err if connection and connection.alias: diff --git a/demo/runners/support/agent.py b/demo/runners/support/agent.py index b01658abae..8e7a3bc2bd 100644 --- a/demo/runners/support/agent.py +++ b/demo/runners/support/agent.py @@ -972,7 +972,6 @@ async def admin_POST( headers["Authorization"] = ( "Bearer " + self.managed_wallet_params["token"] ) - print(" >>> POST with headers:", path, headers) response = await self.admin_request( "POST", path, data, text, params, headers=headers ) @@ -1234,7 +1233,6 @@ async def get_invite( "auto_accept": json.dumps(auto_accept), } payload = {"mediation_id": self.mediator_request_id} - print(" >>> Mediation invitation payload:", payload) invi_rec = await self.admin_POST( "/connections/create-invitation", payload, From bf5a06d3f15b9a8f8a1611d8bcba8c9c5ed18a98 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Mon, 15 May 2023 07:11:23 -0700 Subject: [PATCH 785/872] Formatting Signed-off-by: Ian Costanzo --- demo/runners/support/agent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/runners/support/agent.py b/demo/runners/support/agent.py index 8e7a3bc2bd..22995ef6b8 100644 --- a/demo/runners/support/agent.py +++ b/demo/runners/support/agent.py @@ -341,7 +341,7 @@ def get_agent_args(self): "--preserve-exchange-records", "--auto-provision", "--public-invites", - #("--log-level", "debug"), + # ("--log-level", "debug"), ] if self.aip == 20: result.append("--emit-new-didcomm-prefix") From 86c6f8aa559696a4f20eec1f05c0a48e3a6e5e2d Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Wed, 17 May 2023 07:14:48 -0700 Subject: [PATCH 786/872] Remove code which seems to be causing issues Signed-off-by: Ian Costanzo --- aries_cloudagent/protocols/connections/v1_0/manager.py | 3 --- aries_cloudagent/protocols/connections/v1_0/routes.py | 4 ---- 2 files changed, 7 deletions(-) diff --git a/aries_cloudagent/protocols/connections/v1_0/manager.py b/aries_cloudagent/protocols/connections/v1_0/manager.py index 1fb7e51e45..6e87aeb96f 100644 --- a/aries_cloudagent/protocols/connections/v1_0/manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/manager.py @@ -1082,9 +1082,6 @@ async def get_connection_targets( else: if not connection: async with self.profile.session() as session: - records = await ConnRecord.query( - session, {}, post_filter_positive={}, alt=True - ) connection = await ConnRecord.retrieve_by_id( session, connection_id ) diff --git a/aries_cloudagent/protocols/connections/v1_0/routes.py b/aries_cloudagent/protocols/connections/v1_0/routes.py index 7709c5477a..11d8ba5651 100644 --- a/aries_cloudagent/protocols/connections/v1_0/routes.py +++ b/aries_cloudagent/protocols/connections/v1_0/routes.py @@ -549,10 +549,6 @@ async def connections_create_invitation(request: web.BaseRequest): else invitation_url, } except (ConnectionManagerError, StorageError, BaseModelError) as err: - async with profile.session() as session: - records = await ConnRecord.query( - session, {}, post_filter_positive={}, alt=True - ) raise web.HTTPBadRequest(reason=err.roll_up) from err if connection and connection.alias: From 9ccb8cb7f7d624e1b8aaa467465af2d7602b8cf1 Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 26 May 2023 23:24:34 +0200 Subject: [PATCH 787/872] :arrow_up: upgrade `swagger-codegen-cli` to 2.4.32 Signed-off-by: ff137 --- scripts/generate-open-api-spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/generate-open-api-spec b/scripts/generate-open-api-spec index d2299c3290..acc35e6b8f 100755 --- a/scripts/generate-open-api-spec +++ b/scripts/generate-open-api-spec @@ -137,7 +137,7 @@ function runACAPy() { } -OPEN_API_CONTAINER="swaggerapi/swagger-codegen-cli:2.4.15" +OPEN_API_CONTAINER="swaggerapi/swagger-codegen-cli:2.4.32" # OPEN_API_CONTAINER="openapitools/openapi-generator-cli:v4.3.1" OPEN_API_OPTIONS=" " OPEN_API_MOUNT="/local" From 13961d99707166dd5edc316f0254610c6cd855e9 Mon Sep 17 00:00:00 2001 From: ff137 Date: Sat, 27 May 2023 12:46:32 +0200 Subject: [PATCH 788/872] fix file link Signed-off-by: ff137 --- UsingOpenAPI.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/UsingOpenAPI.md b/UsingOpenAPI.md index 3a88381932..7a1b7b0965 100644 --- a/UsingOpenAPI.md +++ b/UsingOpenAPI.md @@ -2,11 +2,11 @@ ACA-Py provides an OpenAPI-documented REST interface for administering the agent's internal state and initiating communication with connected agents. -The running agent provides a `Swagger User Interface` that can be browsed and used to test various scenarios manually (see the [Admin API Readme](AdminApi.md) for details). However, it is often desirable to produce native language interfaces rather than coding `Controllers` using HTTP primitives. This is possible using several public code generation (codegen) tools. This page provides some suggestions based on experience with these tools when trying to generate `Typescript` wrappers. The information should be useful to those trying to generate other languages. Updates to this page based on experience are encouraged. +The running agent provides a `Swagger User Interface` that can be browsed and used to test various scenarios manually (see the [Admin API Readme](AdminAPI.md) for details). However it is often desirable to produce native language interfaces rather than coding `Controllers` using HTTP primitives. This is possible using several public code generation (codegen) tools. This page provides some suggestions based on experience with these tools when trying to generate `Typescript` wrappers. The information should be useful to those trying to generate other langauages. Updates to this page based on experience are encouraged. ## ACA-Py, OpenAPI Raw Output Characteristics -ACA-Py uses [aiohttp_apispec](https://github.com/maximdanilchenko/aiohttp-apispec) tags in code to produce the OpenAPI spec file at runtime dependent on what features have been loaded. How these tags are created is documented in the [API Standard Behaviour](https://github.com/hyperledger/aries-cloudagent-python/blob/main/AdminAPI.md#api-standard-behaviour) section of the [Admin API Readme](AdminApi.md). The OpenAPI spec is available in raw, unformatted form from a running ACA-Py instance using a route of `http:///api/docs/swagger.json` or from the browser `Swagger User Interface` directly. +ACA-Py uses [aiohttp_apispec](https://github.com/maximdanilchenko/aiohttp-apispec) tags in code to produce the OpenAPI spec file at runtime dependent on what features have been loaded. How these tags are created is documented in the [API Standard Behaviour](https://github.com/hyperledger/aries-cloudagent-python/blob/main/AdminAPI.md#api-standard-behaviour) section of the [Admin API Readme](AdminAPI.md). The OpenAPI spec is available in raw, unformated form from a running ACA-py instance using a route of `http:///api/docs/swagger.json` or from the browser `Swagger User Interface` directly. To help identify changes in the ACA-Py Admin API over releases, there is a tool that can be run located at `scripts/generate-open-api-spec`. This tool will start ACA-Py, pull the `swagger.json` file, run a codegen tool, and specify a language output of `json`. Apart from providing a better format to compare changes (i.e., by comparing this output to the checked-in `open-api/openapi.json` version), the tool can be used to identify any non-conformance to the OpenAPI specification. At the moment, `validation` is turned off via the `open-api/openAPIJSON.config` file, so warning messages are printed for non-conformance, but the `json` is still output. Most of the warnings reported by `generate-open-api-spec` relate to missing `operationId` fields which results in manufactured method names being created by codegen tools. At the moment, [aiohttp_apispec](https://github.com/maximdanilchenko/aiohttp-apispec) does not support adding `operationId` annotations via tags. From 93dd4a55482092aa631de46c2520de9e9d6ede14 Mon Sep 17 00:00:00 2001 From: ff137 Date: Sat, 27 May 2023 12:47:30 +0200 Subject: [PATCH 789/872] upgrade openapi codegen tool to `openapitools/openapi-generator-cli:v6.6.0`, from previous `swaggerapi/swagger-codegen-cli:2.4.32` Signed-off-by: ff137 --- scripts/generate-open-api-spec | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/generate-open-api-spec b/scripts/generate-open-api-spec index acc35e6b8f..f234203eb7 100755 --- a/scripts/generate-open-api-spec +++ b/scripts/generate-open-api-spec @@ -6,6 +6,8 @@ # ########################################################################################## +OPENAPI_GEN_CONTAINER="openapitools/openapi-generator-cli:v6.6.0" + # Make sure everything is done starting in our commands home directory cd $(dirname $0) From 7a81b51c315fdf74aaec700d8a8b2e5277163246 Mon Sep 17 00:00:00 2001 From: ff137 Date: Sat, 27 May 2023 12:48:12 +0200 Subject: [PATCH 790/872] fix invalid exit code Signed-off-by: ff137 --- scripts/generate-open-api-spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/generate-open-api-spec b/scripts/generate-open-api-spec index f234203eb7..211637b0af 100755 --- a/scripts/generate-open-api-spec +++ b/scripts/generate-open-api-spec @@ -53,7 +53,7 @@ function runEval() { returnValue=$? if [ $returnValue != 0 ]; then echo "Command ${1} failed" - exit -1 + exit 1 fi return $returnValue } From 691883fef63269ce8537b0f4b93b92c86e24d44c Mon Sep 17 00:00:00 2001 From: ff137 Date: Sat, 27 May 2023 12:52:38 +0200 Subject: [PATCH 791/872] add var for acapy_spec_file and config_location Signed-off-by: ff137 --- scripts/generate-open-api-spec | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/scripts/generate-open-api-spec b/scripts/generate-open-api-spec index 211637b0af..f01259c41c 100755 --- a/scripts/generate-open-api-spec +++ b/scripts/generate-open-api-spec @@ -138,11 +138,9 @@ function runACAPy() { fi } - -OPEN_API_CONTAINER="swaggerapi/swagger-codegen-cli:2.4.32" -# OPEN_API_CONTAINER="openapitools/openapi-generator-cli:v4.3.1" -OPEN_API_OPTIONS=" " OPEN_API_MOUNT="/local" +ACAPY_SPEC_FILE="${OPEN_API_MOUNT}/acapy-raw.json" +CONFIG_LOCATION="${ROOT_DIR}/open-api/${OPEN_API_JSON_CONFIG}" # Pull the open API docker image and run it against the specified web server # or local spec file. From a3ca3f4a6211a87e75f920d8628105c3b98d687d Mon Sep 17 00:00:00 2001 From: ff137 Date: Sat, 27 May 2023 12:56:47 +0200 Subject: [PATCH 792/872] use generatorType for selecting swagger or openapi Signed-off-by: ff137 --- scripts/generate-open-api-spec | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/scripts/generate-open-api-spec b/scripts/generate-open-api-spec index f01259c41c..a86c7feb90 100755 --- a/scripts/generate-open-api-spec +++ b/scripts/generate-open-api-spec @@ -145,12 +145,12 @@ CONFIG_LOCATION="${ROOT_DIR}/open-api/${OPEN_API_JSON_CONFIG}" # Pull the open API docker image and run it against the specified web server # or local spec file. # $1 : Web OpenAPI URL or local file to generate API routines from. -# $2 : Language to generate +# $2 : Language to generate (swagger or openapi) # $3 : Language config file location # $4 : The host shared dir for input/output function runOpenAPIGenerate() { local specFile="${1}" - local outputLang="${2}" + local generatorType="${2}" local configLocation="${3}" local hostSharedDir="${4}" @@ -164,18 +164,20 @@ function runOpenAPIGenerate() { openAPICmd="docker run --rm --user $(id -u):$(id -g) -v ${hostSharedDir}:${OPEN_API_MOUNT} ${OPEN_API_CONTAINER} generate \ --input-spec ${specFile} \ --output ${OPEN_API_MOUNT}" - - # If using the swagger version of code generator the options are different - # to specify language generator. - if [[ ${OPEN_API_CONTAINER} = *swagger-codegen* ]]; then - if [[ ${outputLang} = "openapi" ]]; then - # Generating the json output is a different language name in swagger - outputLang="swagger" - fi - openAPICmd+=" --lang ${outputLang} ${OPEN_API_OPTIONS} " - else - openAPICmd+=" --generator-name ${outputLang} ${OPEN_API_OPTIONS} " - fi + + # specify language generator. + case ${generatorType} in + swagger) + openAPICmd+=" -g swagger" + ;; + openapi) + openAPICmd+=" -g openapi" + ;; + *) + echo "Unknown generator type: ${generatorType}" + exit 1 + ;; + esac if [ ! -z ${configFile} ]; then openAPICmd+=" --config ${OPEN_API_MOUNT}/${configFile}" From a3d6c76bd60b778e70029f3bb44e3bdbe30db50a Mon Sep 17 00:00:00 2001 From: ff137 Date: Sat, 27 May 2023 12:58:06 +0200 Subject: [PATCH 793/872] no longer overwrite swagger spec as openapi spec Signed-off-by: ff137 --- scripts/generate-open-api-spec | 5 ----- 1 file changed, 5 deletions(-) diff --git a/scripts/generate-open-api-spec b/scripts/generate-open-api-spec index a86c7feb90..bf109e6201 100755 --- a/scripts/generate-open-api-spec +++ b/scripts/generate-open-api-spec @@ -187,11 +187,6 @@ function runOpenAPIGenerate() { \t ${openAPICmd}" ${openAPICmd} - - # Copy the swagger output into the normalised output file name - if [[ ${OPEN_API_CONTAINER} = *swagger-codegen* ]] && [[ ${outputLang} = "swagger" ]]; then - runEval "cp ${hostSharedDir}/swagger.json ${hostSharedDir}/openapi.json" - fi } ########################################################################################## From 3ff5b2b485ae67b82fe8e0ad2a6584515f651f9b Mon Sep 17 00:00:00 2001 From: ff137 Date: Sat, 27 May 2023 13:01:32 +0200 Subject: [PATCH 794/872] generate and copy both swagger & openapi output Signed-off-by: ff137 --- scripts/generate-open-api-spec | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/scripts/generate-open-api-spec b/scripts/generate-open-api-spec index bf109e6201..e9735ec067 100755 --- a/scripts/generate-open-api-spec +++ b/scripts/generate-open-api-spec @@ -209,10 +209,12 @@ if [ ! -d ${OPEN_API_SHARED_DIR} ]; then fi curl --output ${OPEN_API_SHARED_DIR}/acapy-raw.json http://localhost:${ACA_PY_ADMIN_PORT}/api/docs/swagger.json -# Generate the native OpenAPI JSON spec file -runOpenAPIGenerate "${OPEN_API_MOUNT}/acapy-raw.json" openapi "${ROOT_DIR}/open-api/${OPEN_API_JSON_CONFIG}" "${OPEN_API_SHARED_DIR}" +# Generate the native Swagger as well as the OpenAPI JSON spec file +runOpenAPIGenerate "${ACAPY_SPEC_FILE}" swagger "${CONFIG_LOCATION}" "${OPEN_API_SHARED_DIR}" +runOpenAPIGenerate "${ACAPY_SPEC_FILE}" openapi "${CONFIG_LOCATION}" "${OPEN_API_SHARED_DIR}" -# Force over-write the version controlled openapi.json +# Force over-write the version controlled .json files +runEval "cp -f ${OPEN_API_SHARED_DIR}/swagger.json ${ROOT_DIR}/open-api/" runEval "cp -f ${OPEN_API_SHARED_DIR}/openapi.json ${ROOT_DIR}/open-api/" # Clean up the working directory. From afb74c9fb7a15563a8f2f1b00248a0061f23fd76 Mon Sep 17 00:00:00 2001 From: ff137 Date: Sat, 27 May 2023 13:01:54 +0200 Subject: [PATCH 795/872] :art: format Signed-off-by: ff137 --- scripts/generate-open-api-spec | 44 ++++++++++++++++------------------ 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/scripts/generate-open-api-spec b/scripts/generate-open-api-spec index e9735ec067..dc2141a036 100755 --- a/scripts/generate-open-api-spec +++ b/scripts/generate-open-api-spec @@ -1,8 +1,8 @@ #!/bin/bash # -# This script will build an ACA-py docker image, +# This script will build an ACA-py docker image, # execute the openapi/swagger codegen tool and create an openapi.json spec file -# for the ACA-py REST API. +# for the ACA-py REST API. # ########################################################################################## @@ -58,7 +58,7 @@ function runEval() { return $returnValue } -# Print an indication of script reaching a processing +# Print an indication of script reaching a processing # milestone in a noticable way # $1 : Message string to print function printMilestone() { @@ -69,13 +69,11 @@ function printMilestone() { echo -e "##########################################################################################\n" } - # Wait for a web server to provide a funcitoning interface we can use # $1 : Url to poll that indicates webserver initialsation complete # $2 : maximum number of seconds to wait function waitActiveWebInterface() { - for (( i=1; i < ${2}; i++)) - do + for ((i = 1; i < ${2}; i++)); do curl -s -f ${1} if [ $? == 0 ]; then return 0 @@ -106,8 +104,8 @@ function buildACAPyDockerImage() { # $2: The port mapping from docker to local host in format "docker1:local1 docker2:local2" # $3: The ACA-py command line arguements # $4: The name of a variable to return the continer ID to -function runACAPy() { - local acaPyImage="${1}" +function runACAPy() { + local acaPyImage="${1}" local ports="${2}" local acaPyArgs="${3}" local result="${4}" @@ -117,12 +115,12 @@ function runACAPy() { args="${args} -p ${port}" done - # Mount the agent logs onto the hosting machine + # Mount the agent logs onto the hosting machine args="${args} -v /$(pwd)/../logs:/home/indy/logs" randName=$(cat /dev/urandom | env LC_ALL=C tr -dc 'a-zA-Z0-9' | fold -w 16 | head -n 1) acaPyCmd="docker run -d --rm --name ${acaPyImage}_${randName} ${args} \ - ${acaPyImage} start ${acaPyArgs}" + ${acaPyImage} start ${acaPyArgs}" printMilestone "Starting ACA-py docker image with command: \n \ \t ${acaPyCmd}" @@ -130,8 +128,8 @@ function runACAPy() { containerId=$(${acaPyCmd}) local returnStatus=$? if [[ ${returnStatus} != 0 ]]; then - echo "**** FAIL - ACA-Py failed to start, exiting. ****" - exit 1 + echo "**** FAIL - ACA-Py failed to start, exiting. ****" + exit 1 fi if [[ "${result}" ]]; then eval ${result}="'${containerId}'" @@ -155,15 +153,16 @@ function runOpenAPIGenerate() { local hostSharedDir="${4}" runEval "mkdir -p ${hostSharedDir}" - + if [ ! -z ${configLocation} ]; then runEval "cp ${configLocation} ${hostSharedDir}/" configFile="$(basename -- ${configLocation})" fi - openAPICmd="docker run --rm --user $(id -u):$(id -g) -v ${hostSharedDir}:${OPEN_API_MOUNT} ${OPEN_API_CONTAINER} generate \ - --input-spec ${specFile} \ - --output ${OPEN_API_MOUNT}" + openAPICmd="docker run --rm --user $(id -u):$(id -g) -v ${hostSharedDir}:${OPEN_API_MOUNT} \ + ${OPENAPI_GEN_CONTAINER} generate \ + --input-spec ${specFile} \ + --output ${OPEN_API_MOUNT}" # specify language generator. case ${generatorType} in @@ -191,9 +190,9 @@ function runOpenAPIGenerate() { ########################################################################################## # Run docker ACA-py image and pull REST API spec file to generate json format -########################################################################################## +########################################################################################## buildACAPyDockerImage "${ROOT_DIR}" "${ACA_PY_DOCKER_IMAGE_DEFAULT}" -runACAPy "${ACA_PY_DOCKER_IMAGE_DEFAULT}" "${ACA_PY_DOCKER_PORTS}" "${ACA_PY_CMD_OPTIONS}" ACA_PY_CONTAINER_ID +runACAPy "${ACA_PY_DOCKER_IMAGE_DEFAULT}" "${ACA_PY_DOCKER_PORTS}" "${ACA_PY_CMD_OPTIONS}" ACA_PY_CONTAINER_ID # Make sure ACA-py container gets terminated when we do trap 'docker kill ${ACA_PY_CONTAINER_ID}' EXIT waitActiveWebInterface "http://localhost:${ACA_PY_ADMIN_PORT}" 20 @@ -204,10 +203,10 @@ fi printMilestone "ACA-Py Admin interface active\n\t Docker Id '${ACA_PY_CONTAINER_ID}'" # Pull the swagger raw format spec file from ACA-py -if [ ! -d ${OPEN_API_SHARED_DIR} ]; then - mkdir -p ${OPEN_API_SHARED_DIR}; +if [ ! -d ${OPEN_API_SHARED_DIR} ]; then + mkdir -p ${OPEN_API_SHARED_DIR} fi -curl --output ${OPEN_API_SHARED_DIR}/acapy-raw.json http://localhost:${ACA_PY_ADMIN_PORT}/api/docs/swagger.json +curl --output ${ACAPY_SPEC_FILE} http://localhost:${ACA_PY_ADMIN_PORT}/api/docs/swagger.json # Generate the native Swagger as well as the OpenAPI JSON spec file runOpenAPIGenerate "${ACAPY_SPEC_FILE}" swagger "${CONFIG_LOCATION}" "${OPEN_API_SHARED_DIR}" @@ -218,5 +217,4 @@ runEval "cp -f ${OPEN_API_SHARED_DIR}/swagger.json ${ROOT_DIR}/open-api/" runEval "cp -f ${OPEN_API_SHARED_DIR}/openapi.json ${ROOT_DIR}/open-api/" # Clean up the working directory. -rm -Rf ${OPEN_API_SHARED_DIR}; - +rm -Rf ${OPEN_API_SHARED_DIR} From a22cc04b129c16410914e6940474ad8db28267d0 Mon Sep 17 00:00:00 2001 From: ff137 Date: Sat, 27 May 2023 13:18:00 +0200 Subject: [PATCH 796/872] :art: improve comment descriptions Signed-off-by: ff137 --- scripts/generate-open-api-spec | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/scripts/generate-open-api-spec b/scripts/generate-open-api-spec index dc2141a036..a36686add3 100755 --- a/scripts/generate-open-api-spec +++ b/scripts/generate-open-api-spec @@ -1,23 +1,24 @@ #!/bin/bash # # This script will build an ACA-py docker image, -# execute the openapi/swagger codegen tool and create an openapi.json spec file +# execute the openapi/swagger codegen tool and create a swagger.json and openapi.json spec file # for the ACA-py REST API. # ########################################################################################## OPENAPI_GEN_CONTAINER="openapitools/openapi-generator-cli:v6.6.0" -# Make sure everything is done starting in our commands home directory +# Ensure the script is running from the directory containing this script cd $(dirname $0) -# Establish basic context of where things exist +# Define the root and output directories ROOT_DIR="${PWD}/.." OUTPUT_DIR="${ROOT_DIR}/open-api" ########################################################################################## # Global Defaults and Constants ########################################################################################## +# Set some default values for ACA-Py, including the default image name and ports ACA_PY_DOCKER_IMAGE_DEFAULT="aries-cloudagent-run" ACA_PY_ADMIN_PORT="8305" @@ -39,6 +40,7 @@ ACA_PY_CMD_OPTIONS=" \ --jwt-secret test \ --no-ledger" +# Specify openAPI JSON config file and shared directory OPEN_API_JSON_CONFIG="openAPIJSON.config" OPEN_API_SHARED_DIR="${OUTPUT_DIR}/.build" @@ -191,10 +193,17 @@ function runOpenAPIGenerate() { ########################################################################################## # Run docker ACA-py image and pull REST API spec file to generate json format ########################################################################################## + +# Build the ACA-Py Docker image from the current code buildACAPyDockerImage "${ROOT_DIR}" "${ACA_PY_DOCKER_IMAGE_DEFAULT}" + +# Run the ACA-Py Docker image runACAPy "${ACA_PY_DOCKER_IMAGE_DEFAULT}" "${ACA_PY_DOCKER_PORTS}" "${ACA_PY_CMD_OPTIONS}" ACA_PY_CONTAINER_ID -# Make sure ACA-py container gets terminated when we do + +# Ensure the ACA-Py container is killed when the script exits trap 'docker kill ${ACA_PY_CONTAINER_ID}' EXIT + +# Wait for the ACA-Py web interface to become active waitActiveWebInterface "http://localhost:${ACA_PY_ADMIN_PORT}" 20 returnValue=$? if [ $returnValue != 0 ]; then @@ -202,17 +211,18 @@ if [ $returnValue != 0 ]; then fi printMilestone "ACA-Py Admin interface active\n\t Docker Id '${ACA_PY_CONTAINER_ID}'" -# Pull the swagger raw format spec file from ACA-py +# Create the shared directory if it doesn't already exist if [ ! -d ${OPEN_API_SHARED_DIR} ]; then mkdir -p ${OPEN_API_SHARED_DIR} fi -curl --output ${ACAPY_SPEC_FILE} http://localhost:${ACA_PY_ADMIN_PORT}/api/docs/swagger.json -# Generate the native Swagger as well as the OpenAPI JSON spec file +# Download the Swagger specification from the ACA-Py service + +# Use the OpenAPI Generator to create both Swagger and OpenAPI spec files from the downloaded Swagger specification runOpenAPIGenerate "${ACAPY_SPEC_FILE}" swagger "${CONFIG_LOCATION}" "${OPEN_API_SHARED_DIR}" runOpenAPIGenerate "${ACAPY_SPEC_FILE}" openapi "${CONFIG_LOCATION}" "${OPEN_API_SHARED_DIR}" -# Force over-write the version controlled .json files +# Overwrite the existing Swagger and OpenAPI spec files runEval "cp -f ${OPEN_API_SHARED_DIR}/swagger.json ${ROOT_DIR}/open-api/" runEval "cp -f ${OPEN_API_SHARED_DIR}/openapi.json ${ROOT_DIR}/open-api/" From cdce9e6a0b1c644c5239a765f6d53ec36206bcfb Mon Sep 17 00:00:00 2001 From: ff137 Date: Sat, 27 May 2023 13:18:50 +0200 Subject: [PATCH 797/872] upgrade `swagger-codegen` and `openapi-generator` to latest Signed-off-by: ff137 --- scripts/generate-open-api-spec | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/generate-open-api-spec b/scripts/generate-open-api-spec index a36686add3..e31aadc1c3 100755 --- a/scripts/generate-open-api-spec +++ b/scripts/generate-open-api-spec @@ -6,7 +6,8 @@ # ########################################################################################## -OPENAPI_GEN_CONTAINER="openapitools/openapi-generator-cli:v6.6.0" +SWAGGER_GEN_CONTAINER="swaggerapi/swagger-codegen-cli:2.4.32" +OPENAPI_GEN_CONTAINER="openapitools/openapi-generator-cli:v6.6.0" # Ensure the script is running from the directory containing this script cd $(dirname $0) From e832b8a1db1c7833ec98a9a52d9dc680cc36b1d1 Mon Sep 17 00:00:00 2001 From: ff137 Date: Sat, 27 May 2023 13:19:52 +0200 Subject: [PATCH 798/872] fix reference to ACAPY_SPEC_FILE Signed-off-by: ff137 --- scripts/generate-open-api-spec | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/scripts/generate-open-api-spec b/scripts/generate-open-api-spec index e31aadc1c3..4de620b89d 100755 --- a/scripts/generate-open-api-spec +++ b/scripts/generate-open-api-spec @@ -140,7 +140,7 @@ function runACAPy() { } OPEN_API_MOUNT="/local" -ACAPY_SPEC_FILE="${OPEN_API_MOUNT}/acapy-raw.json" +ACAPY_SPEC_FILE="acapy-raw.json" CONFIG_LOCATION="${ROOT_DIR}/open-api/${OPEN_API_JSON_CONFIG}" # Pull the open API docker image and run it against the specified web server @@ -218,10 +218,11 @@ if [ ! -d ${OPEN_API_SHARED_DIR} ]; then fi # Download the Swagger specification from the ACA-Py service +curl --output "${OPEN_API_SHARED_DIR}/${ACAPY_SPEC_FILE}" http://localhost:${ACA_PY_ADMIN_PORT}/api/docs/swagger.json # Use the OpenAPI Generator to create both Swagger and OpenAPI spec files from the downloaded Swagger specification -runOpenAPIGenerate "${ACAPY_SPEC_FILE}" swagger "${CONFIG_LOCATION}" "${OPEN_API_SHARED_DIR}" -runOpenAPIGenerate "${ACAPY_SPEC_FILE}" openapi "${CONFIG_LOCATION}" "${OPEN_API_SHARED_DIR}" +runOpenAPIGenerate "${OPEN_API_MOUNT}/${ACAPY_SPEC_FILE}" swagger "${CONFIG_LOCATION}" "${OPEN_API_SHARED_DIR}" +runOpenAPIGenerate "${OPEN_API_MOUNT}/${ACAPY_SPEC_FILE}" openapi "${CONFIG_LOCATION}" "${OPEN_API_SHARED_DIR}" # Overwrite the existing Swagger and OpenAPI spec files runEval "cp -f ${OPEN_API_SHARED_DIR}/swagger.json ${ROOT_DIR}/open-api/" From a2119513af70e63bcd4a472a6e2cf355c277276e Mon Sep 17 00:00:00 2001 From: ff137 Date: Sat, 27 May 2023 13:20:27 +0200 Subject: [PATCH 799/872] use correct container for swagger or openapi gen Signed-off-by: ff137 --- scripts/generate-open-api-spec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/generate-open-api-spec b/scripts/generate-open-api-spec index 4de620b89d..f7dd92eb53 100755 --- a/scripts/generate-open-api-spec +++ b/scripts/generate-open-api-spec @@ -170,10 +170,10 @@ function runOpenAPIGenerate() { # specify language generator. case ${generatorType} in swagger) - openAPICmd+=" -g swagger" + genSpecCmd+=" ${SWAGGER_GEN_CONTAINER} generate -l swagger" ;; openapi) - openAPICmd+=" -g openapi" + genSpecCmd+=" ${OPENAPI_GEN_CONTAINER} generate -g openapi" ;; *) echo "Unknown generator type: ${generatorType}" From 010d80b8d0580f53c089985cb935e80a7708d169 Mon Sep 17 00:00:00 2001 From: ff137 Date: Sat, 27 May 2023 13:20:53 +0200 Subject: [PATCH 800/872] rename cmd name for clarity Signed-off-by: ff137 --- scripts/generate-open-api-spec | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/scripts/generate-open-api-spec b/scripts/generate-open-api-spec index f7dd92eb53..ef898065ed 100755 --- a/scripts/generate-open-api-spec +++ b/scripts/generate-open-api-spec @@ -162,10 +162,7 @@ function runOpenAPIGenerate() { configFile="$(basename -- ${configLocation})" fi - openAPICmd="docker run --rm --user $(id -u):$(id -g) -v ${hostSharedDir}:${OPEN_API_MOUNT} \ - ${OPENAPI_GEN_CONTAINER} generate \ - --input-spec ${specFile} \ - --output ${OPEN_API_MOUNT}" + genSpecCmd="docker run --rm --user $(id -u):$(id -g) -v ${hostSharedDir}:${OPEN_API_MOUNT}" # specify language generator. case ${generatorType} in @@ -181,14 +178,16 @@ function runOpenAPIGenerate() { ;; esac + genSpecCmd+=" --input-spec ${specFile} --output ${OPEN_API_MOUNT}" + if [ ! -z ${configFile} ]; then - openAPICmd+=" --config ${OPEN_API_MOUNT}/${configFile}" + genSpecCmd+=" --config ${OPEN_API_MOUNT}/${configFile}" fi - printMilestone "Starting Open API code generation with command: \n \ - \t ${openAPICmd}" + printMilestone "Starting ${generatorType} code generation with command: \n \ + \t ${genSpecCmd}" - ${openAPICmd} + ${genSpecCmd} } ########################################################################################## From a14b2942be3806cae2eb1c88f8f88fae58772bcf Mon Sep 17 00:00:00 2001 From: ff137 Date: Sat, 27 May 2023 13:21:11 +0200 Subject: [PATCH 801/872] :art: format Signed-off-by: ff137 --- scripts/generate-open-api-spec | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/generate-open-api-spec b/scripts/generate-open-api-spec index ef898065ed..1ae7c83e20 100755 --- a/scripts/generate-open-api-spec +++ b/scripts/generate-open-api-spec @@ -7,7 +7,7 @@ ########################################################################################## SWAGGER_GEN_CONTAINER="swaggerapi/swagger-codegen-cli:2.4.32" -OPENAPI_GEN_CONTAINER="openapitools/openapi-generator-cli:v6.6.0" +OPENAPI_GEN_CONTAINER="openapitools/openapi-generator-cli:v6.6.0" # Ensure the script is running from the directory containing this script cd $(dirname $0) @@ -21,7 +21,6 @@ OUTPUT_DIR="${ROOT_DIR}/open-api" ########################################################################################## # Set some default values for ACA-Py, including the default image name and ports ACA_PY_DOCKER_IMAGE_DEFAULT="aries-cloudagent-run" - ACA_PY_ADMIN_PORT="8305" ACA_PY_INBOUND_PORT="8307" ACA_PY_DOCKER_PORTS="${ACA_PY_INBOUND_PORT}:${ACA_PY_INBOUND_PORT} ${ACA_PY_ADMIN_PORT}:${ACA_PY_ADMIN_PORT}" From fb0850238900a8a3fd363c118bb21d78954e3688 Mon Sep 17 00:00:00 2001 From: ff137 Date: Sat, 27 May 2023 13:33:11 +0200 Subject: [PATCH 802/872] :art: formatting Signed-off-by: ff137 --- UsingOpenAPI.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/UsingOpenAPI.md b/UsingOpenAPI.md index 7a1b7b0965..1d17323070 100644 --- a/UsingOpenAPI.md +++ b/UsingOpenAPI.md @@ -2,7 +2,7 @@ ACA-Py provides an OpenAPI-documented REST interface for administering the agent's internal state and initiating communication with connected agents. -The running agent provides a `Swagger User Interface` that can be browsed and used to test various scenarios manually (see the [Admin API Readme](AdminAPI.md) for details). However it is often desirable to produce native language interfaces rather than coding `Controllers` using HTTP primitives. This is possible using several public code generation (codegen) tools. This page provides some suggestions based on experience with these tools when trying to generate `Typescript` wrappers. The information should be useful to those trying to generate other langauages. Updates to this page based on experience are encouraged. +The running agent provides a `Swagger User Interface` that can be browsed and used to test various scenarios manually (see the [Admin API Readme](AdminAPI.md) for details). However it is often desirable to produce native language interfaces rather than coding `Controllers` using HTTP primitives. This is possible using several public code generation (codegen) tools. This page provides some suggestions based on experience with these tools when trying to generate `Typescript` wrappers. The information should be useful to those trying to generate other langauages. Updates to this page based on experience are encouraged. ## ACA-Py, OpenAPI Raw Output Characteristics @@ -10,21 +10,21 @@ ACA-Py uses [aiohttp_apispec](https://github.com/maximdanilchenko/aiohttp-apispe To help identify changes in the ACA-Py Admin API over releases, there is a tool that can be run located at `scripts/generate-open-api-spec`. This tool will start ACA-Py, pull the `swagger.json` file, run a codegen tool, and specify a language output of `json`. Apart from providing a better format to compare changes (i.e., by comparing this output to the checked-in `open-api/openapi.json` version), the tool can be used to identify any non-conformance to the OpenAPI specification. At the moment, `validation` is turned off via the `open-api/openAPIJSON.config` file, so warning messages are printed for non-conformance, but the `json` is still output. Most of the warnings reported by `generate-open-api-spec` relate to missing `operationId` fields which results in manufactured method names being created by codegen tools. At the moment, [aiohttp_apispec](https://github.com/maximdanilchenko/aiohttp-apispec) does not support adding `operationId` annotations via tags. -The `generate-open-api-spec` tool was initially created to help identify issues with method parameters not being sorted, resulting in somewhat random ordering each time a codegen operation was performed. This is relevant for languages which do not have support for [named parameters](https://en.wikipedia.org/wiki/Named_parameter) such as `Javascript`. It is recommended that the `generate-open-api-spec` is run prior to each release, and the resulting `open-api/openapi.json` file checked in to allow tracking of API changes over time. At the moment, this process is not automated as part of the release pipeline. +The `generate-open-api-spec` tool was initially created to help identify issues with method parameters not being sorted, resulting in somewhat random ordering each time a codegen operation was performed. This is relevent for languages which do not have support for [named parameters](https://en.wikipedia.org/wiki/Named_parameter) such as `Javascript`. It is recomended that the `generate-open-api-spec` is run prior to each release and the resulting `open-api/openapi.json` file checked in to allow tracking of API changes over time. At the moment this process is not automated as part of the release pipeline. ## Generating Language Wrappers for ACA-Py -There are inevitably differences around `best practice` for method naming based on coding language and organization standards. +There are inevitably differences around `best practice` for method naming based on coding language, and indeed organisation standards. -Best practice for generating ACA-Py language wrappers is to obtain the raw OpenAPI file from a configured/running ACA-Py instance and then post-process it with a merge utility to match routes and insert desired `operationId` fields. This allows the greatest flexibility in conforming to external naming requirements. +Best practice for generating ACA-Py language wrappers is to obtain the raw OpenAPI file from a configured/running ACA-Py instance and then post-process it with a merge utility to match routes and insert desired `operationId` fields. This allows greatest flexibility in conforming to external naming requirements. -Two major open-source code generation tools are [Swagger](https://github.com/swagger-api/swagger-codegen) and [OpenAPI Tools](https://github.com/OpenAPITools/openapi-generator). Which of these to use can be very dependent on language support required and preference for the style of code generated. +Two major open source code generation tools are [Swagger](https://github.com/swagger-api/swagger-codegen) and [OpenAPI Tools](https://github.com/OpenAPITools/openapi-generator). Which of these to use can be very dependent on language support required and preference for the style of code generated. -The [OpenAPI Tools](https://github.com/OpenAPITools/openapi-generator) was found to offer some nice features when generating `Typescript`. It creates separate files for each class and allows the use of a `.openapi-generator-ignore` file to override generation if there is a spec file issue that needs to be maintained manually. +The [OpenAPI Tools](https://github.com/OpenAPITools/openapi-generator) was found to offer some nice features when generating `Typescript`. It creates seperate files for each class and allows use of a `.openapi-generator-ignore` file to override generation if there is a spec file issue that needs to be maintained manually. -If generating code for languages that do not support [named parameters](https://en.wikipedia.org/wiki/Named_parameter), it is recommended to specify the `useSingleRequestParameter` or equivalent in your code generator of choice. The reason is that, as mentioned previously, there have been instances where parameters were not sorted when output into the raw ACA-Py API spec file, and this approach helps remove that risk. +If generating code for languages that do not support [named parameters](https://en.wikipedia.org/wiki/Named_parameter) it is recommended to specify the `useSingleRequestParameter` or equivalent in your code generator of choice. The reason is that as mentioned previously, there have been instances where parameters were not sorted when output into the raw ACA-Py API spec file and this approach helps remove that risk. -Another suggestion for code generation is to keep the `modelPropertyNaming` set to `original` when generating code. Although it is tempting to try and enable marshaling into standard naming formats such as `camelCase`, the reality is that the models represent what is sent on the wire and documented in the [Aries Protocol RFCS](https://github.com/hyperledger/aries-rfcs/tree/master/features). It has proven handy to be able to see code references correspond directly with protocol RFCs when debugging. It will also correspond directly with what the `model` shows when looking at the ACA-Py `Swagger UI` in a browser if you need to try something out manually before coding. One final point is that on occasions, it has been discovered that the code generation tools don't always get the marshaling correct in all circumstances when changing model name format. +Another suggestion for code generation is to keep the `modelPropertyNaming` set to `original` when generating code. Although it is tempting to try and enable marshalling into standard naming formats such as `camelCase`, the reality is that the models represent what is sent on the wire and documented in the [Aries Protocol RFCS](https://github.com/hyperledger/aries-rfcs/tree/master/features). It has proven handy to be able to see code references correspond directly with protocol RFCs when debugging. It will also correspond directly with what the `model` shows when looking at the ACA-py `Swagger UI` in a browser if you need to try something out manually before coding. One final point is that on occasions it has been discovered that the code generation tools don't always get the marshalling correct in all circumstances when changing model name format. ## Existing Language Wrappers for ACA-Py From ffb164fe17c1ed5ccbe93e60b2649e2f8d1892ec Mon Sep 17 00:00:00 2001 From: ff137 Date: Sat, 27 May 2023 13:33:24 +0200 Subject: [PATCH 803/872] :art: add names for naked links Signed-off-by: ff137 --- UsingOpenAPI.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/UsingOpenAPI.md b/UsingOpenAPI.md index 1d17323070..5d78e2cf4f 100644 --- a/UsingOpenAPI.md +++ b/UsingOpenAPI.md @@ -30,15 +30,15 @@ Another suggestion for code generation is to keep the `modelPropertyNaming` set ### Python -- [aries-cloudcontroller (PyPI)](https://pypi.org/project/aries-cloudcontroller/) - - [aries-cloudcontroller-python (GitHub)](https://github.com/didx-xyz/aries-cloudcontroller-python) -- [traction (GitHub)](https://github.com/bcgov/traction/tree/develop/services/traction/acapy_client) -- [acapy-client (GitHub)](https://github.com/Indicio-tech/acapy-client) +- [Aries Cloud Controller (PyPi)](https://pypi.org/project/aries-cloudcontroller/) + - [Aries Cloud Controller Python (GitHub / didx-xyz)](https://github.com/didx-xyz/aries-cloudcontroller-python) +- [Traction (GitHub / bcgov)](https://github.com/bcgov/traction) +- [acapy-client (GitHub / Indicio-tech)](https://github.com/Indicio-tech/acapy-client) ### Go -- [go-acapy-client (GitHub)](https://github.com/ldej/go-acapy-client) +- [go-acapy-client (GitHub / Idej)](https://github.com/ldej/go-acapy-client) ### Java -- [acapy-java-client (GitHub)](https://github.com/hyperledger-labs/acapy-java-client) +- [ACA-PY Java Client Library (GitHub / hyperledger-labs)](https://github.com/hyperledger-labs/acapy-java-client) From 39f06e5fdf4ab98b4b4227c19b2c47c53c9babf3 Mon Sep 17 00:00:00 2001 From: ff137 Date: Sat, 27 May 2023 13:42:54 +0200 Subject: [PATCH 804/872] fix file link Signed-off-by: ff137 --- UsingOpenAPI.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UsingOpenAPI.md b/UsingOpenAPI.md index 5d78e2cf4f..8e589d21a7 100644 --- a/UsingOpenAPI.md +++ b/UsingOpenAPI.md @@ -6,7 +6,7 @@ The running agent provides a `Swagger User Interface` that can be browsed and us ## ACA-Py, OpenAPI Raw Output Characteristics -ACA-Py uses [aiohttp_apispec](https://github.com/maximdanilchenko/aiohttp-apispec) tags in code to produce the OpenAPI spec file at runtime dependent on what features have been loaded. How these tags are created is documented in the [API Standard Behaviour](https://github.com/hyperledger/aries-cloudagent-python/blob/main/AdminAPI.md#api-standard-behaviour) section of the [Admin API Readme](AdminAPI.md). The OpenAPI spec is available in raw, unformated form from a running ACA-py instance using a route of `http:///api/docs/swagger.json` or from the browser `Swagger User Interface` directly. +ACA-Py uses [aiohttp_apispec](https://github.com/maximdanilchenko/aiohttp-apispec) tags in code to produce the OpenAPI spec file at runtime dependent on what features have been loaded. How these tags are created is documented in the [API Standard Behaviour](https://github.com/hyperledger/aries-cloudagent-python/blob/main/AdminAPI.md#api-standard-behaviour) section of the [Admin API Readme](AdminAPI.md). The OpenAPI spec is available in raw, unformated form from a running ACA-py instance using a route of `http:///api/docs/swagger.json` or from the browser `Swagger User Interface` directly. To help identify changes in the ACA-Py Admin API over releases, there is a tool that can be run located at `scripts/generate-open-api-spec`. This tool will start ACA-Py, pull the `swagger.json` file, run a codegen tool, and specify a language output of `json`. Apart from providing a better format to compare changes (i.e., by comparing this output to the checked-in `open-api/openapi.json` version), the tool can be used to identify any non-conformance to the OpenAPI specification. At the moment, `validation` is turned off via the `open-api/openAPIJSON.config` file, so warning messages are printed for non-conformance, but the `json` is still output. Most of the warnings reported by `generate-open-api-spec` relate to missing `operationId` fields which results in manufactured method names being created by codegen tools. At the moment, [aiohttp_apispec](https://github.com/maximdanilchenko/aiohttp-apispec) does not support adding `operationId` annotations via tags. From 93a393423d43253fafa97096e7d90973bd90e3c4 Mon Sep 17 00:00:00 2001 From: ff137 Date: Sat, 27 May 2023 13:44:15 +0200 Subject: [PATCH 805/872] add link to script and clarify that there are two spec outputs for both swagger and openapi Signed-off-by: ff137 --- UsingOpenAPI.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UsingOpenAPI.md b/UsingOpenAPI.md index 8e589d21a7..51621a885c 100644 --- a/UsingOpenAPI.md +++ b/UsingOpenAPI.md @@ -8,7 +8,7 @@ The running agent provides a `Swagger User Interface` that can be browsed and us ACA-Py uses [aiohttp_apispec](https://github.com/maximdanilchenko/aiohttp-apispec) tags in code to produce the OpenAPI spec file at runtime dependent on what features have been loaded. How these tags are created is documented in the [API Standard Behaviour](https://github.com/hyperledger/aries-cloudagent-python/blob/main/AdminAPI.md#api-standard-behaviour) section of the [Admin API Readme](AdminAPI.md). The OpenAPI spec is available in raw, unformated form from a running ACA-py instance using a route of `http:///api/docs/swagger.json` or from the browser `Swagger User Interface` directly. -To help identify changes in the ACA-Py Admin API over releases, there is a tool that can be run located at `scripts/generate-open-api-spec`. This tool will start ACA-Py, pull the `swagger.json` file, run a codegen tool, and specify a language output of `json`. Apart from providing a better format to compare changes (i.e., by comparing this output to the checked-in `open-api/openapi.json` version), the tool can be used to identify any non-conformance to the OpenAPI specification. At the moment, `validation` is turned off via the `open-api/openAPIJSON.config` file, so warning messages are printed for non-conformance, but the `json` is still output. Most of the warnings reported by `generate-open-api-spec` relate to missing `operationId` fields which results in manufactured method names being created by codegen tools. At the moment, [aiohttp_apispec](https://github.com/maximdanilchenko/aiohttp-apispec) does not support adding `operationId` annotations via tags. +The ACA-py Admin API evolves across releases. To track these changes and ensure conformance with the OpenAPI specification, we provide a tool located at [`scripts/generate-open-api-spec`](scripts/generate-open-api-spec). This tool starts ACA-py, retrieves the `swagger.json` file, and runs codegen tools to generate specifications in both Swagger and OpenAPI formats with `json` language output. The output of this tool enables comparison with the checked-in `open-api/swagger.json` and `open-api/openapi.json`, and also serves as a useful resource for identifying any non-conformance to the OpenAPI specification. At the moment `validation` is turned off via the `open-api/openAPIJSON.config` file so that warning messages are printed for non-conformance but the `json` is still output. Most of the warnings reported by `generate-open-api-spec` relate to missing `operationId` fields which results in manufactured method names being created by codgen tools. At the moment [aiohttp_apispec](https://github.com/maximdanilchenko/aiohttp-apispec) does not support adding `operationId` anotations via tags. The `generate-open-api-spec` tool was initially created to help identify issues with method parameters not being sorted, resulting in somewhat random ordering each time a codegen operation was performed. This is relevent for languages which do not have support for [named parameters](https://en.wikipedia.org/wiki/Named_parameter) such as `Javascript`. It is recomended that the `generate-open-api-spec` is run prior to each release and the resulting `open-api/openapi.json` file checked in to allow tracking of API changes over time. At the moment this process is not automated as part of the release pipeline. From aff54e1359336a583df6dcee9a06dbde27c9291e Mon Sep 17 00:00:00 2001 From: ff137 Date: Sat, 27 May 2023 13:44:48 +0200 Subject: [PATCH 806/872] add tracking for Swagger 2.0 spec Signed-off-by: ff137 --- open-api/swagger.json | 12220 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 12220 insertions(+) create mode 100644 open-api/swagger.json diff --git a/open-api/swagger.json b/open-api/swagger.json new file mode 100644 index 0000000000..bebb794b6c --- /dev/null +++ b/open-api/swagger.json @@ -0,0 +1,12220 @@ +{ + "swagger" : "2.0", + "info" : { + "version" : "v0.8.1", + "title" : "Aries Cloud Agent" + }, + "tags" : [ { + "name" : "action-menu", + "description" : "Menu interaction over connection" + }, { + "name" : "basicmessage", + "description" : "Simple messaging", + "externalDocs" : { + "description" : "Specification", + "url" : "https://github.com/hyperledger/aries-rfcs/tree/527849ec3aa2a8fd47a7bb6c57f918ff8bcb5e8c/features/0095-basic-message" + } + }, { + "name" : "connection", + "description" : "Connection management", + "externalDocs" : { + "description" : "Specification", + "url" : "https://github.com/hyperledger/aries-rfcs/tree/9b0aaa39df7e8bd434126c4b33c097aae78d65bf/features/0160-connection-protocol" + } + }, { + "name" : "credential-definition", + "description" : "Credential definition operations", + "externalDocs" : { + "description" : "Specification", + "url" : "https://github.com/hyperledger/indy-node/blob/master/design/anoncreds.md#cred_def" + } + }, { + "name" : "credentials", + "description" : "Holder credential management", + "externalDocs" : { + "description" : "Overview", + "url" : "https://w3c.github.io/vc-data-model/#credentials" + } + }, { + "name" : "did-exchange", + "description" : "Connection management via DID exchange", + "externalDocs" : { + "description" : "Specification", + "url" : "https://github.com/hyperledger/aries-rfcs/tree/25464a5c8f8a17b14edaa4310393df6094ace7b0/features/0023-did-exchange" + } + }, { + "name" : "discover-features", + "description" : "Feature discovery", + "externalDocs" : { + "description" : "Specification", + "url" : "https://github.com/hyperledger/aries-rfcs/tree/b3a3942ef052039e73cd23d847f42947f8287da2/features/0031-discover-features" + } + }, { + "name" : "discover-features v2.0", + "description" : "Feature discovery v2", + "externalDocs" : { + "description" : "Specification", + "url" : "https://github.com/hyperledger/aries-rfcs/tree/b3a3942ef052039e73cd23d847f42947f8287da2/features/0557-discover-features-v2" + } + }, { + "name" : "endorse-transaction", + "description" : "Endorse a Transaction" + }, { + "name" : "introduction", + "description" : "Introduction of known parties" + }, { + "name" : "issue-credential v1.0", + "description" : "Credential issue v1.0", + "externalDocs" : { + "description" : "Specification", + "url" : "https://github.com/hyperledger/aries-rfcs/tree/bb42a6c35e0d5543718fb36dd099551ab192f7b0/features/0036-issue-credential" + } + }, { + "name" : "issue-credential v2.0", + "description" : "Credential issue v2.0", + "externalDocs" : { + "description" : "Specification", + "url" : "https://github.com/hyperledger/aries-rfcs/tree/cd27fc64aa2805f756a118043d7c880354353047/features/0453-issue-credential-v2" + } + }, { + "name" : "jsonld", + "description" : "Sign and verify json-ld data", + "externalDocs" : { + "description" : "Specification", + "url" : "https://tools.ietf.org/html/rfc7515" + } + }, { + "name" : "ledger", + "description" : "Interaction with ledger", + "externalDocs" : { + "description" : "Overview", + "url" : "https://hyperledger-indy.readthedocs.io/projects/plenum/en/latest/storage.html#ledger" + } + }, { + "name" : "mediation", + "description" : "Mediation management", + "externalDocs" : { + "description" : "Specification", + "url" : "https://github.com/hyperledger/aries-rfcs/tree/fa8dc4ea1e667eb07db8f9ffeaf074a4455697c0/features/0211-route-coordination" + } + }, { + "name" : "multitenancy", + "description" : "Multitenant wallet management" + }, { + "name" : "out-of-band", + "description" : "Out-of-band connections", + "externalDocs" : { + "description" : "Design", + "url" : "https://github.com/hyperledger/aries-rfcs/tree/2da7fc4ee043effa3a9960150e7ba8c9a4628b68/features/0434-outofband" + } + }, { + "name" : "present-proof v1.0", + "description" : "Proof presentation v1.0", + "externalDocs" : { + "description" : "Specification", + "url" : "https://github.com/hyperledger/aries-rfcs/tree/4fae574c03f9f1013db30bf2c0c676b1122f7149/features/0037-present-proof" + } + }, { + "name" : "present-proof v2.0", + "description" : "Proof presentation v2.0", + "externalDocs" : { + "description" : "Specification", + "url" : "https://github.com/hyperledger/aries-rfcs/tree/eace815c3e8598d4a8dd7881d8c731fdb2bcc0aa/features/0454-present-proof-v2" + } + }, { + "name" : "resolver", + "description" : "did resolver interface.", + "externalDocs" : { + "description" : "Specification" + } + }, { + "name" : "revocation", + "description" : "Revocation registry management", + "externalDocs" : { + "description" : "Overview", + "url" : "https://github.com/hyperledger/indy-hipe/tree/master/text/0011-cred-revocation" + } + }, { + "name" : "schema", + "description" : "Schema operations", + "externalDocs" : { + "description" : "Specification", + "url" : "https://github.com/hyperledger/indy-node/blob/master/design/anoncreds.md#schema" + } + }, { + "name" : "trustping", + "description" : "Trust-ping over connection", + "externalDocs" : { + "description" : "Specification", + "url" : "https://github.com/hyperledger/aries-rfcs/tree/527849ec3aa2a8fd47a7bb6c57f918ff8bcb5e8c/features/0048-trust-ping" + } + }, { + "name" : "wallet", + "description" : "DID and tag policy management", + "externalDocs" : { + "description" : "Design", + "url" : "https://github.com/hyperledger/indy-sdk/tree/master/docs/design/003-wallet-storage" + } + } ], + "security" : [ { + "AuthorizationHeader" : [ ] + } ], + "paths" : { + "/action-menu/{conn_id}/close" : { + "post" : { + "tags" : [ "action-menu" ], + "summary" : "Close the active menu associated with a connection", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "conn_id", + "in" : "path", + "description" : "Connection identifier", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/ActionMenuModulesResult" + } + } + } + } + }, + "/action-menu/{conn_id}/fetch" : { + "post" : { + "tags" : [ "action-menu" ], + "summary" : "Fetch the active menu", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "conn_id", + "in" : "path", + "description" : "Connection identifier", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/ActionMenuFetchResult" + } + } + } + } + }, + "/action-menu/{conn_id}/perform" : { + "post" : { + "tags" : [ "action-menu" ], + "summary" : "Perform an action associated with the active menu", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/PerformRequest" + } + }, { + "name" : "conn_id", + "in" : "path", + "description" : "Connection identifier", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/ActionMenuModulesResult" + } + } + } + } + }, + "/action-menu/{conn_id}/request" : { + "post" : { + "tags" : [ "action-menu" ], + "summary" : "Request the active menu", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "conn_id", + "in" : "path", + "description" : "Connection identifier", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/ActionMenuModulesResult" + } + } + } + } + }, + "/action-menu/{conn_id}/send-menu" : { + "post" : { + "tags" : [ "action-menu" ], + "summary" : "Send an action menu to a connection", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/SendMenu" + } + }, { + "name" : "conn_id", + "in" : "path", + "description" : "Connection identifier", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/ActionMenuModulesResult" + } + } + } + } + }, + "/connections" : { + "get" : { + "tags" : [ "connection" ], + "summary" : "Query agent-to-agent connections", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "alias", + "in" : "query", + "description" : "Alias", + "required" : false, + "type" : "string" + }, { + "name" : "connection_protocol", + "in" : "query", + "description" : "Connection protocol used", + "required" : false, + "type" : "string", + "enum" : [ "connections/1.0", "didexchange/1.0" ] + }, { + "name" : "invitation_key", + "in" : "query", + "description" : "invitation key", + "required" : false, + "type" : "string", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" + }, { + "name" : "invitation_msg_id", + "in" : "query", + "description" : "Identifier of the associated Invitation Mesage", + "required" : false, + "type" : "string", + "format" : "uuid" + }, { + "name" : "my_did", + "in" : "query", + "description" : "My DID", + "required" : false, + "type" : "string", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + }, { + "name" : "state", + "in" : "query", + "description" : "Connection state", + "required" : false, + "type" : "string", + "enum" : [ "start", "invitation", "request", "abandoned", "error", "init", "response", "active", "completed" ] + }, { + "name" : "their_did", + "in" : "query", + "description" : "Their DID", + "required" : false, + "type" : "string", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + }, { + "name" : "their_public_did", + "in" : "query", + "description" : "Their Public DID", + "required" : false, + "type" : "string", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + }, { + "name" : "their_role", + "in" : "query", + "description" : "Their role in the connection protocol", + "required" : false, + "type" : "string", + "enum" : [ "invitee", "requester", "inviter", "responder" ] + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/ConnectionList" + } + } + } + } + }, + "/connections/create-invitation" : { + "post" : { + "tags" : [ "connection" ], + "summary" : "Create a new connection invitation", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/CreateInvitationRequest" + } + }, { + "name" : "alias", + "in" : "query", + "description" : "Alias", + "required" : false, + "type" : "string" + }, { + "name" : "auto_accept", + "in" : "query", + "description" : "Auto-accept connection (defaults to configuration)", + "required" : false, + "type" : "boolean" + }, { + "name" : "multi_use", + "in" : "query", + "description" : "Create invitation for multiple use (default false)", + "required" : false, + "type" : "boolean" + }, { + "name" : "public", + "in" : "query", + "description" : "Create invitation from public DID (default false)", + "required" : false, + "type" : "boolean" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/InvitationResult" + } + } + } + } + }, + "/connections/create-static" : { + "post" : { + "tags" : [ "connection" ], + "summary" : "Create a new static connection", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/ConnectionStaticRequest" + } + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/ConnectionStaticResult" + } + } + } + } + }, + "/connections/receive-invitation" : { + "post" : { + "tags" : [ "connection" ], + "summary" : "Receive a new connection invitation", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/ReceiveInvitationRequest" + } + }, { + "name" : "alias", + "in" : "query", + "description" : "Alias", + "required" : false, + "type" : "string" + }, { + "name" : "auto_accept", + "in" : "query", + "description" : "Auto-accept connection (defaults to configuration)", + "required" : false, + "type" : "boolean" + }, { + "name" : "mediation_id", + "in" : "query", + "description" : "Identifier for active mediation record to be used", + "required" : false, + "type" : "string", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/ConnRecord" + } + } + } + } + }, + "/connections/{conn_id}" : { + "get" : { + "tags" : [ "connection" ], + "summary" : "Fetch a single connection record", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "conn_id", + "in" : "path", + "description" : "Connection identifier", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/ConnRecord" + } + } + } + }, + "delete" : { + "tags" : [ "connection" ], + "summary" : "Remove an existing connection record", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "conn_id", + "in" : "path", + "description" : "Connection identifier", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/ConnectionModuleResponse" + } + } + } + } + }, + "/connections/{conn_id}/accept-invitation" : { + "post" : { + "tags" : [ "connection" ], + "summary" : "Accept a stored connection invitation", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "conn_id", + "in" : "path", + "description" : "Connection identifier", + "required" : true, + "type" : "string" + }, { + "name" : "mediation_id", + "in" : "query", + "description" : "Identifier for active mediation record to be used", + "required" : false, + "type" : "string", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + }, { + "name" : "my_endpoint", + "in" : "query", + "description" : "My URL endpoint", + "required" : false, + "type" : "string", + "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" + }, { + "name" : "my_label", + "in" : "query", + "description" : "Label for connection", + "required" : false, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/ConnRecord" + } + } + } + } + }, + "/connections/{conn_id}/accept-request" : { + "post" : { + "tags" : [ "connection" ], + "summary" : "Accept a stored connection request", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "conn_id", + "in" : "path", + "description" : "Connection identifier", + "required" : true, + "type" : "string" + }, { + "name" : "my_endpoint", + "in" : "query", + "description" : "My URL endpoint", + "required" : false, + "type" : "string", + "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/ConnRecord" + } + } + } + } + }, + "/connections/{conn_id}/endpoints" : { + "get" : { + "tags" : [ "connection" ], + "summary" : "Fetch connection remote endpoint", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "conn_id", + "in" : "path", + "description" : "Connection identifier", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/EndpointsResult" + } + } + } + } + }, + "/connections/{conn_id}/establish-inbound/{ref_id}" : { + "post" : { + "tags" : [ "connection" ], + "summary" : "Assign another connection as the inbound connection", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "conn_id", + "in" : "path", + "description" : "Connection identifier", + "required" : true, + "type" : "string" + }, { + "name" : "ref_id", + "in" : "path", + "description" : "Inbound connection identifier", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/ConnectionModuleResponse" + } + } + } + } + }, + "/connections/{conn_id}/metadata" : { + "get" : { + "tags" : [ "connection" ], + "summary" : "Fetch connection metadata", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "conn_id", + "in" : "path", + "description" : "Connection identifier", + "required" : true, + "type" : "string" + }, { + "name" : "key", + "in" : "query", + "description" : "Key to retrieve.", + "required" : false, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/ConnectionMetadata" + } + } + } + }, + "post" : { + "tags" : [ "connection" ], + "summary" : "Set connection metadata", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/ConnectionMetadataSetRequest" + } + }, { + "name" : "conn_id", + "in" : "path", + "description" : "Connection identifier", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/ConnectionMetadata" + } + } + } + } + }, + "/connections/{conn_id}/send-message" : { + "post" : { + "tags" : [ "basicmessage" ], + "summary" : "Send a basic message to a connection", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/SendMessage" + } + }, { + "name" : "conn_id", + "in" : "path", + "description" : "Connection identifier", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/BasicMessageModuleResponse" + } + } + } + } + }, + "/connections/{conn_id}/send-ping" : { + "post" : { + "tags" : [ "trustping" ], + "summary" : "Send a trust ping to a connection", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/PingRequest" + } + }, { + "name" : "conn_id", + "in" : "path", + "description" : "Connection identifier", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/PingRequestResponse" + } + } + } + } + }, + "/connections/{conn_id}/start-introduction" : { + "post" : { + "tags" : [ "introduction" ], + "summary" : "Start an introduction between two connections", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "conn_id", + "in" : "path", + "description" : "Connection identifier", + "required" : true, + "type" : "string" + }, { + "name" : "target_connection_id", + "in" : "query", + "description" : "Target connection identifier", + "required" : true, + "type" : "string" + }, { + "name" : "message", + "in" : "query", + "description" : "Message", + "required" : false, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/IntroModuleResponse" + } + } + } + } + }, + "/credential-definitions" : { + "post" : { + "tags" : [ "credential-definition" ], + "summary" : "Sends a credential definition to the ledger", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/CredentialDefinitionSendRequest" + } + }, { + "name" : "conn_id", + "in" : "query", + "description" : "Connection identifier", + "required" : false, + "type" : "string" + }, { + "name" : "create_transaction_for_endorser", + "in" : "query", + "description" : "Create Transaction For Endorser's signature", + "required" : false, + "type" : "boolean" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/TxnOrCredentialDefinitionSendResult" + } + } + } + } + }, + "/credential-definitions/created" : { + "get" : { + "tags" : [ "credential-definition" ], + "summary" : "Search for matching credential definitions that agent originated", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "cred_def_id", + "in" : "query", + "description" : "Credential definition id", + "required" : false, + "type" : "string", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" + }, { + "name" : "issuer_did", + "in" : "query", + "description" : "Issuer DID", + "required" : false, + "type" : "string", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + }, { + "name" : "schema_id", + "in" : "query", + "description" : "Schema identifier", + "required" : false, + "type" : "string", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" + }, { + "name" : "schema_issuer_did", + "in" : "query", + "description" : "Schema issuer DID", + "required" : false, + "type" : "string", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + }, { + "name" : "schema_name", + "in" : "query", + "description" : "Schema name", + "required" : false, + "type" : "string" + }, { + "name" : "schema_version", + "in" : "query", + "description" : "Schema version", + "required" : false, + "type" : "string", + "pattern" : "^[0-9.]+$" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/CredentialDefinitionsCreatedResult" + } + } + } + } + }, + "/credential-definitions/{cred_def_id}" : { + "get" : { + "tags" : [ "credential-definition" ], + "summary" : "Gets a credential definition from the ledger", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "cred_def_id", + "in" : "path", + "description" : "Credential definition identifier", + "required" : true, + "type" : "string", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/CredentialDefinitionGetResult" + } + } + } + } + }, + "/credential-definitions/{cred_def_id}/write_record" : { + "post" : { + "tags" : [ "credential-definition" ], + "summary" : "Writes a credential definition non-secret record to the wallet", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "cred_def_id", + "in" : "path", + "description" : "Credential definition identifier", + "required" : true, + "type" : "string", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/CredentialDefinitionGetResult" + } + } + } + } + }, + "/credential/mime-types/{credential_id}" : { + "get" : { + "tags" : [ "credentials" ], + "summary" : "Get attribute MIME types from wallet", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "credential_id", + "in" : "path", + "description" : "Credential identifier", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/AttributeMimeTypesResult" + } + } + } + } + }, + "/credential/revoked/{credential_id}" : { + "get" : { + "tags" : [ "credentials" ], + "summary" : "Query credential revocation status by id", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "credential_id", + "in" : "path", + "description" : "Credential identifier", + "required" : true, + "type" : "string" + }, { + "name" : "from", + "in" : "query", + "description" : "Earliest epoch of revocation status interval of interest", + "required" : false, + "type" : "string", + "pattern" : "^[0-9]*$" + }, { + "name" : "to", + "in" : "query", + "description" : "Latest epoch of revocation status interval of interest", + "required" : false, + "type" : "string", + "pattern" : "^[0-9]*$" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/CredRevokedResult" + } + } + } + } + }, + "/credential/w3c/{credential_id}" : { + "get" : { + "tags" : [ "credentials" ], + "summary" : "Fetch W3C credential from wallet by id", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "credential_id", + "in" : "path", + "description" : "Credential identifier", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/VCRecord" + } + } + } + }, + "delete" : { + "tags" : [ "credentials" ], + "summary" : "Remove W3C credential from wallet by id", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "credential_id", + "in" : "path", + "description" : "Credential identifier", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/HolderModuleResponse" + } + } + } + } + }, + "/credential/{credential_id}" : { + "get" : { + "tags" : [ "credentials" ], + "summary" : "Fetch credential from wallet by id", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "credential_id", + "in" : "path", + "description" : "Credential identifier", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/IndyCredInfo" + } + } + } + }, + "delete" : { + "tags" : [ "credentials" ], + "summary" : "Remove credential from wallet by id", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "credential_id", + "in" : "path", + "description" : "Credential identifier", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/HolderModuleResponse" + } + } + } + } + }, + "/credentials" : { + "get" : { + "tags" : [ "credentials" ], + "summary" : "Fetch credentials from wallet", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "count", + "in" : "query", + "description" : "Maximum number to retrieve", + "required" : false, + "type" : "string", + "pattern" : "^[1-9][0-9]*$" + }, { + "name" : "start", + "in" : "query", + "description" : "Start index", + "required" : false, + "type" : "string", + "pattern" : "^[0-9]*$" + }, { + "name" : "wql", + "in" : "query", + "description" : "(JSON) WQL query", + "required" : false, + "type" : "string", + "pattern" : "^{.*}$" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/CredInfoList" + } + } + } + } + }, + "/credentials/w3c" : { + "post" : { + "tags" : [ "credentials" ], + "summary" : "Fetch W3C credentials from wallet", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/W3CCredentialsListRequest" + } + }, { + "name" : "count", + "in" : "query", + "description" : "Maximum number to retrieve", + "required" : false, + "type" : "string", + "pattern" : "^[1-9][0-9]*$" + }, { + "name" : "start", + "in" : "query", + "description" : "Start index", + "required" : false, + "type" : "string", + "pattern" : "^[0-9]*$" + }, { + "name" : "wql", + "in" : "query", + "description" : "(JSON) WQL query", + "required" : false, + "type" : "string", + "pattern" : "^{.*}$" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/VCRecordList" + } + } + } + } + }, + "/didexchange/create-request" : { + "post" : { + "tags" : [ "did-exchange" ], + "summary" : "Create and send a request against public DID's implicit invitation", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "their_public_did", + "in" : "query", + "description" : "Qualified public DID to which to request connection", + "required" : true, + "type" : "string", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$|^did:([a-zA-Z0-9_]+):([a-zA-Z0-9_.%-]+(:[a-zA-Z0-9_.%-]+)*)((;[a-zA-Z0-9_.:%-]+=[a-zA-Z0-9_.:%-]*)*)(\\/[^#?]*)?([?][^#]*)?(\\#.*)?$$" + }, { + "name" : "alias", + "in" : "query", + "description" : "Alias for connection", + "required" : false, + "type" : "string" + }, { + "name" : "mediation_id", + "in" : "query", + "description" : "Identifier for active mediation record to be used", + "required" : false, + "type" : "string", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + }, { + "name" : "my_endpoint", + "in" : "query", + "description" : "My URL endpoint", + "required" : false, + "type" : "string", + "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" + }, { + "name" : "my_label", + "in" : "query", + "description" : "Label for connection request", + "required" : false, + "type" : "string" + }, { + "name" : "use_public_did", + "in" : "query", + "description" : "Use public DID for this connection", + "required" : false, + "type" : "boolean" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/ConnRecord" + } + } + } + } + }, + "/didexchange/receive-request" : { + "post" : { + "tags" : [ "did-exchange" ], + "summary" : "Receive request against public DID's implicit invitation", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/DIDXRequest" + } + }, { + "name" : "alias", + "in" : "query", + "description" : "Alias for connection", + "required" : false, + "type" : "string" + }, { + "name" : "auto_accept", + "in" : "query", + "description" : "Auto-accept connection (defaults to configuration)", + "required" : false, + "type" : "boolean" + }, { + "name" : "mediation_id", + "in" : "query", + "description" : "Identifier for active mediation record to be used", + "required" : false, + "type" : "string", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + }, { + "name" : "my_endpoint", + "in" : "query", + "description" : "My URL endpoint", + "required" : false, + "type" : "string", + "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/ConnRecord" + } + } + } + } + }, + "/didexchange/{conn_id}/accept-invitation" : { + "post" : { + "tags" : [ "did-exchange" ], + "summary" : "Accept a stored connection invitation", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "conn_id", + "in" : "path", + "description" : "Connection identifier", + "required" : true, + "type" : "string" + }, { + "name" : "my_endpoint", + "in" : "query", + "description" : "My URL endpoint", + "required" : false, + "type" : "string", + "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" + }, { + "name" : "my_label", + "in" : "query", + "description" : "Label for connection request", + "required" : false, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/ConnRecord" + } + } + } + } + }, + "/didexchange/{conn_id}/accept-request" : { + "post" : { + "tags" : [ "did-exchange" ], + "summary" : "Accept a stored connection request", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "conn_id", + "in" : "path", + "description" : "Connection identifier", + "required" : true, + "type" : "string" + }, { + "name" : "mediation_id", + "in" : "query", + "description" : "Identifier for active mediation record to be used", + "required" : false, + "type" : "string", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + }, { + "name" : "my_endpoint", + "in" : "query", + "description" : "My URL endpoint", + "required" : false, + "type" : "string", + "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/ConnRecord" + } + } + } + } + }, + "/discover-features-2.0/queries" : { + "get" : { + "tags" : [ "discover-features v2.0" ], + "summary" : "Query supported features", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "connection_id", + "in" : "query", + "description" : "Connection identifier, if none specified, then the query will provide features for this agent.", + "required" : false, + "type" : "string" + }, { + "name" : "query_goal_code", + "in" : "query", + "description" : "Goal-code feature-type query", + "required" : false, + "type" : "string" + }, { + "name" : "query_protocol", + "in" : "query", + "description" : "Protocol feature-type query", + "required" : false, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V20DiscoveryExchangeResult" + } + } + } + } + }, + "/discover-features-2.0/records" : { + "get" : { + "tags" : [ "discover-features v2.0" ], + "summary" : "Discover Features v2.0 records", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "connection_id", + "in" : "query", + "description" : "Connection identifier", + "required" : false, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V20DiscoveryExchangeListResult" + } + } + } + } + }, + "/discover-features/query" : { + "get" : { + "tags" : [ "discover-features" ], + "summary" : "Query supported features", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "comment", + "in" : "query", + "description" : "Comment", + "required" : false, + "type" : "string" + }, { + "name" : "connection_id", + "in" : "query", + "description" : "Connection identifier, if none specified, then the query will provide features for this agent.", + "required" : false, + "type" : "string" + }, { + "name" : "query", + "in" : "query", + "description" : "Protocol feature query", + "required" : false, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V10DiscoveryRecord" + } + } + } + } + }, + "/discover-features/records" : { + "get" : { + "tags" : [ "discover-features" ], + "summary" : "Discover Features records", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "connection_id", + "in" : "query", + "description" : "Connection identifier", + "required" : false, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V10DiscoveryExchangeListResult" + } + } + } + } + }, + "/issue-credential-2.0/create" : { + "post" : { + "tags" : [ "issue-credential v2.0" ], + "summary" : "Create a credential record without sending (generally for use with Out-Of-Band)", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/V20IssueCredSchemaCore" + } + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V20CredExRecord" + } + } + } + } + }, + "/issue-credential-2.0/create-offer" : { + "post" : { + "tags" : [ "issue-credential v2.0" ], + "summary" : "Create a credential offer, independent of any proposal or connection", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/V20CredOfferConnFreeRequest" + } + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V20CredExRecord" + } + } + } + } + }, + "/issue-credential-2.0/records" : { + "get" : { + "tags" : [ "issue-credential v2.0" ], + "summary" : "Fetch all credential exchange records", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "connection_id", + "in" : "query", + "description" : "Connection identifier", + "required" : false, + "type" : "string", + "format" : "uuid" + }, { + "name" : "role", + "in" : "query", + "description" : "Role assigned in credential exchange", + "required" : false, + "type" : "string", + "enum" : [ "issuer", "holder" ] + }, { + "name" : "state", + "in" : "query", + "description" : "Credential exchange state", + "required" : false, + "type" : "string", + "enum" : [ "proposal-sent", "proposal-received", "offer-sent", "offer-received", "request-sent", "request-received", "credential-issued", "credential-received", "done", "credential-revoked", "abandoned" ] + }, { + "name" : "thread_id", + "in" : "query", + "description" : "Thread identifier", + "required" : false, + "type" : "string", + "format" : "uuid" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V20CredExRecordListResult" + } + } + } + } + }, + "/issue-credential-2.0/records/{cred_ex_id}" : { + "get" : { + "tags" : [ "issue-credential v2.0" ], + "summary" : "Fetch a single credential exchange record", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "cred_ex_id", + "in" : "path", + "description" : "Credential exchange identifier", + "required" : true, + "type" : "string", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V20CredExRecordDetail" + } + } + } + }, + "delete" : { + "tags" : [ "issue-credential v2.0" ], + "summary" : "Remove an existing credential exchange record", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "cred_ex_id", + "in" : "path", + "description" : "Credential exchange identifier", + "required" : true, + "type" : "string", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V20IssueCredentialModuleResponse" + } + } + } + } + }, + "/issue-credential-2.0/records/{cred_ex_id}/issue" : { + "post" : { + "tags" : [ "issue-credential v2.0" ], + "summary" : "Send holder a credential", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/V20CredIssueRequest" + } + }, { + "name" : "cred_ex_id", + "in" : "path", + "description" : "Credential exchange identifier", + "required" : true, + "type" : "string", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V20CredExRecordDetail" + } + } + } + } + }, + "/issue-credential-2.0/records/{cred_ex_id}/problem-report" : { + "post" : { + "tags" : [ "issue-credential v2.0" ], + "summary" : "Send a problem report for credential exchange", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/V20CredIssueProblemReportRequest" + } + }, { + "name" : "cred_ex_id", + "in" : "path", + "description" : "Credential exchange identifier", + "required" : true, + "type" : "string", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V20IssueCredentialModuleResponse" + } + } + } + } + }, + "/issue-credential-2.0/records/{cred_ex_id}/send-offer" : { + "post" : { + "tags" : [ "issue-credential v2.0" ], + "summary" : "Send holder a credential offer in reference to a proposal with preview", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/V20CredBoundOfferRequest" + } + }, { + "name" : "cred_ex_id", + "in" : "path", + "description" : "Credential exchange identifier", + "required" : true, + "type" : "string", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V20CredExRecord" + } + } + } + } + }, + "/issue-credential-2.0/records/{cred_ex_id}/send-request" : { + "post" : { + "tags" : [ "issue-credential v2.0" ], + "summary" : "Send issuer a credential request", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/V20CredRequestRequest" + } + }, { + "name" : "cred_ex_id", + "in" : "path", + "description" : "Credential exchange identifier", + "required" : true, + "type" : "string", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V20CredExRecord" + } + } + } + } + }, + "/issue-credential-2.0/records/{cred_ex_id}/store" : { + "post" : { + "tags" : [ "issue-credential v2.0" ], + "summary" : "Store a received credential", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/V20CredStoreRequest" + } + }, { + "name" : "cred_ex_id", + "in" : "path", + "description" : "Credential exchange identifier", + "required" : true, + "type" : "string", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V20CredExRecordDetail" + } + } + } + } + }, + "/issue-credential-2.0/send" : { + "post" : { + "tags" : [ "issue-credential v2.0" ], + "summary" : "Send holder a credential, automating entire flow", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/V20CredExFree" + } + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V20CredExRecord" + } + } + } + } + }, + "/issue-credential-2.0/send-offer" : { + "post" : { + "tags" : [ "issue-credential v2.0" ], + "summary" : "Send holder a credential offer, independent of any proposal", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/V20CredOfferRequest" + } + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V20CredExRecord" + } + } + } + } + }, + "/issue-credential-2.0/send-proposal" : { + "post" : { + "tags" : [ "issue-credential v2.0" ], + "summary" : "Send issuer a credential proposal", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/V20CredExFree" + } + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V20CredExRecord" + } + } + } + } + }, + "/issue-credential-2.0/send-request" : { + "post" : { + "tags" : [ "issue-credential v2.0" ], + "summary" : "Send issuer a credential request not bound to an existing thread. Indy credentials cannot start at a request", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/V20CredRequestFree" + } + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V20CredExRecord" + } + } + } + } + }, + "/issue-credential/create" : { + "post" : { + "tags" : [ "issue-credential v1.0" ], + "summary" : "Create a credential record without sending (generally for use with Out-Of-Band)", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/V10CredentialCreate" + } + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V10CredentialExchange" + } + } + } + } + }, + "/issue-credential/create-offer" : { + "post" : { + "tags" : [ "issue-credential v1.0" ], + "summary" : "Create a credential offer, independent of any proposal or connection", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/V10CredentialConnFreeOfferRequest" + } + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V10CredentialExchange" + } + } + } + } + }, + "/issue-credential/records" : { + "get" : { + "tags" : [ "issue-credential v1.0" ], + "summary" : "Fetch all credential exchange records", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "connection_id", + "in" : "query", + "description" : "Connection identifier", + "required" : false, + "type" : "string", + "format" : "uuid" + }, { + "name" : "role", + "in" : "query", + "description" : "Role assigned in credential exchange", + "required" : false, + "type" : "string", + "enum" : [ "issuer", "holder" ] + }, { + "name" : "state", + "in" : "query", + "description" : "Credential exchange state", + "required" : false, + "type" : "string", + "enum" : [ "proposal_sent", "proposal_received", "offer_sent", "offer_received", "request_sent", "request_received", "credential_issued", "credential_received", "credential_acked", "credential_revoked", "abandoned" ] + }, { + "name" : "thread_id", + "in" : "query", + "description" : "Thread identifier", + "required" : false, + "type" : "string", + "format" : "uuid" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V10CredentialExchangeListResult" + } + } + } + } + }, + "/issue-credential/records/{cred_ex_id}" : { + "get" : { + "tags" : [ "issue-credential v1.0" ], + "summary" : "Fetch a single credential exchange record", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "cred_ex_id", + "in" : "path", + "description" : "Credential exchange identifier", + "required" : true, + "type" : "string", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V10CredentialExchange" + } + } + } + }, + "delete" : { + "tags" : [ "issue-credential v1.0" ], + "summary" : "Remove an existing credential exchange record", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "cred_ex_id", + "in" : "path", + "description" : "Credential exchange identifier", + "required" : true, + "type" : "string", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/IssueCredentialModuleResponse" + } + } + } + } + }, + "/issue-credential/records/{cred_ex_id}/issue" : { + "post" : { + "tags" : [ "issue-credential v1.0" ], + "summary" : "Send holder a credential", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/V10CredentialIssueRequest" + } + }, { + "name" : "cred_ex_id", + "in" : "path", + "description" : "Credential exchange identifier", + "required" : true, + "type" : "string", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V10CredentialExchange" + } + } + } + } + }, + "/issue-credential/records/{cred_ex_id}/problem-report" : { + "post" : { + "tags" : [ "issue-credential v1.0" ], + "summary" : "Send a problem report for credential exchange", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/V10CredentialProblemReportRequest" + } + }, { + "name" : "cred_ex_id", + "in" : "path", + "description" : "Credential exchange identifier", + "required" : true, + "type" : "string", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/IssueCredentialModuleResponse" + } + } + } + } + }, + "/issue-credential/records/{cred_ex_id}/send-offer" : { + "post" : { + "tags" : [ "issue-credential v1.0" ], + "summary" : "Send holder a credential offer in reference to a proposal with preview", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/V10CredentialBoundOfferRequest" + } + }, { + "name" : "cred_ex_id", + "in" : "path", + "description" : "Credential exchange identifier", + "required" : true, + "type" : "string", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V10CredentialExchange" + } + } + } + } + }, + "/issue-credential/records/{cred_ex_id}/send-request" : { + "post" : { + "tags" : [ "issue-credential v1.0" ], + "summary" : "Send issuer a credential request", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "cred_ex_id", + "in" : "path", + "description" : "Credential exchange identifier", + "required" : true, + "type" : "string", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V10CredentialExchange" + } + } + } + } + }, + "/issue-credential/records/{cred_ex_id}/store" : { + "post" : { + "tags" : [ "issue-credential v1.0" ], + "summary" : "Store a received credential", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/V10CredentialStoreRequest" + } + }, { + "name" : "cred_ex_id", + "in" : "path", + "description" : "Credential exchange identifier", + "required" : true, + "type" : "string", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V10CredentialExchange" + } + } + } + } + }, + "/issue-credential/send" : { + "post" : { + "tags" : [ "issue-credential v1.0" ], + "summary" : "Send holder a credential, automating entire flow", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/V10CredentialProposalRequestMand" + } + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V10CredentialExchange" + } + } + } + } + }, + "/issue-credential/send-offer" : { + "post" : { + "tags" : [ "issue-credential v1.0" ], + "summary" : "Send holder a credential offer, independent of any proposal", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/V10CredentialFreeOfferRequest" + } + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V10CredentialExchange" + } + } + } + } + }, + "/issue-credential/send-proposal" : { + "post" : { + "tags" : [ "issue-credential v1.0" ], + "summary" : "Send issuer a credential proposal", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/V10CredentialProposalRequestOpt" + } + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V10CredentialExchange" + } + } + } + } + }, + "/jsonld/sign" : { + "post" : { + "tags" : [ "jsonld" ], + "summary" : "Sign a JSON-LD structure and return it", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/SignRequest" + } + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/SignResponse" + } + } + } + } + }, + "/jsonld/verify" : { + "post" : { + "tags" : [ "jsonld" ], + "summary" : "Verify a JSON-LD structure.", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/VerifyRequest" + } + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/VerifyResponse" + } + } + } + } + }, + "/ledger/did-endpoint" : { + "get" : { + "tags" : [ "ledger" ], + "summary" : "Get the endpoint for a DID from the ledger.", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "did", + "in" : "query", + "description" : "DID of interest", + "required" : true, + "type" : "string", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + }, { + "name" : "endpoint_type", + "in" : "query", + "description" : "Endpoint type of interest (default 'Endpoint')", + "required" : false, + "type" : "string", + "enum" : [ "Endpoint", "Profile", "LinkedDomains" ] + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/GetDIDEndpointResponse" + } + } + } + } + }, + "/ledger/did-verkey" : { + "get" : { + "tags" : [ "ledger" ], + "summary" : "Get the verkey for a DID from the ledger.", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "did", + "in" : "query", + "description" : "DID of interest", + "required" : true, + "type" : "string", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/GetDIDVerkeyResponse" + } + } + } + } + }, + "/ledger/get-nym-role" : { + "get" : { + "tags" : [ "ledger" ], + "summary" : "Get the role from the NYM registration of a public DID.", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "did", + "in" : "query", + "description" : "DID of interest", + "required" : true, + "type" : "string", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/GetNymRoleResponse" + } + } + } + } + }, + "/ledger/multiple/config" : { + "get" : { + "tags" : [ "ledger" ], + "summary" : "Fetch the multiple ledger configuration currently in use", + "produces" : [ "application/json" ], + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/LedgerConfigList" + } + } + } + } + }, + "/ledger/multiple/get-write-ledger" : { + "get" : { + "tags" : [ "ledger" ], + "summary" : "Fetch the current write ledger", + "produces" : [ "application/json" ], + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/WriteLedgerRequest" + } + } + } + } + }, + "/ledger/register-nym" : { + "post" : { + "tags" : [ "ledger" ], + "summary" : "Send a NYM registration to the ledger.", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "did", + "in" : "query", + "description" : "DID to register", + "required" : true, + "type" : "string", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + }, { + "name" : "verkey", + "in" : "query", + "description" : "Verification key", + "required" : true, + "type" : "string", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" + }, { + "name" : "alias", + "in" : "query", + "description" : "Alias", + "required" : false, + "type" : "string" + }, { + "name" : "conn_id", + "in" : "query", + "description" : "Connection identifier", + "required" : false, + "type" : "string" + }, { + "name" : "create_transaction_for_endorser", + "in" : "query", + "description" : "Create Transaction For Endorser's signature", + "required" : false, + "type" : "boolean" + }, { + "name" : "role", + "in" : "query", + "description" : "Role", + "required" : false, + "type" : "string", + "enum" : [ "STEWARD", "TRUSTEE", "ENDORSER", "NETWORK_MONITOR", "reset" ] + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/TxnOrRegisterLedgerNymResponse" + } + } + } + } + }, + "/ledger/rotate-public-did-keypair" : { + "patch" : { + "tags" : [ "ledger" ], + "summary" : "Rotate key pair for public DID.", + "produces" : [ "application/json" ], + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/LedgerModulesResult" + } + } + } + } + }, + "/ledger/taa" : { + "get" : { + "tags" : [ "ledger" ], + "summary" : "Fetch the current transaction author agreement, if any", + "produces" : [ "application/json" ], + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/TAAResult" + } + } + } + } + }, + "/ledger/taa/accept" : { + "post" : { + "tags" : [ "ledger" ], + "summary" : "Accept the transaction author agreement", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/TAAAccept" + } + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/LedgerModulesResult" + } + } + } + } + }, + "/mediation/default-mediator" : { + "get" : { + "tags" : [ "mediation" ], + "summary" : "Get default mediator", + "produces" : [ "application/json" ], + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/MediationRecord" + } + } + } + }, + "delete" : { + "tags" : [ "mediation" ], + "summary" : "Clear default mediator", + "produces" : [ "application/json" ], + "parameters" : [ ], + "responses" : { + "201" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/MediationRecord" + } + } + } + } + }, + "/mediation/keylists" : { + "get" : { + "tags" : [ "mediation" ], + "summary" : "Retrieve keylists by connection or role", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "conn_id", + "in" : "query", + "description" : "Connection identifier (optional)", + "required" : false, + "type" : "string", + "format" : "uuid" + }, { + "name" : "role", + "in" : "query", + "description" : "Filer on role, 'client' for keys mediated by other agents, 'server' for keys mediated by this agent", + "required" : false, + "type" : "string", + "default" : "server", + "enum" : [ "client", "server" ] + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/Keylist" + } + } + } + } + }, + "/mediation/keylists/{mediation_id}/send-keylist-query" : { + "post" : { + "tags" : [ "mediation" ], + "summary" : "Send keylist query to mediator", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/KeylistQueryFilterRequest" + } + }, { + "name" : "mediation_id", + "in" : "path", + "description" : "Mediation record identifier", + "required" : true, + "type" : "string", + "format" : "uuid" + }, { + "name" : "paginate_limit", + "in" : "query", + "description" : "limit number of results", + "required" : false, + "type" : "integer", + "default" : -1, + "format" : "int32" + }, { + "name" : "paginate_offset", + "in" : "query", + "description" : "offset to use in pagination", + "required" : false, + "type" : "integer", + "default" : 0, + "format" : "int32" + } ], + "responses" : { + "201" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/KeylistQuery" + } + } + } + } + }, + "/mediation/keylists/{mediation_id}/send-keylist-update" : { + "post" : { + "tags" : [ "mediation" ], + "summary" : "Send keylist update to mediator", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/KeylistUpdateRequest" + } + }, { + "name" : "mediation_id", + "in" : "path", + "description" : "Mediation record identifier", + "required" : true, + "type" : "string", + "format" : "uuid" + } ], + "responses" : { + "201" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/KeylistUpdate" + } + } + } + } + }, + "/mediation/request/{conn_id}" : { + "post" : { + "tags" : [ "mediation" ], + "summary" : "Request mediation from connection", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/MediationCreateRequest" + } + }, { + "name" : "conn_id", + "in" : "path", + "description" : "Connection identifier", + "required" : true, + "type" : "string" + } ], + "responses" : { + "201" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/MediationRecord" + } + } + } + } + }, + "/mediation/requests" : { + "get" : { + "tags" : [ "mediation" ], + "summary" : "Query mediation requests, returns list of all mediation records", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "conn_id", + "in" : "query", + "description" : "Connection identifier (optional)", + "required" : false, + "type" : "string", + "format" : "uuid" + }, { + "name" : "mediator_terms", + "in" : "query", + "description" : "List of mediator rules for recipient", + "required" : false, + "type" : "array", + "items" : { + "type" : "string", + "description" : "Indicate terms to which the mediator requires the recipient to agree" + }, + "collectionFormat" : "multi" + }, { + "name" : "recipient_terms", + "in" : "query", + "description" : "List of recipient rules for mediation", + "required" : false, + "type" : "array", + "items" : { + "type" : "string", + "description" : "Indicate terms to which the recipient requires the mediator to agree" + }, + "collectionFormat" : "multi" + }, { + "name" : "state", + "in" : "query", + "description" : "Mediation state (optional)", + "required" : false, + "type" : "string", + "enum" : [ "request", "granted", "denied" ] + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/MediationList" + } + } + } + } + }, + "/mediation/requests/{mediation_id}" : { + "get" : { + "tags" : [ "mediation" ], + "summary" : "Retrieve mediation request record", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "mediation_id", + "in" : "path", + "description" : "Mediation record identifier", + "required" : true, + "type" : "string", + "format" : "uuid" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/MediationRecord" + } + } + } + }, + "delete" : { + "tags" : [ "mediation" ], + "summary" : "Delete mediation request by ID", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "mediation_id", + "in" : "path", + "description" : "Mediation record identifier", + "required" : true, + "type" : "string", + "format" : "uuid" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/MediationRecord" + } + } + } + } + }, + "/mediation/requests/{mediation_id}/deny" : { + "post" : { + "tags" : [ "mediation" ], + "summary" : "Deny a stored mediation request", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/AdminMediationDeny" + } + }, { + "name" : "mediation_id", + "in" : "path", + "description" : "Mediation record identifier", + "required" : true, + "type" : "string", + "format" : "uuid" + } ], + "responses" : { + "201" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/MediationDeny" + } + } + } + } + }, + "/mediation/requests/{mediation_id}/grant" : { + "post" : { + "tags" : [ "mediation" ], + "summary" : "Grant received mediation", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "mediation_id", + "in" : "path", + "description" : "Mediation record identifier", + "required" : true, + "type" : "string", + "format" : "uuid" + } ], + "responses" : { + "201" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/MediationGrant" + } + } + } + } + }, + "/mediation/update-keylist/{conn_id}" : { + "post" : { + "tags" : [ "mediation" ], + "summary" : "Update keylist for a connection", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/MediationIdMatchInfo" + } + }, { + "name" : "conn_id", + "in" : "path", + "description" : "Connection identifier", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/KeylistUpdate" + } + } + } + } + }, + "/mediation/{mediation_id}/default-mediator" : { + "put" : { + "tags" : [ "mediation" ], + "summary" : "Set default mediator", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "mediation_id", + "in" : "path", + "description" : "Mediation record identifier", + "required" : true, + "type" : "string", + "format" : "uuid" + } ], + "responses" : { + "201" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/MediationRecord" + } + } + } + } + }, + "/multitenancy/wallet" : { + "post" : { + "tags" : [ "multitenancy" ], + "summary" : "Create a subwallet", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/CreateWalletRequest" + } + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/CreateWalletResponse" + } + } + } + } + }, + "/multitenancy/wallet/{wallet_id}" : { + "get" : { + "tags" : [ "multitenancy" ], + "summary" : "Get a single subwallet", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "wallet_id", + "in" : "path", + "description" : "Subwallet identifier", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/WalletRecord" + } + } + } + }, + "put" : { + "tags" : [ "multitenancy" ], + "summary" : "Update a subwallet", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/UpdateWalletRequest" + } + }, { + "name" : "wallet_id", + "in" : "path", + "description" : "Subwallet identifier", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/WalletRecord" + } + } + } + } + }, + "/multitenancy/wallet/{wallet_id}/remove" : { + "post" : { + "tags" : [ "multitenancy" ], + "summary" : "Remove a subwallet", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/RemoveWalletRequest" + } + }, { + "name" : "wallet_id", + "in" : "path", + "description" : "Subwallet identifier", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/MultitenantModuleResponse" + } + } + } + } + }, + "/multitenancy/wallet/{wallet_id}/token" : { + "post" : { + "tags" : [ "multitenancy" ], + "summary" : "Get auth token for a subwallet", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/CreateWalletTokenRequest" + } + }, { + "name" : "wallet_id", + "in" : "path", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/CreateWalletTokenResponse" + } + } + } + } + }, + "/multitenancy/wallets" : { + "get" : { + "tags" : [ "multitenancy" ], + "summary" : "Query subwallets", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "wallet_name", + "in" : "query", + "description" : "Wallet name", + "required" : false, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/WalletList" + } + } + } + } + }, + "/out-of-band/create-invitation" : { + "post" : { + "tags" : [ "out-of-band" ], + "summary" : "Create a new connection invitation", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/InvitationCreateRequest" + } + }, { + "name" : "auto_accept", + "in" : "query", + "description" : "Auto-accept connection (defaults to configuration)", + "required" : false, + "type" : "boolean" + }, { + "name" : "multi_use", + "in" : "query", + "description" : "Create invitation for multiple use (default false)", + "required" : false, + "type" : "boolean" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/InvitationRecord" + } + } + } + } + }, + "/out-of-band/receive-invitation" : { + "post" : { + "tags" : [ "out-of-band" ], + "summary" : "Receive a new connection invitation", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/InvitationMessage" + } + }, { + "name" : "alias", + "in" : "query", + "description" : "Alias for connection", + "required" : false, + "type" : "string" + }, { + "name" : "auto_accept", + "in" : "query", + "description" : "Auto-accept connection (defaults to configuration)", + "required" : false, + "type" : "boolean" + }, { + "name" : "mediation_id", + "in" : "query", + "description" : "Identifier for active mediation record to be used", + "required" : false, + "type" : "string", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + }, { + "name" : "use_existing_connection", + "in" : "query", + "description" : "Use an existing connection, if possible", + "required" : false, + "type" : "boolean" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/OobRecord" + } + } + } + } + }, + "/plugins" : { + "get" : { + "tags" : [ "server" ], + "summary" : "Fetch the list of loaded plugins", + "produces" : [ "application/json" ], + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/AdminModules" + } + } + } + } + }, + "/present-proof-2.0/create-request" : { + "post" : { + "tags" : [ "present-proof v2.0" ], + "summary" : "Creates a presentation request not bound to any proposal or connection", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/V20PresCreateRequestRequest" + } + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V20PresExRecord" + } + } + } + } + }, + "/present-proof-2.0/records" : { + "get" : { + "tags" : [ "present-proof v2.0" ], + "summary" : "Fetch all present-proof exchange records", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "connection_id", + "in" : "query", + "description" : "Connection identifier", + "required" : false, + "type" : "string", + "format" : "uuid" + }, { + "name" : "role", + "in" : "query", + "description" : "Role assigned in presentation exchange", + "required" : false, + "type" : "string", + "enum" : [ "prover", "verifier" ] + }, { + "name" : "state", + "in" : "query", + "description" : "Presentation exchange state", + "required" : false, + "type" : "string", + "enum" : [ "proposal-sent", "proposal-received", "request-sent", "request-received", "presentation-sent", "presentation-received", "done", "abandoned" ] + }, { + "name" : "thread_id", + "in" : "query", + "description" : "Thread identifier", + "required" : false, + "type" : "string", + "format" : "uuid" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V20PresExRecordList" + } + } + } + } + }, + "/present-proof-2.0/records/{pres_ex_id}" : { + "get" : { + "tags" : [ "present-proof v2.0" ], + "summary" : "Fetch a single presentation exchange record", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "pres_ex_id", + "in" : "path", + "description" : "Presentation exchange identifier", + "required" : true, + "type" : "string", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V20PresExRecord" + } + } + } + }, + "delete" : { + "tags" : [ "present-proof v2.0" ], + "summary" : "Remove an existing presentation exchange record", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "pres_ex_id", + "in" : "path", + "description" : "Presentation exchange identifier", + "required" : true, + "type" : "string", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V20PresentProofModuleResponse" + } + } + } + } + }, + "/present-proof-2.0/records/{pres_ex_id}/credentials" : { + "get" : { + "tags" : [ "present-proof v2.0" ], + "summary" : "Fetch credentials from wallet for presentation request", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "pres_ex_id", + "in" : "path", + "description" : "Presentation exchange identifier", + "required" : true, + "type" : "string", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + }, { + "name" : "count", + "in" : "query", + "description" : "Maximum number to retrieve", + "required" : false, + "type" : "string", + "pattern" : "^[1-9][0-9]*$" + }, { + "name" : "extra_query", + "in" : "query", + "description" : "(JSON) object mapping referents to extra WQL queries", + "required" : false, + "type" : "string", + "pattern" : "^{\\s*\".*?\"\\s*:\\s*{.*?}\\s*(,\\s*\".*?\"\\s*:\\s*{.*?}\\s*)*\\s*}$" + }, { + "name" : "referent", + "in" : "query", + "description" : "Proof request referents of interest, comma-separated", + "required" : false, + "type" : "string" + }, { + "name" : "start", + "in" : "query", + "description" : "Start index", + "required" : false, + "type" : "string", + "pattern" : "^[0-9]*$" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/IndyCredPrecis" + } + } + } + } + } + }, + "/present-proof-2.0/records/{pres_ex_id}/problem-report" : { + "post" : { + "tags" : [ "present-proof v2.0" ], + "summary" : "Send a problem report for presentation exchange", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/V20PresProblemReportRequest" + } + }, { + "name" : "pres_ex_id", + "in" : "path", + "description" : "Presentation exchange identifier", + "required" : true, + "type" : "string", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V20PresentProofModuleResponse" + } + } + } + } + }, + "/present-proof-2.0/records/{pres_ex_id}/send-presentation" : { + "post" : { + "tags" : [ "present-proof v2.0" ], + "summary" : "Sends a proof presentation", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/V20PresSpecByFormatRequest" + } + }, { + "name" : "pres_ex_id", + "in" : "path", + "description" : "Presentation exchange identifier", + "required" : true, + "type" : "string", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V20PresExRecord" + } + } + } + } + }, + "/present-proof-2.0/records/{pres_ex_id}/send-request" : { + "post" : { + "tags" : [ "present-proof v2.0" ], + "summary" : "Sends a presentation request in reference to a proposal", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/V20PresentationSendRequestToProposal" + } + }, { + "name" : "pres_ex_id", + "in" : "path", + "description" : "Presentation exchange identifier", + "required" : true, + "type" : "string", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V20PresExRecord" + } + } + } + } + }, + "/present-proof-2.0/records/{pres_ex_id}/verify-presentation" : { + "post" : { + "tags" : [ "present-proof v2.0" ], + "summary" : "Verify a received presentation", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "pres_ex_id", + "in" : "path", + "description" : "Presentation exchange identifier", + "required" : true, + "type" : "string", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V20PresExRecord" + } + } + } + } + }, + "/present-proof-2.0/send-proposal" : { + "post" : { + "tags" : [ "present-proof v2.0" ], + "summary" : "Sends a presentation proposal", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/V20PresProposalRequest" + } + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V20PresExRecord" + } + } + } + } + }, + "/present-proof-2.0/send-request" : { + "post" : { + "tags" : [ "present-proof v2.0" ], + "summary" : "Sends a free presentation request not bound to any proposal", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/V20PresSendRequestRequest" + } + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V20PresExRecord" + } + } + } + } + }, + "/present-proof/create-request" : { + "post" : { + "tags" : [ "present-proof v1.0" ], + "summary" : "Creates a presentation request not bound to any proposal or connection", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/V10PresentationCreateRequestRequest" + } + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V10PresentationExchange" + } + } + } + } + }, + "/present-proof/records" : { + "get" : { + "tags" : [ "present-proof v1.0" ], + "summary" : "Fetch all present-proof exchange records", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "connection_id", + "in" : "query", + "description" : "Connection identifier", + "required" : false, + "type" : "string", + "format" : "uuid" + }, { + "name" : "role", + "in" : "query", + "description" : "Role assigned in presentation exchange", + "required" : false, + "type" : "string", + "enum" : [ "prover", "verifier" ] + }, { + "name" : "state", + "in" : "query", + "description" : "Presentation exchange state", + "required" : false, + "type" : "string", + "enum" : [ "proposal_sent", "proposal_received", "request_sent", "request_received", "presentation_sent", "presentation_received", "verified", "presentation_acked", "abandoned" ] + }, { + "name" : "thread_id", + "in" : "query", + "description" : "Thread identifier", + "required" : false, + "type" : "string", + "format" : "uuid" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V10PresentationExchangeList" + } + } + } + } + }, + "/present-proof/records/{pres_ex_id}" : { + "get" : { + "tags" : [ "present-proof v1.0" ], + "summary" : "Fetch a single presentation exchange record", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "pres_ex_id", + "in" : "path", + "description" : "Presentation exchange identifier", + "required" : true, + "type" : "string", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V10PresentationExchange" + } + } + } + }, + "delete" : { + "tags" : [ "present-proof v1.0" ], + "summary" : "Remove an existing presentation exchange record", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "pres_ex_id", + "in" : "path", + "description" : "Presentation exchange identifier", + "required" : true, + "type" : "string", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V10PresentProofModuleResponse" + } + } + } + } + }, + "/present-proof/records/{pres_ex_id}/credentials" : { + "get" : { + "tags" : [ "present-proof v1.0" ], + "summary" : "Fetch credentials for a presentation request from wallet", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "pres_ex_id", + "in" : "path", + "description" : "Presentation exchange identifier", + "required" : true, + "type" : "string", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + }, { + "name" : "count", + "in" : "query", + "description" : "Maximum number to retrieve", + "required" : false, + "type" : "string", + "pattern" : "^[1-9][0-9]*$" + }, { + "name" : "extra_query", + "in" : "query", + "description" : "(JSON) object mapping referents to extra WQL queries", + "required" : false, + "type" : "string", + "pattern" : "^{\\s*\".*?\"\\s*:\\s*{.*?}\\s*(,\\s*\".*?\"\\s*:\\s*{.*?}\\s*)*\\s*}$" + }, { + "name" : "referent", + "in" : "query", + "description" : "Proof request referents of interest, comma-separated", + "required" : false, + "type" : "string" + }, { + "name" : "start", + "in" : "query", + "description" : "Start index", + "required" : false, + "type" : "string", + "pattern" : "^[0-9]*$" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/IndyCredPrecis" + } + } + } + } + } + }, + "/present-proof/records/{pres_ex_id}/problem-report" : { + "post" : { + "tags" : [ "present-proof v1.0" ], + "summary" : "Send a problem report for presentation exchange", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/V10PresentationProblemReportRequest" + } + }, { + "name" : "pres_ex_id", + "in" : "path", + "description" : "Presentation exchange identifier", + "required" : true, + "type" : "string", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V10PresentProofModuleResponse" + } + } + } + } + }, + "/present-proof/records/{pres_ex_id}/send-presentation" : { + "post" : { + "tags" : [ "present-proof v1.0" ], + "summary" : "Sends a proof presentation", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/IndyPresSpec" + } + }, { + "name" : "pres_ex_id", + "in" : "path", + "description" : "Presentation exchange identifier", + "required" : true, + "type" : "string", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V10PresentationExchange" + } + } + } + } + }, + "/present-proof/records/{pres_ex_id}/send-request" : { + "post" : { + "tags" : [ "present-proof v1.0" ], + "summary" : "Sends a presentation request in reference to a proposal", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/V10PresentationSendRequestToProposal" + } + }, { + "name" : "pres_ex_id", + "in" : "path", + "description" : "Presentation exchange identifier", + "required" : true, + "type" : "string", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V10PresentationExchange" + } + } + } + } + }, + "/present-proof/records/{pres_ex_id}/verify-presentation" : { + "post" : { + "tags" : [ "present-proof v1.0" ], + "summary" : "Verify a received presentation", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "pres_ex_id", + "in" : "path", + "description" : "Presentation exchange identifier", + "required" : true, + "type" : "string", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V10PresentationExchange" + } + } + } + } + }, + "/present-proof/send-proposal" : { + "post" : { + "tags" : [ "present-proof v1.0" ], + "summary" : "Sends a presentation proposal", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/V10PresentationProposalRequest" + } + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V10PresentationExchange" + } + } + } + } + }, + "/present-proof/send-request" : { + "post" : { + "tags" : [ "present-proof v1.0" ], + "summary" : "Sends a free presentation request not bound to any proposal", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/V10PresentationSendRequestRequest" + } + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/V10PresentationExchange" + } + } + } + } + }, + "/resolver/resolve/{did}" : { + "get" : { + "tags" : [ "resolver" ], + "summary" : "Retrieve doc for requested did", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "did", + "in" : "path", + "description" : "DID", + "required" : true, + "type" : "string", + "pattern" : "^did:([a-z0-9]+):((?:[a-zA-Z0-9._%-]*:)*[a-zA-Z0-9._%-]+)$" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/ResolutionResult" + } + } + } + } + }, + "/revocation/active-registry/{cred_def_id}" : { + "get" : { + "tags" : [ "revocation" ], + "summary" : "Get current active revocation registry by credential definition id", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "cred_def_id", + "in" : "path", + "description" : "Credential definition identifier", + "required" : true, + "type" : "string", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/RevRegResult" + } + } + } + } + }, + "/revocation/clear-pending-revocations" : { + "post" : { + "tags" : [ "revocation" ], + "summary" : "Clear pending revocations", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/ClearPendingRevocationsRequest" + } + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/PublishRevocations" + } + } + } + } + }, + "/revocation/create-registry" : { + "post" : { + "tags" : [ "revocation" ], + "summary" : "Creates a new revocation registry", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/RevRegCreateRequest" + } + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/RevRegResult" + } + } + } + } + }, + "/revocation/credential-record" : { + "get" : { + "tags" : [ "revocation" ], + "summary" : "Get credential revocation status", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "cred_ex_id", + "in" : "query", + "description" : "Credential exchange identifier", + "required" : false, + "type" : "string", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + }, { + "name" : "cred_rev_id", + "in" : "query", + "description" : "Credential revocation identifier", + "required" : false, + "type" : "string", + "pattern" : "^[1-9][0-9]*$" + }, { + "name" : "rev_reg_id", + "in" : "query", + "description" : "Revocation registry identifier", + "required" : false, + "type" : "string", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/CredRevRecordResult" + } + } + } + } + }, + "/revocation/publish-revocations" : { + "post" : { + "tags" : [ "revocation" ], + "summary" : "Publish pending revocations to ledger", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/PublishRevocations" + } + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/TxnOrPublishRevocationsResult" + } + } + } + } + }, + "/revocation/registries/created" : { + "get" : { + "tags" : [ "revocation" ], + "summary" : "Search for matching revocation registries that current agent created", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "cred_def_id", + "in" : "query", + "description" : "Credential definition identifier", + "required" : false, + "type" : "string", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" + }, { + "name" : "state", + "in" : "query", + "description" : "Revocation registry state", + "required" : false, + "type" : "string", + "enum" : [ "init", "generated", "posted", "active", "full" ] + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/RevRegsCreated" + } + } + } + } + }, + "/revocation/registry/delete-tails-file" : { + "delete" : { + "tags" : [ "revocation" ], + "summary" : "Delete the tail files", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "cred_def_id", + "in" : "query", + "description" : "Credential definition identifier", + "required" : false, + "type" : "string", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" + }, { + "name" : "rev_reg_id", + "in" : "query", + "description" : "Revocation registry identifier", + "required" : false, + "type" : "string", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/TailsDeleteResponse" + } + } + } + } + }, + "/revocation/registry/{rev_reg_id}" : { + "get" : { + "tags" : [ "revocation" ], + "summary" : "Get revocation registry by revocation registry id", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "rev_reg_id", + "in" : "path", + "description" : "Revocation Registry identifier", + "required" : true, + "type" : "string", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/RevRegResult" + } + } + } + }, + "patch" : { + "tags" : [ "revocation" ], + "summary" : "Update revocation registry with new public URI to its tails file", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/RevRegUpdateTailsFileUri" + } + }, { + "name" : "rev_reg_id", + "in" : "path", + "description" : "Revocation Registry identifier", + "required" : true, + "type" : "string", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/RevRegResult" + } + } + } + } + }, + "/revocation/registry/{rev_reg_id}/definition" : { + "post" : { + "tags" : [ "revocation" ], + "summary" : "Send revocation registry definition to ledger", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "rev_reg_id", + "in" : "path", + "description" : "Revocation Registry identifier", + "required" : true, + "type" : "string", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" + }, { + "name" : "conn_id", + "in" : "query", + "description" : "Connection identifier", + "required" : false, + "type" : "string" + }, { + "name" : "create_transaction_for_endorser", + "in" : "query", + "description" : "Create Transaction For Endorser's signature", + "required" : false, + "type" : "boolean" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/TxnOrRevRegResult" + } + } + } + } + }, + "/revocation/registry/{rev_reg_id}/entry" : { + "post" : { + "tags" : [ "revocation" ], + "summary" : "Send revocation registry entry to ledger", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "rev_reg_id", + "in" : "path", + "description" : "Revocation Registry identifier", + "required" : true, + "type" : "string", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" + }, { + "name" : "conn_id", + "in" : "query", + "description" : "Connection identifier", + "required" : false, + "type" : "string" + }, { + "name" : "create_transaction_for_endorser", + "in" : "query", + "description" : "Create Transaction For Endorser's signature", + "required" : false, + "type" : "boolean" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/RevRegResult" + } + } + } + } + }, + "/revocation/registry/{rev_reg_id}/fix-revocation-entry-state" : { + "put" : { + "tags" : [ "revocation" ], + "summary" : "Fix revocation state in wallet and return number of updated entries", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "rev_reg_id", + "in" : "path", + "description" : "Revocation Registry identifier", + "required" : true, + "type" : "string", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" + }, { + "name" : "apply_ledger_update", + "in" : "query", + "description" : "Apply updated accumulator transaction to ledger", + "required" : true, + "type" : "boolean" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/RevRegWalletUpdatedResult" + } + } + } + } + }, + "/revocation/registry/{rev_reg_id}/issued" : { + "get" : { + "tags" : [ "revocation" ], + "summary" : "Get number of credentials issued against revocation registry", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "rev_reg_id", + "in" : "path", + "description" : "Revocation Registry identifier", + "required" : true, + "type" : "string", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/RevRegIssuedResult" + } + } + } + } + }, + "/revocation/registry/{rev_reg_id}/issued/details" : { + "get" : { + "tags" : [ "revocation" ], + "summary" : "Get details of credentials issued against revocation registry", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "rev_reg_id", + "in" : "path", + "description" : "Revocation Registry identifier", + "required" : true, + "type" : "string", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/CredRevRecordDetailsResult" + } + } + } + } + }, + "/revocation/registry/{rev_reg_id}/issued/indy_recs" : { + "get" : { + "tags" : [ "revocation" ], + "summary" : "Get details of revoked credentials from ledger", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "rev_reg_id", + "in" : "path", + "description" : "Revocation Registry identifier", + "required" : true, + "type" : "string", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/CredRevIndyRecordsResult" + } + } + } + } + }, + "/revocation/registry/{rev_reg_id}/set-state" : { + "patch" : { + "tags" : [ "revocation" ], + "summary" : "Set revocation registry state manually", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "rev_reg_id", + "in" : "path", + "description" : "Revocation Registry identifier", + "required" : true, + "type" : "string", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" + }, { + "name" : "state", + "in" : "query", + "description" : "Revocation registry state to set", + "required" : true, + "type" : "string", + "enum" : [ "init", "generated", "posted", "active", "full" ] + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/RevRegResult" + } + } + } + } + }, + "/revocation/registry/{rev_reg_id}/tails-file" : { + "get" : { + "tags" : [ "revocation" ], + "summary" : "Download tails file", + "produces" : [ "application/octet-stream" ], + "parameters" : [ { + "name" : "rev_reg_id", + "in" : "path", + "description" : "Revocation Registry identifier", + "required" : true, + "type" : "string", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" + } ], + "responses" : { + "200" : { + "description" : "tails file", + "schema" : { + "type" : "string", + "format" : "binary" + } + } + } + }, + "put" : { + "tags" : [ "revocation" ], + "summary" : "Upload local tails file to server", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "rev_reg_id", + "in" : "path", + "description" : "Revocation Registry identifier", + "required" : true, + "type" : "string", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/RevocationModuleResponse" + } + } + } + } + }, + "/revocation/revoke" : { + "post" : { + "tags" : [ "revocation" ], + "summary" : "Revoke an issued credential", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/RevokeRequest" + } + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/RevocationModuleResponse" + } + } + } + } + }, + "/schemas" : { + "post" : { + "tags" : [ "schema" ], + "summary" : "Sends a schema to the ledger", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/SchemaSendRequest" + } + }, { + "name" : "conn_id", + "in" : "query", + "description" : "Connection identifier", + "required" : false, + "type" : "string" + }, { + "name" : "create_transaction_for_endorser", + "in" : "query", + "description" : "Create Transaction For Endorser's signature", + "required" : false, + "type" : "boolean" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/TxnOrSchemaSendResult" + } + } + } + } + }, + "/schemas/created" : { + "get" : { + "tags" : [ "schema" ], + "summary" : "Search for matching schema that agent originated", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "schema_id", + "in" : "query", + "description" : "Schema identifier", + "required" : false, + "type" : "string", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" + }, { + "name" : "schema_issuer_did", + "in" : "query", + "description" : "Schema issuer DID", + "required" : false, + "type" : "string", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + }, { + "name" : "schema_name", + "in" : "query", + "description" : "Schema name", + "required" : false, + "type" : "string" + }, { + "name" : "schema_version", + "in" : "query", + "description" : "Schema version", + "required" : false, + "type" : "string", + "pattern" : "^[0-9.]+$" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/SchemasCreatedResult" + } + } + } + } + }, + "/schemas/{schema_id}" : { + "get" : { + "tags" : [ "schema" ], + "summary" : "Gets a schema from the ledger", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "schema_id", + "in" : "path", + "description" : "Schema identifier", + "required" : true, + "type" : "string", + "pattern" : "^[1-9][0-9]*|[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/SchemaGetResult" + } + } + } + } + }, + "/schemas/{schema_id}/write_record" : { + "post" : { + "tags" : [ "schema" ], + "summary" : "Writes a schema non-secret record to the wallet", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "schema_id", + "in" : "path", + "description" : "Schema identifier", + "required" : true, + "type" : "string", + "pattern" : "^[1-9][0-9]*|[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/SchemaGetResult" + } + } + } + } + }, + "/shutdown" : { + "get" : { + "tags" : [ "server" ], + "summary" : "Shut down server", + "produces" : [ "application/json" ], + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/AdminShutdown" + } + } + } + } + }, + "/status" : { + "get" : { + "tags" : [ "server" ], + "summary" : "Fetch the server status", + "produces" : [ "application/json" ], + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/AdminStatus" + } + } + } + } + }, + "/status/config" : { + "get" : { + "tags" : [ "server" ], + "summary" : "Fetch the server configuration", + "produces" : [ "application/json" ], + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/AdminConfig" + } + } + } + } + }, + "/status/live" : { + "get" : { + "tags" : [ "server" ], + "summary" : "Liveliness check", + "produces" : [ "application/json" ], + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/AdminStatusLiveliness" + } + } + } + } + }, + "/status/ready" : { + "get" : { + "tags" : [ "server" ], + "summary" : "Readiness check", + "produces" : [ "application/json" ], + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/AdminStatusReadiness" + } + } + } + } + }, + "/status/reset" : { + "post" : { + "tags" : [ "server" ], + "summary" : "Reset statistics", + "produces" : [ "application/json" ], + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/AdminReset" + } + } + } + } + }, + "/transaction/{tran_id}/resend" : { + "post" : { + "tags" : [ "endorse-transaction" ], + "summary" : "For Author to resend a particular transaction request", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "tran_id", + "in" : "path", + "description" : "Transaction identifier", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/TransactionRecord" + } + } + } + } + }, + "/transactions" : { + "get" : { + "tags" : [ "endorse-transaction" ], + "summary" : "Query transactions", + "produces" : [ "application/json" ], + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/TransactionList" + } + } + } + } + }, + "/transactions/create-request" : { + "post" : { + "tags" : [ "endorse-transaction" ], + "summary" : "For author to send a transaction request", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/Date" + } + }, { + "name" : "tran_id", + "in" : "query", + "description" : "Transaction identifier", + "required" : true, + "type" : "string" + }, { + "name" : "endorser_write_txn", + "in" : "query", + "description" : "Endorser will write the transaction after endorsing it", + "required" : false, + "type" : "boolean" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/TransactionRecord" + } + } + } + } + }, + "/transactions/{conn_id}/set-endorser-info" : { + "post" : { + "tags" : [ "endorse-transaction" ], + "summary" : "Set Endorser Info", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "conn_id", + "in" : "path", + "description" : "Connection identifier", + "required" : true, + "type" : "string" + }, { + "name" : "endorser_did", + "in" : "query", + "description" : "Endorser DID", + "required" : true, + "type" : "string" + }, { + "name" : "endorser_name", + "in" : "query", + "description" : "Endorser Name", + "required" : false, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/EndorserInfo" + } + } + } + } + }, + "/transactions/{conn_id}/set-endorser-role" : { + "post" : { + "tags" : [ "endorse-transaction" ], + "summary" : "Set transaction jobs", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "conn_id", + "in" : "path", + "description" : "Connection identifier", + "required" : true, + "type" : "string" + }, { + "name" : "transaction_my_job", + "in" : "query", + "description" : "Transaction related jobs", + "required" : false, + "type" : "string", + "enum" : [ "TRANSACTION_AUTHOR", "TRANSACTION_ENDORSER", "reset" ] + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/TransactionJobs" + } + } + } + } + }, + "/transactions/{tran_id}" : { + "get" : { + "tags" : [ "endorse-transaction" ], + "summary" : "Fetch a single transaction record", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "tran_id", + "in" : "path", + "description" : "Transaction identifier", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/TransactionRecord" + } + } + } + } + }, + "/transactions/{tran_id}/cancel" : { + "post" : { + "tags" : [ "endorse-transaction" ], + "summary" : "For Author to cancel a particular transaction request", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "tran_id", + "in" : "path", + "description" : "Transaction identifier", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/TransactionRecord" + } + } + } + } + }, + "/transactions/{tran_id}/endorse" : { + "post" : { + "tags" : [ "endorse-transaction" ], + "summary" : "For Endorser to endorse a particular transaction record", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "tran_id", + "in" : "path", + "description" : "Transaction identifier", + "required" : true, + "type" : "string" + }, { + "name" : "endorser_did", + "in" : "query", + "description" : "Endorser DID", + "required" : false, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/TransactionRecord" + } + } + } + } + }, + "/transactions/{tran_id}/refuse" : { + "post" : { + "tags" : [ "endorse-transaction" ], + "summary" : "For Endorser to refuse a particular transaction record", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "tran_id", + "in" : "path", + "description" : "Transaction identifier", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/TransactionRecord" + } + } + } + } + }, + "/transactions/{tran_id}/write" : { + "post" : { + "tags" : [ "endorse-transaction" ], + "summary" : "For Author / Endorser to write an endorsed transaction to the ledger", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "tran_id", + "in" : "path", + "description" : "Transaction identifier", + "required" : true, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/TransactionRecord" + } + } + } + } + }, + "/wallet/did" : { + "get" : { + "tags" : [ "wallet" ], + "summary" : "List wallet DIDs", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "did", + "in" : "query", + "description" : "DID of interest", + "required" : false, + "type" : "string", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$|^did:([a-zA-Z0-9_]+):([a-zA-Z0-9_.%-]+(:[a-zA-Z0-9_.%-]+)*)((;[a-zA-Z0-9_.:%-]+=[a-zA-Z0-9_.:%-]*)*)(\\/[^#?]*)?([?][^#]*)?(\\#.*)?$$" + }, { + "name" : "key_type", + "in" : "query", + "description" : "Key type to query for.", + "required" : false, + "type" : "string", + "enum" : [ "ed25519", "bls12381g2" ] + }, { + "name" : "method", + "in" : "query", + "description" : "DID method to query for. e.g. sov to only fetch indy/sov DIDs", + "required" : false, + "type" : "string", + "enum" : [ "key", "sov" ] + }, { + "name" : "posture", + "in" : "query", + "description" : "Whether DID is current public DID, posted to ledger but current public DID, or local to the wallet", + "required" : false, + "type" : "string", + "enum" : [ "public", "posted", "wallet_only" ] + }, { + "name" : "verkey", + "in" : "query", + "description" : "Verification key of interest", + "required" : false, + "type" : "string", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/DIDList" + } + } + } + } + }, + "/wallet/did/create" : { + "post" : { + "tags" : [ "wallet" ], + "summary" : "Create a local DID", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/DIDCreate" + } + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/DIDResult" + } + } + } + } + }, + "/wallet/did/local/rotate-keypair" : { + "patch" : { + "tags" : [ "wallet" ], + "summary" : "Rotate keypair for a DID not posted to the ledger", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "did", + "in" : "query", + "description" : "DID of interest", + "required" : true, + "type" : "string", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/WalletModuleResponse" + } + } + } + } + }, + "/wallet/did/public" : { + "get" : { + "tags" : [ "wallet" ], + "summary" : "Fetch the current public DID", + "produces" : [ "application/json" ], + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/DIDResult" + } + } + } + }, + "post" : { + "tags" : [ "wallet" ], + "summary" : "Assign the current public DID", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "did", + "in" : "query", + "description" : "DID of interest", + "required" : true, + "type" : "string", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + }, { + "name" : "conn_id", + "in" : "query", + "description" : "Connection identifier", + "required" : false, + "type" : "string" + }, { + "name" : "create_transaction_for_endorser", + "in" : "query", + "description" : "Create Transaction For Endorser's signature", + "required" : false, + "type" : "boolean" + }, { + "name" : "mediation_id", + "in" : "query", + "description" : "Mediation identifier", + "required" : false, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/DIDResult" + } + } + } + } + }, + "/wallet/get-did-endpoint" : { + "get" : { + "tags" : [ "wallet" ], + "summary" : "Query DID endpoint in wallet", + "produces" : [ "application/json" ], + "parameters" : [ { + "name" : "did", + "in" : "query", + "description" : "DID of interest", + "required" : true, + "type" : "string", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/DIDEndpoint" + } + } + } + } + }, + "/wallet/set-did-endpoint" : { + "post" : { + "tags" : [ "wallet" ], + "summary" : "Update endpoint in wallet and on ledger if posted to it", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/DIDEndpointWithType" + } + }, { + "name" : "conn_id", + "in" : "query", + "description" : "Connection identifier", + "required" : false, + "type" : "string" + }, { + "name" : "create_transaction_for_endorser", + "in" : "query", + "description" : "Create Transaction For Endorser's signature", + "required" : false, + "type" : "boolean" + } ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/WalletModuleResponse" + } + } + } + } + } + }, + "securityDefinitions" : { + "AuthorizationHeader" : { + "description" : "Bearer token. Be sure to preprend token with 'Bearer '", + "type" : "apiKey", + "name" : "Authorization", + "in" : "header" + } + }, + "definitions" : { + "AMLRecord" : { + "type" : "object", + "properties" : { + "aml" : { + "type" : "object", + "additionalProperties" : { + "type" : "string" + } + }, + "amlContext" : { + "type" : "string" + }, + "version" : { + "type" : "string" + } + } + }, + "ActionMenuFetchResult" : { + "type" : "object", + "properties" : { + "result" : { + "$ref" : "#/definitions/ActionMenuFetchResult_result" + } + } + }, + "ActionMenuModulesResult" : { + "type" : "object" + }, + "AdminConfig" : { + "type" : "object", + "properties" : { + "config" : { + "type" : "object", + "description" : "Configuration settings", + "properties" : { } + } + } + }, + "AdminMediationDeny" : { + "type" : "object", + "properties" : { + "mediator_terms" : { + "type" : "array", + "description" : "List of mediator rules for recipient", + "items" : { + "type" : "string", + "description" : "Indicate terms to which the mediator requires the recipient to agree" + } + }, + "recipient_terms" : { + "type" : "array", + "description" : "List of recipient rules for mediation", + "items" : { + "type" : "string", + "description" : "Indicate terms to which the recipient requires the mediator to agree" + } + } + } + }, + "AdminModules" : { + "type" : "object", + "properties" : { + "result" : { + "type" : "array", + "description" : "List of admin modules", + "items" : { + "type" : "string", + "description" : "admin module" + } + } + } + }, + "AdminReset" : { + "type" : "object" + }, + "AdminShutdown" : { + "type" : "object" + }, + "AdminStatus" : { + "type" : "object", + "properties" : { + "conductor" : { + "type" : "object", + "description" : "Conductor statistics", + "properties" : { } + }, + "label" : { + "type" : "string", + "description" : "Default label", + "x-nullable" : true + }, + "timing" : { + "type" : "object", + "description" : "Timing results", + "properties" : { } + }, + "version" : { + "type" : "string", + "description" : "Version code" + } + } + }, + "AdminStatusLiveliness" : { + "type" : "object", + "properties" : { + "alive" : { + "type" : "boolean", + "example" : true, + "description" : "Liveliness status" + } + } + }, + "AdminStatusReadiness" : { + "type" : "object", + "properties" : { + "ready" : { + "type" : "boolean", + "example" : true, + "description" : "Readiness status" + } + } + }, + "AttachDecorator" : { + "type" : "object", + "required" : [ "data" ], + "properties" : { + "@id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Attachment identifier" + }, + "byte_count" : { + "type" : "integer", + "format" : "int32", + "example" : 1234, + "description" : "Byte count of data included by reference" + }, + "data" : { + "$ref" : "#/definitions/AttachDecoratorData" + }, + "description" : { + "type" : "string", + "example" : "view from doorway, facing east, with lights off", + "description" : "Human-readable description of content" + }, + "filename" : { + "type" : "string", + "example" : "IMG1092348.png", + "description" : "File name" + }, + "lastmod_time" : { + "type" : "string", + "example" : "2021-12-31T23:59:59Z", + "description" : "Hint regarding last modification datetime, in ISO-8601 format", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" + }, + "mime-type" : { + "type" : "string", + "example" : "image/png", + "description" : "MIME type" + } + } + }, + "AttachDecoratorData" : { + "type" : "object", + "properties" : { + "base64" : { + "type" : "string", + "example" : "ey4uLn0=", + "description" : "Base64-encoded data", + "pattern" : "^[a-zA-Z0-9+/]*={0,2}$" + }, + "json" : { + "example" : "{\"sample\": \"content\"}", + "description" : "JSON-serialized data" + }, + "jws" : { + "$ref" : "#/definitions/AttachDecoratorData_jws" + }, + "links" : { + "type" : "array", + "description" : "List of hypertext links to data", + "items" : { + "type" : "string", + "example" : "https://link.to/data" + } + }, + "sha256" : { + "type" : "string", + "example" : "617a48c7c8afe0521efdc03e5bb0ad9e655893e6b4b51f0e794d70fba132aacb", + "description" : "SHA256 hash (binhex encoded) of content", + "pattern" : "^[a-fA-F0-9+/]{64}$" + } + } + }, + "AttachDecoratorData1JWS" : { + "type" : "object", + "required" : [ "header", "signature" ], + "properties" : { + "header" : { + "$ref" : "#/definitions/AttachDecoratorDataJWSHeader" + }, + "protected" : { + "type" : "string", + "example" : "ey4uLn0", + "description" : "protected JWS header", + "pattern" : "^[-_a-zA-Z0-9]*$" + }, + "signature" : { + "type" : "string", + "example" : "ey4uLn0", + "description" : "signature", + "pattern" : "^[-_a-zA-Z0-9]*$" + } + } + }, + "AttachDecoratorDataJWS" : { + "type" : "object", + "properties" : { + "header" : { + "$ref" : "#/definitions/AttachDecoratorDataJWSHeader" + }, + "protected" : { + "type" : "string", + "example" : "ey4uLn0", + "description" : "protected JWS header", + "pattern" : "^[-_a-zA-Z0-9]*$" + }, + "signature" : { + "type" : "string", + "example" : "ey4uLn0", + "description" : "signature", + "pattern" : "^[-_a-zA-Z0-9]*$" + }, + "signatures" : { + "type" : "array", + "description" : "List of signatures", + "items" : { + "$ref" : "#/definitions/AttachDecoratorData1JWS" + } + } + } + }, + "AttachDecoratorDataJWSHeader" : { + "type" : "object", + "required" : [ "kid" ], + "properties" : { + "kid" : { + "type" : "string", + "example" : "did:sov:LjgpST2rjsoxYegQDRm7EL#keys-4", + "description" : "Key identifier, in W3C did:key or DID URL format", + "pattern" : "^did:(?:key:z[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+|sov:[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}(;.*)?(\\?.*)?#.+)$" + } + } + }, + "AttachmentDef" : { + "type" : "object", + "properties" : { + "id" : { + "type" : "string", + "example" : "attachment-0", + "description" : "Attachment identifier" + }, + "type" : { + "type" : "string", + "example" : "present-proof", + "description" : "Attachment type", + "enum" : [ "credential-offer", "present-proof" ] + } + } + }, + "AttributeMimeTypesResult" : { + "type" : "object", + "properties" : { + "results" : { + "type" : "object", + "additionalProperties" : { + "type" : "string", + "description" : "MIME type" + }, + "x-nullable" : true + } + } + }, + "BasicMessageModuleResponse" : { + "type" : "object" + }, + "ClaimFormat" : { + "type" : "object", + "properties" : { + "jwt" : { + "type" : "object", + "properties" : { } + }, + "jwt_vc" : { + "type" : "object", + "properties" : { } + }, + "jwt_vp" : { + "type" : "object", + "properties" : { } + }, + "ldp" : { + "type" : "object", + "properties" : { } + }, + "ldp_vc" : { + "type" : "object", + "properties" : { } + }, + "ldp_vp" : { + "type" : "object", + "properties" : { } + } + } + }, + "ClearPendingRevocationsRequest" : { + "type" : "object", + "properties" : { + "purge" : { + "type" : "object", + "description" : "Credential revocation ids by revocation registry id: omit for all, specify null or empty list for all pending per revocation registry", + "additionalProperties" : { + "type" : "array", + "items" : { + "type" : "string", + "example" : "12345", + "description" : "Credential revocation identifier", + "pattern" : "^[1-9][0-9]*$" + } + } + } + } + }, + "ConnRecord" : { + "type" : "object", + "properties" : { + "accept" : { + "type" : "string", + "example" : "auto", + "description" : "Connection acceptance: manual or auto", + "enum" : [ "manual", "auto" ] + }, + "alias" : { + "type" : "string", + "example" : "Bob, providing quotes", + "description" : "Optional alias to apply to connection for later use" + }, + "connection_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Connection identifier" + }, + "connection_protocol" : { + "type" : "string", + "example" : "connections/1.0", + "description" : "Connection protocol used", + "enum" : [ "connections/1.0", "didexchange/1.0" ] + }, + "created_at" : { + "type" : "string", + "example" : "2021-12-31T23:59:59Z", + "description" : "Time of record creation", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" + }, + "error_msg" : { + "type" : "string", + "example" : "No DIDDoc provided; cannot connect to public DID", + "description" : "Error message" + }, + "inbound_connection_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Inbound routing connection id to use" + }, + "invitation_key" : { + "type" : "string", + "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", + "description" : "Public key for connection", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" + }, + "invitation_mode" : { + "type" : "string", + "example" : "once", + "description" : "Invitation mode", + "enum" : [ "once", "multi", "static" ] + }, + "invitation_msg_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "ID of out-of-band invitation message" + }, + "my_did" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv", + "description" : "Our DID for connection", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + }, + "request_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Connection request identifier" + }, + "rfc23_state" : { + "type" : "string", + "example" : "invitation-sent", + "description" : "State per RFC 23", + "readOnly" : true + }, + "routing_state" : { + "type" : "string", + "example" : "active", + "description" : "Routing state of connection", + "enum" : [ "none", "request", "active", "error" ] + }, + "state" : { + "type" : "string", + "example" : "active", + "description" : "Current record state" + }, + "their_did" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv", + "description" : "Their DID for connection", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + }, + "their_label" : { + "type" : "string", + "example" : "Bob", + "description" : "Their label for connection" + }, + "their_public_did" : { + "type" : "string", + "example" : "2cpBmR3FqGKWi5EyUbpRY8", + "description" : "Other agent's public DID for connection" + }, + "their_role" : { + "type" : "string", + "example" : "requester", + "description" : "Their role in the connection protocol", + "enum" : [ "invitee", "requester", "inviter", "responder" ] + }, + "updated_at" : { + "type" : "string", + "example" : "2021-12-31T23:59:59Z", + "description" : "Time of last record update", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" + } + } + }, + "ConnectionInvitation" : { + "type" : "object", + "properties" : { + "@id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Message identifier" + }, + "@type" : { + "type" : "string", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "description" : "Message type", + "readOnly" : true + }, + "did" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv", + "description" : "DID for connection invitation", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + }, + "imageUrl" : { + "type" : "string", + "format" : "url", + "example" : "http://192.168.56.101/img/logo.jpg", + "description" : "Optional image URL for connection invitation", + "x-nullable" : true + }, + "label" : { + "type" : "string", + "example" : "Bob", + "description" : "Optional label for connection invitation" + }, + "recipientKeys" : { + "type" : "array", + "description" : "List of recipient keys", + "items" : { + "type" : "string", + "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", + "description" : "Recipient public key", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" + } + }, + "routingKeys" : { + "type" : "array", + "description" : "List of routing keys", + "items" : { + "type" : "string", + "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", + "description" : "Routing key", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" + } + }, + "serviceEndpoint" : { + "type" : "string", + "example" : "http://192.168.56.101:8020", + "description" : "Service endpoint at which to reach this agent" + } + } + }, + "ConnectionList" : { + "type" : "object", + "properties" : { + "results" : { + "type" : "array", + "description" : "List of connection records", + "items" : { + "$ref" : "#/definitions/ConnRecord" + } + } + } + }, + "ConnectionMetadata" : { + "type" : "object", + "properties" : { + "results" : { + "type" : "object", + "description" : "Dictionary of metadata associated with connection.", + "properties" : { } + } + } + }, + "ConnectionMetadataSetRequest" : { + "type" : "object", + "required" : [ "metadata" ], + "properties" : { + "metadata" : { + "type" : "object", + "description" : "Dictionary of metadata to set for connection.", + "properties" : { } + } + } + }, + "ConnectionModuleResponse" : { + "type" : "object" + }, + "ConnectionStaticRequest" : { + "type" : "object", + "properties" : { + "alias" : { + "type" : "string", + "description" : "Alias to assign to this connection" + }, + "my_did" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv", + "description" : "Local DID", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + }, + "my_seed" : { + "type" : "string", + "description" : "Seed to use for the local DID" + }, + "their_did" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv", + "description" : "Remote DID", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + }, + "their_endpoint" : { + "type" : "string", + "example" : "https://myhost:8021", + "description" : "URL endpoint for other party", + "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" + }, + "their_label" : { + "type" : "string", + "description" : "Other party's label for this connection" + }, + "their_seed" : { + "type" : "string", + "description" : "Seed to use for the remote DID" + }, + "their_verkey" : { + "type" : "string", + "description" : "Remote verification key" + } + } + }, + "ConnectionStaticResult" : { + "type" : "object", + "required" : [ "my_did", "my_endpoint", "my_verkey", "record", "their_did", "their_verkey" ], + "properties" : { + "my_did" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv", + "description" : "Local DID", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + }, + "my_endpoint" : { + "type" : "string", + "example" : "https://myhost:8021", + "description" : "My URL endpoint", + "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" + }, + "my_verkey" : { + "type" : "string", + "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", + "description" : "My verification key", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" + }, + "record" : { + "$ref" : "#/definitions/ConnRecord" + }, + "their_did" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv", + "description" : "Remote DID", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + }, + "their_verkey" : { + "type" : "string", + "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", + "description" : "Remote verification key", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" + } + } + }, + "Constraints" : { + "type" : "object", + "properties" : { + "fields" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/DIFField" + } + }, + "is_holder" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/DIFHolder" + } + }, + "limit_disclosure" : { + "type" : "string", + "description" : "LimitDisclosure" + }, + "status_active" : { + "type" : "string", + "enum" : [ "required", "allowed", "disallowed" ] + }, + "status_revoked" : { + "type" : "string", + "enum" : [ "required", "allowed", "disallowed" ] + }, + "status_suspended" : { + "type" : "string", + "enum" : [ "required", "allowed", "disallowed" ] + }, + "subject_is_issuer" : { + "type" : "string", + "description" : "SubjectIsIssuer", + "enum" : [ "required", "preferred" ] + } + } + }, + "CreateInvitationRequest" : { + "type" : "object", + "properties" : { + "mediation_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Identifier for active mediation record to be used", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + }, + "metadata" : { + "type" : "object", + "description" : "Optional metadata to attach to the connection created with the invitation", + "properties" : { } + }, + "my_label" : { + "type" : "string", + "example" : "Bob", + "description" : "Optional label for connection invitation" + }, + "recipient_keys" : { + "type" : "array", + "description" : "List of recipient keys", + "items" : { + "type" : "string", + "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", + "description" : "Recipient public key", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" + } + }, + "routing_keys" : { + "type" : "array", + "description" : "List of routing keys", + "items" : { + "type" : "string", + "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", + "description" : "Routing key", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" + } + }, + "service_endpoint" : { + "type" : "string", + "example" : "http://192.168.56.102:8020", + "description" : "Connection endpoint" + } + } + }, + "CreateWalletRequest" : { + "type" : "object", + "properties" : { + "image_url" : { + "type" : "string", + "example" : "https://aries.ca/images/sample.png", + "description" : "Image url for this wallet. This image url is publicized (self-attested) to other agents as part of forming a connection." + }, + "key_management_mode" : { + "type" : "string", + "example" : "managed", + "description" : "Key management method to use for this wallet.", + "enum" : [ "managed" ] + }, + "label" : { + "type" : "string", + "example" : "Alice", + "description" : "Label for this wallet. This label is publicized (self-attested) to other agents as part of forming a connection." + }, + "wallet_dispatch_type" : { + "type" : "string", + "example" : "default", + "description" : "Webhook target dispatch type for this wallet. default - Dispatch only to webhooks associated with this wallet. base - Dispatch only to webhooks associated with the base wallet. both - Dispatch to both webhook targets.", + "enum" : [ "default", "both", "base" ] + }, + "wallet_key" : { + "type" : "string", + "example" : "MySecretKey123", + "description" : "Master key used for key derivation." + }, + "wallet_key_derivation" : { + "type" : "string", + "example" : "RAW", + "description" : "Key derivation", + "enum" : [ "ARGON2I_MOD", "ARGON2I_INT", "RAW" ] + }, + "wallet_name" : { + "type" : "string", + "example" : "MyNewWallet", + "description" : "Wallet name" + }, + "wallet_type" : { + "type" : "string", + "example" : "indy", + "description" : "Type of the wallet to create", + "enum" : [ "askar", "in_memory", "indy" ] + }, + "wallet_webhook_urls" : { + "type" : "array", + "description" : "List of Webhook URLs associated with this subwallet", + "items" : { + "type" : "string", + "example" : "http://localhost:8022/webhooks", + "description" : "Optional webhook URL to receive webhook messages" + } + } + } + }, + "CreateWalletResponse" : { + "type" : "object", + "required" : [ "key_management_mode", "wallet_id" ], + "properties" : { + "created_at" : { + "type" : "string", + "example" : "2021-12-31T23:59:59Z", + "description" : "Time of record creation", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" + }, + "key_management_mode" : { + "type" : "string", + "description" : "Mode regarding management of wallet key", + "enum" : [ "managed", "unmanaged" ] + }, + "settings" : { + "type" : "object", + "description" : "Settings for this wallet.", + "properties" : { } + }, + "state" : { + "type" : "string", + "example" : "active", + "description" : "Current record state" + }, + "token" : { + "type" : "string", + "example" : "eyJhbGciOiJFZERTQSJ9.eyJhIjogIjAifQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk", + "description" : "Authorization token to authenticate wallet requests" + }, + "updated_at" : { + "type" : "string", + "example" : "2021-12-31T23:59:59Z", + "description" : "Time of last record update", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" + }, + "wallet_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Wallet record ID" + } + } + }, + "CreateWalletTokenRequest" : { + "type" : "object", + "properties" : { + "wallet_key" : { + "type" : "string", + "example" : "MySecretKey123", + "description" : "Master key used for key derivation. Only required for unamanged wallets." + } + } + }, + "CreateWalletTokenResponse" : { + "type" : "object", + "properties" : { + "token" : { + "type" : "string", + "example" : "eyJhbGciOiJFZERTQSJ9.eyJhIjogIjAifQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk", + "description" : "Authorization token to authenticate wallet requests" + } + } + }, + "CredAttrSpec" : { + "type" : "object", + "required" : [ "name", "value" ], + "properties" : { + "mime-type" : { + "type" : "string", + "example" : "image/jpeg", + "description" : "MIME type: omit for (null) default", + "x-nullable" : true + }, + "name" : { + "type" : "string", + "example" : "favourite_drink", + "description" : "Attribute name" + }, + "value" : { + "type" : "string", + "example" : "martini", + "description" : "Attribute value: base64-encode if MIME type is present" + } + } + }, + "CredDefValue" : { + "type" : "object", + "properties" : { + "primary" : { + "$ref" : "#/definitions/CredDefValue_primary" + }, + "revocation" : { + "$ref" : "#/definitions/CredDefValue_revocation" + } + } + }, + "CredDefValuePrimary" : { + "type" : "object", + "properties" : { + "n" : { + "type" : "string", + "example" : "0", + "pattern" : "^[0-9]*$" + }, + "r" : { + "$ref" : "#/definitions/Generated" + }, + "rctxt" : { + "type" : "string", + "example" : "0", + "pattern" : "^[0-9]*$" + }, + "s" : { + "type" : "string", + "example" : "0", + "pattern" : "^[0-9]*$" + }, + "z" : { + "type" : "string", + "example" : "0", + "pattern" : "^[0-9]*$" + } + } + }, + "CredDefValueRevocation" : { + "type" : "object", + "properties" : { + "g" : { + "type" : "string", + "example" : "1 1F14F&ECB578F 2 095E45DDF417D" + }, + "g_dash" : { + "type" : "string", + "example" : "1 1D64716fCDC00C 1 0C781960FA66E3D3 2 095E45DDF417D" + }, + "h" : { + "type" : "string", + "example" : "1 16675DAE54BFAE8 2 095E45DD417D" + }, + "h0" : { + "type" : "string", + "example" : "1 21E5EF9476EAF18 2 095E45DDF417D" + }, + "h1" : { + "type" : "string", + "example" : "1 236D1D99236090 2 095E45DDF417D" + }, + "h2" : { + "type" : "string", + "example" : "1 1C3AE8D1F1E277 2 095E45DDF417D" + }, + "h_cap" : { + "type" : "string", + "example" : "1 1B2A32CF3167 1 2490FEBF6EE55 1 0000000000000000" + }, + "htilde" : { + "type" : "string", + "example" : "1 1D8549E8C0F8 2 095E45DDF417D" + }, + "pk" : { + "type" : "string", + "example" : "1 142CD5E5A7DC 1 153885BD903312 2 095E45DDF417D" + }, + "u" : { + "type" : "string", + "example" : "1 0C430AAB2B4710 1 1CB3A0932EE7E 1 0000000000000000" + }, + "y" : { + "type" : "string", + "example" : "1 153558BD903312 2 095E45DDF417D 1 0000000000000000" + } + } + }, + "CredInfoList" : { + "type" : "object", + "properties" : { + "results" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/IndyCredInfo" + } + } + } + }, + "CredRevIndyRecordsResult" : { + "type" : "object", + "properties" : { + "rev_reg_delta" : { + "type" : "object", + "description" : "Indy revocation registry delta", + "properties" : { } + } + } + }, + "CredRevRecordDetailsResult" : { + "type" : "object", + "properties" : { + "results" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/IssuerCredRevRecord" + } + } + } + }, + "CredRevRecordResult" : { + "type" : "object", + "properties" : { + "result" : { + "$ref" : "#/definitions/IssuerCredRevRecord" + } + } + }, + "CredRevokedResult" : { + "type" : "object", + "properties" : { + "revoked" : { + "type" : "boolean", + "description" : "Whether credential is revoked on the ledger" + } + } + }, + "Credential" : { + "type" : "object", + "required" : [ "@context", "credentialSubject", "issuanceDate", "issuer", "type" ], + "properties" : { + "@context" : { + "type" : "array", + "example" : [ "https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1" ], + "description" : "The JSON-LD context of the credential", + "items" : { } + }, + "credentialSubject" : { + "example" : "" + }, + "expirationDate" : { + "type" : "string", + "example" : "2010-01-01T19:23:24Z", + "description" : "The expiration date", + "pattern" : "^([0-9]{4})-([0-9]{2})-([0-9]{2})([Tt ]([0-9]{2}):([0-9]{2}):([0-9]{2})(\\.[0-9]+)?)?(([Zz]|([+-])([0-9]{2}):([0-9]{2})))?$" + }, + "id" : { + "type" : "string", + "example" : "http://example.edu/credentials/1872", + "pattern" : "\\w+:(\\/?\\/?)[^\\s]+" + }, + "issuanceDate" : { + "type" : "string", + "example" : "2010-01-01T19:23:24Z", + "description" : "The issuance date", + "pattern" : "^([0-9]{4})-([0-9]{2})-([0-9]{2})([Tt ]([0-9]{2}):([0-9]{2}):([0-9]{2})(\\.[0-9]+)?)?(([Zz]|([+-])([0-9]{2}):([0-9]{2})))?$" + }, + "issuer" : { + "example" : "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH", + "description" : "The JSON-LD Verifiable Credential Issuer. Either string of object with id field." + }, + "proof" : { + "$ref" : "#/definitions/Credential_proof" + }, + "type" : { + "type" : "array", + "example" : [ "VerifiableCredential", "AlumniCredential" ], + "description" : "The JSON-LD type of the credential", + "items" : { + "type" : "string" + } + } + } + }, + "CredentialDefinition" : { + "type" : "object", + "properties" : { + "id" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "description" : "Credential definition identifier", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" + }, + "schemaId" : { + "type" : "string", + "example" : "20", + "description" : "Schema identifier within credential definition identifier" + }, + "tag" : { + "type" : "string", + "example" : "tag", + "description" : "Tag within credential definition identifier" + }, + "type" : { + "example" : "CL", + "description" : "Signature type: CL for Camenisch-Lysyanskaya" + }, + "value" : { + "$ref" : "#/definitions/CredentialDefinition_value" + }, + "ver" : { + "type" : "string", + "example" : "1.0", + "description" : "Node protocol version", + "pattern" : "^[0-9.]+$" + } + } + }, + "CredentialDefinitionGetResult" : { + "type" : "object", + "properties" : { + "credential_definition" : { + "$ref" : "#/definitions/CredentialDefinition" + } + } + }, + "CredentialDefinitionSendRequest" : { + "type" : "object", + "properties" : { + "revocation_registry_size" : { + "type" : "integer", + "format" : "int32", + "example" : 1000, + "description" : "Revocation registry size", + "minimum" : 4, + "maximum" : 32768 + }, + "schema_id" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", + "description" : "Schema identifier", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" + }, + "support_revocation" : { + "type" : "boolean", + "description" : "Revocation supported flag" + }, + "tag" : { + "type" : "string", + "example" : "default", + "description" : "Credential definition identifier tag" + } + } + }, + "CredentialDefinitionSendResult" : { + "type" : "object", + "properties" : { + "credential_definition_id" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "description" : "Credential definition identifier", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" + } + } + }, + "CredentialDefinitionsCreatedResult" : { + "type" : "object", + "properties" : { + "credential_definition_ids" : { + "type" : "array", + "items" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "description" : "Credential definition identifiers", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" + } + } + } + }, + "CredentialOffer" : { + "type" : "object", + "required" : [ "offers~attach" ], + "properties" : { + "@id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Message identifier" + }, + "@type" : { + "type" : "string", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "description" : "Message type", + "readOnly" : true + }, + "comment" : { + "type" : "string", + "description" : "Human-readable comment", + "x-nullable" : true + }, + "credential_preview" : { + "$ref" : "#/definitions/CredentialPreview" + }, + "offers~attach" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/AttachDecorator" + } + } + } + }, + "CredentialPreview" : { + "type" : "object", + "required" : [ "attributes" ], + "properties" : { + "@type" : { + "type" : "string", + "example" : "issue-credential/1.0/credential-preview", + "description" : "Message type identifier" + }, + "attributes" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/CredAttrSpec" + } + } + } + }, + "CredentialProposal" : { + "type" : "object", + "properties" : { + "@id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Message identifier" + }, + "@type" : { + "type" : "string", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "description" : "Message type", + "readOnly" : true + }, + "comment" : { + "type" : "string", + "description" : "Human-readable comment", + "x-nullable" : true + }, + "cred_def_id" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" + }, + "credential_proposal" : { + "$ref" : "#/definitions/CredentialPreview" + }, + "issuer_did" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + }, + "schema_id" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" + }, + "schema_issuer_did" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + }, + "schema_name" : { + "type" : "string" + }, + "schema_version" : { + "type" : "string", + "example" : "1.0", + "pattern" : "^[0-9.]+$" + } + } + }, + "CredentialStatusOptions" : { + "type" : "object", + "required" : [ "type" ], + "properties" : { + "type" : { + "type" : "string", + "example" : "CredentialStatusList2017", + "description" : "Credential status method type to use for the credential. Should match status method registered in the Verifiable Credential Extension Registry" + } + } + }, + "DID" : { + "type" : "object", + "properties" : { + "did" : { + "type" : "string", + "example" : "did:peer:WgWxqztrNooG92RXvxSTWv", + "description" : "DID of interest", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$|^did:([a-zA-Z0-9_]+):([a-zA-Z0-9_.%-]+(:[a-zA-Z0-9_.%-]+)*)((;[a-zA-Z0-9_.:%-]+=[a-zA-Z0-9_.:%-]*)*)(\\/[^#?]*)?([?][^#]*)?(\\#.*)?$$" + }, + "key_type" : { + "type" : "string", + "example" : "ed25519", + "description" : "Key type associated with the DID", + "enum" : [ "ed25519", "bls12381g2" ] + }, + "method" : { + "type" : "string", + "example" : "sov", + "description" : "Did method associated with the DID" + }, + "posture" : { + "type" : "string", + "example" : "wallet_only", + "description" : "Whether DID is current public DID, posted to ledger but not current public DID, or local to the wallet", + "enum" : [ "public", "posted", "wallet_only" ] + }, + "verkey" : { + "type" : "string", + "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", + "description" : "Public verification key", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" + } + } + }, + "DIDCreate" : { + "type" : "object", + "properties" : { + "method" : { + "type" : "string", + "example" : "sov", + "description" : "Method for the requested DID.Supported methods are 'key', 'sov', and any other registered method." + }, + "options" : { + "$ref" : "#/definitions/DIDCreate_options" + }, + "seed" : { + "type" : "string", + "example" : "000000000000000000000000Trustee1", + "description" : "Optional seed to use for DID, Must beenabled in configuration before use." + } + } + }, + "DIDCreateOptions" : { + "type" : "object", + "required" : [ "key_type" ], + "properties" : { + "did" : { + "type" : "string", + "example" : "did:peer:WgWxqztrNooG92RXvxSTWv", + "description" : "Specify final value of the did (including did:: prefix)if the method supports or requires so.", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$|^did:([a-zA-Z0-9_]+):([a-zA-Z0-9_.%-]+(:[a-zA-Z0-9_.%-]+)*)((;[a-zA-Z0-9_.:%-]+=[a-zA-Z0-9_.:%-]*)*)(\\/[^#?]*)?([?][^#]*)?(\\#.*)?$$" + }, + "key_type" : { + "type" : "string", + "example" : "ed25519", + "description" : "Key type to use for the DID keypair. Validated with the chosen DID method's supported key types.", + "enum" : [ "ed25519", "bls12381g2" ] + } + } + }, + "DIDEndpoint" : { + "type" : "object", + "required" : [ "did" ], + "properties" : { + "did" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv", + "description" : "DID of interest", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + }, + "endpoint" : { + "type" : "string", + "example" : "https://myhost:8021", + "description" : "Endpoint to set (omit to delete)", + "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" + } + } + }, + "DIDEndpointWithType" : { + "type" : "object", + "required" : [ "did" ], + "properties" : { + "did" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv", + "description" : "DID of interest", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + }, + "endpoint" : { + "type" : "string", + "example" : "https://myhost:8021", + "description" : "Endpoint to set (omit to delete)", + "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" + }, + "endpoint_type" : { + "type" : "string", + "example" : "Endpoint", + "description" : "Endpoint type to set (default 'Endpoint'); affects only public or posted DIDs", + "enum" : [ "Endpoint", "Profile", "LinkedDomains" ] + } + } + }, + "DIDList" : { + "type" : "object", + "properties" : { + "results" : { + "type" : "array", + "description" : "DID list", + "items" : { + "$ref" : "#/definitions/DID" + } + } + } + }, + "DIDResult" : { + "type" : "object", + "properties" : { + "result" : { + "$ref" : "#/definitions/DID" + } + } + }, + "DIDXRequest" : { + "type" : "object", + "required" : [ "label" ], + "properties" : { + "@id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Message identifier" + }, + "@type" : { + "type" : "string", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "description" : "Message type", + "readOnly" : true + }, + "did" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv", + "description" : "DID of exchange", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + }, + "did_doc~attach" : { + "$ref" : "#/definitions/DIDXRequest_did_docattach" + }, + "label" : { + "type" : "string", + "example" : "Request to connect with Bob", + "description" : "Label for DID exchange request" + } + } + }, + "DIFField" : { + "type" : "object", + "properties" : { + "filter" : { + "$ref" : "#/definitions/Filter" + }, + "id" : { + "type" : "string", + "description" : "ID" + }, + "path" : { + "type" : "array", + "items" : { + "type" : "string", + "description" : "Path" + } + }, + "predicate" : { + "type" : "string", + "description" : "Preference", + "enum" : [ "required", "preferred" ] + }, + "purpose" : { + "type" : "string", + "description" : "Purpose" + } + } + }, + "DIFHolder" : { + "type" : "object", + "properties" : { + "directive" : { + "type" : "string", + "description" : "Preference", + "enum" : [ "required", "preferred" ] + }, + "field_id" : { + "type" : "array", + "items" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "FieldID", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + } + } + } + }, + "DIFOptions" : { + "type" : "object", + "properties" : { + "challenge" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Challenge protect against replay attack", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + }, + "domain" : { + "type" : "string", + "example" : "4jt78h47fh47", + "description" : "Domain protect against replay attack" + } + } + }, + "DIFPresSpec" : { + "type" : "object", + "properties" : { + "issuer_id" : { + "type" : "string", + "description" : "Issuer identifier to sign the presentation, if different from current public DID" + }, + "presentation_definition" : { + "$ref" : "#/definitions/PresentationDefinition" + }, + "record_ids" : { + "type" : "object", + "example" : { + "" : [ "", "" ], + "" : [ "" ] + }, + "description" : "Mapping of input_descriptor id to list of stored W3C credential record_id", + "properties" : { } + }, + "reveal_doc" : { + "type" : "object", + "example" : { + "@context" : [ "https://www.w3.org/2018/credentials/v1", "https://w3id.org/security/bbs/v1" ], + "@explicit" : true, + "@requireAll" : true, + "credentialSubject" : { + "@explicit" : true, + "@requireAll" : true, + "Observation" : [ { + "effectiveDateTime" : { }, + "@explicit" : true, + "@requireAll" : true + } ] + }, + "issuanceDate" : { }, + "issuer" : { }, + "type" : [ "VerifiableCredential", "LabReport" ] + }, + "description" : "reveal doc [JSON-LD frame] dict used to derive the credential when selective disclosure is required", + "properties" : { } + } + } + }, + "DIFProofProposal" : { + "type" : "object", + "properties" : { + "input_descriptors" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/InputDescriptors" + } + }, + "options" : { + "$ref" : "#/definitions/DIFOptions" + } + } + }, + "DIFProofRequest" : { + "type" : "object", + "required" : [ "presentation_definition" ], + "properties" : { + "options" : { + "$ref" : "#/definitions/DIFOptions" + }, + "presentation_definition" : { + "$ref" : "#/definitions/PresentationDefinition" + } + } + }, + "Date" : { + "type" : "object", + "required" : [ "expires_time" ], + "properties" : { + "expires_time" : { + "type" : "string", + "format" : "date-time", + "example" : "2021-03-29T05:22:19Z", + "description" : "Expiry Date" + } + } + }, + "Disclose" : { + "type" : "object", + "required" : [ "protocols" ], + "properties" : { + "@id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Message identifier" + }, + "@type" : { + "type" : "string", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "description" : "Message type", + "readOnly" : true + }, + "protocols" : { + "type" : "array", + "description" : "List of protocol descriptors", + "items" : { + "$ref" : "#/definitions/ProtocolDescriptor" + } + } + } + }, + "Disclosures" : { + "type" : "object", + "required" : [ "disclosures" ], + "properties" : { + "@id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Message identifier" + }, + "@type" : { + "type" : "string", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "description" : "Message type", + "readOnly" : true + }, + "disclosures" : { + "type" : "array", + "description" : "List of protocol or goal_code descriptors", + "items" : { } + } + } + }, + "Doc" : { + "type" : "object", + "required" : [ "credential", "options" ], + "properties" : { + "credential" : { + "type" : "object", + "description" : "Credential to sign", + "properties" : { } + }, + "options" : { + "$ref" : "#/definitions/Doc_options" + } + } + }, + "EndorserInfo" : { + "type" : "object", + "required" : [ "endorser_did" ], + "properties" : { + "endorser_did" : { + "type" : "string", + "description" : "Endorser DID" + }, + "endorser_name" : { + "type" : "string", + "description" : "Endorser Name" + } + } + }, + "EndpointsResult" : { + "type" : "object", + "properties" : { + "my_endpoint" : { + "type" : "string", + "example" : "https://myhost:8021", + "description" : "My endpoint", + "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" + }, + "their_endpoint" : { + "type" : "string", + "example" : "https://myhost:8021", + "description" : "Their endpoint", + "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" + } + } + }, + "Filter" : { + "type" : "object", + "properties" : { + "const" : { + "description" : "Const" + }, + "enum" : { + "type" : "array", + "items" : { + "description" : "Enum" + } + }, + "exclusiveMaximum" : { + "description" : "ExclusiveMaximum" + }, + "exclusiveMinimum" : { + "description" : "ExclusiveMinimum" + }, + "format" : { + "type" : "string", + "description" : "Format" + }, + "maxLength" : { + "type" : "integer", + "format" : "int32", + "example" : 1234, + "description" : "Max Length" + }, + "maximum" : { + "description" : "Maximum" + }, + "minLength" : { + "type" : "integer", + "format" : "int32", + "example" : 1234, + "description" : "Min Length" + }, + "minimum" : { + "description" : "Minimum" + }, + "not" : { + "type" : "boolean", + "example" : false, + "description" : "Not" + }, + "pattern" : { + "type" : "string", + "description" : "Pattern" + }, + "type" : { + "type" : "string", + "description" : "Type" + } + } + }, + "Generated" : { + "type" : "object", + "properties" : { + "master_secret" : { + "type" : "string", + "example" : "0", + "pattern" : "^[0-9]*$" + }, + "number" : { + "type" : "string", + "example" : "0", + "pattern" : "^[0-9]*$" + }, + "remainder" : { + "type" : "string", + "example" : "0", + "pattern" : "^[0-9]*$" + } + } + }, + "GetDIDEndpointResponse" : { + "type" : "object", + "properties" : { + "endpoint" : { + "type" : "string", + "example" : "https://myhost:8021", + "description" : "Full verification key", + "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$", + "x-nullable" : true + } + } + }, + "GetDIDVerkeyResponse" : { + "type" : "object", + "properties" : { + "verkey" : { + "type" : "string", + "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", + "description" : "Full verification key", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$", + "x-nullable" : true + } + } + }, + "GetNymRoleResponse" : { + "type" : "object", + "properties" : { + "role" : { + "type" : "string", + "example" : "ENDORSER", + "description" : "Ledger role", + "enum" : [ "STEWARD", "TRUSTEE", "ENDORSER", "NETWORK_MONITOR", "USER", "ROLE_REMOVE" ] + } + } + }, + "HolderModuleResponse" : { + "type" : "object" + }, + "IndyAttrValue" : { + "type" : "object", + "required" : [ "encoded", "raw" ], + "properties" : { + "encoded" : { + "type" : "string", + "example" : "-1", + "description" : "Attribute encoded value", + "pattern" : "^-?[0-9]*$" + }, + "raw" : { + "type" : "string", + "description" : "Attribute raw value" + } + } + }, + "IndyCredAbstract" : { + "type" : "object", + "required" : [ "cred_def_id", "key_correctness_proof", "nonce", "schema_id" ], + "properties" : { + "cred_def_id" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "description" : "Credential definition identifier", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" + }, + "key_correctness_proof" : { + "$ref" : "#/definitions/IndyCredAbstract_key_correctness_proof" + }, + "nonce" : { + "type" : "string", + "example" : "0", + "description" : "Nonce in credential abstract", + "pattern" : "^[0-9]*$" + }, + "schema_id" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", + "description" : "Schema identifier", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" + } + } + }, + "IndyCredInfo" : { + "type" : "object", + "properties" : { + "attrs" : { + "type" : "object", + "description" : "Attribute names and value", + "additionalProperties" : { + "type" : "string", + "example" : "alice" + } + }, + "cred_def_id" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "description" : "Credential definition identifier", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" + }, + "cred_rev_id" : { + "type" : "string", + "example" : "12345", + "description" : "Credential revocation identifier", + "pattern" : "^[1-9][0-9]*$", + "x-nullable" : true + }, + "referent" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Wallet referent" + }, + "rev_reg_id" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:4:WgWxqztrNooG92RXvxSTWv:3:CL:20:tag:CL_ACCUM:0", + "description" : "Revocation registry identifier", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)", + "x-nullable" : true + }, + "schema_id" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", + "description" : "Schema identifier", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" + } + } + }, + "IndyCredPrecis" : { + "type" : "object", + "properties" : { + "cred_info" : { + "$ref" : "#/definitions/IndyCredPrecis_cred_info" + }, + "interval" : { + "$ref" : "#/definitions/IndyCredPrecis_interval" + }, + "presentation_referents" : { + "type" : "array", + "items" : { + "type" : "string", + "example" : "1_age_uuid", + "description" : "presentation referent" + } + } + } + }, + "IndyCredRequest" : { + "type" : "object", + "required" : [ "blinded_ms", "blinded_ms_correctness_proof", "cred_def_id", "nonce", "prover_did" ], + "properties" : { + "blinded_ms" : { + "type" : "object", + "description" : "Blinded master secret", + "properties" : { } + }, + "blinded_ms_correctness_proof" : { + "type" : "object", + "description" : "Blinded master secret correctness proof", + "properties" : { } + }, + "cred_def_id" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "description" : "Credential definition identifier", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" + }, + "nonce" : { + "type" : "string", + "example" : "0", + "description" : "Nonce in credential request", + "pattern" : "^[0-9]*$" + }, + "prover_did" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv", + "description" : "Prover DID", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + } + } + }, + "IndyCredential" : { + "type" : "object", + "required" : [ "cred_def_id", "schema_id", "signature", "signature_correctness_proof", "values" ], + "properties" : { + "cred_def_id" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "description" : "Credential definition identifier", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" + }, + "rev_reg" : { + "type" : "object", + "description" : "Revocation registry state", + "properties" : { }, + "x-nullable" : true + }, + "rev_reg_id" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:4:WgWxqztrNooG92RXvxSTWv:3:CL:20:tag:CL_ACCUM:0", + "description" : "Revocation registry identifier", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)", + "x-nullable" : true + }, + "schema_id" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", + "description" : "Schema identifier", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" + }, + "signature" : { + "type" : "object", + "description" : "Credential signature", + "properties" : { } + }, + "signature_correctness_proof" : { + "type" : "object", + "description" : "Credential signature correctness proof", + "properties" : { } + }, + "values" : { + "type" : "object", + "description" : "Credential attributes", + "additionalProperties" : { + "type" : "object", + "description" : "Attribute value", + "allOf" : [ { + "$ref" : "#/definitions/IndyAttrValue" + } ] + } + }, + "witness" : { + "type" : "object", + "description" : "Witness for revocation proof", + "properties" : { }, + "x-nullable" : true + } + } + }, + "IndyEQProof" : { + "type" : "object", + "properties" : { + "a_prime" : { + "type" : "string", + "example" : "0", + "pattern" : "^[0-9]*$" + }, + "e" : { + "type" : "string", + "example" : "0", + "pattern" : "^[0-9]*$" + }, + "m" : { + "type" : "object", + "additionalProperties" : { + "type" : "string", + "example" : "0", + "pattern" : "^[0-9]*$" + } + }, + "m2" : { + "type" : "string", + "example" : "0", + "pattern" : "^[0-9]*$" + }, + "revealed_attrs" : { + "type" : "object", + "additionalProperties" : { + "type" : "string", + "example" : "-1", + "pattern" : "^-?[0-9]*$" + } + }, + "v" : { + "type" : "string", + "example" : "0", + "pattern" : "^[0-9]*$" + } + } + }, + "IndyGEProof" : { + "type" : "object", + "properties" : { + "alpha" : { + "type" : "string", + "example" : "0", + "pattern" : "^[0-9]*$" + }, + "mj" : { + "type" : "string", + "example" : "0", + "pattern" : "^[0-9]*$" + }, + "predicate" : { + "$ref" : "#/definitions/IndyGEProofPred" + }, + "r" : { + "type" : "object", + "additionalProperties" : { + "type" : "string", + "example" : "0", + "pattern" : "^[0-9]*$" + } + }, + "t" : { + "type" : "object", + "additionalProperties" : { + "type" : "string", + "example" : "0", + "pattern" : "^[0-9]*$" + } + }, + "u" : { + "type" : "object", + "additionalProperties" : { + "type" : "string", + "example" : "0", + "pattern" : "^[0-9]*$" + } + } + } + }, + "IndyGEProofPred" : { + "type" : "object", + "properties" : { + "attr_name" : { + "type" : "string", + "description" : "Attribute name, indy-canonicalized" + }, + "p_type" : { + "type" : "string", + "description" : "Predicate type", + "enum" : [ "LT", "LE", "GE", "GT" ] + }, + "value" : { + "type" : "integer", + "format" : "int32", + "description" : "Predicate threshold value" + } + } + }, + "IndyKeyCorrectnessProof" : { + "type" : "object", + "required" : [ "c", "xr_cap", "xz_cap" ], + "properties" : { + "c" : { + "type" : "string", + "example" : "0", + "description" : "c in key correctness proof", + "pattern" : "^[0-9]*$" + }, + "xr_cap" : { + "type" : "array", + "description" : "xr_cap in key correctness proof", + "items" : { + "type" : "array", + "description" : "xr_cap components in key correctness proof", + "items" : { + "type" : "string", + "description" : "xr_cap component values in key correctness proof" + } + } + }, + "xz_cap" : { + "type" : "string", + "example" : "0", + "description" : "xz_cap in key correctness proof", + "pattern" : "^[0-9]*$" + } + } + }, + "IndyNonRevocProof" : { + "type" : "object", + "properties" : { + "c_list" : { + "type" : "object", + "additionalProperties" : { + "type" : "string" + } + }, + "x_list" : { + "type" : "object", + "additionalProperties" : { + "type" : "string" + } + } + } + }, + "IndyNonRevocationInterval" : { + "type" : "object", + "properties" : { + "from" : { + "type" : "integer", + "format" : "int32", + "example" : 1640995199, + "description" : "Earliest time of interest in non-revocation interval", + "minimum" : 0, + "maximum" : 18446744073709551615 + }, + "to" : { + "type" : "integer", + "format" : "int32", + "example" : 1640995199, + "description" : "Latest time of interest in non-revocation interval", + "minimum" : 0, + "maximum" : 18446744073709551615 + } + } + }, + "IndyPresAttrSpec" : { + "type" : "object", + "required" : [ "name" ], + "properties" : { + "cred_def_id" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" + }, + "mime-type" : { + "type" : "string", + "example" : "image/jpeg", + "description" : "MIME type (default null)" + }, + "name" : { + "type" : "string", + "example" : "favourite_drink", + "description" : "Attribute name" + }, + "referent" : { + "type" : "string", + "example" : "0", + "description" : "Credential referent" + }, + "value" : { + "type" : "string", + "example" : "martini", + "description" : "Attribute value" + } + } + }, + "IndyPresPredSpec" : { + "type" : "object", + "required" : [ "name", "predicate", "threshold" ], + "properties" : { + "cred_def_id" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "description" : "Credential definition identifier", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" + }, + "name" : { + "type" : "string", + "example" : "high_score", + "description" : "Attribute name" + }, + "predicate" : { + "type" : "string", + "example" : ">=", + "description" : "Predicate type ('<', '<=', '>=', or '>')", + "enum" : [ "<", "<=", ">=", ">" ] + }, + "threshold" : { + "type" : "integer", + "format" : "int32", + "description" : "Threshold value" + } + } + }, + "IndyPresPreview" : { + "type" : "object", + "required" : [ "attributes", "predicates" ], + "properties" : { + "@type" : { + "type" : "string", + "example" : "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/present-proof/1.0/presentation-preview", + "description" : "Message type identifier" + }, + "attributes" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/IndyPresAttrSpec" + } + }, + "predicates" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/IndyPresPredSpec" + } + } + } + }, + "IndyPresSpec" : { + "type" : "object", + "required" : [ "requested_attributes", "requested_predicates", "self_attested_attributes" ], + "properties" : { + "requested_attributes" : { + "type" : "object", + "description" : "Nested object mapping proof request attribute referents to requested-attribute specifiers", + "additionalProperties" : { + "$ref" : "#/definitions/IndyRequestedCredsRequestedAttr" + } + }, + "requested_predicates" : { + "type" : "object", + "description" : "Nested object mapping proof request predicate referents to requested-predicate specifiers", + "additionalProperties" : { + "$ref" : "#/definitions/IndyRequestedCredsRequestedPred" + } + }, + "self_attested_attributes" : { + "type" : "object", + "description" : "Self-attested attributes to build into proof", + "additionalProperties" : { + "type" : "string", + "example" : "self_attested_value", + "description" : "Self-attested attribute values to use in requested-credentials structure for proof construction" + } + }, + "trace" : { + "type" : "boolean", + "example" : false, + "description" : "Whether to trace event (default false)" + } + } + }, + "IndyPrimaryProof" : { + "type" : "object", + "properties" : { + "eq_proof" : { + "$ref" : "#/definitions/IndyPrimaryProof_eq_proof" + }, + "ge_proofs" : { + "type" : "array", + "description" : "Indy GE proofs", + "items" : { + "$ref" : "#/definitions/IndyGEProof" + }, + "x-nullable" : true + } + } + }, + "IndyProof" : { + "type" : "object", + "properties" : { + "identifiers" : { + "type" : "array", + "description" : "Indy proof.identifiers content", + "items" : { + "$ref" : "#/definitions/IndyProofIdentifier" + } + }, + "proof" : { + "$ref" : "#/definitions/IndyProof_proof" + }, + "requested_proof" : { + "$ref" : "#/definitions/IndyProof_requested_proof" + } + } + }, + "IndyProofIdentifier" : { + "type" : "object", + "properties" : { + "cred_def_id" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "description" : "Credential definition identifier", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" + }, + "rev_reg_id" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:4:WgWxqztrNooG92RXvxSTWv:3:CL:20:tag:CL_ACCUM:0", + "description" : "Revocation registry identifier", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)", + "x-nullable" : true + }, + "schema_id" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", + "description" : "Schema identifier", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" + }, + "timestamp" : { + "type" : "integer", + "format" : "int32", + "example" : 1640995199, + "description" : "Timestamp epoch", + "minimum" : 0, + "maximum" : 18446744073709551615, + "x-nullable" : true + } + } + }, + "IndyProofProof" : { + "type" : "object", + "properties" : { + "aggregated_proof" : { + "$ref" : "#/definitions/IndyProofProof_aggregated_proof" + }, + "proofs" : { + "type" : "array", + "description" : "Indy proof proofs", + "items" : { + "$ref" : "#/definitions/IndyProofProofProofsProof" + } + } + } + }, + "IndyProofProofAggregatedProof" : { + "type" : "object", + "properties" : { + "c_hash" : { + "type" : "string", + "description" : "c_hash value" + }, + "c_list" : { + "type" : "array", + "description" : "c_list value", + "items" : { + "type" : "array", + "items" : { + "type" : "integer", + "format" : "int32" + } + } + } + } + }, + "IndyProofProofProofsProof" : { + "type" : "object", + "properties" : { + "non_revoc_proof" : { + "$ref" : "#/definitions/IndyProofProofProofsProof_non_revoc_proof" + }, + "primary_proof" : { + "$ref" : "#/definitions/IndyProofProofProofsProof_primary_proof" + } + } + }, + "IndyProofReqAttrSpec" : { + "type" : "object", + "properties" : { + "name" : { + "type" : "string", + "example" : "favouriteDrink", + "description" : "Attribute name" + }, + "names" : { + "type" : "array", + "description" : "Attribute name group", + "items" : { + "type" : "string", + "example" : "age" + } + }, + "non_revoked" : { + "$ref" : "#/definitions/IndyProofReqAttrSpec_non_revoked" + }, + "restrictions" : { + "type" : "array", + "description" : "If present, credential must satisfy one of given restrictions: specify schema_id, schema_issuer_did, schema_name, schema_version, issuer_did, cred_def_id, and/or attr::::value where represents a credential attribute name", + "items" : { + "type" : "object", + "additionalProperties" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag" + } + } + } + } + }, + "IndyProofReqAttrSpecNonRevoked" : { + "type" : "object", + "properties" : { + "from" : { + "type" : "integer", + "format" : "int32", + "example" : 1640995199, + "description" : "Earliest time of interest in non-revocation interval", + "minimum" : 0, + "maximum" : 18446744073709551615 + }, + "to" : { + "type" : "integer", + "format" : "int32", + "example" : 1640995199, + "description" : "Latest time of interest in non-revocation interval", + "minimum" : 0, + "maximum" : 18446744073709551615 + } + } + }, + "IndyProofReqPredSpec" : { + "type" : "object", + "required" : [ "name", "p_type", "p_value" ], + "properties" : { + "name" : { + "type" : "string", + "example" : "index", + "description" : "Attribute name" + }, + "non_revoked" : { + "$ref" : "#/definitions/IndyProofReqAttrSpec_non_revoked" + }, + "p_type" : { + "type" : "string", + "example" : ">=", + "description" : "Predicate type ('<', '<=', '>=', or '>')", + "enum" : [ "<", "<=", ">=", ">" ] + }, + "p_value" : { + "type" : "integer", + "format" : "int32", + "description" : "Threshold value" + }, + "restrictions" : { + "type" : "array", + "description" : "If present, credential must satisfy one of given restrictions: specify schema_id, schema_issuer_did, schema_name, schema_version, issuer_did, cred_def_id, and/or attr::::value where represents a credential attribute name", + "items" : { + "type" : "object", + "additionalProperties" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag" + } + } + } + } + }, + "IndyProofReqPredSpecNonRevoked" : { + "type" : "object", + "properties" : { + "from" : { + "type" : "integer", + "format" : "int32", + "example" : 1640995199, + "description" : "Earliest time of interest in non-revocation interval", + "minimum" : 0, + "maximum" : 18446744073709551615 + }, + "to" : { + "type" : "integer", + "format" : "int32", + "example" : 1640995199, + "description" : "Latest time of interest in non-revocation interval", + "minimum" : 0, + "maximum" : 18446744073709551615 + } + } + }, + "IndyProofRequest" : { + "type" : "object", + "required" : [ "requested_attributes", "requested_predicates" ], + "properties" : { + "name" : { + "type" : "string", + "example" : "Proof request", + "description" : "Proof request name" + }, + "non_revoked" : { + "$ref" : "#/definitions/IndyProofReqAttrSpec_non_revoked" + }, + "nonce" : { + "type" : "string", + "example" : "1", + "description" : "Nonce", + "pattern" : "^[1-9][0-9]*$" + }, + "requested_attributes" : { + "type" : "object", + "description" : "Requested attribute specifications of proof request", + "additionalProperties" : { + "$ref" : "#/definitions/IndyProofReqAttrSpec" + } + }, + "requested_predicates" : { + "type" : "object", + "description" : "Requested predicate specifications of proof request", + "additionalProperties" : { + "$ref" : "#/definitions/IndyProofReqPredSpec" + } + }, + "version" : { + "type" : "string", + "example" : "1.0", + "description" : "Proof request version", + "pattern" : "^[0-9.]+$" + } + } + }, + "IndyProofRequestNonRevoked" : { + "type" : "object", + "properties" : { + "from" : { + "type" : "integer", + "format" : "int32", + "example" : 1640995199, + "description" : "Earliest time of interest in non-revocation interval", + "minimum" : 0, + "maximum" : 18446744073709551615 + }, + "to" : { + "type" : "integer", + "format" : "int32", + "example" : 1640995199, + "description" : "Latest time of interest in non-revocation interval", + "minimum" : 0, + "maximum" : 18446744073709551615 + } + } + }, + "IndyProofRequestedProof" : { + "type" : "object", + "properties" : { + "predicates" : { + "type" : "object", + "description" : "Proof requested proof predicates.", + "additionalProperties" : { + "$ref" : "#/definitions/IndyProofRequestedProofPredicate" + } + }, + "revealed_attr_groups" : { + "type" : "object", + "description" : "Proof requested proof revealed attribute groups", + "additionalProperties" : { + "$ref" : "#/definitions/IndyProofRequestedProofRevealedAttrGroup" + }, + "x-nullable" : true + }, + "revealed_attrs" : { + "type" : "object", + "description" : "Proof requested proof revealed attributes", + "additionalProperties" : { + "$ref" : "#/definitions/IndyProofRequestedProofRevealedAttr" + }, + "x-nullable" : true + }, + "self_attested_attrs" : { + "type" : "object", + "description" : "Proof requested proof self-attested attributes", + "properties" : { } + }, + "unrevealed_attrs" : { + "type" : "object", + "description" : "Unrevealed attributes", + "properties" : { } + } + } + }, + "IndyProofRequestedProofPredicate" : { + "type" : "object", + "properties" : { + "sub_proof_index" : { + "type" : "integer", + "format" : "int32", + "description" : "Sub-proof index" + } + } + }, + "IndyProofRequestedProofRevealedAttr" : { + "type" : "object", + "properties" : { + "encoded" : { + "type" : "string", + "example" : "-1", + "description" : "Encoded value", + "pattern" : "^-?[0-9]*$" + }, + "raw" : { + "type" : "string", + "description" : "Raw value" + }, + "sub_proof_index" : { + "type" : "integer", + "format" : "int32", + "description" : "Sub-proof index" + } + } + }, + "IndyProofRequestedProofRevealedAttrGroup" : { + "type" : "object", + "properties" : { + "sub_proof_index" : { + "type" : "integer", + "format" : "int32", + "description" : "Sub-proof index" + }, + "values" : { + "type" : "object", + "description" : "Indy proof requested proof revealed attr groups group value", + "additionalProperties" : { + "$ref" : "#/definitions/RawEncoded" + } + } + } + }, + "IndyRequestedCredsRequestedAttr" : { + "type" : "object", + "required" : [ "cred_id" ], + "properties" : { + "cred_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Wallet credential identifier (typically but not necessarily a UUID)" + }, + "revealed" : { + "type" : "boolean", + "description" : "Whether to reveal attribute in proof (default true)" + } + } + }, + "IndyRequestedCredsRequestedPred" : { + "type" : "object", + "required" : [ "cred_id" ], + "properties" : { + "cred_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Wallet credential identifier (typically but not necessarily a UUID)" + }, + "timestamp" : { + "type" : "integer", + "format" : "int32", + "example" : 1640995199, + "description" : "Epoch timestamp of interest for non-revocation proof", + "minimum" : 0, + "maximum" : 18446744073709551615 + } + } + }, + "IndyRevRegDef" : { + "type" : "object", + "properties" : { + "credDefId" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "description" : "Credential definition identifier", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" + }, + "id" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:4:WgWxqztrNooG92RXvxSTWv:3:CL:20:tag:CL_ACCUM:0", + "description" : "Indy revocation registry identifier", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" + }, + "revocDefType" : { + "type" : "string", + "example" : "CL_ACCUM", + "description" : "Revocation registry type (specify CL_ACCUM)", + "enum" : [ "CL_ACCUM" ] + }, + "tag" : { + "type" : "string", + "description" : "Revocation registry tag" + }, + "value" : { + "$ref" : "#/definitions/IndyRevRegDef_value" + }, + "ver" : { + "type" : "string", + "example" : "1.0", + "description" : "Version of revocation registry definition", + "pattern" : "^[0-9.]+$" + } + } + }, + "IndyRevRegDefValue" : { + "type" : "object", + "properties" : { + "issuanceType" : { + "type" : "string", + "description" : "Issuance type", + "enum" : [ "ISSUANCE_ON_DEMAND", "ISSUANCE_BY_DEFAULT" ] + }, + "maxCredNum" : { + "type" : "integer", + "format" : "int32", + "example" : 10, + "description" : "Maximum number of credentials; registry size", + "minimum" : 1 + }, + "publicKeys" : { + "$ref" : "#/definitions/IndyRevRegDefValue_publicKeys" + }, + "tailsHash" : { + "type" : "string", + "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", + "description" : "Tails hash value", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" + }, + "tailsLocation" : { + "type" : "string", + "description" : "Tails file location" + } + } + }, + "IndyRevRegDefValuePublicKeys" : { + "type" : "object", + "properties" : { + "accumKey" : { + "$ref" : "#/definitions/IndyRevRegDefValuePublicKeysAccumKey" + } + } + }, + "IndyRevRegDefValuePublicKeysAccumKey" : { + "type" : "object", + "properties" : { + "z" : { + "type" : "string", + "example" : "1 120F522F81E6B7 1 09F7A59005C4939854", + "description" : "Value for z" + } + } + }, + "IndyRevRegEntry" : { + "type" : "object", + "properties" : { + "value" : { + "$ref" : "#/definitions/IndyRevRegEntry_value" + }, + "ver" : { + "type" : "string", + "example" : "1.0", + "description" : "Version of revocation registry entry", + "pattern" : "^[0-9.]+$" + } + } + }, + "IndyRevRegEntryValue" : { + "type" : "object", + "properties" : { + "accum" : { + "type" : "string", + "example" : "21 11792B036AED0AAA12A4 4 298B2571FFC63A737", + "description" : "Accumulator value" + }, + "prevAccum" : { + "type" : "string", + "example" : "21 137AC810975E4 6 76F0384B6F23", + "description" : "Previous accumulator value" + }, + "revoked" : { + "type" : "array", + "description" : "Revoked credential revocation identifiers", + "items" : { + "type" : "integer", + "format" : "int32" + } + } + } + }, + "InputDescriptors" : { + "type" : "object", + "properties" : { + "constraints" : { + "$ref" : "#/definitions/Constraints" + }, + "group" : { + "type" : "array", + "items" : { + "type" : "string", + "description" : "Group" + } + }, + "id" : { + "type" : "string", + "description" : "ID" + }, + "metadata" : { + "type" : "object", + "description" : "Metadata dictionary", + "properties" : { } + }, + "name" : { + "type" : "string", + "description" : "Name" + }, + "purpose" : { + "type" : "string", + "description" : "Purpose" + }, + "schema" : { + "$ref" : "#/definitions/InputDescriptors_schema" + } + } + }, + "IntroModuleResponse" : { + "type" : "object" + }, + "InvitationCreateRequest" : { + "type" : "object", + "properties" : { + "accept" : { + "type" : "array", + "example" : [ "didcomm/aip1", "didcomm/aip2;env=rfc19" ], + "description" : "List of mime type in order of preference that should be use in responding to the message", + "items" : { + "type" : "string" + } + }, + "alias" : { + "type" : "string", + "example" : "Barry", + "description" : "Alias for connection" + }, + "attachments" : { + "type" : "array", + "description" : "Optional invitation attachments", + "items" : { + "$ref" : "#/definitions/AttachmentDef" + } + }, + "handshake_protocols" : { + "type" : "array", + "items" : { + "type" : "string", + "example" : "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/didexchange/1.0", + "description" : "Handshake protocol to specify in invitation" + } + }, + "mediation_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Identifier for active mediation record to be used", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + }, + "metadata" : { + "type" : "object", + "description" : "Optional metadata to attach to the connection created with the invitation", + "properties" : { } + }, + "my_label" : { + "type" : "string", + "example" : "Invitation to Barry", + "description" : "Label for connection invitation" + }, + "protocol_version" : { + "type" : "string", + "example" : "1.1", + "description" : "OOB protocol version" + }, + "use_public_did" : { + "type" : "boolean", + "example" : false, + "description" : "Whether to use public DID in invitation" + } + } + }, + "InvitationMessage" : { + "type" : "object", + "properties" : { + "@id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Message identifier" + }, + "@type" : { + "type" : "string", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "description" : "Message type" + }, + "accept" : { + "type" : "array", + "example" : [ "didcomm/aip1", "didcomm/aip2;env=rfc19" ], + "description" : "List of mime type in order of preference", + "items" : { + "type" : "string" + } + }, + "handshake_protocols" : { + "type" : "array", + "items" : { + "type" : "string", + "example" : "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/didexchange/1.0", + "description" : "Handshake protocol" + } + }, + "imageUrl" : { + "type" : "string", + "format" : "url", + "example" : "http://192.168.56.101/img/logo.jpg", + "description" : "Optional image URL for out-of-band invitation", + "x-nullable" : true + }, + "label" : { + "type" : "string", + "example" : "Bob", + "description" : "Optional label" + }, + "requests~attach" : { + "type" : "array", + "description" : "Optional request attachment", + "items" : { + "$ref" : "#/definitions/AttachDecorator" + } + }, + "services" : { + "type" : "array", + "example" : [ { + "did" : "WgWxqztrNooG92RXvxSTWv", + "id" : "string", + "recipientKeys" : [ "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH" ], + "routingKeys" : [ "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH" ], + "serviceEndpoint" : "http://192.168.56.101:8020", + "type" : "string" + }, "did:sov:WgWxqztrNooG92RXvxSTWv" ], + "items" : { + "description" : "Either a DIDComm service object (as per RFC0067) or a DID string." + } + } + } + }, + "InvitationRecord" : { + "type" : "object", + "properties" : { + "created_at" : { + "type" : "string", + "example" : "2021-12-31T23:59:59Z", + "description" : "Time of record creation", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" + }, + "invi_msg_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Invitation message identifier" + }, + "invitation" : { + "$ref" : "#/definitions/InvitationRecord_invitation" + }, + "invitation_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Invitation record identifier" + }, + "invitation_url" : { + "type" : "string", + "example" : "https://example.com/endpoint?c_i=eyJAdHlwZSI6ICIuLi4iLCAiLi4uIjogIi4uLiJ9XX0=", + "description" : "Invitation message URL" + }, + "oob_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Out of band record identifier" + }, + "state" : { + "type" : "string", + "example" : "await_response", + "description" : "Out of band message exchange state" + }, + "trace" : { + "type" : "boolean", + "description" : "Record trace information, based on agent configuration" + }, + "updated_at" : { + "type" : "string", + "example" : "2021-12-31T23:59:59Z", + "description" : "Time of last record update", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" + } + } + }, + "InvitationResult" : { + "type" : "object", + "properties" : { + "connection_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Connection identifier" + }, + "invitation" : { + "$ref" : "#/definitions/ConnectionInvitation" + }, + "invitation_url" : { + "type" : "string", + "example" : "http://192.168.56.101:8020/invite?c_i=eyJAdHlwZSI6Li4ufQ==", + "description" : "Invitation URL" + } + } + }, + "IssueCredentialModuleResponse" : { + "type" : "object" + }, + "IssuerCredRevRecord" : { + "type" : "object", + "properties" : { + "created_at" : { + "type" : "string", + "example" : "2021-12-31T23:59:59Z", + "description" : "Time of record creation", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" + }, + "cred_def_id" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "description" : "Credential definition identifier", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" + }, + "cred_ex_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Credential exchange record identifier at credential issue" + }, + "cred_ex_version" : { + "type" : "string", + "description" : "Credential exchange version" + }, + "cred_rev_id" : { + "type" : "string", + "example" : "12345", + "description" : "Credential revocation identifier", + "pattern" : "^[1-9][0-9]*$" + }, + "record_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Issuer credential revocation record identifier" + }, + "rev_reg_id" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:4:WgWxqztrNooG92RXvxSTWv:3:CL:20:tag:CL_ACCUM:0", + "description" : "Revocation registry identifier", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" + }, + "state" : { + "type" : "string", + "example" : "issued", + "description" : "Issue credential revocation record state" + }, + "updated_at" : { + "type" : "string", + "example" : "2021-12-31T23:59:59Z", + "description" : "Time of last record update", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" + } + } + }, + "IssuerRevRegRecord" : { + "type" : "object", + "properties" : { + "created_at" : { + "type" : "string", + "example" : "2021-12-31T23:59:59Z", + "description" : "Time of record creation", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" + }, + "cred_def_id" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "description" : "Credential definition identifier", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" + }, + "error_msg" : { + "type" : "string", + "example" : "Revocation registry undefined", + "description" : "Error message" + }, + "issuer_did" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv", + "description" : "Issuer DID", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + }, + "max_cred_num" : { + "type" : "integer", + "format" : "int32", + "example" : 1000, + "description" : "Maximum number of credentials for revocation registry" + }, + "pending_pub" : { + "type" : "array", + "description" : "Credential revocation identifier for credential revoked and pending publication to ledger", + "items" : { + "type" : "string", + "example" : "23" + } + }, + "record_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Issuer revocation registry record identifier" + }, + "revoc_def_type" : { + "type" : "string", + "example" : "CL_ACCUM", + "description" : "Revocation registry type (specify CL_ACCUM)", + "enum" : [ "CL_ACCUM" ] + }, + "revoc_reg_def" : { + "$ref" : "#/definitions/IssuerRevRegRecord_revoc_reg_def" + }, + "revoc_reg_entry" : { + "$ref" : "#/definitions/IssuerRevRegRecord_revoc_reg_entry" + }, + "revoc_reg_id" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:4:WgWxqztrNooG92RXvxSTWv:3:CL:20:tag:CL_ACCUM:0", + "description" : "Revocation registry identifier", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" + }, + "state" : { + "type" : "string", + "example" : "active", + "description" : "Issue revocation registry record state" + }, + "tag" : { + "type" : "string", + "description" : "Tag within issuer revocation registry identifier" + }, + "tails_hash" : { + "type" : "string", + "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", + "description" : "Tails hash", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" + }, + "tails_local_path" : { + "type" : "string", + "description" : "Local path to tails file" + }, + "tails_public_uri" : { + "type" : "string", + "description" : "Public URI for tails file" + }, + "updated_at" : { + "type" : "string", + "example" : "2021-12-31T23:59:59Z", + "description" : "Time of last record update", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" + } + } + }, + "Keylist" : { + "type" : "object", + "properties" : { + "results" : { + "type" : "array", + "description" : "List of keylist records", + "items" : { + "$ref" : "#/definitions/RouteRecord" + } + } + } + }, + "KeylistQuery" : { + "type" : "object", + "properties" : { + "@id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Message identifier" + }, + "@type" : { + "type" : "string", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "description" : "Message type", + "readOnly" : true + }, + "filter" : { + "type" : "object", + "example" : { + "filter" : { } + }, + "description" : "Query dictionary object", + "properties" : { } + }, + "paginate" : { + "$ref" : "#/definitions/KeylistQuery_paginate" + } + } + }, + "KeylistQueryFilterRequest" : { + "type" : "object", + "properties" : { + "filter" : { + "type" : "object", + "description" : "Filter for keylist query", + "properties" : { } + } + } + }, + "KeylistQueryPaginate" : { + "type" : "object", + "properties" : { + "limit" : { + "type" : "integer", + "format" : "int32", + "example" : 30, + "description" : "Limit for keylist query" + }, + "offset" : { + "type" : "integer", + "format" : "int32", + "example" : 0, + "description" : "Offset value for query" + } + } + }, + "KeylistUpdate" : { + "type" : "object", + "properties" : { + "@id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Message identifier" + }, + "@type" : { + "type" : "string", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "description" : "Message type", + "readOnly" : true + }, + "updates" : { + "type" : "array", + "description" : "List of update rules", + "items" : { + "$ref" : "#/definitions/KeylistUpdateRule" + } + } + } + }, + "KeylistUpdateRequest" : { + "type" : "object", + "properties" : { + "updates" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/KeylistUpdateRule" + } + } + } + }, + "KeylistUpdateRule" : { + "type" : "object", + "required" : [ "action", "recipient_key" ], + "properties" : { + "action" : { + "type" : "string", + "example" : "add", + "description" : "Action for specific key", + "enum" : [ "add", "remove" ] + }, + "recipient_key" : { + "type" : "string", + "example" : "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH", + "description" : "Key to remove or add", + "pattern" : "^did:key:z[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+$|^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" + } + } + }, + "LDProofVCDetail" : { + "type" : "object", + "required" : [ "credential", "options" ], + "properties" : { + "credential" : { + "$ref" : "#/definitions/LDProofVCDetail_credential" + }, + "options" : { + "$ref" : "#/definitions/LDProofVCDetail_options" + } + } + }, + "LDProofVCDetailOptions" : { + "type" : "object", + "required" : [ "proofType" ], + "properties" : { + "challenge" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "A challenge to include in the proof. SHOULD be provided by the requesting party of the credential (=holder)" + }, + "created" : { + "type" : "string", + "example" : "2021-12-31T23:59:59Z", + "description" : "The date and time of the proof (with a maximum accuracy in seconds). Defaults to current system time", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" + }, + "credentialStatus" : { + "$ref" : "#/definitions/LDProofVCDetailOptions_credentialStatus" + }, + "domain" : { + "type" : "string", + "example" : "example.com", + "description" : "The intended domain of validity for the proof" + }, + "proofPurpose" : { + "type" : "string", + "example" : "assertionMethod", + "description" : "The proof purpose used for the proof. Should match proof purposes registered in the Linked Data Proofs Specification" + }, + "proofType" : { + "type" : "string", + "example" : "Ed25519Signature2018", + "description" : "The proof type used for the proof. Should match suites registered in the Linked Data Cryptographic Suite Registry" + } + } + }, + "LedgerConfigInstance" : { + "type" : "object", + "properties" : { + "genesis_file" : { + "type" : "string", + "description" : "genesis_file" + }, + "genesis_transactions" : { + "type" : "string", + "description" : "genesis_transactions" + }, + "genesis_url" : { + "type" : "string", + "description" : "genesis_url" + }, + "id" : { + "type" : "string", + "description" : "ledger_id" + }, + "is_production" : { + "type" : "boolean", + "description" : "is_production" + } + } + }, + "LedgerConfigList" : { + "type" : "object", + "required" : [ "ledger_config_list" ], + "properties" : { + "ledger_config_list" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/LedgerConfigInstance" + } + } + } + }, + "LedgerModulesResult" : { + "type" : "object" + }, + "LinkedDataProof" : { + "type" : "object", + "required" : [ "created", "proofPurpose", "type", "verificationMethod" ], + "properties" : { + "challenge" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Associates a challenge with a proof, for use with a proofPurpose such as authentication" + }, + "created" : { + "type" : "string", + "example" : "2021-12-31T23:59:59Z", + "description" : "The string value of an ISO8601 combined date and time string generated by the Signature Algorithm", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" + }, + "domain" : { + "type" : "string", + "example" : "example.com", + "description" : "A string value specifying the restricted domain of the signature.", + "pattern" : "\\w+:(\\/?\\/?)[^\\s]+" + }, + "jws" : { + "type" : "string", + "example" : "eyJhbGciOiAiRWREUc2UsICJjcml0IjogWyJiNjQiXX0..lKJU0Df_keblRKhZAS9Qq6zybm-HqUXNVZ8vgEPNTAjQ1Ch6YBKY7UBAjg6iBX5qBQ", + "description" : "Associates a Detached Json Web Signature with a proof" + }, + "nonce" : { + "type" : "string", + "example" : "CF69iO3nfvqRsRBNElE8b4wO39SyJHPM7Gg1nExltW5vSfQA1lvDCR/zXX1To0/4NLo==", + "description" : "The nonce" + }, + "proofPurpose" : { + "type" : "string", + "example" : "assertionMethod", + "description" : "Proof purpose" + }, + "proofValue" : { + "type" : "string", + "example" : "sy1AahqbzJQ63n9RtekmwzqZeVj494VppdAVJBnMYrTwft6cLJJGeTSSxCCJ6HKnRtwE7jjDh6sB2z2AAiZY9BBnCD8wUVgwqH3qchGRCuC2RugA4eQ9fUrR4Yuycac3caiaaay", + "description" : "The proof value of a proof" + }, + "type" : { + "type" : "string", + "example" : "Ed25519Signature2018", + "description" : "Identifies the digital signature suite that was used to create the signature" + }, + "verificationMethod" : { + "type" : "string", + "example" : "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", + "description" : "Information used for proof verification", + "pattern" : "\\w+:(\\/?\\/?)[^\\s]+" + } + } + }, + "MediationCreateRequest" : { + "type" : "object", + "properties" : { + "mediator_terms" : { + "type" : "array", + "description" : "List of mediator rules for recipient", + "items" : { + "type" : "string", + "description" : "Indicate terms to which the mediator requires the recipient to agree" + } + }, + "recipient_terms" : { + "type" : "array", + "description" : "List of recipient rules for mediation", + "items" : { + "type" : "string", + "description" : "Indicate terms to which the recipient requires the mediator to agree" + } + } + } + }, + "MediationDeny" : { + "type" : "object", + "properties" : { + "@id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Message identifier" + }, + "@type" : { + "type" : "string", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "description" : "Message type", + "readOnly" : true + }, + "mediator_terms" : { + "type" : "array", + "items" : { + "type" : "string", + "description" : "Terms for mediator to agree" + } + }, + "recipient_terms" : { + "type" : "array", + "items" : { + "type" : "string", + "description" : "Terms for recipient to agree" + } + } + } + }, + "MediationGrant" : { + "type" : "object", + "properties" : { + "@id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Message identifier" + }, + "@type" : { + "type" : "string", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "description" : "Message type", + "readOnly" : true + }, + "endpoint" : { + "type" : "string", + "example" : "http://192.168.56.102:8020/", + "description" : "endpoint on which messages destined for the recipient are received." + }, + "routing_keys" : { + "type" : "array", + "items" : { + "type" : "string", + "description" : "Keys to use for forward message packaging" + } + } + } + }, + "MediationIdMatchInfo" : { + "type" : "object", + "properties" : { + "mediation_id" : { + "type" : "string", + "format" : "uuid", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Mediation record identifier" + } + } + }, + "MediationList" : { + "type" : "object", + "properties" : { + "results" : { + "type" : "array", + "description" : "List of mediation records", + "items" : { + "$ref" : "#/definitions/MediationRecord" + } + } + } + }, + "MediationRecord" : { + "type" : "object", + "required" : [ "connection_id", "role" ], + "properties" : { + "connection_id" : { + "type" : "string" + }, + "created_at" : { + "type" : "string", + "example" : "2021-12-31T23:59:59Z", + "description" : "Time of record creation", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" + }, + "endpoint" : { + "type" : "string" + }, + "mediation_id" : { + "type" : "string" + }, + "mediator_terms" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "recipient_terms" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "role" : { + "type" : "string" + }, + "routing_keys" : { + "type" : "array", + "items" : { + "type" : "string", + "example" : "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH", + "pattern" : "^did:key:z[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+$" + } + }, + "state" : { + "type" : "string", + "example" : "active", + "description" : "Current record state" + }, + "updated_at" : { + "type" : "string", + "example" : "2021-12-31T23:59:59Z", + "description" : "Time of last record update", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" + } + } + }, + "Menu" : { + "type" : "object", + "required" : [ "options" ], + "properties" : { + "@id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Message identifier" + }, + "@type" : { + "type" : "string", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "description" : "Message type", + "readOnly" : true + }, + "description" : { + "type" : "string", + "example" : "This menu presents options", + "description" : "Introductory text for the menu" + }, + "errormsg" : { + "type" : "string", + "example" : "Error: item not found", + "description" : "An optional error message to display in menu header" + }, + "options" : { + "type" : "array", + "description" : "List of menu options", + "items" : { + "$ref" : "#/definitions/MenuOption" + } + }, + "title" : { + "type" : "string", + "example" : "My Menu", + "description" : "Menu title" + } + } + }, + "MenuForm" : { + "type" : "object", + "properties" : { + "description" : { + "type" : "string", + "example" : "Window preference settings", + "description" : "Additional descriptive text for menu form" + }, + "params" : { + "type" : "array", + "description" : "List of form parameters", + "items" : { + "$ref" : "#/definitions/MenuFormParam" + } + }, + "submit-label" : { + "type" : "string", + "example" : "Send", + "description" : "Alternative label for form submit button" + }, + "title" : { + "type" : "string", + "example" : "Preferences", + "description" : "Menu form title" + } + } + }, + "MenuFormParam" : { + "type" : "object", + "required" : [ "name", "title" ], + "properties" : { + "default" : { + "type" : "string", + "example" : "0", + "description" : "Default parameter value" + }, + "description" : { + "type" : "string", + "example" : "Delay in seconds before starting", + "description" : "Additional descriptive text for menu form parameter" + }, + "name" : { + "type" : "string", + "example" : "delay", + "description" : "Menu parameter name" + }, + "required" : { + "type" : "boolean", + "example" : false, + "description" : "Whether parameter is required" + }, + "title" : { + "type" : "string", + "example" : "Delay in seconds", + "description" : "Menu parameter title" + }, + "type" : { + "type" : "string", + "example" : "int", + "description" : "Menu form parameter input type" + } + } + }, + "MenuJson" : { + "type" : "object", + "required" : [ "options" ], + "properties" : { + "description" : { + "type" : "string", + "example" : "User preferences for window settings", + "description" : "Introductory text for the menu" + }, + "errormsg" : { + "type" : "string", + "example" : "Error: item not present", + "description" : "Optional error message to display in menu header" + }, + "options" : { + "type" : "array", + "description" : "List of menu options", + "items" : { + "$ref" : "#/definitions/MenuOption" + } + }, + "title" : { + "type" : "string", + "example" : "My Menu", + "description" : "Menu title" + } + } + }, + "MenuOption" : { + "type" : "object", + "required" : [ "name", "title" ], + "properties" : { + "description" : { + "type" : "string", + "example" : "Window display preferences", + "description" : "Additional descriptive text for menu option" + }, + "disabled" : { + "type" : "boolean", + "example" : false, + "description" : "Whether to show option as disabled" + }, + "form" : { + "$ref" : "#/definitions/MenuForm" + }, + "name" : { + "type" : "string", + "example" : "window_prefs", + "description" : "Menu option name (unique identifier)" + }, + "title" : { + "type" : "string", + "example" : "Window Preferences", + "description" : "Menu option title" + } + } + }, + "MultitenantModuleResponse" : { + "type" : "object" + }, + "OobRecord" : { + "type" : "object", + "required" : [ "invi_msg_id", "invitation", "oob_id", "state" ], + "properties" : { + "attach_thread_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Connection record identifier" + }, + "connection_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Connection record identifier" + }, + "created_at" : { + "type" : "string", + "example" : "2021-12-31T23:59:59Z", + "description" : "Time of record creation", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" + }, + "invi_msg_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Invitation message identifier" + }, + "invitation" : { + "$ref" : "#/definitions/InvitationRecord_invitation" + }, + "oob_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Oob record identifier" + }, + "our_recipient_key" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Recipient key used for oob invitation" + }, + "role" : { + "type" : "string", + "example" : "receiver", + "description" : "OOB Role", + "enum" : [ "sender", "receiver" ] + }, + "state" : { + "type" : "string", + "example" : "await-response", + "description" : "Out of band message exchange state", + "enum" : [ "initial", "prepare-response", "await-response", "reuse-not-accepted", "reuse-accepted", "done", "deleted" ] + }, + "their_service" : { + "$ref" : "#/definitions/ServiceDecorator" + }, + "trace" : { + "type" : "boolean", + "description" : "Record trace information, based on agent configuration" + }, + "updated_at" : { + "type" : "string", + "example" : "2021-12-31T23:59:59Z", + "description" : "Time of last record update", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" + } + } + }, + "PerformRequest" : { + "type" : "object", + "properties" : { + "name" : { + "type" : "string", + "example" : "Query", + "description" : "Menu option name" + }, + "params" : { + "type" : "object", + "description" : "Input parameter values", + "additionalProperties" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6" + } + } + } + }, + "PingRequest" : { + "type" : "object", + "properties" : { + "comment" : { + "type" : "string", + "description" : "Comment for the ping message", + "x-nullable" : true + } + } + }, + "PingRequestResponse" : { + "type" : "object", + "properties" : { + "thread_id" : { + "type" : "string", + "description" : "Thread ID of the ping message" + } + } + }, + "PresentationDefinition" : { + "type" : "object", + "properties" : { + "format" : { + "$ref" : "#/definitions/ClaimFormat" + }, + "id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Unique Resource Identifier", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + }, + "input_descriptors" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/InputDescriptors" + } + }, + "name" : { + "type" : "string", + "description" : "Human-friendly name that describes what the presentation definition pertains to" + }, + "purpose" : { + "type" : "string", + "description" : "Describes the purpose for which the Presentation Definition's inputs are being requested" + }, + "submission_requirements" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/SubmissionRequirements" + } + } + } + }, + "PresentationProposal" : { + "type" : "object", + "required" : [ "presentation_proposal" ], + "properties" : { + "@id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Message identifier" + }, + "@type" : { + "type" : "string", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "description" : "Message type", + "readOnly" : true + }, + "comment" : { + "type" : "string", + "description" : "Human-readable comment", + "x-nullable" : true + }, + "presentation_proposal" : { + "$ref" : "#/definitions/IndyPresPreview" + } + } + }, + "PresentationRequest" : { + "type" : "object", + "required" : [ "request_presentations~attach" ], + "properties" : { + "@id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Message identifier" + }, + "@type" : { + "type" : "string", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "description" : "Message type", + "readOnly" : true + }, + "comment" : { + "type" : "string", + "description" : "Human-readable comment", + "x-nullable" : true + }, + "request_presentations~attach" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/AttachDecorator" + } + } + } + }, + "ProtocolDescriptor" : { + "type" : "object", + "required" : [ "pid" ], + "properties" : { + "pid" : { + "type" : "string" + }, + "roles" : { + "type" : "array", + "description" : "List of roles", + "items" : { + "type" : "string", + "example" : "requester", + "description" : "Role: requester or responder" + }, + "x-nullable" : true + } + } + }, + "PublishRevocations" : { + "type" : "object", + "properties" : { + "rrid2crid" : { + "type" : "object", + "description" : "Credential revocation ids by revocation registry id", + "additionalProperties" : { + "type" : "array", + "items" : { + "type" : "string", + "example" : "12345", + "description" : "Credential revocation identifier", + "pattern" : "^[1-9][0-9]*$" + } + } + } + } + }, + "Queries" : { + "type" : "object", + "properties" : { + "@id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Message identifier" + }, + "@type" : { + "type" : "string", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "description" : "Message type", + "readOnly" : true + }, + "queries" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/QueryItem" + } + } + } + }, + "Query" : { + "type" : "object", + "required" : [ "query" ], + "properties" : { + "@id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Message identifier" + }, + "@type" : { + "type" : "string", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "description" : "Message type", + "readOnly" : true + }, + "comment" : { + "type" : "string", + "x-nullable" : true + }, + "query" : { + "type" : "string" + } + } + }, + "QueryItem" : { + "type" : "object", + "required" : [ "feature-type", "match" ], + "properties" : { + "feature-type" : { + "type" : "string", + "description" : "feature type", + "enum" : [ "protocol", "goal-code" ] + }, + "match" : { + "type" : "string", + "description" : "match" + } + } + }, + "RawEncoded" : { + "type" : "object", + "properties" : { + "encoded" : { + "type" : "string", + "example" : "-1", + "description" : "Encoded value", + "pattern" : "^-?[0-9]*$" + }, + "raw" : { + "type" : "string", + "description" : "Raw value" + } + } + }, + "ReceiveInvitationRequest" : { + "type" : "object", + "properties" : { + "@id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Message identifier" + }, + "@type" : { + "type" : "string", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "description" : "Message type", + "readOnly" : true + }, + "did" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv", + "description" : "DID for connection invitation", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + }, + "imageUrl" : { + "type" : "string", + "format" : "url", + "example" : "http://192.168.56.101/img/logo.jpg", + "description" : "Optional image URL for connection invitation", + "x-nullable" : true + }, + "label" : { + "type" : "string", + "example" : "Bob", + "description" : "Optional label for connection invitation" + }, + "recipientKeys" : { + "type" : "array", + "description" : "List of recipient keys", + "items" : { + "type" : "string", + "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", + "description" : "Recipient public key", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" + } + }, + "routingKeys" : { + "type" : "array", + "description" : "List of routing keys", + "items" : { + "type" : "string", + "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", + "description" : "Routing key", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" + } + }, + "serviceEndpoint" : { + "type" : "string", + "example" : "http://192.168.56.101:8020", + "description" : "Service endpoint at which to reach this agent" + } + } + }, + "RemoveWalletRequest" : { + "type" : "object", + "properties" : { + "wallet_key" : { + "type" : "string", + "example" : "MySecretKey123", + "description" : "Master key used for key derivation. Only required for unmanaged wallets." + } + } + }, + "ResolutionResult" : { + "type" : "object", + "required" : [ "did_document", "metadata" ], + "properties" : { + "did_document" : { + "type" : "object", + "description" : "DID Document", + "properties" : { } + }, + "metadata" : { + "type" : "object", + "description" : "Resolution metadata", + "properties" : { } + } + } + }, + "RevRegCreateRequest" : { + "type" : "object", + "properties" : { + "credential_definition_id" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "description" : "Credential definition identifier", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" + }, + "max_cred_num" : { + "type" : "integer", + "format" : "int32", + "example" : 1000, + "description" : "Revocation registry size", + "minimum" : 4, + "maximum" : 32768 + } + } + }, + "RevRegIssuedResult" : { + "type" : "object", + "properties" : { + "result" : { + "type" : "integer", + "format" : "int32", + "example" : 0, + "description" : "Number of credentials issued against revocation registry", + "minimum" : 0 + } + } + }, + "RevRegResult" : { + "type" : "object", + "properties" : { + "result" : { + "$ref" : "#/definitions/IssuerRevRegRecord" + } + } + }, + "RevRegUpdateTailsFileUri" : { + "type" : "object", + "required" : [ "tails_public_uri" ], + "properties" : { + "tails_public_uri" : { + "type" : "string", + "format" : "url", + "example" : "http://192.168.56.133:6543/revocation/registry/WgWxqztrNooG92RXvxSTWv:4:WgWxqztrNooG92RXvxSTWv:3:CL:20:tag:CL_ACCUM:0/tails-file", + "description" : "Public URI to the tails file" + } + } + }, + "RevRegWalletUpdatedResult" : { + "type" : "object", + "properties" : { + "accum_calculated" : { + "type" : "object", + "description" : "Calculated accumulator for phantom revocations", + "properties" : { } + }, + "accum_fixed" : { + "type" : "object", + "description" : "Applied ledger transaction to fix revocations", + "properties" : { } + }, + "rev_reg_delta" : { + "type" : "object", + "description" : "Indy revocation registry delta", + "properties" : { } + } + } + }, + "RevRegsCreated" : { + "type" : "object", + "properties" : { + "rev_reg_ids" : { + "type" : "array", + "items" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:4:WgWxqztrNooG92RXvxSTWv:3:CL:20:tag:CL_ACCUM:0", + "description" : "Revocation registry identifiers", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" + } + } + } + }, + "RevocationModuleResponse" : { + "type" : "object" + }, + "RevokeRequest" : { + "type" : "object", + "properties" : { + "comment" : { + "type" : "string", + "description" : "Optional comment to include in revocation notification" + }, + "connection_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Connection ID to which the revocation notification will be sent; required if notify is true", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + }, + "cred_ex_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Credential exchange identifier", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + }, + "cred_rev_id" : { + "type" : "string", + "example" : "12345", + "description" : "Credential revocation identifier", + "pattern" : "^[1-9][0-9]*$" + }, + "notify" : { + "type" : "boolean", + "description" : "Send a notification to the credential recipient" + }, + "notify_version" : { + "type" : "string", + "description" : "Specify which version of the revocation notification should be sent", + "enum" : [ "v1_0", "v2_0" ] + }, + "publish" : { + "type" : "boolean", + "description" : "(True) publish revocation to ledger immediately, or (default, False) mark it pending" + }, + "rev_reg_id" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:4:WgWxqztrNooG92RXvxSTWv:3:CL:20:tag:CL_ACCUM:0", + "description" : "Revocation registry identifier", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" + }, + "thread_id" : { + "type" : "string", + "description" : "Thread ID of the credential exchange message thread resulting in the credential now being revoked; required if notify is true" + } + } + }, + "RouteRecord" : { + "type" : "object", + "required" : [ "recipient_key" ], + "properties" : { + "connection_id" : { + "type" : "string" + }, + "created_at" : { + "type" : "string", + "example" : "2021-12-31T23:59:59Z", + "description" : "Time of record creation", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" + }, + "recipient_key" : { + "type" : "string" + }, + "record_id" : { + "type" : "string" + }, + "role" : { + "type" : "string" + }, + "state" : { + "type" : "string", + "example" : "active", + "description" : "Current record state" + }, + "updated_at" : { + "type" : "string", + "example" : "2021-12-31T23:59:59Z", + "description" : "Time of last record update", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" + }, + "wallet_id" : { + "type" : "string" + } + } + }, + "Schema" : { + "type" : "object", + "properties" : { + "attrNames" : { + "type" : "array", + "description" : "Schema attribute names", + "items" : { + "type" : "string", + "example" : "score", + "description" : "Attribute name" + } + }, + "id" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", + "description" : "Schema identifier", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" + }, + "name" : { + "type" : "string", + "example" : "schema_name", + "description" : "Schema name" + }, + "seqNo" : { + "type" : "integer", + "format" : "int32", + "example" : 10, + "description" : "Schema sequence number", + "minimum" : 1 + }, + "ver" : { + "type" : "string", + "example" : "1.0", + "description" : "Node protocol version", + "pattern" : "^[0-9.]+$" + }, + "version" : { + "type" : "string", + "example" : "1.0", + "description" : "Schema version", + "pattern" : "^[0-9.]+$" + } + } + }, + "SchemaGetResult" : { + "type" : "object", + "properties" : { + "schema" : { + "$ref" : "#/definitions/Schema" + } + } + }, + "SchemaInputDescriptor" : { + "type" : "object", + "properties" : { + "required" : { + "type" : "boolean", + "description" : "Required" + }, + "uri" : { + "type" : "string", + "description" : "URI" + } + } + }, + "SchemaSendRequest" : { + "type" : "object", + "required" : [ "attributes", "schema_name", "schema_version" ], + "properties" : { + "attributes" : { + "type" : "array", + "description" : "List of schema attributes", + "items" : { + "type" : "string", + "example" : "score", + "description" : "attribute name" + } + }, + "schema_name" : { + "type" : "string", + "example" : "prefs", + "description" : "Schema name" + }, + "schema_version" : { + "type" : "string", + "example" : "1.0", + "description" : "Schema version", + "pattern" : "^[0-9.]+$" + } + } + }, + "SchemaSendResult" : { + "type" : "object", + "required" : [ "schema_id" ], + "properties" : { + "schema" : { + "$ref" : "#/definitions/SchemaSendResult_schema" + }, + "schema_id" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", + "description" : "Schema identifier", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" + } + } + }, + "SchemasCreatedResult" : { + "type" : "object", + "properties" : { + "schema_ids" : { + "type" : "array", + "items" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", + "description" : "Schema identifiers", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" + } + } + } + }, + "SchemasInputDescriptorFilter" : { + "type" : "object", + "properties" : { + "oneof_filter" : { + "type" : "boolean", + "description" : "oneOf" + }, + "uri_groups" : { + "type" : "array", + "items" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/SchemaInputDescriptor" + } + } + } + } + }, + "SendMenu" : { + "type" : "object", + "required" : [ "menu" ], + "properties" : { + "menu" : { + "$ref" : "#/definitions/SendMenu_menu" + } + } + }, + "SendMessage" : { + "type" : "object", + "properties" : { + "content" : { + "type" : "string", + "example" : "Hello", + "description" : "Message content" + } + } + }, + "ServiceDecorator" : { + "type" : "object", + "required" : [ "recipientKeys", "serviceEndpoint" ], + "properties" : { + "recipientKeys" : { + "type" : "array", + "description" : "List of recipient keys", + "items" : { + "type" : "string", + "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", + "description" : "Recipient public key", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" + } + }, + "routingKeys" : { + "type" : "array", + "description" : "List of routing keys", + "items" : { + "type" : "string", + "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", + "description" : "Routing key", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" + } + }, + "serviceEndpoint" : { + "type" : "string", + "example" : "http://192.168.56.101:8020", + "description" : "Service endpoint at which to reach this agent" + } + } + }, + "SignRequest" : { + "type" : "object", + "required" : [ "doc", "verkey" ], + "properties" : { + "doc" : { + "$ref" : "#/definitions/Doc" + }, + "verkey" : { + "type" : "string", + "description" : "Verkey to use for signing" + } + } + }, + "SignResponse" : { + "type" : "object", + "properties" : { + "error" : { + "type" : "string", + "description" : "Error text" + }, + "signed_doc" : { + "type" : "object", + "description" : "Signed document", + "properties" : { } + } + } + }, + "SignatureOptions" : { + "type" : "object", + "required" : [ "proofPurpose", "verificationMethod" ], + "properties" : { + "challenge" : { + "type" : "string" + }, + "domain" : { + "type" : "string" + }, + "proofPurpose" : { + "type" : "string" + }, + "type" : { + "type" : "string" + }, + "verificationMethod" : { + "type" : "string" + } + } + }, + "SignedDoc" : { + "type" : "object", + "required" : [ "proof" ], + "properties" : { + "proof" : { + "$ref" : "#/definitions/SignedDoc_proof" + } + } + }, + "SubmissionRequirements" : { + "type" : "object", + "properties" : { + "count" : { + "type" : "integer", + "format" : "int32", + "example" : 1234, + "description" : "Count Value" + }, + "from" : { + "type" : "string", + "description" : "From" + }, + "from_nested" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/SubmissionRequirements" + } + }, + "max" : { + "type" : "integer", + "format" : "int32", + "example" : 1234, + "description" : "Max Value" + }, + "min" : { + "type" : "integer", + "format" : "int32", + "example" : 1234, + "description" : "Min Value" + }, + "name" : { + "type" : "string", + "description" : "Name" + }, + "purpose" : { + "type" : "string", + "description" : "Purpose" + }, + "rule" : { + "type" : "string", + "description" : "Selection", + "enum" : [ "all", "pick" ] + } + } + }, + "TAAAccept" : { + "type" : "object", + "properties" : { + "mechanism" : { + "type" : "string" + }, + "text" : { + "type" : "string" + }, + "version" : { + "type" : "string" + } + } + }, + "TAAAcceptance" : { + "type" : "object", + "properties" : { + "mechanism" : { + "type" : "string" + }, + "time" : { + "type" : "integer", + "format" : "int32", + "example" : 1640995199, + "minimum" : 0, + "maximum" : 18446744073709551615 + } + } + }, + "TAAInfo" : { + "type" : "object", + "properties" : { + "aml_record" : { + "$ref" : "#/definitions/AMLRecord" + }, + "taa_accepted" : { + "$ref" : "#/definitions/TAAAcceptance" + }, + "taa_record" : { + "$ref" : "#/definitions/TAARecord" + }, + "taa_required" : { + "type" : "boolean" + } + } + }, + "TAARecord" : { + "type" : "object", + "properties" : { + "digest" : { + "type" : "string" + }, + "text" : { + "type" : "string" + }, + "version" : { + "type" : "string" + } + } + }, + "TAAResult" : { + "type" : "object", + "properties" : { + "result" : { + "$ref" : "#/definitions/TAAInfo" + } + } + }, + "TailsDeleteResponse" : { + "type" : "object", + "properties" : { + "message" : { + "type" : "string" + } + } + }, + "TransactionJobs" : { + "type" : "object", + "properties" : { + "transaction_my_job" : { + "type" : "string", + "description" : "My transaction related job", + "enum" : [ "TRANSACTION_AUTHOR", "TRANSACTION_ENDORSER", "reset" ] + }, + "transaction_their_job" : { + "type" : "string", + "description" : "Their transaction related job", + "enum" : [ "TRANSACTION_AUTHOR", "TRANSACTION_ENDORSER", "reset" ] + } + } + }, + "TransactionList" : { + "type" : "object", + "properties" : { + "results" : { + "type" : "array", + "description" : "List of transaction records", + "items" : { + "$ref" : "#/definitions/TransactionRecord" + } + } + } + }, + "TransactionRecord" : { + "type" : "object", + "properties" : { + "_type" : { + "type" : "string", + "example" : "101", + "description" : "Transaction type" + }, + "connection_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "The connection identifier for thie particular transaction record" + }, + "created_at" : { + "type" : "string", + "example" : "2021-12-31T23:59:59Z", + "description" : "Time of record creation", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" + }, + "endorser_write_txn" : { + "type" : "boolean", + "example" : true, + "description" : "If True, Endorser will write the transaction after endorsing it" + }, + "formats" : { + "type" : "array", + "items" : { + "type" : "object", + "example" : { + "attach_id" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "format" : "dif/endorse-transaction/request@v1.0" + }, + "additionalProperties" : { + "type" : "string" + } + } + }, + "messages_attach" : { + "type" : "array", + "items" : { + "type" : "object", + "example" : { + "@id" : "143c458d-1b1c-40c7-ab85-4d16808ddf0a", + "data" : { + "json" : "{\"endorser\": \"V4SGRU86Z58d6TV7PBUe6f\",\"identifier\": \"LjgpST2rjsoxYegQDRm7EL\",\"operation\": {\"data\": {\"attr_names\": [\"first_name\", \"last_name\"],\"name\": \"test_schema\",\"version\": \"2.1\",},\"type\": \"101\",},\"protocolVersion\": 2,\"reqId\": 1597766666168851000,\"signatures\": {\"LjgpST2rjsox\": \"4ATKMn6Y9sTgwqaGTm7py2c2M8x1EVDTWKZArwyuPgjU\"},\"taaAcceptance\": {\"mechanism\": \"manual\",\"taaDigest\": \"f50fe2c2ab977006761d36bd6f23e4c6a7e0fc2feb9f62\",\"time\": 1597708800,}}" + }, + "mime-type" : "application/json" + }, + "properties" : { } + } + }, + "meta_data" : { + "type" : "object", + "example" : { + "context" : { + "param1" : "param1_value", + "param2" : "param2_value" + }, + "post_process" : [ { + "topic" : "topic_value", + "other" : "other_value" + } ] + }, + "properties" : { } + }, + "signature_request" : { + "type" : "array", + "items" : { + "type" : "object", + "example" : { + "author_goal_code" : "aries.transaction.ledger.write", + "context" : "did:sov", + "method" : "add-signature", + "signature_type" : "", + "signer_goal_code" : "aries.transaction.endorse" + }, + "properties" : { } + } + }, + "signature_response" : { + "type" : "array", + "items" : { + "type" : "object", + "example" : { + "context" : "did:sov", + "message_id" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "method" : "add-signature", + "signer_goal_code" : "aries.transaction.refuse" + }, + "properties" : { } + } + }, + "state" : { + "type" : "string", + "example" : "active", + "description" : "Current record state" + }, + "thread_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Thread Identifier" + }, + "timing" : { + "type" : "object", + "example" : { + "expires_time" : "2020-12-13T17:29:06+0000" + }, + "properties" : { } + }, + "trace" : { + "type" : "boolean", + "description" : "Record trace information, based on agent configuration" + }, + "transaction_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Transaction identifier" + }, + "updated_at" : { + "type" : "string", + "example" : "2021-12-31T23:59:59Z", + "description" : "Time of last record update", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" + } + } + }, + "TxnOrCredentialDefinitionSendResult" : { + "type" : "object", + "properties" : { + "sent" : { + "$ref" : "#/definitions/CredentialDefinitionSendResult" + }, + "txn" : { + "$ref" : "#/definitions/TxnOrCredentialDefinitionSendResult_txn" + } + } + }, + "TxnOrPublishRevocationsResult" : { + "type" : "object", + "properties" : { + "sent" : { + "$ref" : "#/definitions/PublishRevocations" + }, + "txn" : { + "$ref" : "#/definitions/TxnOrPublishRevocationsResult_txn" + } + } + }, + "TxnOrRegisterLedgerNymResponse" : { + "type" : "object", + "properties" : { + "success" : { + "type" : "boolean", + "example" : true, + "description" : "Success of nym registration operation" + }, + "txn" : { + "$ref" : "#/definitions/TxnOrRegisterLedgerNymResponse_txn" + } + } + }, + "TxnOrRevRegResult" : { + "type" : "object", + "properties" : { + "sent" : { + "$ref" : "#/definitions/RevRegResult" + }, + "txn" : { + "$ref" : "#/definitions/TxnOrRevRegResult_txn" + } + } + }, + "TxnOrSchemaSendResult" : { + "type" : "object", + "properties" : { + "sent" : { + "$ref" : "#/definitions/TxnOrSchemaSendResult_sent" + }, + "txn" : { + "$ref" : "#/definitions/TxnOrSchemaSendResult_txn" + } + } + }, + "UpdateWalletRequest" : { + "type" : "object", + "properties" : { + "image_url" : { + "type" : "string", + "example" : "https://aries.ca/images/sample.png", + "description" : "Image url for this wallet. This image url is publicized (self-attested) to other agents as part of forming a connection." + }, + "label" : { + "type" : "string", + "example" : "Alice", + "description" : "Label for this wallet. This label is publicized (self-attested) to other agents as part of forming a connection." + }, + "wallet_dispatch_type" : { + "type" : "string", + "example" : "default", + "description" : "Webhook target dispatch type for this wallet. default - Dispatch only to webhooks associated with this wallet. base - Dispatch only to webhooks associated with the base wallet. both - Dispatch to both webhook targets.", + "enum" : [ "default", "both", "base" ] + }, + "wallet_webhook_urls" : { + "type" : "array", + "description" : "List of Webhook URLs associated with this subwallet", + "items" : { + "type" : "string", + "example" : "http://localhost:8022/webhooks", + "description" : "Optional webhook URL to receive webhook messages" + } + } + } + }, + "V10CredentialBoundOfferRequest" : { + "type" : "object", + "properties" : { + "counter_proposal" : { + "$ref" : "#/definitions/V10CredentialBoundOfferRequest_counter_proposal" + } + } + }, + "V10CredentialConnFreeOfferRequest" : { + "type" : "object", + "required" : [ "cred_def_id", "credential_preview" ], + "properties" : { + "auto_issue" : { + "type" : "boolean", + "description" : "Whether to respond automatically to credential requests, creating and issuing requested credentials" + }, + "auto_remove" : { + "type" : "boolean", + "description" : "Whether to remove the credential exchange record on completion (overrides --preserve-exchange-records configuration setting)" + }, + "comment" : { + "type" : "string", + "description" : "Human-readable comment", + "x-nullable" : true + }, + "cred_def_id" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "description" : "Credential definition identifier", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" + }, + "credential_preview" : { + "$ref" : "#/definitions/CredentialPreview" + }, + "trace" : { + "type" : "boolean", + "description" : "Record trace information, based on agent configuration" + } + } + }, + "V10CredentialCreate" : { + "type" : "object", + "required" : [ "credential_proposal" ], + "properties" : { + "auto_remove" : { + "type" : "boolean", + "description" : "Whether to remove the credential exchange record on completion (overrides --preserve-exchange-records configuration setting)" + }, + "comment" : { + "type" : "string", + "description" : "Human-readable comment", + "x-nullable" : true + }, + "cred_def_id" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "description" : "Credential definition identifier", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" + }, + "credential_proposal" : { + "$ref" : "#/definitions/CredentialPreview" + }, + "issuer_did" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv", + "description" : "Credential issuer DID", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + }, + "schema_id" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", + "description" : "Schema identifier", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" + }, + "schema_issuer_did" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv", + "description" : "Schema issuer DID", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + }, + "schema_name" : { + "type" : "string", + "example" : "preferences", + "description" : "Schema name" + }, + "schema_version" : { + "type" : "string", + "example" : "1.0", + "description" : "Schema version", + "pattern" : "^[0-9.]+$" + }, + "trace" : { + "type" : "boolean", + "description" : "Record trace information, based on agent configuration" + } + } + }, + "V10CredentialExchange" : { + "type" : "object", + "properties" : { + "auto_issue" : { + "type" : "boolean", + "example" : false, + "description" : "Issuer choice to issue to request in this credential exchange" + }, + "auto_offer" : { + "type" : "boolean", + "example" : false, + "description" : "Holder choice to accept offer in this credential exchange" + }, + "auto_remove" : { + "type" : "boolean", + "example" : false, + "description" : "Issuer choice to remove this credential exchange record when complete" + }, + "connection_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Connection identifier" + }, + "created_at" : { + "type" : "string", + "example" : "2021-12-31T23:59:59Z", + "description" : "Time of record creation", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" + }, + "credential" : { + "$ref" : "#/definitions/V10CredentialExchange_credential" + }, + "credential_definition_id" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "description" : "Credential definition identifier", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" + }, + "credential_exchange_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Credential exchange identifier" + }, + "credential_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Credential identifier" + }, + "credential_offer" : { + "$ref" : "#/definitions/V10CredentialExchange_credential_offer" + }, + "credential_offer_dict" : { + "$ref" : "#/definitions/V10CredentialExchange_credential_offer_dict" + }, + "credential_proposal_dict" : { + "$ref" : "#/definitions/V10CredentialExchange_credential_proposal_dict" + }, + "credential_request" : { + "$ref" : "#/definitions/V10CredentialExchange_credential_request" + }, + "credential_request_metadata" : { + "type" : "object", + "description" : "(Indy) credential request metadata", + "properties" : { } + }, + "error_msg" : { + "type" : "string", + "example" : "Credential definition identifier is not set in proposal", + "description" : "Error message" + }, + "initiator" : { + "type" : "string", + "example" : "self", + "description" : "Issue-credential exchange initiator: self or external", + "enum" : [ "self", "external" ] + }, + "parent_thread_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Parent thread identifier" + }, + "raw_credential" : { + "$ref" : "#/definitions/V10CredentialExchange_raw_credential" + }, + "revoc_reg_id" : { + "type" : "string", + "description" : "Revocation registry identifier" + }, + "revocation_id" : { + "type" : "string", + "description" : "Credential identifier within revocation registry" + }, + "role" : { + "type" : "string", + "example" : "issuer", + "description" : "Issue-credential exchange role: holder or issuer", + "enum" : [ "holder", "issuer" ] + }, + "schema_id" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", + "description" : "Schema identifier", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" + }, + "state" : { + "type" : "string", + "example" : "credential_acked", + "description" : "Issue-credential exchange state" + }, + "thread_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Thread identifier" + }, + "trace" : { + "type" : "boolean", + "description" : "Record trace information, based on agent configuration" + }, + "updated_at" : { + "type" : "string", + "example" : "2021-12-31T23:59:59Z", + "description" : "Time of last record update", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" + } + } + }, + "V10CredentialExchangeListResult" : { + "type" : "object", + "properties" : { + "results" : { + "type" : "array", + "description" : "Aries#0036 v1.0 credential exchange records", + "items" : { + "$ref" : "#/definitions/V10CredentialExchange" + } + } + } + }, + "V10CredentialFreeOfferRequest" : { + "type" : "object", + "required" : [ "connection_id", "cred_def_id", "credential_preview" ], + "properties" : { + "auto_issue" : { + "type" : "boolean", + "description" : "Whether to respond automatically to credential requests, creating and issuing requested credentials" + }, + "auto_remove" : { + "type" : "boolean", + "description" : "Whether to remove the credential exchange record on completion (overrides --preserve-exchange-records configuration setting)" + }, + "comment" : { + "type" : "string", + "description" : "Human-readable comment", + "x-nullable" : true + }, + "connection_id" : { + "type" : "string", + "format" : "uuid", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Connection identifier" + }, + "cred_def_id" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "description" : "Credential definition identifier", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" + }, + "credential_preview" : { + "$ref" : "#/definitions/CredentialPreview" + }, + "trace" : { + "type" : "boolean", + "description" : "Record trace information, based on agent configuration" + } + } + }, + "V10CredentialIssueRequest" : { + "type" : "object", + "properties" : { + "comment" : { + "type" : "string", + "description" : "Human-readable comment", + "x-nullable" : true + } + } + }, + "V10CredentialProblemReportRequest" : { + "type" : "object", + "required" : [ "description" ], + "properties" : { + "description" : { + "type" : "string" + } + } + }, + "V10CredentialProposalRequestMand" : { + "type" : "object", + "required" : [ "connection_id", "credential_proposal" ], + "properties" : { + "auto_remove" : { + "type" : "boolean", + "description" : "Whether to remove the credential exchange record on completion (overrides --preserve-exchange-records configuration setting)" + }, + "comment" : { + "type" : "string", + "description" : "Human-readable comment", + "x-nullable" : true + }, + "connection_id" : { + "type" : "string", + "format" : "uuid", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Connection identifier" + }, + "cred_def_id" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "description" : "Credential definition identifier", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" + }, + "credential_proposal" : { + "$ref" : "#/definitions/CredentialPreview" + }, + "issuer_did" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv", + "description" : "Credential issuer DID", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + }, + "schema_id" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", + "description" : "Schema identifier", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" + }, + "schema_issuer_did" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv", + "description" : "Schema issuer DID", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + }, + "schema_name" : { + "type" : "string", + "example" : "preferences", + "description" : "Schema name" + }, + "schema_version" : { + "type" : "string", + "example" : "1.0", + "description" : "Schema version", + "pattern" : "^[0-9.]+$" + }, + "trace" : { + "type" : "boolean", + "description" : "Record trace information, based on agent configuration" + } + } + }, + "V10CredentialProposalRequestOpt" : { + "type" : "object", + "required" : [ "connection_id" ], + "properties" : { + "auto_remove" : { + "type" : "boolean", + "description" : "Whether to remove the credential exchange record on completion (overrides --preserve-exchange-records configuration setting)" + }, + "comment" : { + "type" : "string", + "description" : "Human-readable comment", + "x-nullable" : true + }, + "connection_id" : { + "type" : "string", + "format" : "uuid", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Connection identifier" + }, + "cred_def_id" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "description" : "Credential definition identifier", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" + }, + "credential_proposal" : { + "$ref" : "#/definitions/CredentialPreview" + }, + "issuer_did" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv", + "description" : "Credential issuer DID", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + }, + "schema_id" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", + "description" : "Schema identifier", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" + }, + "schema_issuer_did" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv", + "description" : "Schema issuer DID", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + }, + "schema_name" : { + "type" : "string", + "example" : "preferences", + "description" : "Schema name" + }, + "schema_version" : { + "type" : "string", + "example" : "1.0", + "description" : "Schema version", + "pattern" : "^[0-9.]+$" + }, + "trace" : { + "type" : "boolean", + "description" : "Record trace information, based on agent configuration" + } + } + }, + "V10CredentialStoreRequest" : { + "type" : "object", + "properties" : { + "credential_id" : { + "type" : "string" + } + } + }, + "V10DiscoveryExchangeListResult" : { + "type" : "object", + "properties" : { + "results" : { + "type" : "array", + "items" : { + "type" : "object", + "description" : "Discover Features v1.0 exchange record", + "allOf" : [ { + "$ref" : "#/definitions/V10DiscoveryRecord" + } ] + } + } + } + }, + "V10DiscoveryRecord" : { + "type" : "object", + "properties" : { + "connection_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Connection identifier" + }, + "created_at" : { + "type" : "string", + "example" : "2021-12-31T23:59:59Z", + "description" : "Time of record creation", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" + }, + "disclose" : { + "$ref" : "#/definitions/V10DiscoveryRecord_disclose" + }, + "discovery_exchange_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Credential exchange identifier" + }, + "query_msg" : { + "$ref" : "#/definitions/V10DiscoveryRecord_query_msg" + }, + "state" : { + "type" : "string", + "example" : "active", + "description" : "Current record state" + }, + "thread_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Thread identifier" + }, + "trace" : { + "type" : "boolean", + "description" : "Record trace information, based on agent configuration" + }, + "updated_at" : { + "type" : "string", + "example" : "2021-12-31T23:59:59Z", + "description" : "Time of last record update", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" + } + } + }, + "V10PresentProofModuleResponse" : { + "type" : "object" + }, + "V10PresentationCreateRequestRequest" : { + "type" : "object", + "required" : [ "proof_request" ], + "properties" : { + "auto_verify" : { + "type" : "boolean", + "example" : false, + "description" : "Verifier choice to auto-verify proof presentation" + }, + "comment" : { + "type" : "string", + "x-nullable" : true + }, + "proof_request" : { + "$ref" : "#/definitions/IndyProofRequest" + }, + "trace" : { + "type" : "boolean", + "example" : false, + "description" : "Whether to trace event (default false)" + } + } + }, + "V10PresentationExchange" : { + "type" : "object", + "properties" : { + "auto_present" : { + "type" : "boolean", + "example" : false, + "description" : "Prover choice to auto-present proof as verifier requests" + }, + "auto_verify" : { + "type" : "boolean", + "description" : "Verifier choice to auto-verify proof presentation" + }, + "connection_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Connection identifier" + }, + "created_at" : { + "type" : "string", + "example" : "2021-12-31T23:59:59Z", + "description" : "Time of record creation", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" + }, + "error_msg" : { + "type" : "string", + "example" : "Invalid structure", + "description" : "Error message" + }, + "initiator" : { + "type" : "string", + "example" : "self", + "description" : "Present-proof exchange initiator: self or external", + "enum" : [ "self", "external" ] + }, + "presentation" : { + "$ref" : "#/definitions/V10PresentationExchange_presentation" + }, + "presentation_exchange_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Presentation exchange identifier" + }, + "presentation_proposal_dict" : { + "$ref" : "#/definitions/V10PresentationExchange_presentation_proposal_dict" + }, + "presentation_request" : { + "$ref" : "#/definitions/V10PresentationExchange_presentation_request" + }, + "presentation_request_dict" : { + "$ref" : "#/definitions/V10PresentationExchange_presentation_request_dict" + }, + "role" : { + "type" : "string", + "example" : "prover", + "description" : "Present-proof exchange role: prover or verifier", + "enum" : [ "prover", "verifier" ] + }, + "state" : { + "type" : "string", + "example" : "verified", + "description" : "Present-proof exchange state" + }, + "thread_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Thread identifier" + }, + "trace" : { + "type" : "boolean", + "description" : "Record trace information, based on agent configuration" + }, + "updated_at" : { + "type" : "string", + "example" : "2021-12-31T23:59:59Z", + "description" : "Time of last record update", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" + }, + "verified" : { + "type" : "string", + "example" : "true", + "description" : "Whether presentation is verified: true or false", + "enum" : [ "true", "false" ] + }, + "verified_msgs" : { + "type" : "array", + "items" : { + "type" : "string", + "description" : "Proof verification warning or error information" + } + } + } + }, + "V10PresentationExchangeList" : { + "type" : "object", + "properties" : { + "results" : { + "type" : "array", + "description" : "Aries RFC 37 v1.0 presentation exchange records", + "items" : { + "$ref" : "#/definitions/V10PresentationExchange" + } + } + } + }, + "V10PresentationProblemReportRequest" : { + "type" : "object", + "required" : [ "description" ], + "properties" : { + "description" : { + "type" : "string" + } + } + }, + "V10PresentationProposalRequest" : { + "type" : "object", + "required" : [ "connection_id", "presentation_proposal" ], + "properties" : { + "auto_present" : { + "type" : "boolean", + "description" : "Whether to respond automatically to presentation requests, building and presenting requested proof" + }, + "comment" : { + "type" : "string", + "description" : "Human-readable comment", + "x-nullable" : true + }, + "connection_id" : { + "type" : "string", + "format" : "uuid", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Connection identifier" + }, + "presentation_proposal" : { + "$ref" : "#/definitions/IndyPresPreview" + }, + "trace" : { + "type" : "boolean", + "example" : false, + "description" : "Whether to trace event (default false)" + } + } + }, + "V10PresentationSendRequestRequest" : { + "type" : "object", + "required" : [ "connection_id", "proof_request" ], + "properties" : { + "auto_verify" : { + "type" : "boolean", + "example" : false, + "description" : "Verifier choice to auto-verify proof presentation" + }, + "comment" : { + "type" : "string", + "x-nullable" : true + }, + "connection_id" : { + "type" : "string", + "format" : "uuid", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Connection identifier" + }, + "proof_request" : { + "$ref" : "#/definitions/IndyProofRequest" + }, + "trace" : { + "type" : "boolean", + "example" : false, + "description" : "Whether to trace event (default false)" + } + } + }, + "V10PresentationSendRequestToProposal" : { + "type" : "object", + "properties" : { + "auto_verify" : { + "type" : "boolean", + "example" : false, + "description" : "Verifier choice to auto-verify proof presentation" + }, + "trace" : { + "type" : "boolean", + "example" : false, + "description" : "Whether to trace event (default false)" + } + } + }, + "V20CredAttrSpec" : { + "type" : "object", + "required" : [ "name", "value" ], + "properties" : { + "mime-type" : { + "type" : "string", + "example" : "image/jpeg", + "description" : "MIME type: omit for (null) default", + "x-nullable" : true + }, + "name" : { + "type" : "string", + "example" : "favourite_drink", + "description" : "Attribute name" + }, + "value" : { + "type" : "string", + "example" : "martini", + "description" : "Attribute value: base64-encode if MIME type is present" + } + } + }, + "V20CredBoundOfferRequest" : { + "type" : "object", + "properties" : { + "counter_preview" : { + "$ref" : "#/definitions/V20CredBoundOfferRequest_counter_preview" + }, + "filter" : { + "$ref" : "#/definitions/V20CredBoundOfferRequest_filter" + } + } + }, + "V20CredExFree" : { + "type" : "object", + "required" : [ "connection_id", "filter" ], + "properties" : { + "auto_remove" : { + "type" : "boolean", + "description" : "Whether to remove the credential exchange record on completion (overrides --preserve-exchange-records configuration setting)" + }, + "comment" : { + "type" : "string", + "description" : "Human-readable comment", + "x-nullable" : true + }, + "connection_id" : { + "type" : "string", + "format" : "uuid", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Connection identifier" + }, + "credential_preview" : { + "$ref" : "#/definitions/V20CredPreview" + }, + "filter" : { + "$ref" : "#/definitions/V20CredBoundOfferRequest_filter" + }, + "trace" : { + "type" : "boolean", + "description" : "Record trace information, based on agent configuration" + }, + "verification_method" : { + "type" : "string", + "description" : "For ld-proofs. Verification method for signing.", + "x-nullable" : true + } + } + }, + "V20CredExRecord" : { + "type" : "object", + "properties" : { + "auto_issue" : { + "type" : "boolean", + "example" : false, + "description" : "Issuer choice to issue to request in this credential exchange" + }, + "auto_offer" : { + "type" : "boolean", + "example" : false, + "description" : "Holder choice to accept offer in this credential exchange" + }, + "auto_remove" : { + "type" : "boolean", + "example" : false, + "description" : "Issuer choice to remove this credential exchange record when complete" + }, + "by_format" : { + "$ref" : "#/definitions/V20CredExRecord_by_format" + }, + "connection_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Connection identifier" + }, + "created_at" : { + "type" : "string", + "example" : "2021-12-31T23:59:59Z", + "description" : "Time of record creation", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" + }, + "cred_ex_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Credential exchange identifier" + }, + "cred_issue" : { + "$ref" : "#/definitions/V20CredExRecord_cred_issue" + }, + "cred_offer" : { + "$ref" : "#/definitions/V10CredentialExchange_credential_offer_dict" + }, + "cred_preview" : { + "$ref" : "#/definitions/V20CredExRecord_cred_preview" + }, + "cred_proposal" : { + "$ref" : "#/definitions/V10CredentialExchange_credential_proposal_dict" + }, + "cred_request" : { + "$ref" : "#/definitions/V20CredExRecord_cred_request" + }, + "error_msg" : { + "type" : "string", + "example" : "The front fell off", + "description" : "Error message" + }, + "initiator" : { + "type" : "string", + "example" : "self", + "description" : "Issue-credential exchange initiator: self or external", + "enum" : [ "self", "external" ] + }, + "parent_thread_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Parent thread identifier" + }, + "role" : { + "type" : "string", + "example" : "issuer", + "description" : "Issue-credential exchange role: holder or issuer", + "enum" : [ "issuer", "holder" ] + }, + "state" : { + "type" : "string", + "example" : "done", + "description" : "Issue-credential exchange state", + "enum" : [ "proposal-sent", "proposal-received", "offer-sent", "offer-received", "request-sent", "request-received", "credential-issued", "credential-received", "done", "credential-revoked", "abandoned", "deleted" ] + }, + "thread_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Thread identifier" + }, + "trace" : { + "type" : "boolean", + "description" : "Record trace information, based on agent configuration" + }, + "updated_at" : { + "type" : "string", + "example" : "2021-12-31T23:59:59Z", + "description" : "Time of last record update", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" + } + } + }, + "V20CredExRecordByFormat" : { + "type" : "object", + "properties" : { + "cred_issue" : { + "type" : "object", + "properties" : { } + }, + "cred_offer" : { + "type" : "object", + "properties" : { } + }, + "cred_proposal" : { + "type" : "object", + "properties" : { } + }, + "cred_request" : { + "type" : "object", + "properties" : { } + } + } + }, + "V20CredExRecordDetail" : { + "type" : "object", + "properties" : { + "cred_ex_record" : { + "$ref" : "#/definitions/V20CredExRecordDetail_cred_ex_record" + }, + "indy" : { + "$ref" : "#/definitions/V20CredExRecordIndy" + }, + "ld_proof" : { + "$ref" : "#/definitions/V20CredExRecordLDProof" + } + } + }, + "V20CredExRecordIndy" : { + "type" : "object", + "properties" : { + "created_at" : { + "type" : "string", + "example" : "2021-12-31T23:59:59Z", + "description" : "Time of record creation", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" + }, + "cred_ex_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Corresponding v2.0 credential exchange record identifier" + }, + "cred_ex_indy_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Record identifier" + }, + "cred_id_stored" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Credential identifier stored in wallet" + }, + "cred_request_metadata" : { + "type" : "object", + "description" : "Credential request metadata for indy holder", + "properties" : { } + }, + "cred_rev_id" : { + "type" : "string", + "example" : "12345", + "description" : "Credential revocation identifier within revocation registry", + "pattern" : "^[1-9][0-9]*$" + }, + "rev_reg_id" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:4:WgWxqztrNooG92RXvxSTWv:3:CL:20:tag:CL_ACCUM:0", + "description" : "Revocation registry identifier", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" + }, + "state" : { + "type" : "string", + "example" : "active", + "description" : "Current record state" + }, + "updated_at" : { + "type" : "string", + "example" : "2021-12-31T23:59:59Z", + "description" : "Time of last record update", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" + } + } + }, + "V20CredExRecordLDProof" : { + "type" : "object", + "properties" : { + "created_at" : { + "type" : "string", + "example" : "2021-12-31T23:59:59Z", + "description" : "Time of record creation", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" + }, + "cred_ex_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Corresponding v2.0 credential exchange record identifier" + }, + "cred_ex_ld_proof_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Record identifier" + }, + "cred_id_stored" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Credential identifier stored in wallet" + }, + "state" : { + "type" : "string", + "example" : "active", + "description" : "Current record state" + }, + "updated_at" : { + "type" : "string", + "example" : "2021-12-31T23:59:59Z", + "description" : "Time of last record update", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" + } + } + }, + "V20CredExRecordListResult" : { + "type" : "object", + "properties" : { + "results" : { + "type" : "array", + "description" : "Credential exchange records and corresponding detail records", + "items" : { + "$ref" : "#/definitions/V20CredExRecordDetail" + } + } + } + }, + "V20CredFilter" : { + "type" : "object", + "properties" : { + "indy" : { + "$ref" : "#/definitions/V20CredFilter_indy" + }, + "ld_proof" : { + "$ref" : "#/definitions/V20CredFilter_ld_proof" + } + } + }, + "V20CredFilterIndy" : { + "type" : "object", + "properties" : { + "cred_def_id" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "description" : "Credential definition identifier", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" + }, + "issuer_did" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv", + "description" : "Credential issuer DID", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + }, + "schema_id" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", + "description" : "Schema identifier", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" + }, + "schema_issuer_did" : { + "type" : "string", + "example" : "WgWxqztrNooG92RXvxSTWv", + "description" : "Schema issuer DID", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + }, + "schema_name" : { + "type" : "string", + "example" : "preferences", + "description" : "Schema name" + }, + "schema_version" : { + "type" : "string", + "example" : "1.0", + "description" : "Schema version", + "pattern" : "^[0-9.]+$" + } + } + }, + "V20CredFilterLDProof" : { + "type" : "object", + "required" : [ "ld_proof" ], + "properties" : { + "ld_proof" : { + "$ref" : "#/definitions/V20CredFilter_ld_proof" + } + } + }, + "V20CredFormat" : { + "type" : "object", + "required" : [ "attach_id", "format" ], + "properties" : { + "attach_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Attachment identifier" + }, + "format" : { + "type" : "string", + "example" : "aries/ld-proof-vc-detail@v1.0", + "description" : "Attachment format specifier" + } + } + }, + "V20CredIssue" : { + "type" : "object", + "required" : [ "credentials~attach", "formats" ], + "properties" : { + "@id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Message identifier" + }, + "@type" : { + "type" : "string", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "description" : "Message type", + "readOnly" : true + }, + "comment" : { + "type" : "string", + "description" : "Human-readable comment", + "x-nullable" : true + }, + "credentials~attach" : { + "type" : "array", + "description" : "Credential attachments", + "items" : { + "$ref" : "#/definitions/AttachDecorator" + } + }, + "formats" : { + "type" : "array", + "description" : "Acceptable attachment formats", + "items" : { + "$ref" : "#/definitions/V20CredFormat" + } + }, + "replacement_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Issuer-unique identifier to coordinate credential replacement" + } + } + }, + "V20CredIssueProblemReportRequest" : { + "type" : "object", + "required" : [ "description" ], + "properties" : { + "description" : { + "type" : "string" + } + } + }, + "V20CredIssueRequest" : { + "type" : "object", + "properties" : { + "comment" : { + "type" : "string", + "description" : "Human-readable comment", + "x-nullable" : true + } + } + }, + "V20CredOffer" : { + "type" : "object", + "required" : [ "formats", "offers~attach" ], + "properties" : { + "@id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Message identifier" + }, + "@type" : { + "type" : "string", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "description" : "Message type", + "readOnly" : true + }, + "comment" : { + "type" : "string", + "description" : "Human-readable comment", + "x-nullable" : true + }, + "credential_preview" : { + "$ref" : "#/definitions/V20CredPreview" + }, + "formats" : { + "type" : "array", + "description" : "Acceptable credential formats", + "items" : { + "$ref" : "#/definitions/V20CredFormat" + } + }, + "offers~attach" : { + "type" : "array", + "description" : "Offer attachments", + "items" : { + "$ref" : "#/definitions/AttachDecorator" + } + }, + "replacement_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Issuer-unique identifier to coordinate credential replacement" + } + } + }, + "V20CredOfferConnFreeRequest" : { + "type" : "object", + "required" : [ "filter" ], + "properties" : { + "auto_issue" : { + "type" : "boolean", + "description" : "Whether to respond automatically to credential requests, creating and issuing requested credentials" + }, + "auto_remove" : { + "type" : "boolean", + "description" : "Whether to remove the credential exchange record on completion (overrides --preserve-exchange-records configuration setting)" + }, + "comment" : { + "type" : "string", + "description" : "Human-readable comment", + "x-nullable" : true + }, + "credential_preview" : { + "$ref" : "#/definitions/V20CredPreview" + }, + "filter" : { + "$ref" : "#/definitions/V20CredBoundOfferRequest_filter" + }, + "trace" : { + "type" : "boolean", + "description" : "Record trace information, based on agent configuration" + } + } + }, + "V20CredOfferRequest" : { + "type" : "object", + "required" : [ "connection_id", "filter" ], + "properties" : { + "auto_issue" : { + "type" : "boolean", + "description" : "Whether to respond automatically to credential requests, creating and issuing requested credentials" + }, + "auto_remove" : { + "type" : "boolean", + "description" : "Whether to remove the credential exchange record on completion (overrides --preserve-exchange-records configuration setting)" + }, + "comment" : { + "type" : "string", + "description" : "Human-readable comment", + "x-nullable" : true + }, + "connection_id" : { + "type" : "string", + "format" : "uuid", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Connection identifier" + }, + "credential_preview" : { + "$ref" : "#/definitions/V20CredPreview" + }, + "filter" : { + "$ref" : "#/definitions/V20CredBoundOfferRequest_filter" + }, + "trace" : { + "type" : "boolean", + "description" : "Record trace information, based on agent configuration" + } + } + }, + "V20CredPreview" : { + "type" : "object", + "required" : [ "attributes" ], + "properties" : { + "@type" : { + "type" : "string", + "example" : "issue-credential/2.0/credential-preview", + "description" : "Message type identifier" + }, + "attributes" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/V20CredAttrSpec" + } + } + } + }, + "V20CredProposal" : { + "type" : "object", + "required" : [ "filters~attach", "formats" ], + "properties" : { + "@id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Message identifier" + }, + "@type" : { + "type" : "string", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "description" : "Message type", + "readOnly" : true + }, + "comment" : { + "type" : "string", + "description" : "Human-readable comment", + "x-nullable" : true + }, + "credential_preview" : { + "$ref" : "#/definitions/V20CredProposal_credential_preview" + }, + "filters~attach" : { + "type" : "array", + "description" : "Credential filter per acceptable format on corresponding identifier", + "items" : { + "$ref" : "#/definitions/AttachDecorator" + } + }, + "formats" : { + "type" : "array", + "description" : "Attachment formats", + "items" : { + "$ref" : "#/definitions/V20CredFormat" + } + } + } + }, + "V20CredRequest" : { + "type" : "object", + "required" : [ "formats", "requests~attach" ], + "properties" : { + "@id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Message identifier" + }, + "@type" : { + "type" : "string", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "description" : "Message type", + "readOnly" : true + }, + "comment" : { + "type" : "string", + "description" : "Human-readable comment", + "x-nullable" : true + }, + "formats" : { + "type" : "array", + "description" : "Acceptable attachment formats", + "items" : { + "$ref" : "#/definitions/V20CredFormat" + } + }, + "requests~attach" : { + "type" : "array", + "description" : "Request attachments", + "items" : { + "$ref" : "#/definitions/AttachDecorator" + } + } + } + }, + "V20CredRequestFree" : { + "type" : "object", + "required" : [ "connection_id", "filter" ], + "properties" : { + "auto_remove" : { + "type" : "boolean", + "description" : "Whether to remove the credential exchange record on completion (overrides --preserve-exchange-records configuration setting)" + }, + "comment" : { + "type" : "string", + "description" : "Human-readable comment", + "x-nullable" : true + }, + "connection_id" : { + "type" : "string", + "format" : "uuid", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Connection identifier" + }, + "filter" : { + "$ref" : "#/definitions/V20CredBoundOfferRequest_filter" + }, + "holder_did" : { + "type" : "string", + "example" : "did:key:ahsdkjahsdkjhaskjdhakjshdkajhsdkjahs", + "description" : "Holder DID to substitute for the credentialSubject.id", + "x-nullable" : true + }, + "trace" : { + "type" : "boolean", + "example" : false, + "description" : "Whether to trace event (default false)" + } + } + }, + "V20CredRequestRequest" : { + "type" : "object", + "properties" : { + "holder_did" : { + "type" : "string", + "example" : "did:key:ahsdkjahsdkjhaskjdhakjshdkajhsdkjahs", + "description" : "Holder DID to substitute for the credentialSubject.id", + "x-nullable" : true + } + } + }, + "V20CredStoreRequest" : { + "type" : "object", + "properties" : { + "credential_id" : { + "type" : "string" + } + } + }, + "V20DiscoveryExchangeListResult" : { + "type" : "object", + "properties" : { + "results" : { + "type" : "array", + "items" : { + "type" : "object", + "description" : "Discover Features v2.0 exchange record", + "allOf" : [ { + "$ref" : "#/definitions/V20DiscoveryRecord" + } ] + } + } + } + }, + "V20DiscoveryExchangeResult" : { + "type" : "object", + "properties" : { + "results" : { + "$ref" : "#/definitions/V20DiscoveryExchangeResult_results" + } + } + }, + "V20DiscoveryRecord" : { + "type" : "object", + "properties" : { + "connection_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Connection identifier" + }, + "created_at" : { + "type" : "string", + "example" : "2021-12-31T23:59:59Z", + "description" : "Time of record creation", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" + }, + "disclosures" : { + "$ref" : "#/definitions/V20DiscoveryRecord_disclosures" + }, + "discovery_exchange_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Credential exchange identifier" + }, + "queries_msg" : { + "$ref" : "#/definitions/V20DiscoveryRecord_queries_msg" + }, + "state" : { + "type" : "string", + "example" : "active", + "description" : "Current record state" + }, + "thread_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Thread identifier" + }, + "trace" : { + "type" : "boolean", + "description" : "Record trace information, based on agent configuration" + }, + "updated_at" : { + "type" : "string", + "example" : "2021-12-31T23:59:59Z", + "description" : "Time of last record update", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" + } + } + }, + "V20IssueCredSchemaCore" : { + "type" : "object", + "required" : [ "filter" ], + "properties" : { + "auto_remove" : { + "type" : "boolean", + "description" : "Whether to remove the credential exchange record on completion (overrides --preserve-exchange-records configuration setting)" + }, + "comment" : { + "type" : "string", + "description" : "Human-readable comment", + "x-nullable" : true + }, + "credential_preview" : { + "$ref" : "#/definitions/V20CredPreview" + }, + "filter" : { + "$ref" : "#/definitions/V20CredBoundOfferRequest_filter" + }, + "trace" : { + "type" : "boolean", + "description" : "Record trace information, based on agent configuration" + } + } + }, + "V20IssueCredentialModuleResponse" : { + "type" : "object" + }, + "V20Pres" : { + "type" : "object", + "required" : [ "formats", "presentations~attach" ], + "properties" : { + "@id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Message identifier" + }, + "@type" : { + "type" : "string", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "description" : "Message type", + "readOnly" : true + }, + "comment" : { + "type" : "string", + "description" : "Human-readable comment", + "x-nullable" : true + }, + "formats" : { + "type" : "array", + "description" : "Acceptable attachment formats", + "items" : { + "$ref" : "#/definitions/V20PresFormat" + } + }, + "presentations~attach" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/AttachDecorator" + } + } + } + }, + "V20PresCreateRequestRequest" : { + "type" : "object", + "required" : [ "presentation_request" ], + "properties" : { + "auto_verify" : { + "type" : "boolean", + "example" : false, + "description" : "Verifier choice to auto-verify proof presentation" + }, + "comment" : { + "type" : "string", + "x-nullable" : true + }, + "presentation_request" : { + "$ref" : "#/definitions/V20PresRequestByFormat" + }, + "trace" : { + "type" : "boolean", + "example" : false, + "description" : "Whether to trace event (default false)" + } + } + }, + "V20PresExRecord" : { + "type" : "object", + "properties" : { + "auto_present" : { + "type" : "boolean", + "example" : false, + "description" : "Prover choice to auto-present proof as verifier requests" + }, + "auto_verify" : { + "type" : "boolean", + "description" : "Verifier choice to auto-verify proof presentation" + }, + "by_format" : { + "$ref" : "#/definitions/V20PresExRecord_by_format" + }, + "connection_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Connection identifier" + }, + "created_at" : { + "type" : "string", + "example" : "2021-12-31T23:59:59Z", + "description" : "Time of record creation", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" + }, + "error_msg" : { + "type" : "string", + "example" : "Invalid structure", + "description" : "Error message" + }, + "initiator" : { + "type" : "string", + "example" : "self", + "description" : "Present-proof exchange initiator: self or external", + "enum" : [ "self", "external" ] + }, + "pres" : { + "$ref" : "#/definitions/V20PresExRecord_pres" + }, + "pres_ex_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Presentation exchange identifier" + }, + "pres_proposal" : { + "$ref" : "#/definitions/V10PresentationExchange_presentation_proposal_dict" + }, + "pres_request" : { + "$ref" : "#/definitions/V10PresentationExchange_presentation_request_dict" + }, + "role" : { + "type" : "string", + "example" : "prover", + "description" : "Present-proof exchange role: prover or verifier", + "enum" : [ "prover", "verifier" ] + }, + "state" : { + "type" : "string", + "description" : "Present-proof exchange state", + "enum" : [ "proposal-sent", "proposal-received", "request-sent", "request-received", "presentation-sent", "presentation-received", "done", "abandoned", "deleted" ] + }, + "thread_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Thread identifier" + }, + "trace" : { + "type" : "boolean", + "description" : "Record trace information, based on agent configuration" + }, + "updated_at" : { + "type" : "string", + "example" : "2021-12-31T23:59:59Z", + "description" : "Time of last record update", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" + }, + "verified" : { + "type" : "string", + "example" : "true", + "description" : "Whether presentation is verified: 'true' or 'false'", + "enum" : [ "true", "false" ] + }, + "verified_msgs" : { + "type" : "array", + "items" : { + "type" : "string", + "description" : "Proof verification warning or error information" + } + } + } + }, + "V20PresExRecordByFormat" : { + "type" : "object", + "properties" : { + "pres" : { + "type" : "object", + "properties" : { } + }, + "pres_proposal" : { + "type" : "object", + "properties" : { } + }, + "pres_request" : { + "type" : "object", + "properties" : { } + } + } + }, + "V20PresExRecordList" : { + "type" : "object", + "properties" : { + "results" : { + "type" : "array", + "description" : "Presentation exchange records", + "items" : { + "$ref" : "#/definitions/V20PresExRecord" + } + } + } + }, + "V20PresFormat" : { + "type" : "object", + "required" : [ "attach_id", "format" ], + "properties" : { + "attach_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Attachment identifier" + }, + "format" : { + "type" : "string", + "example" : "dif/presentation-exchange/submission@v1.0", + "description" : "Attachment format specifier" + } + } + }, + "V20PresProblemReportRequest" : { + "type" : "object", + "required" : [ "description" ], + "properties" : { + "description" : { + "type" : "string" + } + } + }, + "V20PresProposal" : { + "type" : "object", + "required" : [ "formats", "proposals~attach" ], + "properties" : { + "@id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Message identifier" + }, + "@type" : { + "type" : "string", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "description" : "Message type", + "readOnly" : true + }, + "comment" : { + "type" : "string", + "description" : "Human-readable comment" + }, + "formats" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/V20PresFormat" + } + }, + "proposals~attach" : { + "type" : "array", + "description" : "Attachment per acceptable format on corresponding identifier", + "items" : { + "$ref" : "#/definitions/AttachDecorator" + } + } + } + }, + "V20PresProposalByFormat" : { + "type" : "object", + "properties" : { + "dif" : { + "$ref" : "#/definitions/V20PresProposalByFormat_dif" + }, + "indy" : { + "$ref" : "#/definitions/V20PresProposalByFormat_indy" + } + } + }, + "V20PresProposalRequest" : { + "type" : "object", + "required" : [ "connection_id", "presentation_proposal" ], + "properties" : { + "auto_present" : { + "type" : "boolean", + "description" : "Whether to respond automatically to presentation requests, building and presenting requested proof" + }, + "comment" : { + "type" : "string", + "description" : "Human-readable comment", + "x-nullable" : true + }, + "connection_id" : { + "type" : "string", + "format" : "uuid", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Connection identifier" + }, + "presentation_proposal" : { + "$ref" : "#/definitions/V20PresProposalByFormat" + }, + "trace" : { + "type" : "boolean", + "example" : false, + "description" : "Whether to trace event (default false)" + } + } + }, + "V20PresRequest" : { + "type" : "object", + "required" : [ "formats", "request_presentations~attach" ], + "properties" : { + "@id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Message identifier" + }, + "@type" : { + "type" : "string", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "description" : "Message type", + "readOnly" : true + }, + "comment" : { + "type" : "string", + "description" : "Human-readable comment" + }, + "formats" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/V20PresFormat" + } + }, + "request_presentations~attach" : { + "type" : "array", + "description" : "Attachment per acceptable format on corresponding identifier", + "items" : { + "$ref" : "#/definitions/AttachDecorator" + } + }, + "will_confirm" : { + "type" : "boolean", + "description" : "Whether verifier will send confirmation ack" + } + } + }, + "V20PresRequestByFormat" : { + "type" : "object", + "properties" : { + "dif" : { + "$ref" : "#/definitions/V20PresRequestByFormat_dif" + }, + "indy" : { + "$ref" : "#/definitions/V20PresRequestByFormat_indy" + } + } + }, + "V20PresSendRequestRequest" : { + "type" : "object", + "required" : [ "connection_id", "presentation_request" ], + "properties" : { + "auto_verify" : { + "type" : "boolean", + "example" : false, + "description" : "Verifier choice to auto-verify proof presentation" + }, + "comment" : { + "type" : "string", + "x-nullable" : true + }, + "connection_id" : { + "type" : "string", + "format" : "uuid", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Connection identifier" + }, + "presentation_request" : { + "$ref" : "#/definitions/V20PresRequestByFormat" + }, + "trace" : { + "type" : "boolean", + "example" : false, + "description" : "Whether to trace event (default false)" + } + } + }, + "V20PresSpecByFormatRequest" : { + "type" : "object", + "properties" : { + "dif" : { + "$ref" : "#/definitions/V20PresSpecByFormatRequest_dif" + }, + "indy" : { + "$ref" : "#/definitions/V20PresSpecByFormatRequest_indy" + }, + "trace" : { + "type" : "boolean", + "description" : "Record trace information, based on agent configuration" + } + } + }, + "V20PresentProofModuleResponse" : { + "type" : "object" + }, + "V20PresentationSendRequestToProposal" : { + "type" : "object", + "properties" : { + "auto_verify" : { + "type" : "boolean", + "example" : false, + "description" : "Verifier choice to auto-verify proof presentation" + }, + "trace" : { + "type" : "boolean", + "example" : false, + "description" : "Whether to trace event (default false)" + } + } + }, + "VCRecord" : { + "type" : "object", + "properties" : { + "contexts" : { + "type" : "array", + "items" : { + "type" : "string", + "example" : "https://myhost:8021", + "description" : "Context", + "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" + } + }, + "cred_tags" : { + "type" : "object", + "additionalProperties" : { + "type" : "string", + "description" : "Retrieval tag value" + } + }, + "cred_value" : { + "type" : "object", + "description" : "(JSON-serializable) credential value", + "properties" : { } + }, + "expanded_types" : { + "type" : "array", + "items" : { + "type" : "string", + "example" : "https://w3id.org/citizenship#PermanentResidentCard", + "description" : "JSON-LD expanded type extracted from type and context" + } + }, + "given_id" : { + "type" : "string", + "example" : "http://example.edu/credentials/3732", + "description" : "Credential identifier" + }, + "issuer_id" : { + "type" : "string", + "example" : "https://example.edu/issuers/14", + "description" : "Issuer identifier" + }, + "proof_types" : { + "type" : "array", + "items" : { + "type" : "string", + "example" : "Ed25519Signature2018", + "description" : "Signature suite used for proof" + } + }, + "record_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Record identifier" + }, + "schema_ids" : { + "type" : "array", + "items" : { + "type" : "string", + "example" : "https://example.org/examples/degree.json", + "description" : "Schema identifier" + } + }, + "subject_ids" : { + "type" : "array", + "items" : { + "type" : "string", + "example" : "did:example:ebfeb1f712ebc6f1c276e12ec21", + "description" : "Subject identifier" + } + } + } + }, + "VCRecordList" : { + "type" : "object", + "properties" : { + "results" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/VCRecord" + } + } + } + }, + "VerifyRequest" : { + "type" : "object", + "required" : [ "doc" ], + "properties" : { + "doc" : { + "$ref" : "#/definitions/VerifyRequest_doc" + }, + "verkey" : { + "type" : "string", + "description" : "Verkey to use for doc verification" + } + } + }, + "VerifyResponse" : { + "type" : "object", + "required" : [ "valid" ], + "properties" : { + "error" : { + "type" : "string", + "description" : "Error text" + }, + "valid" : { + "type" : "boolean" + } + } + }, + "W3CCredentialsListRequest" : { + "type" : "object", + "properties" : { + "contexts" : { + "type" : "array", + "items" : { + "type" : "string", + "example" : "https://myhost:8021", + "description" : "Credential context to match", + "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" + } + }, + "given_id" : { + "type" : "string", + "description" : "Given credential id to match" + }, + "issuer_id" : { + "type" : "string", + "description" : "Credential issuer identifier to match" + }, + "max_results" : { + "type" : "integer", + "format" : "int32", + "description" : "Maximum number of results to return" + }, + "proof_types" : { + "type" : "array", + "items" : { + "type" : "string", + "example" : "Ed25519Signature2018", + "description" : "Signature suite used for proof" + } + }, + "schema_ids" : { + "type" : "array", + "description" : "Schema identifiers, all of which to match", + "items" : { + "type" : "string", + "example" : "https://myhost:8021", + "description" : "Credential schema identifier", + "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" + } + }, + "subject_ids" : { + "type" : "array", + "description" : "Subject identifiers, all of which to match", + "items" : { + "type" : "string", + "description" : "Subject identifier" + } + }, + "tag_query" : { + "type" : "object", + "description" : "Tag filter", + "additionalProperties" : { + "type" : "string", + "description" : "Tag value" + } + }, + "types" : { + "type" : "array", + "items" : { + "type" : "string", + "example" : "https://myhost:8021", + "description" : "Credential type to match", + "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" + } + } + } + }, + "WalletList" : { + "type" : "object", + "properties" : { + "results" : { + "type" : "array", + "description" : "List of wallet records", + "items" : { + "$ref" : "#/definitions/WalletRecord" + } + } + } + }, + "WalletModuleResponse" : { + "type" : "object" + }, + "WalletRecord" : { + "type" : "object", + "required" : [ "key_management_mode", "wallet_id" ], + "properties" : { + "created_at" : { + "type" : "string", + "example" : "2021-12-31T23:59:59Z", + "description" : "Time of record creation", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" + }, + "key_management_mode" : { + "type" : "string", + "description" : "Mode regarding management of wallet key", + "enum" : [ "managed", "unmanaged" ] + }, + "settings" : { + "type" : "object", + "description" : "Settings for this wallet.", + "properties" : { } + }, + "state" : { + "type" : "string", + "example" : "active", + "description" : "Current record state" + }, + "updated_at" : { + "type" : "string", + "example" : "2021-12-31T23:59:59Z", + "description" : "Time of last record update", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" + }, + "wallet_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Wallet record ID" + } + } + }, + "WriteLedgerRequest" : { + "type" : "object", + "properties" : { + "ledger_id" : { + "type" : "string" + } + } + }, + "ActionMenuFetchResult_result" : { + "type" : "object", + "description" : "Action menu" + }, + "AttachDecoratorData_jws" : { + "type" : "object", + "description" : "Detached Java Web Signature" + }, + "CredDefValue_primary" : { + "type" : "object", + "description" : "Primary value for credential definition" + }, + "CredDefValue_revocation" : { + "type" : "object", + "description" : "Revocation value for credential definition" + }, + "Credential_proof" : { + "type" : "object", + "description" : "The proof of the credential", + "example" : "{\"created\":\"2019-12-11T03:50:55\",\"jws\":\"eyJhbGciOiAiRWREU0EiLCAiYjY0IjogZmFsc2UsICJjcml0JiNjQiXX0..lKJU0Df_keblRKhZAS9Qq6zybm-HqUXNVZ8vgEPNTAjQKBhQDxvXNo7nvtUBb_Eq1Ch6YBKY5qBQ\",\"proofPurpose\":\"assertionMethod\",\"type\":\"Ed25519Signature2018\",\"verificationMethod\":\"did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL\"}" + }, + "CredentialDefinition_value" : { + "type" : "object", + "description" : "Credential definition primary and revocation values" + }, + "DIDCreate_options" : { + "type" : "object", + "description" : "To define a key type and/or a did depending on chosen DID method." + }, + "DIDXRequest_did_docattach" : { + "type" : "object", + "description" : "As signed attachment, DID Doc associated with DID" + }, + "Doc_options" : { + "type" : "object", + "description" : "Signature options" + }, + "IndyCredAbstract_key_correctness_proof" : { + "type" : "object", + "description" : "Key correctness proof" + }, + "IndyCredPrecis_cred_info" : { + "type" : "object", + "description" : "Credential info" + }, + "IndyCredPrecis_interval" : { + "type" : "object", + "description" : "Non-revocation interval from presentation request" + }, + "IndyPrimaryProof_eq_proof" : { + "type" : "object", + "description" : "Indy equality proof", + "x-nullable" : true + }, + "IndyProof_proof" : { + "type" : "object", + "description" : "Indy proof.proof content" + }, + "IndyProof_requested_proof" : { + "type" : "object", + "description" : "Indy proof.requested_proof content" + }, + "IndyProofProof_aggregated_proof" : { + "type" : "object", + "description" : "Indy proof aggregated proof" + }, + "IndyProofProofProofsProof_non_revoc_proof" : { + "type" : "object", + "description" : "Indy non-revocation proof", + "x-nullable" : true + }, + "IndyProofProofProofsProof_primary_proof" : { + "type" : "object", + "description" : "Indy primary proof" + }, + "IndyProofReqAttrSpec_non_revoked" : { + "type" : "object", + "x-nullable" : true + }, + "IndyRevRegDef_value" : { + "type" : "object", + "description" : "Revocation registry definition value" + }, + "IndyRevRegDefValue_publicKeys" : { + "type" : "object", + "description" : "Public keys" + }, + "IndyRevRegEntry_value" : { + "type" : "object", + "description" : "Revocation registry entry value" + }, + "InputDescriptors_schema" : { + "type" : "object", + "description" : "Accepts a list of schema or a dict containing filters like oneof_filter.", + "example" : "{\"oneof_filter\":[[{\"uri\":\"https://www.w3.org/Test1#Test1\"},{\"uri\":\"https://www.w3.org/Test2#Test2\"}],{\"oneof_filter\":[[{\"uri\":\"https://www.w3.org/Test1#Test1\"}],[{\"uri\":\"https://www.w3.org/Test2#Test2\"}]]}]}" + }, + "InvitationRecord_invitation" : { + "type" : "object", + "description" : "Out of band invitation message" + }, + "IssuerRevRegRecord_revoc_reg_def" : { + "type" : "object", + "description" : "Revocation registry definition" + }, + "IssuerRevRegRecord_revoc_reg_entry" : { + "type" : "object", + "description" : "Revocation registry entry" + }, + "KeylistQuery_paginate" : { + "type" : "object", + "description" : "Pagination info" + }, + "LDProofVCDetail_credential" : { + "type" : "object", + "description" : "Detail of the JSON-LD Credential to be issued", + "example" : "{\"@context\":[\"https://www.w3.org/2018/credentials/v1\",\"https://w3id.org/citizenship/v1\"],\"credentialSubject\":{\"familyName\":\"SMITH\",\"gender\":\"Male\",\"givenName\":\"JOHN\",\"type\":[\"PermanentResident\",\"Person\"]},\"description\":\"Government of Example Permanent Resident Card.\",\"identifier\":\"83627465\",\"issuanceDate\":\"2019-12-03T12:19:52Z\",\"issuer\":\"did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th\",\"name\":\"Permanent Resident Card\",\"type\":[\"VerifiableCredential\",\"PermanentResidentCard\"]}" + }, + "LDProofVCDetail_options" : { + "type" : "object", + "description" : "Options for specifying how the linked data proof is created.", + "example" : "{\"proofType\":\"Ed25519Signature2018\"}" + }, + "LDProofVCDetailOptions_credentialStatus" : { + "type" : "object", + "description" : "The credential status mechanism to use for the credential. Omitting the property indicates the issued credential will not include a credential status" + }, + "SchemaSendResult_schema" : { + "type" : "object", + "description" : "Schema definition" + }, + "SendMenu_menu" : { + "type" : "object", + "description" : "Menu to send to connection" + }, + "SignedDoc_proof" : { + "type" : "object", + "description" : "Linked data proof" + }, + "TxnOrCredentialDefinitionSendResult_txn" : { + "type" : "object", + "description" : "Credential definition transaction to endorse" + }, + "TxnOrPublishRevocationsResult_txn" : { + "type" : "object", + "description" : "Revocation registry revocations transaction to endorse" + }, + "TxnOrRegisterLedgerNymResponse_txn" : { + "type" : "object", + "description" : "DID transaction to endorse" + }, + "TxnOrRevRegResult_txn" : { + "type" : "object", + "description" : "Revocation registry definition transaction to endorse" + }, + "TxnOrSchemaSendResult_sent" : { + "type" : "object", + "description" : "Content sent" + }, + "TxnOrSchemaSendResult_txn" : { + "type" : "object", + "description" : "Schema transaction to endorse" + }, + "V10CredentialBoundOfferRequest_counter_proposal" : { + "type" : "object", + "description" : "Optional counter-proposal" + }, + "V10CredentialExchange_credential" : { + "type" : "object", + "description" : "Credential as stored" + }, + "V10CredentialExchange_credential_offer" : { + "type" : "object", + "description" : "(Indy) credential offer" + }, + "V10CredentialExchange_credential_offer_dict" : { + "type" : "object", + "description" : "Credential offer message" + }, + "V10CredentialExchange_credential_proposal_dict" : { + "type" : "object", + "description" : "Credential proposal message" + }, + "V10CredentialExchange_credential_request" : { + "type" : "object", + "description" : "(Indy) credential request" + }, + "V10CredentialExchange_raw_credential" : { + "type" : "object", + "description" : "Credential as received, prior to storage in holder wallet" + }, + "V10DiscoveryRecord_disclose" : { + "type" : "object", + "description" : "Disclose message" + }, + "V10DiscoveryRecord_query_msg" : { + "type" : "object", + "description" : "Query message" + }, + "V10PresentationExchange_presentation" : { + "type" : "object", + "description" : "(Indy) presentation (also known as proof)" + }, + "V10PresentationExchange_presentation_proposal_dict" : { + "type" : "object", + "description" : "Presentation proposal message" + }, + "V10PresentationExchange_presentation_request" : { + "type" : "object", + "description" : "(Indy) presentation request (also known as proof request)" + }, + "V10PresentationExchange_presentation_request_dict" : { + "type" : "object", + "description" : "Presentation request message" + }, + "V20CredBoundOfferRequest_counter_preview" : { + "type" : "object", + "description" : "Optional content for counter-proposal" + }, + "V20CredBoundOfferRequest_filter" : { + "type" : "object", + "description" : "Credential specification criteria by format" + }, + "V20CredExRecord_by_format" : { + "type" : "object", + "description" : "Attachment content by format for proposal, offer, request, and issue" + }, + "V20CredExRecord_cred_issue" : { + "type" : "object", + "description" : "Serialized credential issue message" + }, + "V20CredExRecord_cred_preview" : { + "type" : "object", + "description" : "Credential preview from credential proposal" + }, + "V20CredExRecord_cred_request" : { + "type" : "object", + "description" : "Serialized credential request message" + }, + "V20CredExRecordDetail_cred_ex_record" : { + "type" : "object", + "description" : "Credential exchange record" + }, + "V20CredFilter_indy" : { + "type" : "object", + "description" : "Credential filter for indy" + }, + "V20CredFilter_ld_proof" : { + "type" : "object", + "description" : "Credential filter for linked data proof" + }, + "V20CredProposal_credential_preview" : { + "type" : "object", + "description" : "Credential preview" + }, + "V20DiscoveryExchangeResult_results" : { + "type" : "object", + "description" : "Discover Features v2.0 exchange record" + }, + "V20DiscoveryRecord_disclosures" : { + "type" : "object", + "description" : "Disclosures message" + }, + "V20DiscoveryRecord_queries_msg" : { + "type" : "object", + "description" : "Queries message" + }, + "V20PresExRecord_by_format" : { + "type" : "object", + "description" : "Attachment content by format for proposal, request, and presentation" + }, + "V20PresExRecord_pres" : { + "type" : "object", + "description" : "Presentation message" + }, + "V20PresProposalByFormat_dif" : { + "type" : "object", + "description" : "Presentation proposal for DIF" + }, + "V20PresProposalByFormat_indy" : { + "type" : "object", + "description" : "Presentation proposal for indy" + }, + "V20PresRequestByFormat_dif" : { + "type" : "object", + "description" : "Presentation request for DIF" + }, + "V20PresRequestByFormat_indy" : { + "type" : "object", + "description" : "Presentation request for indy" + }, + "V20PresSpecByFormatRequest_dif" : { + "type" : "object", + "description" : "Optional Presentation specification for DIF, overrides the PresentionExchange record's PresRequest" + }, + "V20PresSpecByFormatRequest_indy" : { + "type" : "object", + "description" : "Presentation specification for indy" + }, + "VerifyRequest_doc" : { + "type" : "object", + "description" : "Signed document" + } + } +} \ No newline at end of file From 62ce4ba0448ca35fa64b31499c327625bbd0e8bf Mon Sep 17 00:00:00 2001 From: ff137 Date: Sat, 27 May 2023 13:45:27 +0200 Subject: [PATCH 807/872] update openapi spec to 3.0.1 (new codegen in `scripts/generate-open-api-spec`) Signed-off-by: ff137 --- open-api/openapi.json | 21048 ++++++++++++++++++++++------------------ 1 file changed, 11706 insertions(+), 9342 deletions(-) diff --git a/open-api/openapi.json b/open-api/openapi.json index 2ee691f00d..a77d9ac129 100644 --- a/open-api/openapi.json +++ b/open-api/openapi.json @@ -1,11259 +1,13623 @@ { - "swagger" : "2.0", + "openapi" : "3.0.1", "info" : { - "version" : "v0.8.1", - "title" : "Aries Cloud Agent" + "title" : "Aries Cloud Agent", + "version" : "v0.8.1" }, + "servers" : [ { + "url" : "/" + } ], + "security" : [ { + "AuthorizationHeader" : [ ] + } ], "tags" : [ { - "name" : "action-menu", - "description" : "Menu interaction over connection" + "description" : "Menu interaction over connection", + "name" : "action-menu" }, { - "name" : "basicmessage", "description" : "Simple messaging", "externalDocs" : { "description" : "Specification", "url" : "https://github.com/hyperledger/aries-rfcs/tree/527849ec3aa2a8fd47a7bb6c57f918ff8bcb5e8c/features/0095-basic-message" - } + }, + "name" : "basicmessage" }, { - "name" : "connection", "description" : "Connection management", "externalDocs" : { "description" : "Specification", "url" : "https://github.com/hyperledger/aries-rfcs/tree/9b0aaa39df7e8bd434126c4b33c097aae78d65bf/features/0160-connection-protocol" - } + }, + "name" : "connection" }, { - "name" : "credential-definition", "description" : "Credential definition operations", "externalDocs" : { "description" : "Specification", "url" : "https://github.com/hyperledger/indy-node/blob/master/design/anoncreds.md#cred_def" - } + }, + "name" : "credential-definition" }, { - "name" : "credentials", "description" : "Holder credential management", "externalDocs" : { "description" : "Overview", "url" : "https://w3c.github.io/vc-data-model/#credentials" - } + }, + "name" : "credentials" }, { - "name" : "did-exchange", "description" : "Connection management via DID exchange", "externalDocs" : { "description" : "Specification", "url" : "https://github.com/hyperledger/aries-rfcs/tree/25464a5c8f8a17b14edaa4310393df6094ace7b0/features/0023-did-exchange" - } + }, + "name" : "did-exchange" + }, { + "description" : "Feature discovery", + "externalDocs" : { + "description" : "Specification", + "url" : "https://github.com/hyperledger/aries-rfcs/tree/b3a3942ef052039e73cd23d847f42947f8287da2/features/0031-discover-features" + }, + "name" : "discover-features" + }, { + "description" : "Feature discovery v2", + "externalDocs" : { + "description" : "Specification", + "url" : "https://github.com/hyperledger/aries-rfcs/tree/b3a3942ef052039e73cd23d847f42947f8287da2/features/0557-discover-features-v2" + }, + "name" : "discover-features v2.0" }, { - "name" : "endorse-transaction", - "description" : "Endorse a Transaction" + "description" : "Endorse a Transaction", + "name" : "endorse-transaction" }, { - "name" : "introduction", - "description" : "Introduction of known parties" + "description" : "Introduction of known parties", + "name" : "introduction" }, { - "name" : "issue-credential v1.0", "description" : "Credential issue v1.0", "externalDocs" : { "description" : "Specification", "url" : "https://github.com/hyperledger/aries-rfcs/tree/bb42a6c35e0d5543718fb36dd099551ab192f7b0/features/0036-issue-credential" - } + }, + "name" : "issue-credential v1.0" }, { - "name" : "issue-credential v2.0", "description" : "Credential issue v2.0", "externalDocs" : { "description" : "Specification", "url" : "https://github.com/hyperledger/aries-rfcs/tree/cd27fc64aa2805f756a118043d7c880354353047/features/0453-issue-credential-v2" - } + }, + "name" : "issue-credential v2.0" }, { - "name" : "jsonld", "description" : "Sign and verify json-ld data", "externalDocs" : { "description" : "Specification", "url" : "https://tools.ietf.org/html/rfc7515" - } + }, + "name" : "jsonld" }, { - "name" : "ledger", "description" : "Interaction with ledger", "externalDocs" : { "description" : "Overview", "url" : "https://hyperledger-indy.readthedocs.io/projects/plenum/en/latest/storage.html#ledger" - } + }, + "name" : "ledger" }, { - "name" : "mediation", "description" : "Mediation management", "externalDocs" : { "description" : "Specification", "url" : "https://github.com/hyperledger/aries-rfcs/tree/fa8dc4ea1e667eb07db8f9ffeaf074a4455697c0/features/0211-route-coordination" - } + }, + "name" : "mediation" }, { - "name" : "multitenancy", - "description" : "Multitenant wallet management" + "description" : "Multitenant wallet management", + "name" : "multitenancy" }, { - "name" : "out-of-band", "description" : "Out-of-band connections", "externalDocs" : { "description" : "Design", "url" : "https://github.com/hyperledger/aries-rfcs/tree/2da7fc4ee043effa3a9960150e7ba8c9a4628b68/features/0434-outofband" - } + }, + "name" : "out-of-band" }, { - "name" : "present-proof v1.0", "description" : "Proof presentation v1.0", "externalDocs" : { "description" : "Specification", "url" : "https://github.com/hyperledger/aries-rfcs/tree/4fae574c03f9f1013db30bf2c0c676b1122f7149/features/0037-present-proof" - } + }, + "name" : "present-proof v1.0" }, { - "name" : "present-proof v2.0", "description" : "Proof presentation v2.0", "externalDocs" : { "description" : "Specification", "url" : "https://github.com/hyperledger/aries-rfcs/tree/eace815c3e8598d4a8dd7881d8c731fdb2bcc0aa/features/0454-present-proof-v2" - } + }, + "name" : "present-proof v2.0" }, { - "name" : "resolver", - "description" : "DID resolver interface", + "description" : "did resolver interface.", "externalDocs" : { - "description" : "Specification", - "url" : "https://github.com/hyperledger/aries-rfcs/tree/fa4b1947c6077168d2c69f45ed6bee2bb1eae4c8/features/0124-did-resolution-protocol" - } + "description" : "Specification" + }, + "name" : "resolver" }, { - "name" : "revocation", "description" : "Revocation registry management", "externalDocs" : { "description" : "Overview", "url" : "https://github.com/hyperledger/indy-hipe/tree/master/text/0011-cred-revocation" - } + }, + "name" : "revocation" }, { - "name" : "schema", "description" : "Schema operations", "externalDocs" : { "description" : "Specification", "url" : "https://github.com/hyperledger/indy-node/blob/master/design/anoncreds.md#schema" - } - }, { - "name" : "server", - "description" : "Feature discovery", - "externalDocs" : { - "description" : "Specification", - "url" : "https://github.com/hyperledger/aries-rfcs/tree/9b7ab9814f2e7d1108f74aca6f3d2e5d62899473/features/0031-discover-features" - } + }, + "name" : "schema" }, { - "name" : "trustping", "description" : "Trust-ping over connection", "externalDocs" : { "description" : "Specification", "url" : "https://github.com/hyperledger/aries-rfcs/tree/527849ec3aa2a8fd47a7bb6c57f918ff8bcb5e8c/features/0048-trust-ping" - } + }, + "name" : "trustping" }, { - "name" : "wallet", "description" : "DID and tag policy management", "externalDocs" : { "description" : "Design", "url" : "https://github.com/hyperledger/indy-sdk/tree/master/docs/design/003-wallet-storage" - } - } ], - "security" : [ { - "AuthorizationHeader" : [ ] + }, + "name" : "wallet" } ], "paths" : { "/action-menu/{conn_id}/close" : { "post" : { - "tags" : [ "action-menu" ], - "summary" : "Close the active menu associated with a connection", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "conn_id", - "in" : "path", "description" : "Connection identifier", + "in" : "path", + "name" : "conn_id", "required" : true, - "type" : "string" + "schema" : { + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/ActionMenuModulesResult" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ActionMenuModulesResult" + } + } + }, + "description" : "" } - } + }, + "summary" : "Close the active menu associated with a connection", + "tags" : [ "action-menu" ] } }, "/action-menu/{conn_id}/fetch" : { "post" : { - "tags" : [ "action-menu" ], - "summary" : "Fetch the active menu", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "conn_id", - "in" : "path", "description" : "Connection identifier", + "in" : "path", + "name" : "conn_id", "required" : true, - "type" : "string" + "schema" : { + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/ActionMenuFetchResult" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ActionMenuFetchResult" + } + } + }, + "description" : "" } - } + }, + "summary" : "Fetch the active menu", + "tags" : [ "action-menu" ] } }, "/action-menu/{conn_id}/perform" : { "post" : { - "tags" : [ "action-menu" ], - "summary" : "Perform an action associated with the active menu", - "produces" : [ "application/json" ], "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/PerformRequest" - } - }, { - "name" : "conn_id", - "in" : "path", "description" : "Connection identifier", + "in" : "path", + "name" : "conn_id", "required" : true, - "type" : "string" + "schema" : { + "type" : "string" + } } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/PerformRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/ActionMenuModulesResult" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ActionMenuModulesResult" + } + } + }, + "description" : "" } - } + }, + "summary" : "Perform an action associated with the active menu", + "tags" : [ "action-menu" ], + "x-codegen-request-body-name" : "body" } }, "/action-menu/{conn_id}/request" : { "post" : { - "tags" : [ "action-menu" ], - "summary" : "Request the active menu", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "conn_id", - "in" : "path", "description" : "Connection identifier", + "in" : "path", + "name" : "conn_id", "required" : true, - "type" : "string" + "schema" : { + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/ActionMenuModulesResult" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ActionMenuModulesResult" + } + } + }, + "description" : "" } - } + }, + "summary" : "Request the active menu", + "tags" : [ "action-menu" ] } }, "/action-menu/{conn_id}/send-menu" : { "post" : { - "tags" : [ "action-menu" ], - "summary" : "Send an action menu to a connection", - "produces" : [ "application/json" ], "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/SendMenu" - } - }, { - "name" : "conn_id", - "in" : "path", "description" : "Connection identifier", + "in" : "path", + "name" : "conn_id", "required" : true, - "type" : "string" + "schema" : { + "type" : "string" + } } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/SendMenu" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/ActionMenuModulesResult" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ActionMenuModulesResult" + } + } + }, + "description" : "" } - } + }, + "summary" : "Send an action menu to a connection", + "tags" : [ "action-menu" ], + "x-codegen-request-body-name" : "body" } }, "/connections" : { "get" : { - "tags" : [ "connection" ], - "summary" : "Query agent-to-agent connections", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "alias", - "in" : "query", "description" : "Alias", - "required" : false, - "type" : "string" - }, { - "name" : "connection_protocol", "in" : "query", - "description" : "Connection protocol used", - "required" : false, - "type" : "string", - "enum" : [ "connections/1.0", "didexchange/1.0" ] + "name" : "alias", + "schema" : { + "type" : "string" + } }, { - "name" : "invitation_key", + "description" : "Connection protocol used", "in" : "query", + "name" : "connection_protocol", + "schema" : { + "enum" : [ "connections/1.0", "didexchange/1.0" ], + "type" : "string" + } + }, { "description" : "invitation key", - "required" : false, - "type" : "string", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" + "in" : "query", + "name" : "invitation_key", + "schema" : { + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$", + "type" : "string" + } }, { - "name" : "my_did", + "description" : "Identifier of the associated Invitation Mesage", "in" : "query", - "description" : "My DID", - "required" : false, - "type" : "string", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + "name" : "invitation_msg_id", + "schema" : { + "format" : "uuid", + "type" : "string" + } }, { - "name" : "state", + "description" : "My DID", "in" : "query", - "description" : "Connection state", - "required" : false, - "type" : "string", - "enum" : [ "start", "abandoned", "active", "completed", "response", "init", "invitation", "error", "request" ] + "name" : "my_did", + "schema" : { + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$", + "type" : "string" + } }, { - "name" : "their_did", + "description" : "Connection state", "in" : "query", + "name" : "state", + "schema" : { + "enum" : [ "start", "invitation", "request", "abandoned", "error", "init", "response", "active", "completed" ], + "type" : "string" + } + }, { "description" : "Their DID", - "required" : false, - "type" : "string", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + "in" : "query", + "name" : "their_did", + "schema" : { + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$", + "type" : "string" + } }, { - "name" : "their_role", + "description" : "Their Public DID", "in" : "query", + "name" : "their_public_did", + "schema" : { + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$", + "type" : "string" + } + }, { "description" : "Their role in the connection protocol", - "required" : false, - "type" : "string", - "enum" : [ "invitee", "requester", "inviter", "responder" ] + "in" : "query", + "name" : "their_role", + "schema" : { + "enum" : [ "invitee", "requester", "inviter", "responder" ], + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/ConnectionList" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ConnectionList" + } + } + }, + "description" : "" } - } + }, + "summary" : "Query agent-to-agent connections", + "tags" : [ "connection" ] } }, "/connections/create-invitation" : { "post" : { - "tags" : [ "connection" ], - "summary" : "Create a new connection invitation", - "produces" : [ "application/json" ], "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, + "description" : "Alias", + "in" : "query", + "name" : "alias", "schema" : { - "$ref" : "#/definitions/CreateInvitationRequest" + "type" : "string" } }, { - "name" : "alias", + "description" : "Auto-accept connection (defaults to configuration)", "in" : "query", - "description" : "Alias", - "required" : false, - "type" : "string" - }, { "name" : "auto_accept", - "in" : "query", - "description" : "Auto-accept connection (defaults to configuration)", - "required" : false, - "type" : "boolean" + "schema" : { + "type" : "boolean" + } }, { - "name" : "multi_use", - "in" : "query", "description" : "Create invitation for multiple use (default false)", - "required" : false, - "type" : "boolean" - }, { - "name" : "public", "in" : "query", + "name" : "multi_use", + "schema" : { + "type" : "boolean" + } + }, { "description" : "Create invitation from public DID (default false)", - "required" : false, - "type" : "boolean" + "in" : "query", + "name" : "public", + "schema" : { + "type" : "boolean" + } } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/CreateInvitationRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/InvitationResult" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/InvitationResult" + } + } + }, + "description" : "" } - } + }, + "summary" : "Create a new connection invitation", + "tags" : [ "connection" ], + "x-codegen-request-body-name" : "body" } }, "/connections/create-static" : { "post" : { - "tags" : [ "connection" ], - "summary" : "Create a new static connection", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/ConnectionStaticRequest" - } - } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/ConnectionStaticRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/ConnectionStaticResult" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ConnectionStaticResult" + } + } + }, + "description" : "" } - } + }, + "summary" : "Create a new static connection", + "tags" : [ "connection" ], + "x-codegen-request-body-name" : "body" } }, "/connections/receive-invitation" : { "post" : { - "tags" : [ "connection" ], - "summary" : "Receive a new connection invitation", - "produces" : [ "application/json" ], "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, + "description" : "Alias", + "in" : "query", + "name" : "alias", "schema" : { - "$ref" : "#/definitions/ReceiveInvitationRequest" + "type" : "string" } }, { - "name" : "alias", + "description" : "Auto-accept connection (defaults to configuration)", "in" : "query", - "description" : "Alias", - "required" : false, - "type" : "string" - }, { "name" : "auto_accept", - "in" : "query", - "description" : "Auto-accept connection (defaults to configuration)", - "required" : false, - "type" : "boolean" + "schema" : { + "type" : "boolean" + } }, { - "name" : "mediation_id", - "in" : "query", "description" : "Identifier for active mediation record to be used", - "required" : false, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + "in" : "query", + "name" : "mediation_id", + "schema" : { + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", + "type" : "string" + } } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/ReceiveInvitationRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/ConnRecord" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ConnRecord" + } + } + }, + "description" : "" } - } + }, + "summary" : "Receive a new connection invitation", + "tags" : [ "connection" ], + "x-codegen-request-body-name" : "body" } }, "/connections/{conn_id}" : { - "get" : { - "tags" : [ "connection" ], - "summary" : "Fetch a single connection record", - "produces" : [ "application/json" ], + "delete" : { "parameters" : [ { - "name" : "conn_id", - "in" : "path", "description" : "Connection identifier", + "in" : "path", + "name" : "conn_id", "required" : true, - "type" : "string" + "schema" : { + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/ConnRecord" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ConnectionModuleResponse" + } + } + }, + "description" : "" } - } - }, - "delete" : { - "tags" : [ "connection" ], + }, "summary" : "Remove an existing connection record", - "produces" : [ "application/json" ], + "tags" : [ "connection" ] + }, + "get" : { "parameters" : [ { - "name" : "conn_id", - "in" : "path", "description" : "Connection identifier", + "in" : "path", + "name" : "conn_id", "required" : true, - "type" : "string" + "schema" : { + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/ConnectionModuleResponse" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ConnRecord" + } + } + }, + "description" : "" } - } + }, + "summary" : "Fetch a single connection record", + "tags" : [ "connection" ] } }, "/connections/{conn_id}/accept-invitation" : { "post" : { - "tags" : [ "connection" ], - "summary" : "Accept a stored connection invitation", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "conn_id", - "in" : "path", "description" : "Connection identifier", + "in" : "path", + "name" : "conn_id", "required" : true, - "type" : "string" + "schema" : { + "type" : "string" + } }, { - "name" : "mediation_id", - "in" : "query", "description" : "Identifier for active mediation record to be used", - "required" : false, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - }, { - "name" : "my_endpoint", "in" : "query", - "description" : "My URL endpoint", - "required" : false, - "type" : "string", - "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" + "name" : "mediation_id", + "schema" : { + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", + "type" : "string" + } }, { - "name" : "my_label", + "description" : "My URL endpoint", "in" : "query", + "name" : "my_endpoint", + "schema" : { + "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$", + "type" : "string" + } + }, { "description" : "Label for connection", - "required" : false, - "type" : "string" + "in" : "query", + "name" : "my_label", + "schema" : { + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/ConnRecord" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ConnRecord" + } + } + }, + "description" : "" } - } + }, + "summary" : "Accept a stored connection invitation", + "tags" : [ "connection" ] } }, "/connections/{conn_id}/accept-request" : { "post" : { - "tags" : [ "connection" ], - "summary" : "Accept a stored connection request", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "conn_id", - "in" : "path", "description" : "Connection identifier", + "in" : "path", + "name" : "conn_id", "required" : true, - "type" : "string" + "schema" : { + "type" : "string" + } }, { - "name" : "my_endpoint", - "in" : "query", "description" : "My URL endpoint", - "required" : false, - "type" : "string", - "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" + "in" : "query", + "name" : "my_endpoint", + "schema" : { + "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$", + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/ConnRecord" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ConnRecord" + } + } + }, + "description" : "" } - } + }, + "summary" : "Accept a stored connection request", + "tags" : [ "connection" ] } }, "/connections/{conn_id}/endpoints" : { "get" : { - "tags" : [ "connection" ], - "summary" : "Fetch connection remote endpoint", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "conn_id", - "in" : "path", "description" : "Connection identifier", + "in" : "path", + "name" : "conn_id", "required" : true, - "type" : "string" + "schema" : { + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/EndpointsResult" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/EndpointsResult" + } + } + }, + "description" : "" } - } + }, + "summary" : "Fetch connection remote endpoint", + "tags" : [ "connection" ] } }, "/connections/{conn_id}/establish-inbound/{ref_id}" : { "post" : { - "tags" : [ "connection" ], - "summary" : "Assign another connection as the inbound connection", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "conn_id", - "in" : "path", "description" : "Connection identifier", + "in" : "path", + "name" : "conn_id", "required" : true, - "type" : "string" + "schema" : { + "type" : "string" + } }, { - "name" : "ref_id", - "in" : "path", "description" : "Inbound connection identifier", + "in" : "path", + "name" : "ref_id", "required" : true, - "type" : "string" + "schema" : { + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/ConnectionModuleResponse" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ConnectionModuleResponse" + } + } + }, + "description" : "" } - } + }, + "summary" : "Assign another connection as the inbound connection", + "tags" : [ "connection" ] } }, "/connections/{conn_id}/metadata" : { "get" : { - "tags" : [ "connection" ], - "summary" : "Fetch connection metadata", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "conn_id", - "in" : "path", "description" : "Connection identifier", + "in" : "path", + "name" : "conn_id", "required" : true, - "type" : "string" + "schema" : { + "type" : "string" + } }, { - "name" : "key", - "in" : "query", "description" : "Key to retrieve.", - "required" : false, - "type" : "string" + "in" : "query", + "name" : "key", + "schema" : { + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/ConnectionMetadata" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ConnectionMetadata" + } + } + }, + "description" : "" } - } + }, + "summary" : "Fetch connection metadata", + "tags" : [ "connection" ] }, "post" : { - "tags" : [ "connection" ], - "summary" : "Set connection metadata", - "produces" : [ "application/json" ], "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/ConnectionMetadataSetRequest" - } - }, { - "name" : "conn_id", - "in" : "path", "description" : "Connection identifier", + "in" : "path", + "name" : "conn_id", "required" : true, - "type" : "string" + "schema" : { + "type" : "string" + } } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/ConnectionMetadataSetRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/ConnectionMetadata" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ConnectionMetadata" + } + } + }, + "description" : "" } - } + }, + "summary" : "Set connection metadata", + "tags" : [ "connection" ], + "x-codegen-request-body-name" : "body" } }, "/connections/{conn_id}/send-message" : { "post" : { - "tags" : [ "basicmessage" ], - "summary" : "Send a basic message to a connection", - "produces" : [ "application/json" ], "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/SendMessage" - } - }, { - "name" : "conn_id", - "in" : "path", "description" : "Connection identifier", + "in" : "path", + "name" : "conn_id", "required" : true, - "type" : "string" + "schema" : { + "type" : "string" + } } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/SendMessage" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/BasicMessageModuleResponse" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/BasicMessageModuleResponse" + } + } + }, + "description" : "" } - } + }, + "summary" : "Send a basic message to a connection", + "tags" : [ "basicmessage" ], + "x-codegen-request-body-name" : "body" } }, "/connections/{conn_id}/send-ping" : { "post" : { - "tags" : [ "trustping" ], - "summary" : "Send a trust ping to a connection", - "produces" : [ "application/json" ], "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/PingRequest" - } - }, { - "name" : "conn_id", - "in" : "path", "description" : "Connection identifier", + "in" : "path", + "name" : "conn_id", "required" : true, - "type" : "string" + "schema" : { + "type" : "string" + } } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/PingRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/PingRequestResponse" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/PingRequestResponse" + } + } + }, + "description" : "" } - } + }, + "summary" : "Send a trust ping to a connection", + "tags" : [ "trustping" ], + "x-codegen-request-body-name" : "body" } }, "/connections/{conn_id}/start-introduction" : { "post" : { - "tags" : [ "introduction" ], - "summary" : "Start an introduction between two connections", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "conn_id", - "in" : "path", "description" : "Connection identifier", + "in" : "path", + "name" : "conn_id", "required" : true, - "type" : "string" + "schema" : { + "type" : "string" + } }, { - "name" : "target_connection_id", - "in" : "query", "description" : "Target connection identifier", + "in" : "query", + "name" : "target_connection_id", "required" : true, - "type" : "string" + "schema" : { + "type" : "string" + } }, { - "name" : "message", - "in" : "query", "description" : "Message", - "required" : false, - "type" : "string" + "in" : "query", + "name" : "message", + "schema" : { + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/IntroModuleResponse" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/IntroModuleResponse" + } + } + }, + "description" : "" } - } + }, + "summary" : "Start an introduction between two connections", + "tags" : [ "introduction" ] } }, "/credential-definitions" : { "post" : { - "tags" : [ "credential-definition" ], - "summary" : "Sends a credential definition to the ledger", - "produces" : [ "application/json" ], "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, + "description" : "Connection identifier", + "in" : "query", + "name" : "conn_id", "schema" : { - "$ref" : "#/definitions/CredentialDefinitionSendRequest" + "type" : "string" } }, { - "name" : "conn_id", + "description" : "Create Transaction For Endorser's signature", "in" : "query", - "description" : "Connection identifier", - "required" : false, - "type" : "string" - }, { "name" : "create_transaction_for_endorser", - "in" : "query", - "description" : "Create Transaction For Endorser's signature", - "required" : false, - "type" : "boolean" + "schema" : { + "type" : "boolean" + } } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/CredentialDefinitionSendRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/TxnOrCredentialDefinitionSendResult" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/TxnOrCredentialDefinitionSendResult" + } + } + }, + "description" : "" } - } + }, + "summary" : "Sends a credential definition to the ledger", + "tags" : [ "credential-definition" ], + "x-codegen-request-body-name" : "body" } }, "/credential-definitions/created" : { "get" : { - "tags" : [ "credential-definition" ], - "summary" : "Search for matching credential definitions that agent originated", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "cred_def_id", - "in" : "query", "description" : "Credential definition id", - "required" : false, - "type" : "string", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - }, { - "name" : "issuer_did", "in" : "query", - "description" : "Issuer DID", - "required" : false, - "type" : "string", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + "name" : "cred_def_id", + "schema" : { + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$", + "type" : "string" + } }, { - "name" : "schema_id", + "description" : "Issuer DID", "in" : "query", - "description" : "Schema identifier", - "required" : false, - "type" : "string", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" + "name" : "issuer_did", + "schema" : { + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$", + "type" : "string" + } }, { - "name" : "schema_issuer_did", + "description" : "Schema identifier", "in" : "query", - "description" : "Schema issuer DID", - "required" : false, - "type" : "string", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + "name" : "schema_id", + "schema" : { + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$", + "type" : "string" + } }, { - "name" : "schema_name", + "description" : "Schema issuer DID", "in" : "query", - "description" : "Schema name", - "required" : false, - "type" : "string" + "name" : "schema_issuer_did", + "schema" : { + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$", + "type" : "string" + } }, { - "name" : "schema_version", + "description" : "Schema name", "in" : "query", + "name" : "schema_name", + "schema" : { + "type" : "string" + } + }, { "description" : "Schema version", - "required" : false, - "type" : "string", - "pattern" : "^[0-9.]+$" + "in" : "query", + "name" : "schema_version", + "schema" : { + "pattern" : "^[0-9.]+$", + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/CredentialDefinitionsCreatedResult" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/CredentialDefinitionsCreatedResult" + } + } + }, + "description" : "" } - } + }, + "summary" : "Search for matching credential definitions that agent originated", + "tags" : [ "credential-definition" ] } }, "/credential-definitions/{cred_def_id}" : { "get" : { - "tags" : [ "credential-definition" ], - "summary" : "Gets a credential definition from the ledger", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "cred_def_id", - "in" : "path", "description" : "Credential definition identifier", + "in" : "path", + "name" : "cred_def_id", "required" : true, - "type" : "string", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" + "schema" : { + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$", + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/CredentialDefinitionGetResult" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/CredentialDefinitionGetResult" + } + } + }, + "description" : "" } - } + }, + "summary" : "Gets a credential definition from the ledger", + "tags" : [ "credential-definition" ] } }, "/credential-definitions/{cred_def_id}/write_record" : { "post" : { - "tags" : [ "credential-definition" ], - "summary" : "Writes a credential definition non-secret record to the wallet", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "cred_def_id", - "in" : "path", "description" : "Credential definition identifier", + "in" : "path", + "name" : "cred_def_id", "required" : true, - "type" : "string", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" + "schema" : { + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$", + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/CredentialDefinitionGetResult" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/CredentialDefinitionGetResult" + } + } + }, + "description" : "" } - } + }, + "summary" : "Writes a credential definition non-secret record to the wallet", + "tags" : [ "credential-definition" ] } }, "/credential/mime-types/{credential_id}" : { "get" : { - "tags" : [ "credentials" ], - "summary" : "Get attribute MIME types from wallet", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "credential_id", - "in" : "path", "description" : "Credential identifier", + "in" : "path", + "name" : "credential_id", "required" : true, - "type" : "string" + "schema" : { + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/AttributeMimeTypesResult" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/AttributeMimeTypesResult" + } + } + }, + "description" : "" } - } + }, + "summary" : "Get attribute MIME types from wallet", + "tags" : [ "credentials" ] } }, "/credential/revoked/{credential_id}" : { "get" : { - "tags" : [ "credentials" ], - "summary" : "Query credential revocation status by id", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "credential_id", - "in" : "path", "description" : "Credential identifier", + "in" : "path", + "name" : "credential_id", "required" : true, - "type" : "string" + "schema" : { + "type" : "string" + } }, { - "name" : "from", - "in" : "query", "description" : "Earliest epoch of revocation status interval of interest", - "required" : false, - "type" : "string", - "pattern" : "^[0-9]*$" - }, { - "name" : "to", "in" : "query", + "name" : "from", + "schema" : { + "pattern" : "^[0-9]*$", + "type" : "string" + } + }, { "description" : "Latest epoch of revocation status interval of interest", - "required" : false, - "type" : "string", - "pattern" : "^[0-9]*$" + "in" : "query", + "name" : "to", + "schema" : { + "pattern" : "^[0-9]*$", + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/CredRevokedResult" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/CredRevokedResult" + } + } + }, + "description" : "" } - } + }, + "summary" : "Query credential revocation status by id", + "tags" : [ "credentials" ] } }, "/credential/w3c/{credential_id}" : { - "get" : { - "tags" : [ "credentials" ], - "summary" : "Fetch W3C credential from wallet by id", - "produces" : [ "application/json" ], + "delete" : { "parameters" : [ { - "name" : "credential_id", - "in" : "path", "description" : "Credential identifier", + "in" : "path", + "name" : "credential_id", "required" : true, - "type" : "string" + "schema" : { + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/VCRecord" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/HolderModuleResponse" + } + } + }, + "description" : "" } - } - }, - "delete" : { - "tags" : [ "credentials" ], + }, "summary" : "Remove W3C credential from wallet by id", - "produces" : [ "application/json" ], + "tags" : [ "credentials" ] + }, + "get" : { "parameters" : [ { - "name" : "credential_id", - "in" : "path", "description" : "Credential identifier", + "in" : "path", + "name" : "credential_id", "required" : true, - "type" : "string" + "schema" : { + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/HolderModuleResponse" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/VCRecord" + } + } + }, + "description" : "" } - } + }, + "summary" : "Fetch W3C credential from wallet by id", + "tags" : [ "credentials" ] } }, "/credential/{credential_id}" : { - "get" : { - "tags" : [ "credentials" ], - "summary" : "Fetch credential from wallet by id", - "produces" : [ "application/json" ], + "delete" : { "parameters" : [ { - "name" : "credential_id", - "in" : "path", "description" : "Credential identifier", + "in" : "path", + "name" : "credential_id", "required" : true, - "type" : "string" + "schema" : { + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/IndyCredInfo" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/HolderModuleResponse" + } + } + }, + "description" : "" } - } - }, - "delete" : { - "tags" : [ "credentials" ], + }, "summary" : "Remove credential from wallet by id", - "produces" : [ "application/json" ], + "tags" : [ "credentials" ] + }, + "get" : { "parameters" : [ { - "name" : "credential_id", - "in" : "path", "description" : "Credential identifier", + "in" : "path", + "name" : "credential_id", "required" : true, - "type" : "string" + "schema" : { + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/HolderModuleResponse" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/IndyCredInfo" + } + } + }, + "description" : "" } - } + }, + "summary" : "Fetch credential from wallet by id", + "tags" : [ "credentials" ] } }, "/credentials" : { "get" : { - "tags" : [ "credentials" ], - "summary" : "Fetch credentials from wallet", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "count", - "in" : "query", "description" : "Maximum number to retrieve", - "required" : false, - "type" : "string", - "pattern" : "^[1-9][0-9]*$" - }, { - "name" : "start", "in" : "query", - "description" : "Start index", - "required" : false, - "type" : "string", - "pattern" : "^[0-9]*$" + "name" : "count", + "schema" : { + "pattern" : "^[1-9][0-9]*$", + "type" : "string" + } }, { - "name" : "wql", + "description" : "Start index", "in" : "query", + "name" : "start", + "schema" : { + "pattern" : "^[0-9]*$", + "type" : "string" + } + }, { "description" : "(JSON) WQL query", - "required" : false, - "type" : "string", - "pattern" : "^{.*}$" + "in" : "query", + "name" : "wql", + "schema" : { + "pattern" : "^{.*}$", + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/CredInfoList" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/CredInfoList" + } + } + }, + "description" : "" } - } + }, + "summary" : "Fetch credentials from wallet", + "tags" : [ "credentials" ] } }, "/credentials/w3c" : { "post" : { - "tags" : [ "credentials" ], - "summary" : "Fetch W3C credentials from wallet", - "produces" : [ "application/json" ], "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, + "description" : "Maximum number to retrieve", + "in" : "query", + "name" : "count", "schema" : { - "$ref" : "#/definitions/W3CCredentialsListRequest" + "pattern" : "^[1-9][0-9]*$", + "type" : "string" } }, { - "name" : "count", + "description" : "Start index", "in" : "query", - "description" : "Maximum number to retrieve", - "required" : false, - "type" : "string", - "pattern" : "^[1-9][0-9]*$" - }, { "name" : "start", - "in" : "query", - "description" : "Start index", - "required" : false, - "type" : "string", - "pattern" : "^[0-9]*$" + "schema" : { + "pattern" : "^[0-9]*$", + "type" : "string" + } }, { - "name" : "wql", - "in" : "query", "description" : "(JSON) WQL query", - "required" : false, - "type" : "string", - "pattern" : "^{.*}$" + "in" : "query", + "name" : "wql", + "schema" : { + "pattern" : "^{.*}$", + "type" : "string" + } } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/W3CCredentialsListRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/VCRecordList" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/VCRecordList" + } + } + }, + "description" : "" } - } + }, + "summary" : "Fetch W3C credentials from wallet", + "tags" : [ "credentials" ], + "x-codegen-request-body-name" : "body" } }, "/didexchange/create-request" : { "post" : { - "tags" : [ "did-exchange" ], - "summary" : "Create and send a request against public DID's implicit invitation", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "their_public_did", - "in" : "query", "description" : "Qualified public DID to which to request connection", + "in" : "query", + "name" : "their_public_did", "required" : true, - "type" : "string", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$|^did:([a-zA-Z0-9_]+):([a-zA-Z0-9_.%-]+(:[a-zA-Z0-9_.%-]+)*)((;[a-zA-Z0-9_.:%-]+=[a-zA-Z0-9_.:%-]*)*)(\\/[^#?]*)?([?][^#]*)?(\\#.*)?$$" + "schema" : { + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$|^did:([a-zA-Z0-9_]+):([a-zA-Z0-9_.%-]+(:[a-zA-Z0-9_.%-]+)*)((;[a-zA-Z0-9_.:%-]+=[a-zA-Z0-9_.:%-]*)*)(\\/[^#?]*)?([?][^#]*)?(\\#.*)?$$", + "type" : "string" + } }, { - "name" : "mediation_id", + "description" : "Alias for connection", "in" : "query", - "description" : "Identifier for active mediation record to be used", - "required" : false, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + "name" : "alias", + "schema" : { + "type" : "string" + } }, { - "name" : "my_endpoint", + "description" : "Identifier for active mediation record to be used", "in" : "query", - "description" : "My URL endpoint", - "required" : false, - "type" : "string", - "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" + "name" : "mediation_id", + "schema" : { + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", + "type" : "string" + } }, { - "name" : "my_label", + "description" : "My URL endpoint", "in" : "query", - "description" : "Label for connection request", - "required" : false, - "type" : "string" + "name" : "my_endpoint", + "schema" : { + "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$", + "type" : "string" + } }, { - "name" : "use_public_did", + "description" : "Label for connection request", "in" : "query", + "name" : "my_label", + "schema" : { + "type" : "string" + } + }, { "description" : "Use public DID for this connection", - "required" : false, - "type" : "boolean" + "in" : "query", + "name" : "use_public_did", + "schema" : { + "type" : "boolean" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/ConnRecord" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ConnRecord" + } + } + }, + "description" : "" } - } + }, + "summary" : "Create and send a request against public DID's implicit invitation", + "tags" : [ "did-exchange" ] } }, "/didexchange/receive-request" : { "post" : { - "tags" : [ "did-exchange" ], - "summary" : "Receive request against public DID's implicit invitation", - "produces" : [ "application/json" ], "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, + "description" : "Alias for connection", + "in" : "query", + "name" : "alias", "schema" : { - "$ref" : "#/definitions/DIDXRequest" + "type" : "string" } }, { - "name" : "alias", + "description" : "Auto-accept connection (defaults to configuration)", "in" : "query", - "description" : "Alias for connection", - "required" : false, - "type" : "string" - }, { "name" : "auto_accept", - "in" : "query", - "description" : "Auto-accept connection (defaults to configuration)", - "required" : false, - "type" : "boolean" + "schema" : { + "type" : "boolean" + } }, { - "name" : "mediation_id", - "in" : "query", "description" : "Identifier for active mediation record to be used", - "required" : false, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - }, { - "name" : "my_endpoint", "in" : "query", + "name" : "mediation_id", + "schema" : { + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", + "type" : "string" + } + }, { "description" : "My URL endpoint", - "required" : false, - "type" : "string", - "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" + "in" : "query", + "name" : "my_endpoint", + "schema" : { + "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$", + "type" : "string" + } } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/DIDXRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/ConnRecord" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ConnRecord" + } + } + }, + "description" : "" } - } + }, + "summary" : "Receive request against public DID's implicit invitation", + "tags" : [ "did-exchange" ], + "x-codegen-request-body-name" : "body" } }, "/didexchange/{conn_id}/accept-invitation" : { "post" : { - "tags" : [ "did-exchange" ], - "summary" : "Accept a stored connection invitation", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "conn_id", - "in" : "path", "description" : "Connection identifier", + "in" : "path", + "name" : "conn_id", "required" : true, - "type" : "string" + "schema" : { + "type" : "string" + } }, { - "name" : "my_endpoint", - "in" : "query", "description" : "My URL endpoint", - "required" : false, - "type" : "string", - "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" - }, { - "name" : "my_label", "in" : "query", + "name" : "my_endpoint", + "schema" : { + "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$", + "type" : "string" + } + }, { "description" : "Label for connection request", - "required" : false, - "type" : "string" + "in" : "query", + "name" : "my_label", + "schema" : { + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/ConnRecord" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ConnRecord" + } + } + }, + "description" : "" } - } + }, + "summary" : "Accept a stored connection invitation", + "tags" : [ "did-exchange" ] } }, "/didexchange/{conn_id}/accept-request" : { "post" : { - "tags" : [ "did-exchange" ], - "summary" : "Accept a stored connection request", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "conn_id", - "in" : "path", "description" : "Connection identifier", + "in" : "path", + "name" : "conn_id", "required" : true, - "type" : "string" + "schema" : { + "type" : "string" + } }, { - "name" : "mediation_id", - "in" : "query", "description" : "Identifier for active mediation record to be used", - "required" : false, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - }, { - "name" : "my_endpoint", "in" : "query", + "name" : "mediation_id", + "schema" : { + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", + "type" : "string" + } + }, { "description" : "My URL endpoint", - "required" : false, - "type" : "string", - "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" + "in" : "query", + "name" : "my_endpoint", + "schema" : { + "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$", + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/ConnRecord" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ConnRecord" + } + } + }, + "description" : "" } - } + }, + "summary" : "Accept a stored connection request", + "tags" : [ "did-exchange" ] } }, - "/features" : { + "/discover-features-2.0/queries" : { "get" : { - "tags" : [ "server" ], - "summary" : "Query supported features", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "query", + "description" : "Connection identifier, if none specified, then the query will provide features for this agent.", "in" : "query", - "description" : "Query", - "required" : false, - "type" : "string" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/QueryResult" - } + "name" : "connection_id", + "schema" : { + "type" : "string" } - } - } - }, - "/issue-credential-2.0/create" : { - "post" : { - "tags" : [ "issue-credential v2.0" ], - "summary" : "Create credential from attribute values", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, + }, { + "description" : "Goal-code feature-type query", + "in" : "query", + "name" : "query_goal_code", + "schema" : { + "type" : "string" + } + }, { + "description" : "Protocol feature-type query", + "in" : "query", + "name" : "query_protocol", "schema" : { - "$ref" : "#/definitions/V20IssueCredSchemaCore" + "type" : "string" } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20CredExRecord" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V20DiscoveryExchangeResult" + } + } + }, + "description" : "" } - } + }, + "summary" : "Query supported features", + "tags" : [ "discover-features v2.0" ] } }, - "/issue-credential-2.0/create-offer" : { - "post" : { - "tags" : [ "issue-credential v2.0" ], - "summary" : "Create a credential offer, independent of any proposal or connection", - "produces" : [ "application/json" ], + "/discover-features-2.0/records" : { + "get" : { "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, + "description" : "Connection identifier", + "in" : "query", + "name" : "connection_id", "schema" : { - "$ref" : "#/definitions/V20CredOfferConnFreeRequest" + "type" : "string" } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20CredExRecord" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V20DiscoveryExchangeListResult" + } + } + }, + "description" : "" } - } + }, + "summary" : "Discover Features v2.0 records", + "tags" : [ "discover-features v2.0" ] } }, - "/issue-credential-2.0/records" : { + "/discover-features/query" : { "get" : { - "tags" : [ "issue-credential v2.0" ], - "summary" : "Fetch all credential exchange records", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "connection_id", - "in" : "query", - "description" : "Connection identifier", - "required" : false, - "type" : "string", - "format" : "uuid" - }, { - "name" : "role", + "description" : "Comment", "in" : "query", - "description" : "Role assigned in credential exchange", - "required" : false, - "type" : "string", - "enum" : [ "issuer", "holder" ] + "name" : "comment", + "schema" : { + "type" : "string" + } }, { - "name" : "state", + "description" : "Connection identifier, if none specified, then the query will provide features for this agent.", "in" : "query", - "description" : "Credential exchange state", - "required" : false, - "type" : "string", - "enum" : [ "proposal-sent", "proposal-received", "offer-sent", "offer-received", "request-sent", "request-received", "credential-issued", "credential-received", "done" ] + "name" : "connection_id", + "schema" : { + "type" : "string" + } }, { - "name" : "thread_id", + "description" : "Protocol feature query", "in" : "query", - "description" : "Thread identifier", - "required" : false, - "type" : "string", - "format" : "uuid" + "name" : "query", + "schema" : { + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20CredExRecordListResult" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V10DiscoveryRecord" + } + } + }, + "description" : "" } - } + }, + "summary" : "Query supported features", + "tags" : [ "discover-features" ] } }, - "/issue-credential-2.0/records/{cred_ex_id}" : { + "/discover-features/records" : { "get" : { - "tags" : [ "issue-credential v2.0" ], - "summary" : "Fetch a single credential exchange record", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "cred_ex_id", - "in" : "path", - "description" : "Credential exchange identifier", - "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + "description" : "Connection identifier", + "in" : "query", + "name" : "connection_id", + "schema" : { + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20CredExRecordDetail" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V10DiscoveryExchangeListResult" + } + } + }, + "description" : "" } - } - }, - "delete" : { - "tags" : [ "issue-credential v2.0" ], - "summary" : "Remove an existing credential exchange record", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "cred_ex_id", - "in" : "path", - "description" : "Credential exchange identifier", - "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - } ], + }, + "summary" : "Discover Features records", + "tags" : [ "discover-features" ] + } + }, + "/issue-credential-2.0/create" : { + "post" : { + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/V20IssueCredSchemaCore" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20IssueCredentialModuleResponse" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V20CredExRecord" + } + } + }, + "description" : "" } - } + }, + "summary" : "Create a credential record without sending (generally for use with Out-Of-Band)", + "tags" : [ "issue-credential v2.0" ], + "x-codegen-request-body-name" : "body" } }, - "/issue-credential-2.0/records/{cred_ex_id}/issue" : { + "/issue-credential-2.0/create-offer" : { "post" : { - "tags" : [ "issue-credential v2.0" ], - "summary" : "Send holder a credential", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V20CredIssueRequest" - } - }, { - "name" : "cred_ex_id", - "in" : "path", - "description" : "Credential exchange identifier", - "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/V20CredOfferConnFreeRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20CredExRecordDetail" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V20CredExRecord" + } + } + }, + "description" : "" } - } + }, + "summary" : "Create a credential offer, independent of any proposal or connection", + "tags" : [ "issue-credential v2.0" ], + "x-codegen-request-body-name" : "body" } }, - "/issue-credential-2.0/records/{cred_ex_id}/problem-report" : { - "post" : { - "tags" : [ "issue-credential v2.0" ], - "summary" : "Send a problem report for credential exchange", - "produces" : [ "application/json" ], + "/issue-credential-2.0/records" : { + "get" : { "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, + "description" : "Connection identifier", + "in" : "query", + "name" : "connection_id", "schema" : { - "$ref" : "#/definitions/V20CredIssueProblemReportRequest" + "format" : "uuid", + "type" : "string" } }, { - "name" : "cred_ex_id", - "in" : "path", - "description" : "Credential exchange identifier", - "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20IssueCredentialModuleResponse" - } + "description" : "Role assigned in credential exchange", + "in" : "query", + "name" : "role", + "schema" : { + "enum" : [ "issuer", "holder" ], + "type" : "string" } - } - } - }, - "/issue-credential-2.0/records/{cred_ex_id}/send-offer" : { - "post" : { - "tags" : [ "issue-credential v2.0" ], - "summary" : "Send holder a credential offer in reference to a proposal with preview", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, + }, { + "description" : "Credential exchange state", + "in" : "query", + "name" : "state", "schema" : { - "$ref" : "#/definitions/V20CredBoundOfferRequest" + "enum" : [ "proposal-sent", "proposal-received", "offer-sent", "offer-received", "request-sent", "request-received", "credential-issued", "credential-received", "done", "credential-revoked", "abandoned" ], + "type" : "string" } }, { - "name" : "cred_ex_id", - "in" : "path", - "description" : "Credential exchange identifier", - "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + "description" : "Thread identifier", + "in" : "query", + "name" : "thread_id", + "schema" : { + "format" : "uuid", + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20CredExRecord" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V20CredExRecordListResult" + } + } + }, + "description" : "" } - } + }, + "summary" : "Fetch all credential exchange records", + "tags" : [ "issue-credential v2.0" ] } }, - "/issue-credential-2.0/records/{cred_ex_id}/send-request" : { - "post" : { - "tags" : [ "issue-credential v2.0" ], - "summary" : "Send issuer a credential request", - "produces" : [ "application/json" ], + "/issue-credential-2.0/records/{cred_ex_id}" : { + "delete" : { "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V20CredRequestRequest" - } - }, { - "name" : "cred_ex_id", - "in" : "path", "description" : "Credential exchange identifier", + "in" : "path", + "name" : "cred_ex_id", "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + "schema" : { + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20CredExRecord" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V20IssueCredentialModuleResponse" + } + } + }, + "description" : "" } - } - } - }, - "/issue-credential-2.0/records/{cred_ex_id}/store" : { - "post" : { - "tags" : [ "issue-credential v2.0" ], - "summary" : "Store a received credential", - "produces" : [ "application/json" ], + }, + "summary" : "Remove an existing credential exchange record", + "tags" : [ "issue-credential v2.0" ] + }, + "get" : { "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V20CredStoreRequest" - } - }, { - "name" : "cred_ex_id", - "in" : "path", "description" : "Credential exchange identifier", + "in" : "path", + "name" : "cred_ex_id", "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + "schema" : { + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20CredExRecordDetail" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V20CredExRecordDetail" + } + } + }, + "description" : "" } - } + }, + "summary" : "Fetch a single credential exchange record", + "tags" : [ "issue-credential v2.0" ] } }, - "/issue-credential-2.0/send" : { + "/issue-credential-2.0/records/{cred_ex_id}/issue" : { "post" : { - "tags" : [ "issue-credential v2.0" ], - "summary" : "Send holder a credential, automating entire flow", - "produces" : [ "application/json" ], "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, + "description" : "Credential exchange identifier", + "in" : "path", + "name" : "cred_ex_id", + "required" : true, "schema" : { - "$ref" : "#/definitions/V20CredExFree" + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", + "type" : "string" } } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/V20CredIssueRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20CredExRecord" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V20CredExRecordDetail" + } + } + }, + "description" : "" } - } + }, + "summary" : "Send holder a credential", + "tags" : [ "issue-credential v2.0" ], + "x-codegen-request-body-name" : "body" } }, - "/issue-credential-2.0/send-offer" : { + "/issue-credential-2.0/records/{cred_ex_id}/problem-report" : { "post" : { - "tags" : [ "issue-credential v2.0" ], - "summary" : "Send holder a credential offer, independent of any proposal", - "produces" : [ "application/json" ], "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, + "description" : "Credential exchange identifier", + "in" : "path", + "name" : "cred_ex_id", + "required" : true, "schema" : { - "$ref" : "#/definitions/V20CredOfferRequest" + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", + "type" : "string" } } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/V20CredIssueProblemReportRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20CredExRecord" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V20IssueCredentialModuleResponse" + } + } + }, + "description" : "" } - } + }, + "summary" : "Send a problem report for credential exchange", + "tags" : [ "issue-credential v2.0" ], + "x-codegen-request-body-name" : "body" } }, - "/issue-credential-2.0/send-proposal" : { + "/issue-credential-2.0/records/{cred_ex_id}/send-offer" : { "post" : { - "tags" : [ "issue-credential v2.0" ], - "summary" : "Send issuer a credential proposal", - "produces" : [ "application/json" ], "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, + "description" : "Credential exchange identifier", + "in" : "path", + "name" : "cred_ex_id", + "required" : true, "schema" : { - "$ref" : "#/definitions/V20CredExFree" + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", + "type" : "string" } } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/V20CredBoundOfferRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20CredExRecord" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V20CredExRecord" + } + } + }, + "description" : "" } - } + }, + "summary" : "Send holder a credential offer in reference to a proposal with preview", + "tags" : [ "issue-credential v2.0" ], + "x-codegen-request-body-name" : "body" } }, - "/issue-credential-2.0/send-request" : { + "/issue-credential-2.0/records/{cred_ex_id}/send-request" : { "post" : { - "tags" : [ "issue-credential v2.0" ], - "summary" : "Send issuer a credential request not bound to an existing thread. Indy credentials cannot start at a request", - "produces" : [ "application/json" ], "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, + "description" : "Credential exchange identifier", + "in" : "path", + "name" : "cred_ex_id", + "required" : true, "schema" : { - "$ref" : "#/definitions/V20CredRequestFree" + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", + "type" : "string" } } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/V20CredRequestRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20CredExRecord" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V20CredExRecord" + } + } + }, + "description" : "" } - } + }, + "summary" : "Send issuer a credential request", + "tags" : [ "issue-credential v2.0" ], + "x-codegen-request-body-name" : "body" } }, - "/issue-credential/create" : { + "/issue-credential-2.0/records/{cred_ex_id}/store" : { "post" : { - "tags" : [ "issue-credential v1.0" ], - "summary" : "Send holder a credential, automating entire flow", - "produces" : [ "application/json" ], "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, + "description" : "Credential exchange identifier", + "in" : "path", + "name" : "cred_ex_id", + "required" : true, "schema" : { - "$ref" : "#/definitions/V10CredentialCreate" + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", + "type" : "string" } } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/V20CredStoreRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V10CredentialExchange" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V20CredExRecordDetail" + } + } + }, + "description" : "" } - } + }, + "summary" : "Store a received credential", + "tags" : [ "issue-credential v2.0" ], + "x-codegen-request-body-name" : "body" } }, - "/issue-credential/create-offer" : { + "/issue-credential-2.0/send" : { "post" : { - "tags" : [ "issue-credential v1.0" ], - "summary" : "Create a credential offer, independent of any proposal or connection", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V10CredentialConnFreeOfferRequest" - } - } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/V20CredExFree" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V10CredentialExchange" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V20CredExRecord" + } + } + }, + "description" : "" } - } + }, + "summary" : "Send holder a credential, automating entire flow", + "tags" : [ "issue-credential v2.0" ], + "x-codegen-request-body-name" : "body" } }, - "/issue-credential/records" : { - "get" : { - "tags" : [ "issue-credential v1.0" ], - "summary" : "Fetch all credential exchange records", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "connection_id", - "in" : "query", + "/issue-credential-2.0/send-offer" : { + "post" : { + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/V20CredOfferRequest" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V20CredExRecord" + } + } + }, + "description" : "" + } + }, + "summary" : "Send holder a credential offer, independent of any proposal", + "tags" : [ "issue-credential v2.0" ], + "x-codegen-request-body-name" : "body" + } + }, + "/issue-credential-2.0/send-proposal" : { + "post" : { + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/V20CredExFree" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V20CredExRecord" + } + } + }, + "description" : "" + } + }, + "summary" : "Send issuer a credential proposal", + "tags" : [ "issue-credential v2.0" ], + "x-codegen-request-body-name" : "body" + } + }, + "/issue-credential-2.0/send-request" : { + "post" : { + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/V20CredRequestFree" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V20CredExRecord" + } + } + }, + "description" : "" + } + }, + "summary" : "Send issuer a credential request not bound to an existing thread. Indy credentials cannot start at a request", + "tags" : [ "issue-credential v2.0" ], + "x-codegen-request-body-name" : "body" + } + }, + "/issue-credential/create" : { + "post" : { + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/V10CredentialCreate" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V10CredentialExchange" + } + } + }, + "description" : "" + } + }, + "summary" : "Create a credential record without sending (generally for use with Out-Of-Band)", + "tags" : [ "issue-credential v1.0" ], + "x-codegen-request-body-name" : "body" + } + }, + "/issue-credential/create-offer" : { + "post" : { + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/V10CredentialConnFreeOfferRequest" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V10CredentialExchange" + } + } + }, + "description" : "" + } + }, + "summary" : "Create a credential offer, independent of any proposal or connection", + "tags" : [ "issue-credential v1.0" ], + "x-codegen-request-body-name" : "body" + } + }, + "/issue-credential/records" : { + "get" : { + "parameters" : [ { "description" : "Connection identifier", - "required" : false, - "type" : "string", - "format" : "uuid" - }, { - "name" : "role", "in" : "query", - "description" : "Role assigned in credential exchange", - "required" : false, - "type" : "string", - "enum" : [ "issuer", "holder" ] + "name" : "connection_id", + "schema" : { + "format" : "uuid", + "type" : "string" + } }, { - "name" : "state", + "description" : "Role assigned in credential exchange", "in" : "query", - "description" : "Credential exchange state", - "required" : false, - "type" : "string", - "enum" : [ "proposal_sent", "proposal_received", "offer_sent", "offer_received", "request_sent", "request_received", "credential_issued", "credential_received", "credential_acked" ] + "name" : "role", + "schema" : { + "enum" : [ "issuer", "holder" ], + "type" : "string" + } }, { - "name" : "thread_id", + "description" : "Credential exchange state", "in" : "query", + "name" : "state", + "schema" : { + "enum" : [ "proposal_sent", "proposal_received", "offer_sent", "offer_received", "request_sent", "request_received", "credential_issued", "credential_received", "credential_acked", "credential_revoked", "abandoned" ], + "type" : "string" + } + }, { "description" : "Thread identifier", - "required" : false, - "type" : "string", - "format" : "uuid" + "in" : "query", + "name" : "thread_id", + "schema" : { + "format" : "uuid", + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V10CredentialExchangeListResult" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V10CredentialExchangeListResult" + } + } + }, + "description" : "" } - } + }, + "summary" : "Fetch all credential exchange records", + "tags" : [ "issue-credential v1.0" ] } }, "/issue-credential/records/{cred_ex_id}" : { - "get" : { - "tags" : [ "issue-credential v1.0" ], - "summary" : "Fetch a single credential exchange record", - "produces" : [ "application/json" ], + "delete" : { "parameters" : [ { - "name" : "cred_ex_id", - "in" : "path", "description" : "Credential exchange identifier", + "in" : "path", + "name" : "cred_ex_id", "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + "schema" : { + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V10CredentialExchange" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/IssueCredentialModuleResponse" + } + } + }, + "description" : "" } - } - }, - "delete" : { - "tags" : [ "issue-credential v1.0" ], + }, "summary" : "Remove an existing credential exchange record", - "produces" : [ "application/json" ], + "tags" : [ "issue-credential v1.0" ] + }, + "get" : { "parameters" : [ { - "name" : "cred_ex_id", - "in" : "path", "description" : "Credential exchange identifier", + "in" : "path", + "name" : "cred_ex_id", "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + "schema" : { + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/IssueCredentialModuleResponse" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V10CredentialExchange" + } + } + }, + "description" : "" } - } + }, + "summary" : "Fetch a single credential exchange record", + "tags" : [ "issue-credential v1.0" ] } }, "/issue-credential/records/{cred_ex_id}/issue" : { "post" : { - "tags" : [ "issue-credential v1.0" ], - "summary" : "Send holder a credential", - "produces" : [ "application/json" ], "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V10CredentialIssueRequest" - } - }, { - "name" : "cred_ex_id", - "in" : "path", "description" : "Credential exchange identifier", + "in" : "path", + "name" : "cred_ex_id", "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + "schema" : { + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", + "type" : "string" + } } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/V10CredentialIssueRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V10CredentialExchange" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V10CredentialExchange" + } + } + }, + "description" : "" } - } + }, + "summary" : "Send holder a credential", + "tags" : [ "issue-credential v1.0" ], + "x-codegen-request-body-name" : "body" } }, "/issue-credential/records/{cred_ex_id}/problem-report" : { "post" : { - "tags" : [ "issue-credential v1.0" ], - "summary" : "Send a problem report for credential exchange", - "produces" : [ "application/json" ], "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V10CredentialProblemReportRequest" - } - }, { - "name" : "cred_ex_id", - "in" : "path", "description" : "Credential exchange identifier", + "in" : "path", + "name" : "cred_ex_id", "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + "schema" : { + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", + "type" : "string" + } } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/V10CredentialProblemReportRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/IssueCredentialModuleResponse" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/IssueCredentialModuleResponse" + } + } + }, + "description" : "" } - } + }, + "summary" : "Send a problem report for credential exchange", + "tags" : [ "issue-credential v1.0" ], + "x-codegen-request-body-name" : "body" } }, "/issue-credential/records/{cred_ex_id}/send-offer" : { "post" : { - "tags" : [ "issue-credential v1.0" ], - "summary" : "Send holder a credential offer in reference to a proposal with preview", - "produces" : [ "application/json" ], "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V10CredentialBoundOfferRequest" - } - }, { - "name" : "cred_ex_id", - "in" : "path", "description" : "Credential exchange identifier", + "in" : "path", + "name" : "cred_ex_id", "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + "schema" : { + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", + "type" : "string" + } } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/V10CredentialBoundOfferRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V10CredentialExchange" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V10CredentialExchange" + } + } + }, + "description" : "" } - } + }, + "summary" : "Send holder a credential offer in reference to a proposal with preview", + "tags" : [ "issue-credential v1.0" ], + "x-codegen-request-body-name" : "body" } }, "/issue-credential/records/{cred_ex_id}/send-request" : { "post" : { - "tags" : [ "issue-credential v1.0" ], - "summary" : "Send issuer a credential request", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "cred_ex_id", - "in" : "path", "description" : "Credential exchange identifier", + "in" : "path", + "name" : "cred_ex_id", "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + "schema" : { + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V10CredentialExchange" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V10CredentialExchange" + } + } + }, + "description" : "" } - } + }, + "summary" : "Send issuer a credential request", + "tags" : [ "issue-credential v1.0" ] } }, "/issue-credential/records/{cred_ex_id}/store" : { "post" : { - "tags" : [ "issue-credential v1.0" ], - "summary" : "Store a received credential", - "produces" : [ "application/json" ], "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V10CredentialStoreRequest" - } - }, { - "name" : "cred_ex_id", - "in" : "path", "description" : "Credential exchange identifier", + "in" : "path", + "name" : "cred_ex_id", "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + "schema" : { + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", + "type" : "string" + } } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/V10CredentialStoreRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V10CredentialExchange" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V10CredentialExchange" + } + } + }, + "description" : "" } - } + }, + "summary" : "Store a received credential", + "tags" : [ "issue-credential v1.0" ], + "x-codegen-request-body-name" : "body" } }, "/issue-credential/send" : { "post" : { - "tags" : [ "issue-credential v1.0" ], - "summary" : "Send holder a credential, automating entire flow", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V10CredentialProposalRequestMand" - } - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V10CredentialExchange" + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/V10CredentialProposalRequestMand" + } } + }, + "required" : false + }, + "responses" : { + "200" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V10CredentialExchange" + } + } + }, + "description" : "" } - } + }, + "summary" : "Send holder a credential, automating entire flow", + "tags" : [ "issue-credential v1.0" ], + "x-codegen-request-body-name" : "body" } }, "/issue-credential/send-offer" : { "post" : { - "tags" : [ "issue-credential v1.0" ], - "summary" : "Send holder a credential offer, independent of any proposal", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V10CredentialFreeOfferRequest" - } - } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/V10CredentialFreeOfferRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V10CredentialExchange" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V10CredentialExchange" + } + } + }, + "description" : "" } - } + }, + "summary" : "Send holder a credential offer, independent of any proposal", + "tags" : [ "issue-credential v1.0" ], + "x-codegen-request-body-name" : "body" } }, "/issue-credential/send-proposal" : { "post" : { - "tags" : [ "issue-credential v1.0" ], - "summary" : "Send issuer a credential proposal", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V10CredentialProposalRequestOpt" - } - } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/V10CredentialProposalRequestOpt" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V10CredentialExchange" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V10CredentialExchange" + } + } + }, + "description" : "" } - } + }, + "summary" : "Send issuer a credential proposal", + "tags" : [ "issue-credential v1.0" ], + "x-codegen-request-body-name" : "body" } }, "/jsonld/sign" : { "post" : { - "tags" : [ "jsonld" ], - "summary" : "Sign a JSON-LD structure and return it", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/SignRequest" - } - } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/SignRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/SignResponse" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SignResponse" + } + } + }, + "description" : "" } - } + }, + "summary" : "Sign a JSON-LD structure and return it", + "tags" : [ "jsonld" ], + "x-codegen-request-body-name" : "body" } }, "/jsonld/verify" : { "post" : { - "tags" : [ "jsonld" ], - "summary" : "Verify a JSON-LD structure.", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/VerifyRequest" - } - } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/VerifyRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/VerifyResponse" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/VerifyResponse" + } + } + }, + "description" : "" } - } + }, + "summary" : "Verify a JSON-LD structure.", + "tags" : [ "jsonld" ], + "x-codegen-request-body-name" : "body" } }, "/ledger/did-endpoint" : { "get" : { - "tags" : [ "ledger" ], - "summary" : "Get the endpoint for a DID from the ledger.", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "did", - "in" : "query", "description" : "DID of interest", + "in" : "query", + "name" : "did", "required" : true, - "type" : "string", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + "schema" : { + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$", + "type" : "string" + } }, { - "name" : "endpoint_type", - "in" : "query", "description" : "Endpoint type of interest (default 'Endpoint')", - "required" : false, - "type" : "string", - "enum" : [ "Endpoint", "Profile", "LinkedDomains" ] + "in" : "query", + "name" : "endpoint_type", + "schema" : { + "enum" : [ "Endpoint", "Profile", "LinkedDomains" ], + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/GetDIDEndpointResponse" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/GetDIDEndpointResponse" + } + } + }, + "description" : "" } - } + }, + "summary" : "Get the endpoint for a DID from the ledger.", + "tags" : [ "ledger" ] } }, "/ledger/did-verkey" : { "get" : { - "tags" : [ "ledger" ], - "summary" : "Get the verkey for a DID from the ledger.", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "did", - "in" : "query", "description" : "DID of interest", + "in" : "query", + "name" : "did", "required" : true, - "type" : "string", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + "schema" : { + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$", + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/GetDIDVerkeyResponse" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/GetDIDVerkeyResponse" + } + } + }, + "description" : "" } - } + }, + "summary" : "Get the verkey for a DID from the ledger.", + "tags" : [ "ledger" ] } }, "/ledger/get-nym-role" : { "get" : { - "tags" : [ "ledger" ], - "summary" : "Get the role from the NYM registration of a public DID.", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "did", - "in" : "query", "description" : "DID of interest", + "in" : "query", + "name" : "did", "required" : true, - "type" : "string", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + "schema" : { + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$", + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/GetNymRoleResponse" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/GetNymRoleResponse" + } + } + }, + "description" : "" + } + }, + "summary" : "Get the role from the NYM registration of a public DID.", + "tags" : [ "ledger" ] + } + }, + "/ledger/multiple/config" : { + "get" : { + "responses" : { + "200" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/LedgerConfigList" + } + } + }, + "description" : "" + } + }, + "summary" : "Fetch the multiple ledger configuration currently in use", + "tags" : [ "ledger" ] + } + }, + "/ledger/multiple/get-write-ledger" : { + "get" : { + "responses" : { + "200" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/WriteLedgerRequest" + } + } + }, + "description" : "" } - } + }, + "summary" : "Fetch the current write ledger", + "tags" : [ "ledger" ] } }, "/ledger/register-nym" : { "post" : { - "tags" : [ "ledger" ], - "summary" : "Send a NYM registration to the ledger.", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "did", - "in" : "query", "description" : "DID to register", + "in" : "query", + "name" : "did", "required" : true, - "type" : "string", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + "schema" : { + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$", + "type" : "string" + } }, { - "name" : "verkey", - "in" : "query", "description" : "Verification key", + "in" : "query", + "name" : "verkey", "required" : true, - "type" : "string", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" + "schema" : { + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$", + "type" : "string" + } }, { + "description" : "Alias", + "in" : "query", "name" : "alias", + "schema" : { + "type" : "string" + } + }, { + "description" : "Connection identifier", "in" : "query", - "description" : "Alias", - "required" : false, - "type" : "string" + "name" : "conn_id", + "schema" : { + "type" : "string" + } }, { - "name" : "role", + "description" : "Create Transaction For Endorser's signature", "in" : "query", + "name" : "create_transaction_for_endorser", + "schema" : { + "type" : "boolean" + } + }, { "description" : "Role", - "required" : false, - "type" : "string", - "enum" : [ "STEWARD", "TRUSTEE", "ENDORSER", "NETWORK_MONITOR", "reset" ] + "in" : "query", + "name" : "role", + "schema" : { + "enum" : [ "STEWARD", "TRUSTEE", "ENDORSER", "NETWORK_MONITOR", "reset" ], + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/RegisterLedgerNymResponse" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/TxnOrRegisterLedgerNymResponse" + } + } + }, + "description" : "" } - } + }, + "summary" : "Send a NYM registration to the ledger.", + "tags" : [ "ledger" ] } }, "/ledger/rotate-public-did-keypair" : { "patch" : { - "tags" : [ "ledger" ], - "summary" : "Rotate key pair for public DID.", - "produces" : [ "application/json" ], - "parameters" : [ ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/LedgerModulesResult" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/LedgerModulesResult" + } + } + }, + "description" : "" } - } + }, + "summary" : "Rotate key pair for public DID.", + "tags" : [ "ledger" ] } }, "/ledger/taa" : { "get" : { - "tags" : [ "ledger" ], - "summary" : "Fetch the current transaction author agreement, if any", - "produces" : [ "application/json" ], - "parameters" : [ ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/TAAResult" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/TAAResult" + } + } + }, + "description" : "" } - } + }, + "summary" : "Fetch the current transaction author agreement, if any", + "tags" : [ "ledger" ] } }, "/ledger/taa/accept" : { "post" : { - "tags" : [ "ledger" ], - "summary" : "Accept the transaction author agreement", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/TAAAccept" - } - } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/TAAAccept" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/LedgerModulesResult" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/LedgerModulesResult" + } + } + }, + "description" : "" } - } + }, + "summary" : "Accept the transaction author agreement", + "tags" : [ "ledger" ], + "x-codegen-request-body-name" : "body" } }, "/mediation/default-mediator" : { - "get" : { - "tags" : [ "mediation" ], - "summary" : "Get default mediator", - "produces" : [ "application/json" ], - "parameters" : [ ], + "delete" : { "responses" : { - "200" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/MediationRecord" - } + "201" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/MediationRecord" + } + } + }, + "description" : "" } - } - }, - "delete" : { - "tags" : [ "mediation" ], + }, "summary" : "Clear default mediator", - "produces" : [ "application/json" ], - "parameters" : [ ], + "tags" : [ "mediation" ] + }, + "get" : { "responses" : { - "201" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/MediationRecord" - } + "200" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/MediationRecord" + } + } + }, + "description" : "" } - } + }, + "summary" : "Get default mediator", + "tags" : [ "mediation" ] } }, "/mediation/keylists" : { "get" : { - "tags" : [ "mediation" ], - "summary" : "Retrieve keylists by connection or role", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "conn_id", - "in" : "query", "description" : "Connection identifier (optional)", - "required" : false, - "type" : "string", - "format" : "uuid" - }, { - "name" : "role", "in" : "query", + "name" : "conn_id", + "schema" : { + "format" : "uuid", + "type" : "string" + } + }, { "description" : "Filer on role, 'client' for keys mediated by other agents, 'server' for keys mediated by this agent", - "required" : false, - "type" : "string", - "default" : "server", - "enum" : [ "client", "server" ] + "in" : "query", + "name" : "role", + "schema" : { + "default" : "server", + "enum" : [ "client", "server" ], + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/Keylist" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/Keylist" + } + } + }, + "description" : "" } - } + }, + "summary" : "Retrieve keylists by connection or role", + "tags" : [ "mediation" ] } }, "/mediation/keylists/{mediation_id}/send-keylist-query" : { "post" : { - "tags" : [ "mediation" ], - "summary" : "Send keylist query to mediator", - "produces" : [ "application/json" ], "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/KeylistQueryFilterRequest" - } - }, { - "name" : "mediation_id", - "in" : "path", "description" : "Mediation record identifier", + "in" : "path", + "name" : "mediation_id", "required" : true, - "type" : "string", - "format" : "uuid" + "schema" : { + "format" : "uuid", + "type" : "string" + } }, { - "name" : "paginate_limit", - "in" : "query", "description" : "limit number of results", - "required" : false, - "type" : "integer", - "default" : -1, - "format" : "int32" - }, { - "name" : "paginate_offset", "in" : "query", + "name" : "paginate_limit", + "schema" : { + "default" : -1, + "format" : "int32", + "type" : "integer" + } + }, { "description" : "offset to use in pagination", - "required" : false, - "type" : "integer", - "default" : 0, - "format" : "int32" + "in" : "query", + "name" : "paginate_offset", + "schema" : { + "default" : 0, + "format" : "int32", + "type" : "integer" + } } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/KeylistQueryFilterRequest" + } + } + }, + "required" : false + }, "responses" : { "201" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/KeylistQuery" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/KeylistQuery" + } + } + }, + "description" : "" } - } + }, + "summary" : "Send keylist query to mediator", + "tags" : [ "mediation" ], + "x-codegen-request-body-name" : "body" } }, "/mediation/keylists/{mediation_id}/send-keylist-update" : { "post" : { - "tags" : [ "mediation" ], - "summary" : "Send keylist update to mediator", - "produces" : [ "application/json" ], "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/KeylistUpdateRequest" - } - }, { - "name" : "mediation_id", - "in" : "path", "description" : "Mediation record identifier", + "in" : "path", + "name" : "mediation_id", "required" : true, - "type" : "string", - "format" : "uuid" + "schema" : { + "format" : "uuid", + "type" : "string" + } } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/KeylistUpdateRequest" + } + } + }, + "required" : false + }, "responses" : { "201" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/KeylistUpdate" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/KeylistUpdate" + } + } + }, + "description" : "" } - } + }, + "summary" : "Send keylist update to mediator", + "tags" : [ "mediation" ], + "x-codegen-request-body-name" : "body" } }, "/mediation/request/{conn_id}" : { "post" : { - "tags" : [ "mediation" ], - "summary" : "Request mediation from connection", - "produces" : [ "application/json" ], "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/MediationCreateRequest" - } - }, { - "name" : "conn_id", - "in" : "path", "description" : "Connection identifier", + "in" : "path", + "name" : "conn_id", "required" : true, - "type" : "string" + "schema" : { + "type" : "string" + } } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/MediationCreateRequest" + } + } + }, + "required" : false + }, "responses" : { "201" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/MediationRecord" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/MediationRecord" + } + } + }, + "description" : "" } - } + }, + "summary" : "Request mediation from connection", + "tags" : [ "mediation" ], + "x-codegen-request-body-name" : "body" } }, "/mediation/requests" : { "get" : { - "tags" : [ "mediation" ], - "summary" : "Query mediation requests, returns list of all mediation records", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "conn_id", - "in" : "query", "description" : "Connection identifier (optional)", - "required" : false, - "type" : "string", - "format" : "uuid" - }, { - "name" : "mediator_terms", "in" : "query", - "description" : "List of mediator rules for recipient", - "required" : false, - "type" : "array", - "items" : { + "name" : "conn_id", + "schema" : { + "format" : "uuid", "type" : "string" - }, - "collectionFormat" : "multi" + } }, { - "name" : "recipient_terms", + "description" : "List of mediator rules for recipient", + "explode" : true, "in" : "query", - "description" : "List of recipient rules for mediation", - "required" : false, - "type" : "array", - "items" : { - "type" : "string" + "name" : "mediator_terms", + "schema" : { + "items" : { + "description" : "Indicate terms to which the mediator requires the recipient to agree", + "type" : "string" + }, + "type" : "array" }, - "collectionFormat" : "multi" + "style" : "form" }, { - "name" : "state", + "description" : "List of recipient rules for mediation", + "explode" : true, "in" : "query", + "name" : "recipient_terms", + "schema" : { + "items" : { + "description" : "Indicate terms to which the recipient requires the mediator to agree", + "type" : "string" + }, + "type" : "array" + }, + "style" : "form" + }, { "description" : "Mediation state (optional)", - "required" : false, - "type" : "string", - "enum" : [ "request", "granted", "denied" ] + "in" : "query", + "name" : "state", + "schema" : { + "enum" : [ "request", "granted", "denied" ], + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/MediationList" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/MediationList" + } + } + }, + "description" : "" } - } + }, + "summary" : "Query mediation requests, returns list of all mediation records", + "tags" : [ "mediation" ] } }, "/mediation/requests/{mediation_id}" : { - "get" : { - "tags" : [ "mediation" ], - "summary" : "Retrieve mediation request record", - "produces" : [ "application/json" ], + "delete" : { "parameters" : [ { - "name" : "mediation_id", - "in" : "path", "description" : "Mediation record identifier", + "in" : "path", + "name" : "mediation_id", "required" : true, - "type" : "string", - "format" : "uuid" + "schema" : { + "format" : "uuid", + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/MediationRecord" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/MediationRecord" + } + } + }, + "description" : "" } - } - }, - "delete" : { - "tags" : [ "mediation" ], + }, "summary" : "Delete mediation request by ID", - "produces" : [ "application/json" ], + "tags" : [ "mediation" ] + }, + "get" : { "parameters" : [ { - "name" : "mediation_id", - "in" : "path", "description" : "Mediation record identifier", + "in" : "path", + "name" : "mediation_id", "required" : true, - "type" : "string", - "format" : "uuid" + "schema" : { + "format" : "uuid", + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/MediationRecord" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/MediationRecord" + } + } + }, + "description" : "" } - } + }, + "summary" : "Retrieve mediation request record", + "tags" : [ "mediation" ] } }, "/mediation/requests/{mediation_id}/deny" : { "post" : { - "tags" : [ "mediation" ], - "summary" : "Deny a stored mediation request", - "produces" : [ "application/json" ], "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/AdminMediationDeny" - } - }, { - "name" : "mediation_id", - "in" : "path", "description" : "Mediation record identifier", + "in" : "path", + "name" : "mediation_id", "required" : true, - "type" : "string", - "format" : "uuid" + "schema" : { + "format" : "uuid", + "type" : "string" + } } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/AdminMediationDeny" + } + } + }, + "required" : false + }, "responses" : { "201" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/MediationDeny" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/MediationDeny" + } + } + }, + "description" : "" } - } + }, + "summary" : "Deny a stored mediation request", + "tags" : [ "mediation" ], + "x-codegen-request-body-name" : "body" } }, "/mediation/requests/{mediation_id}/grant" : { "post" : { - "tags" : [ "mediation" ], - "summary" : "Grant received mediation", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "mediation_id", - "in" : "path", "description" : "Mediation record identifier", + "in" : "path", + "name" : "mediation_id", "required" : true, - "type" : "string", - "format" : "uuid" + "schema" : { + "format" : "uuid", + "type" : "string" + } } ], "responses" : { "201" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/MediationGrant" + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/MediationGrant" + } + } + }, + "description" : "" + } + }, + "summary" : "Grant received mediation", + "tags" : [ "mediation" ] + } + }, + "/mediation/update-keylist/{conn_id}" : { + "post" : { + "parameters" : [ { + "description" : "Connection identifier", + "in" : "path", + "name" : "conn_id", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/MediationIdMatchInfo" + } } + }, + "required" : false + }, + "responses" : { + "200" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/KeylistUpdate" + } + } + }, + "description" : "" } - } + }, + "summary" : "Update keylist for a connection", + "tags" : [ "mediation" ], + "x-codegen-request-body-name" : "body" } }, "/mediation/{mediation_id}/default-mediator" : { "put" : { - "tags" : [ "mediation" ], - "summary" : "Set default mediator", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "mediation_id", - "in" : "path", "description" : "Mediation record identifier", + "in" : "path", + "name" : "mediation_id", "required" : true, - "type" : "string", - "format" : "uuid" + "schema" : { + "format" : "uuid", + "type" : "string" + } } ], "responses" : { "201" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/MediationRecord" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/MediationRecord" + } + } + }, + "description" : "" } - } + }, + "summary" : "Set default mediator", + "tags" : [ "mediation" ] } }, "/multitenancy/wallet" : { "post" : { - "tags" : [ "multitenancy" ], - "summary" : "Create a subwallet", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/CreateWalletRequest" - } - } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/CreateWalletRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/CreateWalletResponse" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/CreateWalletResponse" + } + } + }, + "description" : "" } - } + }, + "summary" : "Create a subwallet", + "tags" : [ "multitenancy" ], + "x-codegen-request-body-name" : "body" } }, "/multitenancy/wallet/{wallet_id}" : { "get" : { - "tags" : [ "multitenancy" ], - "summary" : "Get a single subwallet", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "wallet_id", - "in" : "path", "description" : "Subwallet identifier", + "in" : "path", + "name" : "wallet_id", "required" : true, - "type" : "string" + "schema" : { + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/WalletRecord" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/WalletRecord" + } + } + }, + "description" : "" } - } + }, + "summary" : "Get a single subwallet", + "tags" : [ "multitenancy" ] }, "put" : { - "tags" : [ "multitenancy" ], - "summary" : "Update a subwallet", - "produces" : [ "application/json" ], "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/UpdateWalletRequest" - } - }, { - "name" : "wallet_id", - "in" : "path", "description" : "Subwallet identifier", + "in" : "path", + "name" : "wallet_id", "required" : true, - "type" : "string" + "schema" : { + "type" : "string" + } } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/UpdateWalletRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/WalletRecord" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/WalletRecord" + } + } + }, + "description" : "" } - } + }, + "summary" : "Update a subwallet", + "tags" : [ "multitenancy" ], + "x-codegen-request-body-name" : "body" } }, "/multitenancy/wallet/{wallet_id}/remove" : { "post" : { - "tags" : [ "multitenancy" ], - "summary" : "Remove a subwallet", - "produces" : [ "application/json" ], "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/RemoveWalletRequest" - } - }, { - "name" : "wallet_id", - "in" : "path", "description" : "Subwallet identifier", + "in" : "path", + "name" : "wallet_id", "required" : true, - "type" : "string" + "schema" : { + "type" : "string" + } } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/RemoveWalletRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/MultitenantModuleResponse" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/MultitenantModuleResponse" + } + } + }, + "description" : "" } - } + }, + "summary" : "Remove a subwallet", + "tags" : [ "multitenancy" ], + "x-codegen-request-body-name" : "body" } }, "/multitenancy/wallet/{wallet_id}/token" : { "post" : { - "tags" : [ "multitenancy" ], - "summary" : "Get auth token for a subwallet", - "produces" : [ "application/json" ], "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/CreateWalletTokenRequest" - } - }, { - "name" : "wallet_id", "in" : "path", + "name" : "wallet_id", "required" : true, - "type" : "string" + "schema" : { + "type" : "string" + } } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/CreateWalletTokenRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/CreateWalletTokenResponse" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/CreateWalletTokenResponse" + } + } + }, + "description" : "" } - } + }, + "summary" : "Get auth token for a subwallet", + "tags" : [ "multitenancy" ], + "x-codegen-request-body-name" : "body" } }, "/multitenancy/wallets" : { "get" : { - "tags" : [ "multitenancy" ], - "summary" : "Query subwallets", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "wallet_name", - "in" : "query", "description" : "Wallet name", - "required" : false, - "type" : "string" + "in" : "query", + "name" : "wallet_name", + "schema" : { + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/WalletList" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/WalletList" + } + } + }, + "description" : "" } - } + }, + "summary" : "Query subwallets", + "tags" : [ "multitenancy" ] } }, "/out-of-band/create-invitation" : { "post" : { - "tags" : [ "out-of-band" ], - "summary" : "Create a new connection invitation", - "produces" : [ "application/json" ], "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, + "description" : "Auto-accept connection (defaults to configuration)", + "in" : "query", + "name" : "auto_accept", "schema" : { - "$ref" : "#/definitions/InvitationCreateRequest" + "type" : "boolean" } }, { - "name" : "auto_accept", + "description" : "Create invitation for multiple use (default false)", "in" : "query", - "description" : "Auto-accept connection (defaults to configuration)", - "required" : false, - "type" : "boolean" - }, { "name" : "multi_use", - "in" : "query", - "description" : "Create invitation for multiple use (default false)", - "required" : false, - "type" : "boolean" + "schema" : { + "type" : "boolean" + } } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/InvitationCreateRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/InvitationRecord" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/InvitationRecord" + } + } + }, + "description" : "" } - } + }, + "summary" : "Create a new connection invitation", + "tags" : [ "out-of-band" ], + "x-codegen-request-body-name" : "body" } }, "/out-of-band/receive-invitation" : { "post" : { - "tags" : [ "out-of-band" ], - "summary" : "Receive a new connection invitation", - "produces" : [ "application/json" ], "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, + "description" : "Alias for connection", + "in" : "query", + "name" : "alias", "schema" : { - "$ref" : "#/definitions/InvitationMessage" + "type" : "string" } }, { - "name" : "alias", + "description" : "Auto-accept connection (defaults to configuration)", "in" : "query", - "description" : "Alias for connection", - "required" : false, - "type" : "string" - }, { "name" : "auto_accept", - "in" : "query", - "description" : "Auto-accept connection (defaults to configuration)", - "required" : false, - "type" : "boolean" + "schema" : { + "type" : "boolean" + } }, { - "name" : "mediation_id", - "in" : "query", "description" : "Identifier for active mediation record to be used", - "required" : false, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - }, { - "name" : "use_existing_connection", "in" : "query", + "name" : "mediation_id", + "schema" : { + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", + "type" : "string" + } + }, { "description" : "Use an existing connection, if possible", - "required" : false, - "type" : "boolean" + "in" : "query", + "name" : "use_existing_connection", + "schema" : { + "type" : "boolean" + } } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/InvitationMessage" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/ConnRecord" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/OobRecord" + } + } + }, + "description" : "" } - } + }, + "summary" : "Receive a new connection invitation", + "tags" : [ "out-of-band" ], + "x-codegen-request-body-name" : "body" } }, "/plugins" : { "get" : { - "tags" : [ "server" ], - "summary" : "Fetch the list of loaded plugins", - "produces" : [ "application/json" ], - "parameters" : [ ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/AdminModules" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/AdminModules" + } + } + }, + "description" : "" } - } + }, + "summary" : "Fetch the list of loaded plugins", + "tags" : [ "server" ] } }, "/present-proof-2.0/create-request" : { "post" : { - "tags" : [ "present-proof v2.0" ], - "summary" : "Creates a presentation request not bound to any proposal or connection", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V20PresCreateRequestRequest" - } - } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/V20PresCreateRequestRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20PresExRecord" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V20PresExRecord" + } + } + }, + "description" : "" } - } + }, + "summary" : "Creates a presentation request not bound to any proposal or connection", + "tags" : [ "present-proof v2.0" ], + "x-codegen-request-body-name" : "body" } }, "/present-proof-2.0/records" : { "get" : { - "tags" : [ "present-proof v2.0" ], - "summary" : "Fetch all present-proof exchange records", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "connection_id", - "in" : "query", "description" : "Connection identifier", - "required" : false, - "type" : "string", - "format" : "uuid" - }, { - "name" : "role", "in" : "query", - "description" : "Role assigned in presentation exchange", - "required" : false, - "type" : "string", - "enum" : [ "prover", "verifier" ] + "name" : "connection_id", + "schema" : { + "format" : "uuid", + "type" : "string" + } }, { - "name" : "state", + "description" : "Role assigned in presentation exchange", "in" : "query", - "description" : "Presentation exchange state", - "required" : false, - "type" : "string", - "enum" : [ "proposal-sent", "proposal-received", "request-sent", "request-received", "presentation-sent", "presentation-received", "done", "abandoned" ] + "name" : "role", + "schema" : { + "enum" : [ "prover", "verifier" ], + "type" : "string" + } }, { - "name" : "thread_id", + "description" : "Presentation exchange state", "in" : "query", + "name" : "state", + "schema" : { + "enum" : [ "proposal-sent", "proposal-received", "request-sent", "request-received", "presentation-sent", "presentation-received", "done", "abandoned" ], + "type" : "string" + } + }, { "description" : "Thread identifier", - "required" : false, - "type" : "string", - "format" : "uuid" + "in" : "query", + "name" : "thread_id", + "schema" : { + "format" : "uuid", + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20PresExRecordList" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V20PresExRecordList" + } + } + }, + "description" : "" } - } + }, + "summary" : "Fetch all present-proof exchange records", + "tags" : [ "present-proof v2.0" ] } }, "/present-proof-2.0/records/{pres_ex_id}" : { - "get" : { - "tags" : [ "present-proof v2.0" ], - "summary" : "Fetch a single presentation exchange record", - "produces" : [ "application/json" ], + "delete" : { "parameters" : [ { - "name" : "pres_ex_id", - "in" : "path", "description" : "Presentation exchange identifier", + "in" : "path", + "name" : "pres_ex_id", "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + "schema" : { + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20PresExRecord" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V20PresentProofModuleResponse" + } + } + }, + "description" : "" } - } - }, - "delete" : { - "tags" : [ "present-proof v2.0" ], + }, "summary" : "Remove an existing presentation exchange record", - "produces" : [ "application/json" ], + "tags" : [ "present-proof v2.0" ] + }, + "get" : { "parameters" : [ { - "name" : "pres_ex_id", - "in" : "path", "description" : "Presentation exchange identifier", + "in" : "path", + "name" : "pres_ex_id", "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + "schema" : { + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20PresentProofModuleResponse" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V20PresExRecord" + } + } + }, + "description" : "" } - } + }, + "summary" : "Fetch a single presentation exchange record", + "tags" : [ "present-proof v2.0" ] } }, "/present-proof-2.0/records/{pres_ex_id}/credentials" : { "get" : { - "tags" : [ "present-proof v2.0" ], - "summary" : "Fetch credentials from wallet for presentation request", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "pres_ex_id", - "in" : "path", "description" : "Presentation exchange identifier", + "in" : "path", + "name" : "pres_ex_id", "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + "schema" : { + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", + "type" : "string" + } }, { - "name" : "count", - "in" : "query", "description" : "Maximum number to retrieve", - "required" : false, - "type" : "string", - "pattern" : "^[1-9][0-9]*$" - }, { - "name" : "extra_query", "in" : "query", - "description" : "(JSON) object mapping referents to extra WQL queries", - "required" : false, - "type" : "string", - "pattern" : "^{\\s*\".*?\"\\s*:\\s*{.*?}\\s*(,\\s*\".*?\"\\s*:\\s*{.*?}\\s*)*\\s*}$" + "name" : "count", + "schema" : { + "pattern" : "^[1-9][0-9]*$", + "type" : "string" + } }, { - "name" : "referent", + "description" : "(JSON) object mapping referents to extra WQL queries", "in" : "query", - "description" : "Proof request referents of interest, comma-separated", - "required" : false, - "type" : "string" + "name" : "extra_query", + "schema" : { + "pattern" : "^{\\s*\".*?\"\\s*:\\s*{.*?}\\s*(,\\s*\".*?\"\\s*:\\s*{.*?}\\s*)*\\s*}$", + "type" : "string" + } }, { - "name" : "start", + "description" : "Proof request referents of interest, comma-separated", "in" : "query", + "name" : "referent", + "schema" : { + "type" : "string" + } + }, { "description" : "Start index", - "required" : false, - "type" : "string", - "pattern" : "^[0-9]*$" - } ], + "in" : "query", + "name" : "start", + "schema" : { + "pattern" : "^[0-9]*$", + "type" : "string" + } + } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/definitions/IndyCredPrecis" + "content" : { + "application/json" : { + "schema" : { + "items" : { + "$ref" : "#/components/schemas/IndyCredPrecis" + }, + "type" : "array" + } } - } + }, + "description" : "" } - } + }, + "summary" : "Fetch credentials from wallet for presentation request", + "tags" : [ "present-proof v2.0" ] } }, "/present-proof-2.0/records/{pres_ex_id}/problem-report" : { "post" : { - "tags" : [ "present-proof v2.0" ], - "summary" : "Send a problem report for presentation exchange", - "produces" : [ "application/json" ], "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V20PresProblemReportRequest" - } - }, { - "name" : "pres_ex_id", - "in" : "path", "description" : "Presentation exchange identifier", + "in" : "path", + "name" : "pres_ex_id", "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + "schema" : { + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", + "type" : "string" + } } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/V20PresProblemReportRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20PresentProofModuleResponse" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V20PresentProofModuleResponse" + } + } + }, + "description" : "" } - } + }, + "summary" : "Send a problem report for presentation exchange", + "tags" : [ "present-proof v2.0" ], + "x-codegen-request-body-name" : "body" } }, "/present-proof-2.0/records/{pres_ex_id}/send-presentation" : { "post" : { - "tags" : [ "present-proof v2.0" ], - "summary" : "Sends a proof presentation", - "produces" : [ "application/json" ], "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V20PresSpecByFormatRequest" - } - }, { - "name" : "pres_ex_id", - "in" : "path", "description" : "Presentation exchange identifier", + "in" : "path", + "name" : "pres_ex_id", "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + "schema" : { + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", + "type" : "string" + } } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/V20PresSpecByFormatRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20PresExRecord" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V20PresExRecord" + } + } + }, + "description" : "" } - } + }, + "summary" : "Sends a proof presentation", + "tags" : [ "present-proof v2.0" ], + "x-codegen-request-body-name" : "body" } }, "/present-proof-2.0/records/{pres_ex_id}/send-request" : { "post" : { - "tags" : [ "present-proof v2.0" ], - "summary" : "Sends a presentation request in reference to a proposal", - "produces" : [ "application/json" ], "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/AdminAPIMessageTracing" - } - }, { - "name" : "pres_ex_id", - "in" : "path", "description" : "Presentation exchange identifier", + "in" : "path", + "name" : "pres_ex_id", "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + "schema" : { + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", + "type" : "string" + } } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/V20PresentationSendRequestToProposal" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20PresExRecord" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V20PresExRecord" + } + } + }, + "description" : "" } - } + }, + "summary" : "Sends a presentation request in reference to a proposal", + "tags" : [ "present-proof v2.0" ], + "x-codegen-request-body-name" : "body" } }, "/present-proof-2.0/records/{pres_ex_id}/verify-presentation" : { "post" : { - "tags" : [ "present-proof v2.0" ], - "summary" : "Verify a received presentation", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "pres_ex_id", - "in" : "path", "description" : "Presentation exchange identifier", + "in" : "path", + "name" : "pres_ex_id", "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + "schema" : { + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20PresExRecord" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V20PresExRecord" + } + } + }, + "description" : "" } - } + }, + "summary" : "Verify a received presentation", + "tags" : [ "present-proof v2.0" ] } }, "/present-proof-2.0/send-proposal" : { "post" : { - "tags" : [ "present-proof v2.0" ], - "summary" : "Sends a presentation proposal", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V20PresProposalRequest" - } - } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/V20PresProposalRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20PresExRecord" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V20PresExRecord" + } + } + }, + "description" : "" } - } + }, + "summary" : "Sends a presentation proposal", + "tags" : [ "present-proof v2.0" ], + "x-codegen-request-body-name" : "body" } }, "/present-proof-2.0/send-request" : { "post" : { - "tags" : [ "present-proof v2.0" ], - "summary" : "Sends a free presentation request not bound to any proposal", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V20PresSendRequestRequest" - } - } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/V20PresSendRequestRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20PresExRecord" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V20PresExRecord" + } + } + }, + "description" : "" } - } + }, + "summary" : "Sends a free presentation request not bound to any proposal", + "tags" : [ "present-proof v2.0" ], + "x-codegen-request-body-name" : "body" } }, "/present-proof/create-request" : { "post" : { - "tags" : [ "present-proof v1.0" ], - "summary" : "Creates a presentation request not bound to any proposal or connection", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V10PresentationCreateRequestRequest" - } - } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/V10PresentationCreateRequestRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V10PresentationExchange" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V10PresentationExchange" + } + } + }, + "description" : "" } - } + }, + "summary" : "Creates a presentation request not bound to any proposal or connection", + "tags" : [ "present-proof v1.0" ], + "x-codegen-request-body-name" : "body" } }, "/present-proof/records" : { "get" : { - "tags" : [ "present-proof v1.0" ], - "summary" : "Fetch all present-proof exchange records", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "connection_id", - "in" : "query", "description" : "Connection identifier", - "required" : false, - "type" : "string", - "format" : "uuid" - }, { - "name" : "role", "in" : "query", - "description" : "Role assigned in presentation exchange", - "required" : false, - "type" : "string", - "enum" : [ "prover", "verifier" ] + "name" : "connection_id", + "schema" : { + "format" : "uuid", + "type" : "string" + } }, { - "name" : "state", + "description" : "Role assigned in presentation exchange", "in" : "query", - "description" : "Presentation exchange state", - "required" : false, - "type" : "string", - "enum" : [ "proposal_sent", "proposal_received", "request_sent", "request_received", "presentation_sent", "presentation_received", "verified", "presentation_acked" ] + "name" : "role", + "schema" : { + "enum" : [ "prover", "verifier" ], + "type" : "string" + } }, { - "name" : "thread_id", + "description" : "Presentation exchange state", "in" : "query", + "name" : "state", + "schema" : { + "enum" : [ "proposal_sent", "proposal_received", "request_sent", "request_received", "presentation_sent", "presentation_received", "verified", "presentation_acked", "abandoned" ], + "type" : "string" + } + }, { "description" : "Thread identifier", - "required" : false, - "type" : "string", - "format" : "uuid" + "in" : "query", + "name" : "thread_id", + "schema" : { + "format" : "uuid", + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V10PresentationExchangeList" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V10PresentationExchangeList" + } + } + }, + "description" : "" } - } + }, + "summary" : "Fetch all present-proof exchange records", + "tags" : [ "present-proof v1.0" ] } }, "/present-proof/records/{pres_ex_id}" : { - "get" : { - "tags" : [ "present-proof v1.0" ], - "summary" : "Fetch a single presentation exchange record", - "produces" : [ "application/json" ], + "delete" : { "parameters" : [ { - "name" : "pres_ex_id", - "in" : "path", "description" : "Presentation exchange identifier", + "in" : "path", + "name" : "pres_ex_id", "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + "schema" : { + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V10PresentationExchange" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V10PresentProofModuleResponse" + } + } + }, + "description" : "" } - } - }, - "delete" : { - "tags" : [ "present-proof v1.0" ], + }, "summary" : "Remove an existing presentation exchange record", - "produces" : [ "application/json" ], + "tags" : [ "present-proof v1.0" ] + }, + "get" : { "parameters" : [ { - "name" : "pres_ex_id", - "in" : "path", "description" : "Presentation exchange identifier", + "in" : "path", + "name" : "pres_ex_id", "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + "schema" : { + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V10PresentProofModuleResponse" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V10PresentationExchange" + } + } + }, + "description" : "" } - } + }, + "summary" : "Fetch a single presentation exchange record", + "tags" : [ "present-proof v1.0" ] } }, "/present-proof/records/{pres_ex_id}/credentials" : { "get" : { - "tags" : [ "present-proof v1.0" ], - "summary" : "Fetch credentials for a presentation request from wallet", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "pres_ex_id", - "in" : "path", "description" : "Presentation exchange identifier", + "in" : "path", + "name" : "pres_ex_id", "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + "schema" : { + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", + "type" : "string" + } }, { - "name" : "count", - "in" : "query", "description" : "Maximum number to retrieve", - "required" : false, - "type" : "string", - "pattern" : "^[1-9][0-9]*$" - }, { - "name" : "extra_query", "in" : "query", - "description" : "(JSON) object mapping referents to extra WQL queries", - "required" : false, - "type" : "string", - "pattern" : "^{\\s*\".*?\"\\s*:\\s*{.*?}\\s*(,\\s*\".*?\"\\s*:\\s*{.*?}\\s*)*\\s*}$" + "name" : "count", + "schema" : { + "pattern" : "^[1-9][0-9]*$", + "type" : "string" + } }, { - "name" : "referent", + "description" : "(JSON) object mapping referents to extra WQL queries", "in" : "query", - "description" : "Proof request referents of interest, comma-separated", - "required" : false, - "type" : "string" + "name" : "extra_query", + "schema" : { + "pattern" : "^{\\s*\".*?\"\\s*:\\s*{.*?}\\s*(,\\s*\".*?\"\\s*:\\s*{.*?}\\s*)*\\s*}$", + "type" : "string" + } }, { - "name" : "start", + "description" : "Proof request referents of interest, comma-separated", "in" : "query", + "name" : "referent", + "schema" : { + "type" : "string" + } + }, { "description" : "Start index", - "required" : false, - "type" : "string", - "pattern" : "^[0-9]*$" + "in" : "query", + "name" : "start", + "schema" : { + "pattern" : "^[0-9]*$", + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/definitions/IndyCredPrecis" + "content" : { + "application/json" : { + "schema" : { + "items" : { + "$ref" : "#/components/schemas/IndyCredPrecis" + }, + "type" : "array" + } } - } + }, + "description" : "" } - } + }, + "summary" : "Fetch credentials for a presentation request from wallet", + "tags" : [ "present-proof v1.0" ] } }, "/present-proof/records/{pres_ex_id}/problem-report" : { "post" : { - "tags" : [ "present-proof v1.0" ], - "summary" : "Send a problem report for presentation exchange", - "produces" : [ "application/json" ], "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, + "description" : "Presentation exchange identifier", + "in" : "path", + "name" : "pres_ex_id", + "required" : true, "schema" : { - "$ref" : "#/definitions/V10PresentationProblemReportRequest" + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", + "type" : "string" } - }, { - "name" : "pres_ex_id", - "in" : "path", - "description" : "Presentation exchange identifier", - "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/V10PresentationProblemReportRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V10PresentProofModuleResponse" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V10PresentProofModuleResponse" + } + } + }, + "description" : "" } - } + }, + "summary" : "Send a problem report for presentation exchange", + "tags" : [ "present-proof v1.0" ], + "x-codegen-request-body-name" : "body" } }, "/present-proof/records/{pres_ex_id}/send-presentation" : { "post" : { - "tags" : [ "present-proof v1.0" ], - "summary" : "Sends a proof presentation", - "produces" : [ "application/json" ], "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/IndyPresSpec" - } - }, { - "name" : "pres_ex_id", - "in" : "path", "description" : "Presentation exchange identifier", + "in" : "path", + "name" : "pres_ex_id", "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + "schema" : { + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", + "type" : "string" + } } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/IndyPresSpec" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V10PresentationExchange" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V10PresentationExchange" + } + } + }, + "description" : "" } - } + }, + "summary" : "Sends a proof presentation", + "tags" : [ "present-proof v1.0" ], + "x-codegen-request-body-name" : "body" } }, "/present-proof/records/{pres_ex_id}/send-request" : { "post" : { - "tags" : [ "present-proof v1.0" ], - "summary" : "Sends a presentation request in reference to a proposal", - "produces" : [ "application/json" ], "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/AdminAPIMessageTracing" - } - }, { - "name" : "pres_ex_id", - "in" : "path", "description" : "Presentation exchange identifier", + "in" : "path", + "name" : "pres_ex_id", "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + "schema" : { + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", + "type" : "string" + } } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/V10PresentationSendRequestToProposal" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V10PresentationExchange" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V10PresentationExchange" + } + } + }, + "description" : "" } - } + }, + "summary" : "Sends a presentation request in reference to a proposal", + "tags" : [ "present-proof v1.0" ], + "x-codegen-request-body-name" : "body" } }, "/present-proof/records/{pres_ex_id}/verify-presentation" : { "post" : { - "tags" : [ "present-proof v1.0" ], - "summary" : "Verify a received presentation", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "pres_ex_id", - "in" : "path", "description" : "Presentation exchange identifier", + "in" : "path", + "name" : "pres_ex_id", "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + "schema" : { + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V10PresentationExchange" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V10PresentationExchange" + } + } + }, + "description" : "" } - } + }, + "summary" : "Verify a received presentation", + "tags" : [ "present-proof v1.0" ] } }, "/present-proof/send-proposal" : { "post" : { - "tags" : [ "present-proof v1.0" ], - "summary" : "Sends a presentation proposal", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V10PresentationProposalRequest" - } - } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/V10PresentationProposalRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V10PresentationExchange" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V10PresentationExchange" + } + } + }, + "description" : "" } - } + }, + "summary" : "Sends a presentation proposal", + "tags" : [ "present-proof v1.0" ], + "x-codegen-request-body-name" : "body" } }, "/present-proof/send-request" : { "post" : { - "tags" : [ "present-proof v1.0" ], - "summary" : "Sends a free presentation request not bound to any proposal", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V10PresentationSendRequestRequest" - } - } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/V10PresentationSendRequestRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V10PresentationExchange" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/V10PresentationExchange" + } + } + }, + "description" : "" } - } + }, + "summary" : "Sends a free presentation request not bound to any proposal", + "tags" : [ "present-proof v1.0" ], + "x-codegen-request-body-name" : "body" } }, "/resolver/resolve/{did}" : { "get" : { - "tags" : [ "resolver" ], - "summary" : "Retrieve doc for requested did", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "did", - "in" : "path", "description" : "DID", + "in" : "path", + "name" : "did", "required" : true, - "type" : "string", - "pattern" : "^did:([a-z0-9]+):((?:[a-zA-Z0-9._%-]*:)*[a-zA-Z0-9._%-]+)$" + "schema" : { + "pattern" : "^did:([a-z0-9]+):((?:[a-zA-Z0-9._%-]*:)*[a-zA-Z0-9._%-]+)$", + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/ResolutionResult" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ResolutionResult" + } + } + }, + "description" : "" } - } + }, + "summary" : "Retrieve doc for requested did", + "tags" : [ "resolver" ] } }, "/revocation/active-registry/{cred_def_id}" : { "get" : { - "tags" : [ "revocation" ], - "summary" : "Get current active revocation registry by credential definition id", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "cred_def_id", - "in" : "path", "description" : "Credential definition identifier", + "in" : "path", + "name" : "cred_def_id", "required" : true, - "type" : "string", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" + "schema" : { + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$", + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/RevRegResult" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/RevRegResult" + } + } + }, + "description" : "" } - } + }, + "summary" : "Get current active revocation registry by credential definition id", + "tags" : [ "revocation" ] } }, "/revocation/clear-pending-revocations" : { "post" : { - "tags" : [ "revocation" ], - "summary" : "Clear pending revocations", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/ClearPendingRevocationsRequest" - } - } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/ClearPendingRevocationsRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/PublishRevocations" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/PublishRevocations" + } + } + }, + "description" : "" } - } + }, + "summary" : "Clear pending revocations", + "tags" : [ "revocation" ], + "x-codegen-request-body-name" : "body" } }, "/revocation/create-registry" : { "post" : { - "tags" : [ "revocation" ], - "summary" : "Creates a new revocation registry", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/RevRegCreateRequest" - } - } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/RevRegCreateRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/RevRegResult" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/RevRegResult" + } + } + }, + "description" : "" } - } + }, + "summary" : "Creates a new revocation registry", + "tags" : [ "revocation" ], + "x-codegen-request-body-name" : "body" } }, "/revocation/credential-record" : { "get" : { - "tags" : [ "revocation" ], - "summary" : "Get credential revocation status", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "cred_ex_id", - "in" : "query", "description" : "Credential exchange identifier", - "required" : false, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - }, { - "name" : "cred_rev_id", "in" : "query", - "description" : "Credential revocation identifier", - "required" : false, - "type" : "string", - "pattern" : "^[1-9][0-9]*$" + "name" : "cred_ex_id", + "schema" : { + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", + "type" : "string" + } }, { - "name" : "rev_reg_id", + "description" : "Credential revocation identifier", "in" : "query", + "name" : "cred_rev_id", + "schema" : { + "pattern" : "^[1-9][0-9]*$", + "type" : "string" + } + }, { "description" : "Revocation registry identifier", - "required" : false, - "type" : "string", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" + "in" : "query", + "name" : "rev_reg_id", + "schema" : { + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)", + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/CredRevRecordResult" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/CredRevRecordResult" + } + } + }, + "description" : "" } - } + }, + "summary" : "Get credential revocation status", + "tags" : [ "revocation" ] } }, "/revocation/publish-revocations" : { "post" : { - "tags" : [ "revocation" ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/PublishRevocations" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/TxnOrPublishRevocationsResult" + } + } + }, + "description" : "" + } + }, "summary" : "Publish pending revocations to ledger", - "produces" : [ "application/json" ], + "tags" : [ "revocation" ], + "x-codegen-request-body-name" : "body" + } + }, + "/revocation/registries/created" : { + "get" : { "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, + "description" : "Credential definition identifier", + "in" : "query", + "name" : "cred_def_id", "schema" : { - "$ref" : "#/definitions/PublishRevocations" + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$", + "type" : "string" } }, { - "name" : "conn_id", - "in" : "query", - "description" : "Connection identifier", - "required" : false, - "type" : "string" - }, { - "name" : "create_transaction_for_endorser", + "description" : "Revocation registry state", "in" : "query", - "description" : "Create Transaction For Endorser's signature", - "required" : false, - "type" : "boolean" + "name" : "state", + "schema" : { + "enum" : [ "init", "generated", "posted", "active", "full" ], + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/TxnOrPublishRevocationsResult" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/RevRegsCreated" + } + } + }, + "description" : "" } - } + }, + "summary" : "Search for matching revocation registries that current agent created", + "tags" : [ "revocation" ] } }, - "/revocation/registries/created" : { - "get" : { - "tags" : [ "revocation" ], - "summary" : "Search for matching revocation registries that current agent created", - "produces" : [ "application/json" ], + "/revocation/registry/delete-tails-file" : { + "delete" : { "parameters" : [ { - "name" : "cred_def_id", - "in" : "query", "description" : "Credential definition identifier", - "required" : false, - "type" : "string", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" + "in" : "query", + "name" : "cred_def_id", + "schema" : { + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$", + "type" : "string" + } }, { - "name" : "state", + "description" : "Revocation registry identifier", "in" : "query", - "description" : "Revocation registry state", - "required" : false, - "type" : "string", - "enum" : [ "init", "generated", "posted", "active", "full" ] + "name" : "rev_reg_id", + "schema" : { + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)", + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/RevRegsCreated" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/TailsDeleteResponse" + } + } + }, + "description" : "" } - } + }, + "summary" : "Delete the tail files", + "tags" : [ "revocation" ] } }, "/revocation/registry/{rev_reg_id}" : { "get" : { - "tags" : [ "revocation" ], - "summary" : "Get revocation registry by revocation registry id", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "rev_reg_id", - "in" : "path", "description" : "Revocation Registry identifier", + "in" : "path", + "name" : "rev_reg_id", "required" : true, - "type" : "string", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" + "schema" : { + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)", + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/RevRegResult" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/RevRegResult" + } + } + }, + "description" : "" } - } + }, + "summary" : "Get revocation registry by revocation registry id", + "tags" : [ "revocation" ] }, "patch" : { - "tags" : [ "revocation" ], - "summary" : "Update revocation registry with new public URI to its tails file", - "produces" : [ "application/json" ], "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/RevRegUpdateTailsFileUri" - } - }, { - "name" : "rev_reg_id", - "in" : "path", "description" : "Revocation Registry identifier", + "in" : "path", + "name" : "rev_reg_id", "required" : true, - "type" : "string", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" + "schema" : { + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)", + "type" : "string" + } } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/RevRegUpdateTailsFileUri" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/RevRegResult" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/RevRegResult" + } + } + }, + "description" : "" } - } + }, + "summary" : "Update revocation registry with new public URI to its tails file", + "tags" : [ "revocation" ], + "x-codegen-request-body-name" : "body" } }, "/revocation/registry/{rev_reg_id}/definition" : { "post" : { - "tags" : [ "revocation" ], - "summary" : "Send revocation registry definition to ledger", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "rev_reg_id", - "in" : "path", "description" : "Revocation Registry identifier", + "in" : "path", + "name" : "rev_reg_id", "required" : true, - "type" : "string", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" + "schema" : { + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)", + "type" : "string" + } }, { - "name" : "conn_id", - "in" : "query", "description" : "Connection identifier", - "required" : false, - "type" : "string" - }, { - "name" : "create_transaction_for_endorser", "in" : "query", + "name" : "conn_id", + "schema" : { + "type" : "string" + } + }, { "description" : "Create Transaction For Endorser's signature", - "required" : false, - "type" : "boolean" + "in" : "query", + "name" : "create_transaction_for_endorser", + "schema" : { + "type" : "boolean" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/TxnOrRevRegResult" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/TxnOrRevRegResult" + } + } + }, + "description" : "" } - } + }, + "summary" : "Send revocation registry definition to ledger", + "tags" : [ "revocation" ] } }, "/revocation/registry/{rev_reg_id}/entry" : { "post" : { - "tags" : [ "revocation" ], - "summary" : "Send revocation registry entry to ledger", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "rev_reg_id", - "in" : "path", "description" : "Revocation Registry identifier", + "in" : "path", + "name" : "rev_reg_id", "required" : true, - "type" : "string", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" + "schema" : { + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)", + "type" : "string" + } }, { - "name" : "conn_id", - "in" : "query", "description" : "Connection identifier", - "required" : false, - "type" : "string" + "in" : "query", + "name" : "conn_id", + "schema" : { + "type" : "string" + } }, { + "description" : "Create Transaction For Endorser's signature", + "in" : "query", "name" : "create_transaction_for_endorser", + "schema" : { + "type" : "boolean" + } + } ], + "responses" : { + "200" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/RevRegResult" + } + } + }, + "description" : "" + } + }, + "summary" : "Send revocation registry entry to ledger", + "tags" : [ "revocation" ] + } + }, + "/revocation/registry/{rev_reg_id}/fix-revocation-entry-state" : { + "put" : { + "parameters" : [ { + "description" : "Revocation Registry identifier", + "in" : "path", + "name" : "rev_reg_id", + "required" : true, + "schema" : { + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)", + "type" : "string" + } + }, { + "description" : "Apply updated accumulator transaction to ledger", "in" : "query", - "description" : "Create Transaction For Endorser's signature", - "required" : false, - "type" : "boolean" + "name" : "apply_ledger_update", + "required" : true, + "schema" : { + "type" : "boolean" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/RevRegResult" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/RevRegWalletUpdatedResult" + } + } + }, + "description" : "" } - } + }, + "summary" : "Fix revocation state in wallet and return number of updated entries", + "tags" : [ "revocation" ] } }, "/revocation/registry/{rev_reg_id}/issued" : { "get" : { - "tags" : [ "revocation" ], - "summary" : "Get number of credentials issued against revocation registry", - "produces" : [ "application/json" ], "parameters" : [ { + "description" : "Revocation Registry identifier", + "in" : "path", "name" : "rev_reg_id", + "required" : true, + "schema" : { + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)", + "type" : "string" + } + } ], + "responses" : { + "200" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/RevRegIssuedResult" + } + } + }, + "description" : "" + } + }, + "summary" : "Get number of credentials issued against revocation registry", + "tags" : [ "revocation" ] + } + }, + "/revocation/registry/{rev_reg_id}/issued/details" : { + "get" : { + "parameters" : [ { + "description" : "Revocation Registry identifier", "in" : "path", + "name" : "rev_reg_id", + "required" : true, + "schema" : { + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)", + "type" : "string" + } + } ], + "responses" : { + "200" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/CredRevRecordDetailsResult" + } + } + }, + "description" : "" + } + }, + "summary" : "Get details of credentials issued against revocation registry", + "tags" : [ "revocation" ] + } + }, + "/revocation/registry/{rev_reg_id}/issued/indy_recs" : { + "get" : { + "parameters" : [ { "description" : "Revocation Registry identifier", + "in" : "path", + "name" : "rev_reg_id", "required" : true, - "type" : "string", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" + "schema" : { + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)", + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/RevRegIssuedResult" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/CredRevIndyRecordsResult" + } + } + }, + "description" : "" } - } + }, + "summary" : "Get details of revoked credentials from ledger", + "tags" : [ "revocation" ] } }, "/revocation/registry/{rev_reg_id}/set-state" : { "patch" : { - "tags" : [ "revocation" ], - "summary" : "Set revocation registry state manually", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "rev_reg_id", - "in" : "path", "description" : "Revocation Registry identifier", + "in" : "path", + "name" : "rev_reg_id", "required" : true, - "type" : "string", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" + "schema" : { + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)", + "type" : "string" + } }, { - "name" : "state", - "in" : "query", "description" : "Revocation registry state to set", + "in" : "query", + "name" : "state", "required" : true, - "type" : "string", - "enum" : [ "init", "generated", "posted", "active", "full" ] + "schema" : { + "enum" : [ "init", "generated", "posted", "active", "full" ], + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/RevRegResult" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/RevRegResult" + } + } + }, + "description" : "" } - } + }, + "summary" : "Set revocation registry state manually", + "tags" : [ "revocation" ] } }, "/revocation/registry/{rev_reg_id}/tails-file" : { "get" : { - "tags" : [ "revocation" ], - "summary" : "Download tails file", - "produces" : [ "application/octet-stream" ], "parameters" : [ { - "name" : "rev_reg_id", - "in" : "path", "description" : "Revocation Registry identifier", + "in" : "path", + "name" : "rev_reg_id", "required" : true, - "type" : "string", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" + "schema" : { + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)", + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "tails file", - "schema" : { - "type" : "string", - "format" : "binary" - } + "content" : { + "application/octet-stream" : { + "schema" : { + "format" : "binary", + "type" : "string" + } + } + }, + "description" : "tails file" } - } + }, + "summary" : "Download tails file", + "tags" : [ "revocation" ] }, "put" : { - "tags" : [ "revocation" ], - "summary" : "Upload local tails file to server", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "rev_reg_id", - "in" : "path", "description" : "Revocation Registry identifier", + "in" : "path", + "name" : "rev_reg_id", "required" : true, - "type" : "string", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" + "schema" : { + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)", + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/RevocationModuleResponse" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/RevocationModuleResponse" + } + } + }, + "description" : "" } - } + }, + "summary" : "Upload local tails file to server", + "tags" : [ "revocation" ] } }, "/revocation/revoke" : { "post" : { - "tags" : [ "revocation" ], - "summary" : "Revoke an issued credential", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/RevokeRequest" - } - } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/RevokeRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/RevocationModuleResponse" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/RevocationModuleResponse" + } + } + }, + "description" : "" } - } + }, + "summary" : "Revoke an issued credential", + "tags" : [ "revocation" ], + "x-codegen-request-body-name" : "body" } }, "/schemas" : { "post" : { - "tags" : [ "schema" ], - "summary" : "Sends a schema to the ledger", - "produces" : [ "application/json" ], "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, + "description" : "Connection identifier", + "in" : "query", + "name" : "conn_id", "schema" : { - "$ref" : "#/definitions/SchemaSendRequest" + "type" : "string" } }, { - "name" : "conn_id", + "description" : "Create Transaction For Endorser's signature", "in" : "query", - "description" : "Connection identifier", - "required" : false, - "type" : "string" - }, { "name" : "create_transaction_for_endorser", - "in" : "query", - "description" : "Create Transaction For Endorser's signature", - "required" : false, - "type" : "boolean" + "schema" : { + "type" : "boolean" + } } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/SchemaSendRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/TxnOrSchemaSendResult" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/TxnOrSchemaSendResult" + } + } + }, + "description" : "" } - } + }, + "summary" : "Sends a schema to the ledger", + "tags" : [ "schema" ], + "x-codegen-request-body-name" : "body" } }, "/schemas/created" : { "get" : { - "tags" : [ "schema" ], - "summary" : "Search for matching schema that agent originated", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "schema_id", - "in" : "query", "description" : "Schema identifier", - "required" : false, - "type" : "string", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" - }, { - "name" : "schema_issuer_did", "in" : "query", - "description" : "Schema issuer DID", - "required" : false, - "type" : "string", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + "name" : "schema_id", + "schema" : { + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$", + "type" : "string" + } }, { - "name" : "schema_name", + "description" : "Schema issuer DID", "in" : "query", - "description" : "Schema name", - "required" : false, - "type" : "string" - }, { - "name" : "schema_version", + "name" : "schema_issuer_did", + "schema" : { + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$", + "type" : "string" + } + }, { + "description" : "Schema name", "in" : "query", + "name" : "schema_name", + "schema" : { + "type" : "string" + } + }, { "description" : "Schema version", - "required" : false, - "type" : "string", - "pattern" : "^[0-9.]+$" + "in" : "query", + "name" : "schema_version", + "schema" : { + "pattern" : "^[0-9.]+$", + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/SchemasCreatedResult" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SchemasCreatedResult" + } + } + }, + "description" : "" } - } + }, + "summary" : "Search for matching schema that agent originated", + "tags" : [ "schema" ] } }, "/schemas/{schema_id}" : { "get" : { - "tags" : [ "schema" ], - "summary" : "Gets a schema from the ledger", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "schema_id", - "in" : "path", "description" : "Schema identifier", + "in" : "path", + "name" : "schema_id", "required" : true, - "type" : "string", - "pattern" : "^[1-9][0-9]*|[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" + "schema" : { + "pattern" : "^[1-9][0-9]*|[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$", + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/SchemaGetResult" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SchemaGetResult" + } + } + }, + "description" : "" } - } + }, + "summary" : "Gets a schema from the ledger", + "tags" : [ "schema" ] } }, "/schemas/{schema_id}/write_record" : { "post" : { - "tags" : [ "schema" ], - "summary" : "Writes a schema non-secret record to the wallet", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "schema_id", - "in" : "path", "description" : "Schema identifier", + "in" : "path", + "name" : "schema_id", "required" : true, - "type" : "string", - "pattern" : "^[1-9][0-9]*|[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" + "schema" : { + "pattern" : "^[1-9][0-9]*|[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$", + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/SchemaGetResult" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SchemaGetResult" + } + } + }, + "description" : "" } - } + }, + "summary" : "Writes a schema non-secret record to the wallet", + "tags" : [ "schema" ] } }, "/shutdown" : { "get" : { - "tags" : [ "server" ], - "summary" : "Shut down server", - "produces" : [ "application/json" ], - "parameters" : [ ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/AdminShutdown" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/AdminShutdown" + } + } + }, + "description" : "" } - } + }, + "summary" : "Shut down server", + "tags" : [ "server" ] } }, "/status" : { "get" : { - "tags" : [ "server" ], - "summary" : "Fetch the server status", - "produces" : [ "application/json" ], - "parameters" : [ ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/AdminStatus" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/AdminStatus" + } + } + }, + "description" : "" } - } + }, + "summary" : "Fetch the server status", + "tags" : [ "server" ] } }, "/status/config" : { "get" : { - "tags" : [ "server" ], - "summary" : "Fetch the server configuration", - "produces" : [ "application/json" ], - "parameters" : [ ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/AdminConfig" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/AdminConfig" + } + } + }, + "description" : "" } - } + }, + "summary" : "Fetch the server configuration", + "tags" : [ "server" ] } }, "/status/live" : { "get" : { - "tags" : [ "server" ], - "summary" : "Liveliness check", - "produces" : [ "application/json" ], - "parameters" : [ ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/AdminStatusLiveliness" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/AdminStatusLiveliness" + } + } + }, + "description" : "" } - } + }, + "summary" : "Liveliness check", + "tags" : [ "server" ] } }, "/status/ready" : { "get" : { - "tags" : [ "server" ], - "summary" : "Readiness check", - "produces" : [ "application/json" ], - "parameters" : [ ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/AdminStatusReadiness" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/AdminStatusReadiness" + } + } + }, + "description" : "" } - } + }, + "summary" : "Readiness check", + "tags" : [ "server" ] } }, "/status/reset" : { "post" : { - "tags" : [ "server" ], - "summary" : "Reset statistics", - "produces" : [ "application/json" ], - "parameters" : [ ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/AdminReset" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/AdminReset" + } + } + }, + "description" : "" } - } + }, + "summary" : "Reset statistics", + "tags" : [ "server" ] } }, "/transaction/{tran_id}/resend" : { "post" : { - "tags" : [ "endorse-transaction" ], - "summary" : "For Author to resend a particular transaction request", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "tran_id", - "in" : "path", "description" : "Transaction identifier", + "in" : "path", + "name" : "tran_id", "required" : true, - "type" : "string" + "schema" : { + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/TransactionRecord" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/TransactionRecord" + } + } + }, + "description" : "" } - } + }, + "summary" : "For Author to resend a particular transaction request", + "tags" : [ "endorse-transaction" ] } }, "/transactions" : { "get" : { - "tags" : [ "endorse-transaction" ], - "summary" : "Query transactions", - "produces" : [ "application/json" ], - "parameters" : [ ], "responses" : { "200" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/TransactionList" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/TransactionList" + } + } + }, + "description" : "" } - } + }, + "summary" : "Query transactions", + "tags" : [ "endorse-transaction" ] } }, "/transactions/create-request" : { "post" : { - "tags" : [ "endorse-transaction" ], - "summary" : "For author to send a transaction request", - "produces" : [ "application/json" ], "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, + "description" : "Transaction identifier", + "in" : "query", + "name" : "tran_id", + "required" : true, "schema" : { - "$ref" : "#/definitions/Date" + "type" : "string" } }, { - "name" : "tran_id", + "description" : "Endorser will write the transaction after endorsing it", "in" : "query", - "description" : "Transaction identifier", - "required" : true, - "type" : "string" - }, { "name" : "endorser_write_txn", - "in" : "query", - "description" : "Endorser will write the transaction after endorsing it", - "required" : false, - "type" : "boolean" + "schema" : { + "type" : "boolean" + } } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/Date" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/TransactionRecord" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/TransactionRecord" + } + } + }, + "description" : "" } - } + }, + "summary" : "For author to send a transaction request", + "tags" : [ "endorse-transaction" ], + "x-codegen-request-body-name" : "body" } }, "/transactions/{conn_id}/set-endorser-info" : { "post" : { - "tags" : [ "endorse-transaction" ], - "summary" : "Set Endorser Info", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "conn_id", - "in" : "path", "description" : "Connection identifier", + "in" : "path", + "name" : "conn_id", "required" : true, - "type" : "string" + "schema" : { + "type" : "string" + } }, { - "name" : "endorser_did", - "in" : "query", "description" : "Endorser DID", + "in" : "query", + "name" : "endorser_did", "required" : true, - "type" : "string" + "schema" : { + "type" : "string" + } }, { - "name" : "endorser_name", - "in" : "query", "description" : "Endorser Name", - "required" : false, - "type" : "string" + "in" : "query", + "name" : "endorser_name", + "schema" : { + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/EndorserInfo" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/EndorserInfo" + } + } + }, + "description" : "" } - } + }, + "summary" : "Set Endorser Info", + "tags" : [ "endorse-transaction" ] } }, "/transactions/{conn_id}/set-endorser-role" : { "post" : { - "tags" : [ "endorse-transaction" ], - "summary" : "Set transaction jobs", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "conn_id", - "in" : "path", "description" : "Connection identifier", + "in" : "path", + "name" : "conn_id", "required" : true, - "type" : "string" + "schema" : { + "type" : "string" + } }, { - "name" : "transaction_my_job", - "in" : "query", "description" : "Transaction related jobs", - "required" : false, - "type" : "string", - "enum" : [ "TRANSACTION_AUTHOR", "TRANSACTION_ENDORSER", "reset" ] + "in" : "query", + "name" : "transaction_my_job", + "schema" : { + "enum" : [ "TRANSACTION_AUTHOR", "TRANSACTION_ENDORSER", "reset" ], + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/TransactionJobs" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/TransactionJobs" + } + } + }, + "description" : "" } - } + }, + "summary" : "Set transaction jobs", + "tags" : [ "endorse-transaction" ] } }, "/transactions/{tran_id}" : { "get" : { - "tags" : [ "endorse-transaction" ], - "summary" : "Fetch a single transaction record", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "tran_id", - "in" : "path", "description" : "Transaction identifier", + "in" : "path", + "name" : "tran_id", "required" : true, - "type" : "string" + "schema" : { + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/TransactionRecord" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/TransactionRecord" + } + } + }, + "description" : "" } - } + }, + "summary" : "Fetch a single transaction record", + "tags" : [ "endorse-transaction" ] } }, "/transactions/{tran_id}/cancel" : { "post" : { - "tags" : [ "endorse-transaction" ], - "summary" : "For Author to cancel a particular transaction request", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "tran_id", - "in" : "path", "description" : "Transaction identifier", + "in" : "path", + "name" : "tran_id", "required" : true, - "type" : "string" + "schema" : { + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/TransactionRecord" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/TransactionRecord" + } + } + }, + "description" : "" } - } + }, + "summary" : "For Author to cancel a particular transaction request", + "tags" : [ "endorse-transaction" ] } }, "/transactions/{tran_id}/endorse" : { "post" : { - "tags" : [ "endorse-transaction" ], - "summary" : "For Endorser to endorse a particular transaction record", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "tran_id", - "in" : "path", "description" : "Transaction identifier", + "in" : "path", + "name" : "tran_id", "required" : true, - "type" : "string" + "schema" : { + "type" : "string" + } + }, { + "description" : "Endorser DID", + "in" : "query", + "name" : "endorser_did", + "schema" : { + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/TransactionRecord" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/TransactionRecord" + } + } + }, + "description" : "" } - } + }, + "summary" : "For Endorser to endorse a particular transaction record", + "tags" : [ "endorse-transaction" ] } }, "/transactions/{tran_id}/refuse" : { "post" : { - "tags" : [ "endorse-transaction" ], - "summary" : "For Endorser to refuse a particular transaction record", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "tran_id", - "in" : "path", "description" : "Transaction identifier", + "in" : "path", + "name" : "tran_id", "required" : true, - "type" : "string" + "schema" : { + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/TransactionRecord" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/TransactionRecord" + } + } + }, + "description" : "" } - } + }, + "summary" : "For Endorser to refuse a particular transaction record", + "tags" : [ "endorse-transaction" ] } }, "/transactions/{tran_id}/write" : { "post" : { - "tags" : [ "endorse-transaction" ], - "summary" : "For Author / Endorser to write an endorsed transaction to the ledger", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "tran_id", - "in" : "path", "description" : "Transaction identifier", + "in" : "path", + "name" : "tran_id", "required" : true, - "type" : "string" + "schema" : { + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/TransactionRecord" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/TransactionRecord" + } + } + }, + "description" : "" } - } + }, + "summary" : "For Author / Endorser to write an endorsed transaction to the ledger", + "tags" : [ "endorse-transaction" ] } }, "/wallet/did" : { "get" : { - "tags" : [ "wallet" ], - "summary" : "List wallet DIDs", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "did", - "in" : "query", "description" : "DID of interest", - "required" : false, - "type" : "string", - "pattern" : "^did:key:z[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+$|^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, { - "name" : "key_type", "in" : "query", - "description" : "Key type to query for.", - "required" : false, - "type" : "string", - "enum" : [ "ed25519", "bls12381g2" ] + "name" : "did", + "schema" : { + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$|^did:([a-zA-Z0-9_]+):([a-zA-Z0-9_.%-]+(:[a-zA-Z0-9_.%-]+)*)((;[a-zA-Z0-9_.:%-]+=[a-zA-Z0-9_.:%-]*)*)(\\/[^#?]*)?([?][^#]*)?(\\#.*)?$$", + "type" : "string" + } }, { - "name" : "method", + "description" : "Key type to query for.", "in" : "query", - "description" : "DID method to query for. e.g. sov to only fetch indy/sov DIDs", - "required" : false, - "type" : "string", - "enum" : [ "key", "sov" ] + "name" : "key_type", + "schema" : { + "enum" : [ "ed25519", "bls12381g2" ], + "type" : "string" + } }, { - "name" : "posture", + "description" : "DID method to query for. e.g. sov to only fetch indy/sov DIDs", "in" : "query", - "description" : "Whether DID is current public DID, posted to ledger but current public DID, or local to the wallet", - "required" : false, - "type" : "string", - "enum" : [ "public", "posted", "wallet_only" ] + "name" : "method", + "schema" : { + "enum" : [ "key", "sov" ], + "type" : "string" + } }, { - "name" : "verkey", + "description" : "Whether DID is current public DID, posted to ledger but current public DID, or local to the wallet", "in" : "query", + "name" : "posture", + "schema" : { + "enum" : [ "public", "posted", "wallet_only" ], + "type" : "string" + } + }, { "description" : "Verification key of interest", - "required" : false, - "type" : "string", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" + "in" : "query", + "name" : "verkey", + "schema" : { + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$", + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/DIDList" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/DIDList" + } + } + }, + "description" : "" } - } + }, + "summary" : "List wallet DIDs", + "tags" : [ "wallet" ] } }, "/wallet/did/create" : { "post" : { - "tags" : [ "wallet" ], - "summary" : "Create a local DID", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/DIDCreate" - } - } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/DIDCreate" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/DIDResult" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/DIDResult" + } + } + }, + "description" : "" } - } + }, + "summary" : "Create a local DID", + "tags" : [ "wallet" ], + "x-codegen-request-body-name" : "body" } }, "/wallet/did/local/rotate-keypair" : { "patch" : { - "tags" : [ "wallet" ], - "summary" : "Rotate keypair for a DID not posted to the ledger", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "did", - "in" : "query", "description" : "DID of interest", + "in" : "query", + "name" : "did", "required" : true, - "type" : "string", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + "schema" : { + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$", + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/WalletModuleResponse" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/WalletModuleResponse" + } + } + }, + "description" : "" } - } + }, + "summary" : "Rotate keypair for a DID not posted to the ledger", + "tags" : [ "wallet" ] } }, "/wallet/did/public" : { "get" : { - "tags" : [ "wallet" ], - "summary" : "Fetch the current public DID", - "produces" : [ "application/json" ], - "parameters" : [ ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/DIDResult" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/DIDResult" + } + } + }, + "description" : "" } - } + }, + "summary" : "Fetch the current public DID", + "tags" : [ "wallet" ] }, "post" : { - "tags" : [ "wallet" ], - "summary" : "Assign the current public DID", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "did", - "in" : "query", "description" : "DID of interest", + "in" : "query", + "name" : "did", "required" : true, - "type" : "string", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + "schema" : { + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$", + "type" : "string" + } + }, { + "description" : "Connection identifier", + "in" : "query", + "name" : "conn_id", + "schema" : { + "type" : "string" + } + }, { + "description" : "Create Transaction For Endorser's signature", + "in" : "query", + "name" : "create_transaction_for_endorser", + "schema" : { + "type" : "boolean" + } + }, { + "description" : "Mediation identifier", + "in" : "query", + "name" : "mediation_id", + "schema" : { + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/DIDResult" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/DIDResult" + } + } + }, + "description" : "" } - } + }, + "summary" : "Assign the current public DID", + "tags" : [ "wallet" ] } }, "/wallet/get-did-endpoint" : { "get" : { - "tags" : [ "wallet" ], - "summary" : "Query DID endpoint in wallet", - "produces" : [ "application/json" ], "parameters" : [ { - "name" : "did", - "in" : "query", "description" : "DID of interest", + "in" : "query", + "name" : "did", "required" : true, - "type" : "string", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + "schema" : { + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$", + "type" : "string" + } } ], "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/DIDEndpoint" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/DIDEndpoint" + } + } + }, + "description" : "" } - } + }, + "summary" : "Query DID endpoint in wallet", + "tags" : [ "wallet" ] } }, "/wallet/set-did-endpoint" : { "post" : { - "tags" : [ "wallet" ], - "summary" : "Update endpoint in wallet and on ledger if posted to it", - "produces" : [ "application/json" ], "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, + "description" : "Connection identifier", + "in" : "query", + "name" : "conn_id", + "schema" : { + "type" : "string" + } + }, { + "description" : "Create Transaction For Endorser's signature", + "in" : "query", + "name" : "create_transaction_for_endorser", "schema" : { - "$ref" : "#/definitions/DIDEndpointWithType" + "type" : "boolean" } } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/DIDEndpointWithType" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/WalletModuleResponse" - } + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/WalletModuleResponse" + } + } + }, + "description" : "" } - } + }, + "summary" : "Update endpoint in wallet and on ledger if posted to it", + "tags" : [ "wallet" ], + "x-codegen-request-body-name" : "body" } } }, - "securityDefinitions" : { - "AuthorizationHeader" : { - "description" : "Bearer token. Be sure to preprend token with 'Bearer '", - "type" : "apiKey", - "name" : "Authorization", - "in" : "header" - } - }, - "definitions" : { - "AMLRecord" : { - "type" : "object", - "properties" : { - "aml" : { - "type" : "object", - "additionalProperties" : { + "components" : { + "schemas" : { + "AMLRecord" : { + "properties" : { + "aml" : { + "additionalProperties" : { + "type" : "string" + }, + "type" : "object" + }, + "amlContext" : { + "type" : "string" + }, + "version" : { "type" : "string" } }, - "amlContext" : { - "type" : "string" + "type" : "object" + }, + "ActionMenuFetchResult" : { + "properties" : { + "result" : { + "$ref" : "#/components/schemas/ActionMenuFetchResult_result" + } }, - "version" : { - "type" : "string" - } - } - }, - "ActionMenuFetchResult" : { - "type" : "object", - "properties" : { - "result" : { - "$ref" : "#/definitions/ActionMenuFetchResult_result" - } - } - }, - "ActionMenuModulesResult" : { - "type" : "object" - }, - "AdminAPIMessageTracing" : { - "type" : "object", - "properties" : { - "trace" : { - "type" : "boolean", - "description" : "Record trace information, based on agent configuration" - } - } - }, - "AdminConfig" : { - "type" : "object", - "properties" : { - "config" : { - "type" : "object", - "description" : "Configuration settings", - "properties" : { } - } - } - }, - "AdminMediationDeny" : { - "type" : "object", - "properties" : { - "mediator_terms" : { - "type" : "array", - "description" : "List of mediator rules for recipient", - "items" : { - "type" : "string", - "description" : "Indicate terms to which the mediator requires the recipient to agree" + "type" : "object" + }, + "ActionMenuModulesResult" : { + "type" : "object" + }, + "AdminConfig" : { + "properties" : { + "config" : { + "description" : "Configuration settings", + "properties" : { }, + "type" : "object" } }, - "recipient_terms" : { - "type" : "array", - "description" : "List of recipient rules for mediation", - "items" : { - "type" : "string", - "description" : "Indicate terms to which the recipient requires the mediator to agree" - } - } - } - }, - "AdminModules" : { - "type" : "object", - "properties" : { - "result" : { - "type" : "array", - "description" : "List of admin modules", - "items" : { - "type" : "string", - "description" : "admin module" - } - } - } - }, - "AdminReset" : { - "type" : "object" - }, - "AdminShutdown" : { - "type" : "object" - }, - "AdminStatus" : { - "type" : "object", - "properties" : { - "conductor" : { - "type" : "object", - "description" : "Conductor statistics", - "properties" : { } - }, - "label" : { - "type" : "string", - "description" : "Default label", - "x-nullable" : true - }, - "timing" : { - "type" : "object", - "description" : "Timing results", - "properties" : { } - }, - "version" : { - "type" : "string", - "description" : "Version code" - } - } - }, - "AdminStatusLiveliness" : { - "type" : "object", - "properties" : { - "alive" : { - "type" : "boolean", - "example" : true, - "description" : "Liveliness status" - } - } - }, - "AdminStatusReadiness" : { - "type" : "object", - "properties" : { - "ready" : { - "type" : "boolean", - "example" : true, - "description" : "Readiness status" - } - } - }, - "AttachDecorator" : { - "type" : "object", - "required" : [ "data" ], - "properties" : { - "@id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Attachment identifier" - }, - "byte_count" : { - "type" : "integer", - "format" : "int32", - "example" : 1234, - "description" : "Byte count of data included by reference" - }, - "data" : { - "$ref" : "#/definitions/AttachDecoratorData" - }, - "description" : { - "type" : "string", - "example" : "view from doorway, facing east, with lights off", - "description" : "Human-readable description of content" - }, - "filename" : { - "type" : "string", - "example" : "IMG1092348.png", - "description" : "File name" - }, - "lastmod_time" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Hint regarding last modification datetime, in ISO-8601 format", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "mime-type" : { - "type" : "string", - "example" : "image/png", - "description" : "MIME type" - } - } - }, - "AttachDecoratorData" : { - "type" : "object", - "properties" : { - "base64" : { - "type" : "string", - "example" : "ey4uLn0=", - "description" : "Base64-encoded data", - "pattern" : "^[a-zA-Z0-9+/]*={0,2}$" - }, - "json" : { - "type" : "object", - "example" : "{\"sample\": \"content\"}", - "description" : "JSON-serialized data", - "properties" : { } - }, - "jws" : { - "$ref" : "#/definitions/AttachDecoratorData_jws" - }, - "links" : { - "type" : "array", - "description" : "List of hypertext links to data", - "items" : { - "type" : "string", - "example" : "https://link.to/data" - } - }, - "sha256" : { - "type" : "string", - "example" : "617a48c7c8afe0521efdc03e5bb0ad9e655893e6b4b51f0e794d70fba132aacb", - "description" : "SHA256 hash (binhex encoded) of content", - "pattern" : "^[a-fA-F0-9+/]{64}$" - } - } - }, - "AttachDecoratorData1JWS" : { - "type" : "object", - "required" : [ "header", "signature" ], - "properties" : { - "header" : { - "$ref" : "#/definitions/AttachDecoratorDataJWSHeader" - }, - "protected" : { - "type" : "string", - "example" : "ey4uLn0", - "description" : "protected JWS header", - "pattern" : "^[-_a-zA-Z0-9]*$" - }, - "signature" : { - "type" : "string", - "example" : "ey4uLn0", - "description" : "signature", - "pattern" : "^[-_a-zA-Z0-9]*$" - } - } - }, - "AttachDecoratorDataJWS" : { - "type" : "object", - "properties" : { - "header" : { - "$ref" : "#/definitions/AttachDecoratorDataJWSHeader" - }, - "protected" : { - "type" : "string", - "example" : "ey4uLn0", - "description" : "protected JWS header", - "pattern" : "^[-_a-zA-Z0-9]*$" - }, - "signature" : { - "type" : "string", - "example" : "ey4uLn0", - "description" : "signature", - "pattern" : "^[-_a-zA-Z0-9]*$" - }, - "signatures" : { - "type" : "array", - "description" : "List of signatures", - "items" : { - "$ref" : "#/definitions/AttachDecoratorData1JWS" - } - } - } - }, - "AttachDecoratorDataJWSHeader" : { - "type" : "object", - "required" : [ "kid" ], - "properties" : { - "kid" : { - "type" : "string", - "example" : "did:sov:LjgpST2rjsoxYegQDRm7EL#keys-4", - "description" : "Key identifier, in W3C did:key or DID URL format", - "pattern" : "^did:(?:key:z[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+|sov:[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}(;.*)?(\\?.*)?#.+)$" - } - } - }, - "AttachmentDef" : { - "type" : "object", - "properties" : { - "id" : { - "type" : "string", - "example" : "attachment-0", - "description" : "Attachment identifier" - }, - "type" : { - "type" : "string", - "example" : "present-proof", - "description" : "Attachment type", - "enum" : [ "credential-offer", "present-proof" ] - } - } - }, - "AttributeMimeTypesResult" : { - "type" : "object", - "properties" : { - "results" : { - "type" : "object", - "additionalProperties" : { - "type" : "string", - "description" : "MIME type" - }, - "x-nullable" : true - } - } - }, - "BasicMessageModuleResponse" : { - "type" : "object" - }, - "ClaimFormat" : { - "type" : "object", - "properties" : { - "jwt" : { - "type" : "object", - "properties" : { } - }, - "jwt_vc" : { - "type" : "object", - "properties" : { } - }, - "jwt_vp" : { - "type" : "object", - "properties" : { } - }, - "ldp" : { - "type" : "object", - "properties" : { } - }, - "ldp_vc" : { - "type" : "object", - "properties" : { } - }, - "ldp_vp" : { - "type" : "object", - "properties" : { } - } - } - }, - "ClearPendingRevocationsRequest" : { - "type" : "object", - "properties" : { - "purge" : { - "type" : "object", - "description" : "Credential revocation ids by revocation registry id: omit for all, specify null or empty list for all pending per revocation registry", - "additionalProperties" : { - "type" : "array", + "type" : "object" + }, + "AdminMediationDeny" : { + "properties" : { + "mediator_terms" : { + "description" : "List of mediator rules for recipient", "items" : { - "type" : "string", - "example" : "12345", - "description" : "Credential revocation identifier", - "pattern" : "^[1-9][0-9]*$" - } + "description" : "Indicate terms to which the mediator requires the recipient to agree", + "type" : "string" + }, + "type" : "array" + }, + "recipient_terms" : { + "description" : "List of recipient rules for mediation", + "items" : { + "description" : "Indicate terms to which the recipient requires the mediator to agree", + "type" : "string" + }, + "type" : "array" } - } - } - }, - "ConnRecord" : { - "type" : "object", - "properties" : { - "accept" : { - "type" : "string", - "example" : "auto", - "description" : "Connection acceptance: manual or auto", - "enum" : [ "manual", "auto" ] - }, - "alias" : { - "type" : "string", - "example" : "Bob, providing quotes", - "description" : "Optional alias to apply to connection for later use" }, - "connection_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Connection identifier" - }, - "connection_protocol" : { - "type" : "string", - "example" : "connections/1.0", - "description" : "Connection protocol used", - "enum" : [ "connections/1.0", "didexchange/1.0" ] - }, - "created_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of record creation", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "error_msg" : { - "type" : "string", - "example" : "No DIDDoc provided; cannot connect to public DID", - "description" : "Error message" - }, - "inbound_connection_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Inbound routing connection id to use" - }, - "invitation_key" : { - "type" : "string", - "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", - "description" : "Public key for connection", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" - }, - "invitation_mode" : { - "type" : "string", - "example" : "once", - "description" : "Invitation mode", - "enum" : [ "once", "multi", "static" ] - }, - "invitation_msg_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "ID of out-of-band invitation message" - }, - "my_did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "Our DID for connection", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "request_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Connection request identifier" - }, - "rfc23_state" : { - "type" : "string", - "example" : "invitation-sent", - "description" : "State per RFC 23", - "readOnly" : true - }, - "routing_state" : { - "type" : "string", - "example" : "active", - "description" : "Routing state of connection", - "enum" : [ "none", "request", "active", "error" ] - }, - "state" : { - "type" : "string", - "example" : "active", - "description" : "Current record state" - }, - "their_did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "Their DID for connection", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "their_label" : { - "type" : "string", - "example" : "Bob", - "description" : "Their label for connection" - }, - "their_public_did" : { - "type" : "string", - "example" : "2cpBmR3FqGKWi5EyUbpRY8", - "description" : "Other agent's public DID for connection" - }, - "their_role" : { - "type" : "string", - "example" : "requester", - "description" : "Their role in the connection protocol", - "enum" : [ "invitee", "requester", "inviter", "responder" ] - }, - "updated_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of last record update", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - } - } - }, - "ConnectionInvitation" : { - "type" : "object", - "properties" : { - "@id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Message identifier" - }, - "@type" : { - "type" : "string", - "example" : "https://didcomm.org/my-family/1.0/my-message-type", - "description" : "Message type", - "readOnly" : true - }, - "did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "DID for connection invitation", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "imageUrl" : { - "type" : "string", - "format" : "url", - "example" : "http://192.168.56.101/img/logo.jpg", - "description" : "Optional image URL for connection invitation", - "x-nullable" : true - }, - "label" : { - "type" : "string", - "example" : "Bob", - "description" : "Optional label for connection invitation" - }, - "recipientKeys" : { - "type" : "array", - "description" : "List of recipient keys", - "items" : { - "type" : "string", - "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", - "description" : "Recipient public key", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" + "type" : "object" + }, + "AdminModules" : { + "properties" : { + "result" : { + "description" : "List of admin modules", + "items" : { + "description" : "admin module", + "type" : "string" + }, + "type" : "array" } }, - "routingKeys" : { - "type" : "array", - "description" : "List of routing keys", - "items" : { - "type" : "string", - "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", - "description" : "Routing key", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" - } - }, - "serviceEndpoint" : { - "type" : "string", - "example" : "http://192.168.56.101:8020", - "description" : "Service endpoint at which to reach this agent" - } - } - }, - "ConnectionList" : { - "type" : "object", - "properties" : { - "results" : { - "type" : "array", - "description" : "List of connection records", - "items" : { - "$ref" : "#/definitions/ConnRecord" - } - } - } - }, - "ConnectionMetadata" : { - "type" : "object", - "properties" : { - "results" : { - "type" : "object", - "description" : "Dictionary of metadata associated with connection.", - "properties" : { } - } - } - }, - "ConnectionMetadataSetRequest" : { - "type" : "object", - "required" : [ "metadata" ], - "properties" : { - "metadata" : { - "type" : "object", - "description" : "Dictionary of metadata to set for connection.", - "properties" : { } - } - } - }, - "ConnectionModuleResponse" : { - "type" : "object" - }, - "ConnectionStaticRequest" : { - "type" : "object", - "properties" : { - "alias" : { - "type" : "string", - "description" : "Alias to assign to this connection" - }, - "my_did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "Local DID", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "my_seed" : { - "type" : "string", - "description" : "Seed to use for the local DID" - }, - "their_did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "Remote DID", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "their_endpoint" : { - "type" : "string", - "example" : "https://myhost:8021", - "description" : "URL endpoint for other party", - "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" - }, - "their_label" : { - "type" : "string", - "description" : "Other party's label for this connection" - }, - "their_seed" : { - "type" : "string", - "description" : "Seed to use for the remote DID" - }, - "their_verkey" : { - "type" : "string", - "description" : "Remote verification key" - } - } - }, - "ConnectionStaticResult" : { - "type" : "object", - "required" : [ "mv_verkey", "my_did", "my_endpoint", "record", "their_did", "their_verkey" ], - "properties" : { - "mv_verkey" : { - "type" : "string", - "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", - "description" : "My verification key", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" - }, - "my_did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "Local DID", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "my_endpoint" : { - "type" : "string", - "example" : "https://myhost:8021", - "description" : "My URL endpoint", - "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" - }, - "record" : { - "$ref" : "#/definitions/ConnRecord" - }, - "their_did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "Remote DID", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + "type" : "object" + }, + "AdminReset" : { + "type" : "object" + }, + "AdminShutdown" : { + "type" : "object" + }, + "AdminStatus" : { + "properties" : { + "conductor" : { + "description" : "Conductor statistics", + "properties" : { }, + "type" : "object" + }, + "label" : { + "description" : "Default label", + "nullable" : true, + "type" : "string" + }, + "timing" : { + "description" : "Timing results", + "properties" : { }, + "type" : "object" + }, + "version" : { + "description" : "Version code", + "type" : "string" + } }, - "their_verkey" : { - "type" : "string", - "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", - "description" : "Remote verification key", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" - } - } - }, - "Constraints" : { - "type" : "object", - "properties" : { - "fields" : { - "type" : "array", - "items" : { - "$ref" : "#/definitions/DIFField" + "type" : "object" + }, + "AdminStatusLiveliness" : { + "properties" : { + "alive" : { + "description" : "Liveliness status", + "example" : true, + "type" : "boolean" } }, - "is_holder" : { - "type" : "array", - "items" : { - "$ref" : "#/definitions/DIFHolder" + "type" : "object" + }, + "AdminStatusReadiness" : { + "properties" : { + "ready" : { + "description" : "Readiness status", + "example" : true, + "type" : "boolean" } }, - "limit_disclosure" : { - "type" : "string", - "description" : "LimitDisclosure" + "type" : "object" + }, + "AttachDecorator" : { + "properties" : { + "@id" : { + "description" : "Attachment identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "byte_count" : { + "description" : "Byte count of data included by reference", + "example" : 1234, + "format" : "int32", + "type" : "integer" + }, + "data" : { + "$ref" : "#/components/schemas/AttachDecoratorData" + }, + "description" : { + "description" : "Human-readable description of content", + "example" : "view from doorway, facing east, with lights off", + "type" : "string" + }, + "filename" : { + "description" : "File name", + "example" : "IMG1092348.png", + "type" : "string" + }, + "lastmod_time" : { + "description" : "Hint regarding last modification datetime, in ISO-8601 format", + "example" : "2021-12-31T23:59:59Z", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$", + "type" : "string" + }, + "mime-type" : { + "description" : "MIME type", + "example" : "image/png", + "type" : "string" + } }, - "status_active" : { - "type" : "string", - "enum" : [ "required", "allowed", "disallowed" ] + "required" : [ "data" ], + "type" : "object" + }, + "AttachDecoratorData" : { + "properties" : { + "base64" : { + "description" : "Base64-encoded data", + "example" : "ey4uLn0=", + "pattern" : "^[a-zA-Z0-9+/]*={0,2}$", + "type" : "string" + }, + "json" : { + "description" : "JSON-serialized data", + "example" : "{\"sample\": \"content\"}", + "type" : "object" + }, + "jws" : { + "$ref" : "#/components/schemas/AttachDecoratorData_jws" + }, + "links" : { + "description" : "List of hypertext links to data", + "items" : { + "example" : "https://link.to/data", + "type" : "string" + }, + "type" : "array" + }, + "sha256" : { + "description" : "SHA256 hash (binhex encoded) of content", + "example" : "617a48c7c8afe0521efdc03e5bb0ad9e655893e6b4b51f0e794d70fba132aacb", + "pattern" : "^[a-fA-F0-9+/]{64}$", + "type" : "string" + } }, - "status_revoked" : { - "type" : "string", - "enum" : [ "required", "allowed", "disallowed" ] + "type" : "object" + }, + "AttachDecoratorData1JWS" : { + "properties" : { + "header" : { + "$ref" : "#/components/schemas/AttachDecoratorDataJWSHeader" + }, + "protected" : { + "description" : "protected JWS header", + "example" : "ey4uLn0", + "pattern" : "^[-_a-zA-Z0-9]*$", + "type" : "string" + }, + "signature" : { + "description" : "signature", + "example" : "ey4uLn0", + "pattern" : "^[-_a-zA-Z0-9]*$", + "type" : "string" + } }, - "status_suspended" : { - "type" : "string", - "enum" : [ "required", "allowed", "disallowed" ] + "required" : [ "header", "signature" ], + "type" : "object" + }, + "AttachDecoratorDataJWS" : { + "properties" : { + "header" : { + "$ref" : "#/components/schemas/AttachDecoratorDataJWSHeader" + }, + "protected" : { + "description" : "protected JWS header", + "example" : "ey4uLn0", + "pattern" : "^[-_a-zA-Z0-9]*$", + "type" : "string" + }, + "signature" : { + "description" : "signature", + "example" : "ey4uLn0", + "pattern" : "^[-_a-zA-Z0-9]*$", + "type" : "string" + }, + "signatures" : { + "description" : "List of signatures", + "items" : { + "$ref" : "#/components/schemas/AttachDecoratorData1JWS" + }, + "type" : "array" + } }, - "subject_is_issuer" : { - "type" : "string", - "description" : "SubjectIsIssuer", - "enum" : [ "required", "preferred" ] - } - } - }, - "CreateInvitationRequest" : { - "type" : "object", - "properties" : { - "mediation_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Identifier for active mediation record to be used", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - }, - "metadata" : { - "type" : "object", - "description" : "Optional metadata to attach to the connection created with the invitation", - "properties" : { } - }, - "my_label" : { - "type" : "string", - "example" : "Bob", - "description" : "Optional label for connection invitation" - }, - "recipient_keys" : { - "type" : "array", - "description" : "List of recipient keys", - "items" : { - "type" : "string", - "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", - "description" : "Recipient public key", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" + "type" : "object" + }, + "AttachDecoratorDataJWSHeader" : { + "properties" : { + "kid" : { + "description" : "Key identifier, in W3C did:key or DID URL format", + "example" : "did:sov:LjgpST2rjsoxYegQDRm7EL#keys-4", + "pattern" : "^did:(?:key:z[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+|sov:[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}(;.*)?(\\?.*)?#.+)$", + "type" : "string" } }, - "routing_keys" : { - "type" : "array", - "description" : "List of routing keys", - "items" : { - "type" : "string", - "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", - "description" : "Routing key", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" - } - }, - "service_endpoint" : { - "type" : "string", - "example" : "http://192.168.56.102:8020", - "description" : "Connection endpoint" - } - } - }, - "CreateWalletRequest" : { - "type" : "object", - "properties" : { - "image_url" : { - "type" : "string", - "example" : "https://aries.ca/images/sample.png", - "description" : "Image url for this wallet. This image url is publicized (self-attested) to other agents as part of forming a connection." - }, - "key_management_mode" : { - "type" : "string", - "example" : "managed", - "description" : "Key management method to use for this wallet.", - "enum" : [ "managed" ] - }, - "label" : { - "type" : "string", - "example" : "Alice", - "description" : "Label for this wallet. This label is publicized (self-attested) to other agents as part of forming a connection." - }, - "wallet_dispatch_type" : { - "type" : "string", - "example" : "default", - "description" : "Webhook target dispatch type for this wallet. default - Dispatch only to webhooks associated with this wallet. base - Dispatch only to webhooks associated with the base wallet. both - Dispatch to both webhook targets.", - "enum" : [ "default", "both", "base" ] - }, - "wallet_key" : { - "type" : "string", - "example" : "MySecretKey123", - "description" : "Master key used for key derivation." - }, - "wallet_name" : { - "type" : "string", - "example" : "MyNewWallet", - "description" : "Wallet name" - }, - "wallet_type" : { - "type" : "string", - "example" : "indy", - "description" : "Type of the wallet to create", - "enum" : [ "askar", "in_memory", "indy" ] - }, - "wallet_webhook_urls" : { - "type" : "array", - "description" : "List of Webhook URLs associated with this subwallet", - "items" : { - "type" : "string", - "example" : "http://localhost:8022/webhooks", - "description" : "Optional webhook URL to receive webhook messages" - } - } - } - }, - "CreateWalletResponse" : { - "type" : "object", - "required" : [ "key_management_mode", "wallet_id" ], - "properties" : { - "created_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of record creation", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "key_management_mode" : { - "type" : "string", - "description" : "Mode regarding management of wallet key", - "enum" : [ "managed", "unmanaged" ] - }, - "settings" : { - "type" : "object", - "description" : "Settings for this wallet.", - "properties" : { } - }, - "state" : { - "type" : "string", - "example" : "active", - "description" : "Current record state" - }, - "token" : { - "type" : "string", - "example" : "eyJhbGciOiJFZERTQSJ9.eyJhIjogIjAifQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk", - "description" : "Authorization token to authenticate wallet requests" - }, - "updated_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of last record update", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "wallet_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Wallet record ID" - } - } - }, - "CreateWalletTokenRequest" : { - "type" : "object", - "properties" : { - "wallet_key" : { - "type" : "string", - "example" : "MySecretKey123", - "description" : "Master key used for key derivation. Only required for unamanged wallets." - } - } - }, - "CreateWalletTokenResponse" : { - "type" : "object", - "properties" : { - "token" : { - "type" : "string", - "example" : "eyJhbGciOiJFZERTQSJ9.eyJhIjogIjAifQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk", - "description" : "Authorization token to authenticate wallet requests" - } - } - }, - "CredAttrSpec" : { - "type" : "object", - "required" : [ "name", "value" ], - "properties" : { - "mime-type" : { - "type" : "string", - "example" : "image/jpeg", - "description" : "MIME type: omit for (null) default", - "x-nullable" : true - }, - "name" : { - "type" : "string", - "example" : "favourite_drink", - "description" : "Attribute name" - }, - "value" : { - "type" : "string", - "example" : "martini", - "description" : "Attribute value: base64-encode if MIME type is present" - } - } - }, - "CredDefValue" : { - "type" : "object", - "properties" : { - "primary" : { - "$ref" : "#/definitions/CredDefValue_primary" - }, - "revocation" : { - "$ref" : "#/definitions/CredDefValue_revocation" - } - } - }, - "CredDefValuePrimary" : { - "type" : "object", - "properties" : { - "n" : { - "type" : "string", - "example" : "0", - "pattern" : "^[0-9]*$" - }, - "r" : { - "$ref" : "#/definitions/Generated" - }, - "rctxt" : { - "type" : "string", - "example" : "0", - "pattern" : "^[0-9]*$" - }, - "s" : { - "type" : "string", - "example" : "0", - "pattern" : "^[0-9]*$" - }, - "z" : { - "type" : "string", - "example" : "0", - "pattern" : "^[0-9]*$" - } - } - }, - "CredDefValueRevocation" : { - "type" : "object", - "properties" : { - "g" : { - "type" : "string", - "example" : "1 1F14F&ECB578F 2 095E45DDF417D" - }, - "g_dash" : { - "type" : "string", - "example" : "1 1D64716fCDC00C 1 0C781960FA66E3D3 2 095E45DDF417D" - }, - "h" : { - "type" : "string", - "example" : "1 16675DAE54BFAE8 2 095E45DD417D" - }, - "h0" : { - "type" : "string", - "example" : "1 21E5EF9476EAF18 2 095E45DDF417D" - }, - "h1" : { - "type" : "string", - "example" : "1 236D1D99236090 2 095E45DDF417D" - }, - "h2" : { - "type" : "string", - "example" : "1 1C3AE8D1F1E277 2 095E45DDF417D" - }, - "h_cap" : { - "type" : "string", - "example" : "1 1B2A32CF3167 1 2490FEBF6EE55 1 0000000000000000" - }, - "htilde" : { - "type" : "string", - "example" : "1 1D8549E8C0F8 2 095E45DDF417D" - }, - "pk" : { - "type" : "string", - "example" : "1 142CD5E5A7DC 1 153885BD903312 2 095E45DDF417D" - }, - "u" : { - "type" : "string", - "example" : "1 0C430AAB2B4710 1 1CB3A0932EE7E 1 0000000000000000" - }, - "y" : { - "type" : "string", - "example" : "1 153558BD903312 2 095E45DDF417D 1 0000000000000000" - } - } - }, - "CredInfoList" : { - "type" : "object", - "properties" : { - "results" : { - "type" : "array", - "items" : { - "$ref" : "#/definitions/IndyCredInfo" - } - } - } - }, - "CredRevRecordResult" : { - "type" : "object", - "properties" : { - "result" : { - "$ref" : "#/definitions/IssuerCredRevRecord" - } - } - }, - "CredRevokedResult" : { - "type" : "object", - "properties" : { - "revoked" : { - "type" : "boolean", - "description" : "Whether credential is revoked on the ledger" - } - } - }, - "Credential" : { - "type" : "object", - "required" : [ "@context", "credentialSubject", "issuanceDate", "issuer", "type" ], - "properties" : { - "@context" : { - "type" : "array", - "example" : [ "https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1" ], - "description" : "The JSON-LD context of the credential", - "items" : { } - }, - "credentialSubject" : { - "example" : "" - }, - "expirationDate" : { - "type" : "string", - "example" : "2010-01-01T19:23:24Z", - "description" : "The expiration date", - "pattern" : "^([0-9]{4})-([0-9]{2})-([0-9]{2})([Tt ]([0-9]{2}):([0-9]{2}):([0-9]{2})(\\.[0-9]+)?)?(([Zz]|([+-])([0-9]{2}):([0-9]{2})))?$" - }, - "id" : { - "type" : "string", - "example" : "http://example.edu/credentials/1872", - "pattern" : "\\w+:(\\/?\\/?)[^\\s]+" - }, - "issuanceDate" : { - "type" : "string", - "example" : "2010-01-01T19:23:24Z", - "description" : "The issuance date", - "pattern" : "^([0-9]{4})-([0-9]{2})-([0-9]{2})([Tt ]([0-9]{2}):([0-9]{2}):([0-9]{2})(\\.[0-9]+)?)?(([Zz]|([+-])([0-9]{2}):([0-9]{2})))?$" - }, - "issuer" : { - "example" : "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH", - "description" : "The JSON-LD Verifiable Credential Issuer. Either string of object with id field." - }, - "proof" : { - "$ref" : "#/definitions/Credential_proof" - }, - "type" : { - "type" : "array", - "example" : [ "VerifiableCredential", "AlumniCredential" ], - "description" : "The JSON-LD type of the credential", - "items" : { - "type" : "string" - } - } - } - }, - "CredentialDefinition" : { - "type" : "object", - "properties" : { - "id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", - "description" : "Credential definition identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - }, - "schemaId" : { - "type" : "string", - "example" : "20", - "description" : "Schema identifier within credential definition identifier" - }, - "tag" : { - "type" : "string", - "example" : "tag", - "description" : "Tag within credential definition identifier" - }, - "type" : { - "example" : "CL", - "description" : "Signature type: CL for Camenisch-Lysyanskaya" - }, - "value" : { - "$ref" : "#/definitions/CredentialDefinition_value" - }, - "ver" : { - "type" : "string", - "example" : "1.0", - "description" : "Node protocol version", - "pattern" : "^[0-9.]+$" - } - } - }, - "CredentialDefinitionGetResult" : { - "type" : "object", - "properties" : { - "credential_definition" : { - "$ref" : "#/definitions/CredentialDefinition" - } - } - }, - "CredentialDefinitionSendRequest" : { - "type" : "object", - "properties" : { - "revocation_registry_size" : { - "type" : "integer", - "format" : "int32", - "example" : 1000, - "description" : "Revocation registry size", - "minimum" : 4, - "maximum" : 32768 - }, - "schema_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", - "description" : "Schema identifier", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" - }, - "support_revocation" : { - "type" : "boolean", - "description" : "Revocation supported flag" - }, - "tag" : { - "type" : "string", - "example" : "default", - "description" : "Credential definition identifier tag" - } - } - }, - "CredentialDefinitionSendResult" : { - "type" : "object", - "properties" : { - "credential_definition_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", - "description" : "Credential definition identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - } - } - }, - "CredentialDefinitionsCreatedResult" : { - "type" : "object", - "properties" : { - "credential_definition_ids" : { - "type" : "array", - "items" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", - "description" : "Credential definition identifiers", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - } - } - } - }, - "CredentialOffer" : { - "type" : "object", - "required" : [ "offers~attach" ], - "properties" : { - "@id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Message identifier" - }, - "@type" : { - "type" : "string", - "example" : "https://didcomm.org/my-family/1.0/my-message-type", - "description" : "Message type", - "readOnly" : true - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - }, - "credential_preview" : { - "$ref" : "#/definitions/CredentialPreview" - }, - "offers~attach" : { - "type" : "array", - "items" : { - "$ref" : "#/definitions/AttachDecorator" - } - } - } - }, - "CredentialPreview" : { - "type" : "object", - "required" : [ "attributes" ], - "properties" : { - "@type" : { - "type" : "string", - "example" : "issue-credential/1.0/credential-preview", - "description" : "Message type identifier" - }, - "attributes" : { - "type" : "array", - "items" : { - "$ref" : "#/definitions/CredAttrSpec" - } - } - } - }, - "CredentialProposal" : { - "type" : "object", - "properties" : { - "@id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Message identifier" - }, - "@type" : { - "type" : "string", - "example" : "https://didcomm.org/my-family/1.0/my-message-type", - "description" : "Message type", - "readOnly" : true - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - }, - "cred_def_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - }, - "credential_proposal" : { - "$ref" : "#/definitions/CredentialPreview" - }, - "issuer_did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "schema_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" - }, - "schema_issuer_did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "schema_name" : { - "type" : "string" - }, - "schema_version" : { - "type" : "string", - "example" : "1.0", - "pattern" : "^[0-9.]+$" - } - } - }, - "CredentialStatusOptions" : { - "type" : "object", - "required" : [ "type" ], - "properties" : { - "type" : { - "type" : "string", - "example" : "CredentialStatusList2017", - "description" : "Credential status method type to use for the credential. Should match status method registered in the Verifiable Credential Extension Registry" - } - } - }, - "DID" : { - "type" : "object", - "properties" : { - "did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "DID of interest", - "pattern" : "^did:key:z[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+$|^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "key_type" : { - "type" : "string", - "example" : "ed25519", - "description" : "Key type associated with the DID", - "enum" : [ "ed25519", "bls12381g2" ] - }, - "method" : { - "type" : "string", - "example" : "sov", - "description" : "Did method associated with the DID", - "enum" : [ "sov", "key" ] - }, - "posture" : { - "type" : "string", - "example" : "wallet_only", - "description" : "Whether DID is current public DID, posted to ledger but not current public DID, or local to the wallet", - "enum" : [ "public", "posted", "wallet_only" ] - }, - "verkey" : { - "type" : "string", - "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", - "description" : "Public verification key", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" - } - } - }, - "DIDCreate" : { - "type" : "object", - "properties" : { - "method" : { - "type" : "string", - "example" : "sov", - "enum" : [ "key", "sov" ] - }, - "options" : { - "$ref" : "#/definitions/DIDCreate_options" - } - } - }, - "DIDCreateOptions" : { - "type" : "object", - "required" : [ "key_type" ], - "properties" : { - "key_type" : { - "type" : "string", - "example" : "ed25519", - "enum" : [ "ed25519", "bls12381g2" ] - } - } - }, - "DIDEndpoint" : { - "type" : "object", - "required" : [ "did" ], - "properties" : { - "did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "DID of interest", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "endpoint" : { - "type" : "string", - "example" : "https://myhost:8021", - "description" : "Endpoint to set (omit to delete)", - "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" - } - } - }, - "DIDEndpointWithType" : { - "type" : "object", - "required" : [ "did" ], - "properties" : { - "did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "DID of interest", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "endpoint" : { - "type" : "string", - "example" : "https://myhost:8021", - "description" : "Endpoint to set (omit to delete)", - "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" - }, - "endpoint_type" : { - "type" : "string", - "example" : "Endpoint", - "description" : "Endpoint type to set (default 'Endpoint'); affects only public or posted DIDs", - "enum" : [ "Endpoint", "Profile", "LinkedDomains" ] - } - } - }, - "DIDList" : { - "type" : "object", - "properties" : { - "results" : { - "type" : "array", - "description" : "DID list", - "items" : { - "$ref" : "#/definitions/DID" - } - } - } - }, - "DIDResult" : { - "type" : "object", - "properties" : { - "result" : { - "$ref" : "#/definitions/DID" - } - } - }, - "DIDXRequest" : { - "type" : "object", - "required" : [ "label" ], - "properties" : { - "@id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Message identifier" - }, - "@type" : { - "type" : "string", - "example" : "https://didcomm.org/my-family/1.0/my-message-type", - "description" : "Message type", - "readOnly" : true - }, - "did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "DID of exchange", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "did_doc~attach" : { - "$ref" : "#/definitions/DIDXRequest_did_docattach" - }, - "label" : { - "type" : "string", - "example" : "Request to connect with Bob", - "description" : "Label for DID exchange request" - } - } - }, - "DIFField" : { - "type" : "object", - "properties" : { - "filter" : { - "$ref" : "#/definitions/Filter" - }, - "id" : { - "type" : "string", - "description" : "ID" - }, - "path" : { - "type" : "array", - "items" : { - "type" : "string", - "description" : "Path" - } - }, - "predicate" : { - "type" : "string", - "description" : "Preference", - "enum" : [ "required", "preferred" ] - }, - "purpose" : { - "type" : "string", - "description" : "Purpose" - } - } - }, - "DIFHolder" : { - "type" : "object", - "properties" : { - "directive" : { - "type" : "string", - "description" : "Preference", - "enum" : [ "required", "preferred" ] - }, - "field_id" : { - "type" : "array", - "items" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "FieldID", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - } - } - } - }, - "DIFOptions" : { - "type" : "object", - "properties" : { - "challenge" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Challenge protect against replay attack", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - }, - "domain" : { - "type" : "string", - "example" : "4jt78h47fh47", - "description" : "Domain protect against replay attack" - } - } - }, - "DIFPresSpec" : { - "type" : "object", - "properties" : { - "issuer_id" : { - "type" : "string", - "description" : "Issuer identifier to sign the presentation, if different from current public DID" - }, - "presentation_definition" : { - "$ref" : "#/definitions/PresentationDefinition" - }, - "record_ids" : { - "type" : "object", - "example" : { - "" : [ "", "" ], - "" : [ "" ] - }, - "description" : "Mapping of input_descriptor id to list of stored W3C credential record_id", - "properties" : { } - }, - "reveal_doc" : { - "type" : "object", - "example" : { - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://w3id.org/security/bbs/v1" - ], - "type": ["VerifiableCredential", "LabReport"], - "@explicit": true, - "@requireAll": true, - "issuanceDate": {}, - "issuer": {}, - "credentialSubject": { - "Observation": [ - {"effectiveDateTime": {}, "@explicit": true, "@requireAll": true} - ], - "@explicit": true, - "@requireAll": true - } + "required" : [ "kid" ], + "type" : "object" + }, + "AttachmentDef" : { + "properties" : { + "id" : { + "description" : "Attachment identifier", + "example" : "attachment-0", + "type" : "string" }, - "description" : "reveal doc [JSON-LD frame] dict used to derive the credential when selective disclosure is required", - "properties" : { } - } - } - }, - "DIFProofProposal" : { - "type" : "object", - "properties" : { - "input_descriptors" : { - "type" : "array", - "items" : { - "$ref" : "#/definitions/InputDescriptors" - } - } - } - }, - "DIFProofRequest" : { - "type" : "object", - "required" : [ "presentation_definition" ], - "properties" : { - "options" : { - "$ref" : "#/definitions/DIFOptions" - }, - "presentation_definition" : { - "$ref" : "#/definitions/PresentationDefinition" - } - } - }, - "Date" : { - "type" : "object", - "required" : [ "expires_time" ], - "properties" : { - "expires_time" : { - "type" : "string", - "format" : "date-time", - "example" : "2021-03-29T05:22:19Z", - "description" : "Expiry Date" - } - } - }, - "Doc" : { - "type" : "object", - "required" : [ "credential", "options" ], - "properties" : { - "credential" : { - "type" : "object", - "description" : "Credential to sign", - "properties" : { } - }, - "options" : { - "$ref" : "#/definitions/Doc_options" - } - } - }, - "EndorserInfo" : { - "type" : "object", - "required" : [ "endorser_did" ], - "properties" : { - "endorser_did" : { - "type" : "string", - "description" : "Endorser DID" - }, - "endorser_name" : { - "type" : "string", - "description" : "Endorser Name" - } - } - }, - "EndpointsResult" : { - "type" : "object", - "properties" : { - "my_endpoint" : { - "type" : "string", - "example" : "https://myhost:8021", - "description" : "My endpoint", - "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" - }, - "their_endpoint" : { - "type" : "string", - "example" : "https://myhost:8021", - "description" : "Their endpoint", - "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" - } - } - }, - "Filter" : { - "type" : "object", - "properties" : { - "const" : { - "description" : "Const" - }, - "enum" : { - "type" : "array", - "items" : { - "description" : "Enum" - } - }, - "exclusiveMaximum" : { - "description" : "ExclusiveMaximum" - }, - "exclusiveMinimum" : { - "description" : "ExclusiveMinimum" - }, - "format" : { - "type" : "string", - "description" : "Format" - }, - "maxLength" : { - "type" : "integer", - "format" : "int32", - "example" : 1234, - "description" : "Max Length" - }, - "maximum" : { - "description" : "Maximum" - }, - "minLength" : { - "type" : "integer", - "format" : "int32", - "example" : 1234, - "description" : "Min Length" - }, - "minimum" : { - "description" : "Minimum" - }, - "not" : { - "type" : "boolean", - "example" : false, - "description" : "Not" - }, - "pattern" : { - "type" : "string", - "description" : "Pattern" - }, - "type" : { - "type" : "string", - "description" : "Type" - } - } - }, - "Generated" : { - "type" : "object", - "properties" : { - "master_secret" : { - "type" : "string", - "example" : "0", - "pattern" : "^[0-9]*$" - }, - "number" : { - "type" : "string", - "example" : "0", - "pattern" : "^[0-9]*$" - }, - "remainder" : { - "type" : "string", - "example" : "0", - "pattern" : "^[0-9]*$" - } - } - }, - "GetDIDEndpointResponse" : { - "type" : "object", - "properties" : { - "endpoint" : { - "type" : "string", - "example" : "https://myhost:8021", - "description" : "Full verification key", - "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$", - "x-nullable" : true - } - } - }, - "GetDIDVerkeyResponse" : { - "type" : "object", - "properties" : { - "verkey" : { - "type" : "string", - "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", - "description" : "Full verification key", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$", - "x-nullable" : true - } - } - }, - "GetNymRoleResponse" : { - "type" : "object", - "properties" : { - "role" : { - "type" : "string", - "example" : "ENDORSER", - "description" : "Ledger role", - "enum" : [ "STEWARD", "TRUSTEE", "ENDORSER", "NETWORK_MONITOR", "USER", "ROLE_REMOVE" ] - } - } - }, - "HolderModuleResponse" : { - "type" : "object" - }, - "IndyAttrValue" : { - "type" : "object", - "required" : [ "encoded", "raw" ], - "properties" : { - "encoded" : { - "type" : "string", - "example" : "0", - "description" : "Attribute encoded value", - "pattern" : "^[0-9]*$" - }, - "raw" : { - "type" : "string", - "description" : "Attribute raw value" - } - } - }, - "IndyCredAbstract" : { - "type" : "object", - "required" : [ "cred_def_id", "key_correctness_proof", "nonce", "schema_id" ], - "properties" : { - "cred_def_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", - "description" : "Credential definition identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - }, - "key_correctness_proof" : { - "$ref" : "#/definitions/IndyCredAbstract_key_correctness_proof" - }, - "nonce" : { - "type" : "string", - "example" : "0", - "description" : "Nonce in credential abstract", - "pattern" : "^[0-9]*$" - }, - "schema_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", - "description" : "Schema identifier", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" - } - } - }, - "IndyCredInfo" : { - "type" : "object", - "properties" : { - "attrs" : { - "type" : "object", - "description" : "Attribute names and value", - "additionalProperties" : { - "type" : "string", - "example" : "alice" + "type" : { + "description" : "Attachment type", + "enum" : [ "credential-offer", "present-proof" ], + "example" : "present-proof", + "type" : "string" } }, - "cred_def_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", - "description" : "Credential definition identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - }, - "cred_rev_id" : { - "type" : "string", - "example" : "12345", - "description" : "Credential revocation identifier", - "pattern" : "^[1-9][0-9]*$", - "x-nullable" : true - }, - "referent" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Wallet referent" - }, - "rev_reg_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:4:WgWxqztrNooG92RXvxSTWv:3:CL:20:tag:CL_ACCUM:0", - "description" : "Revocation registry identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)", - "x-nullable" : true - }, - "schema_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", - "description" : "Schema identifier", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" - } - } - }, - "IndyCredPrecis" : { - "type" : "object", - "properties" : { - "cred_info" : { - "$ref" : "#/definitions/IndyCredPrecis_cred_info" - }, - "interval" : { - "$ref" : "#/definitions/IndyCredPrecis_interval" - }, - "presentation_referents" : { - "type" : "array", - "items" : { - "type" : "string", - "example" : "1_age_uuid", - "description" : "presentation referent" - } - } - } - }, - "IndyCredRequest" : { - "type" : "object", - "required" : [ "blinded_ms", "blinded_ms_correctness_proof", "cred_def_id", "nonce" ], - "properties" : { - "blinded_ms" : { - "type" : "object", - "description" : "Blinded master secret", - "properties" : { } - }, - "blinded_ms_correctness_proof" : { - "type" : "object", - "description" : "Blinded master secret correctness proof", - "properties" : { } - }, - "cred_def_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", - "description" : "Credential definition identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - }, - "nonce" : { - "type" : "string", - "example" : "0", - "description" : "Nonce in credential request", - "pattern" : "^[0-9]*$" - }, - "prover_did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "Prover DID", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - } - } - }, - "IndyCredential" : { - "type" : "object", - "required" : [ "cred_def_id", "schema_id", "signature", "signature_correctness_proof", "values" ], - "properties" : { - "cred_def_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", - "description" : "Credential definition identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - }, - "rev_reg" : { - "type" : "object", - "description" : "Revocation registry state", - "properties" : { }, - "x-nullable" : true - }, - "rev_reg_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:4:WgWxqztrNooG92RXvxSTWv:3:CL:20:tag:CL_ACCUM:0", - "description" : "Revocation registry identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)", - "x-nullable" : true - }, - "schema_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", - "description" : "Schema identifier", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" - }, - "signature" : { - "type" : "object", - "description" : "Credential signature", - "properties" : { } - }, - "signature_correctness_proof" : { - "type" : "object", - "description" : "Credential signature correctness proof", - "properties" : { } - }, - "values" : { - "type" : "object", - "description" : "Credential attributes", - "additionalProperties" : { - "type" : "object", - "description" : "Attribute value", - "allOf" : [ { - "$ref" : "#/definitions/IndyAttrValue" - } ] - } - }, - "witness" : { - "type" : "object", - "description" : "Witness for revocation proof", - "properties" : { }, - "x-nullable" : true - } - } - }, - "IndyEQProof" : { - "type" : "object", - "properties" : { - "a_prime" : { - "type" : "string", - "example" : "0", - "pattern" : "^[0-9]*$" - }, - "e" : { - "type" : "string", - "example" : "0", - "pattern" : "^[0-9]*$" - }, - "m" : { - "type" : "object", - "additionalProperties" : { - "type" : "string", - "example" : "0", - "pattern" : "^[0-9]*$" + "type" : "object" + }, + "AttributeMimeTypesResult" : { + "properties" : { + "results" : { + "additionalProperties" : { + "description" : "MIME type", + "type" : "string" + }, + "nullable" : true, + "type" : "object" } }, - "m2" : { - "type" : "string", - "example" : "0", - "pattern" : "^[0-9]*$" - }, - "revealed_attrs" : { - "type" : "object", - "additionalProperties" : { - "type" : "string", - "example" : "0", - "pattern" : "^[0-9]*$" + "type" : "object" + }, + "BasicMessageModuleResponse" : { + "type" : "object" + }, + "ClaimFormat" : { + "properties" : { + "jwt" : { + "properties" : { }, + "type" : "object" + }, + "jwt_vc" : { + "properties" : { }, + "type" : "object" + }, + "jwt_vp" : { + "properties" : { }, + "type" : "object" + }, + "ldp" : { + "properties" : { }, + "type" : "object" + }, + "ldp_vc" : { + "properties" : { }, + "type" : "object" + }, + "ldp_vp" : { + "properties" : { }, + "type" : "object" } }, - "v" : { - "type" : "string", - "example" : "0", - "pattern" : "^[0-9]*$" - } - } - }, - "IndyGEProof" : { - "type" : "object", - "properties" : { - "alpha" : { - "type" : "string", - "example" : "0", - "pattern" : "^[0-9]*$" - }, - "mj" : { - "type" : "string", - "example" : "0", - "pattern" : "^[0-9]*$" - }, - "predicate" : { - "$ref" : "#/definitions/IndyGEProofPred" - }, - "r" : { - "type" : "object", - "additionalProperties" : { - "type" : "string", - "example" : "0", - "pattern" : "^[0-9]*$" + "type" : "object" + }, + "ClearPendingRevocationsRequest" : { + "properties" : { + "purge" : { + "additionalProperties" : { + "items" : { + "description" : "Credential revocation identifier", + "example" : "12345", + "pattern" : "^[1-9][0-9]*$", + "type" : "string" + }, + "type" : "array" + }, + "description" : "Credential revocation ids by revocation registry id: omit for all, specify null or empty list for all pending per revocation registry", + "type" : "object" } }, - "t" : { - "type" : "object", - "additionalProperties" : { - "type" : "string", - "example" : "0", - "pattern" : "^[0-9]*$" + "type" : "object" + }, + "ConnRecord" : { + "properties" : { + "accept" : { + "description" : "Connection acceptance: manual or auto", + "enum" : [ "manual", "auto" ], + "example" : "auto", + "type" : "string" + }, + "alias" : { + "description" : "Optional alias to apply to connection for later use", + "example" : "Bob, providing quotes", + "type" : "string" + }, + "connection_id" : { + "description" : "Connection identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "connection_protocol" : { + "description" : "Connection protocol used", + "enum" : [ "connections/1.0", "didexchange/1.0" ], + "example" : "connections/1.0", + "type" : "string" + }, + "created_at" : { + "description" : "Time of record creation", + "example" : "2021-12-31T23:59:59Z", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$", + "type" : "string" + }, + "error_msg" : { + "description" : "Error message", + "example" : "No DIDDoc provided; cannot connect to public DID", + "type" : "string" + }, + "inbound_connection_id" : { + "description" : "Inbound routing connection id to use", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "invitation_key" : { + "description" : "Public key for connection", + "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$", + "type" : "string" + }, + "invitation_mode" : { + "description" : "Invitation mode", + "enum" : [ "once", "multi", "static" ], + "example" : "once", + "type" : "string" + }, + "invitation_msg_id" : { + "description" : "ID of out-of-band invitation message", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "my_did" : { + "description" : "Our DID for connection", + "example" : "WgWxqztrNooG92RXvxSTWv", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$", + "type" : "string" + }, + "request_id" : { + "description" : "Connection request identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "rfc23_state" : { + "description" : "State per RFC 23", + "example" : "invitation-sent", + "readOnly" : true, + "type" : "string" + }, + "routing_state" : { + "description" : "Routing state of connection", + "enum" : [ "none", "request", "active", "error" ], + "example" : "active", + "type" : "string" + }, + "state" : { + "description" : "Current record state", + "example" : "active", + "type" : "string" + }, + "their_did" : { + "description" : "Their DID for connection", + "example" : "WgWxqztrNooG92RXvxSTWv", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$", + "type" : "string" + }, + "their_label" : { + "description" : "Their label for connection", + "example" : "Bob", + "type" : "string" + }, + "their_public_did" : { + "description" : "Other agent's public DID for connection", + "example" : "2cpBmR3FqGKWi5EyUbpRY8", + "type" : "string" + }, + "their_role" : { + "description" : "Their role in the connection protocol", + "enum" : [ "invitee", "requester", "inviter", "responder" ], + "example" : "requester", + "type" : "string" + }, + "updated_at" : { + "description" : "Time of last record update", + "example" : "2021-12-31T23:59:59Z", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$", + "type" : "string" } }, - "u" : { - "type" : "object", - "additionalProperties" : { - "type" : "string", - "example" : "0", - "pattern" : "^[0-9]*$" - } - } - } - }, - "IndyGEProofPred" : { - "type" : "object", - "properties" : { - "attr_name" : { - "type" : "string", - "description" : "Attribute name, indy-canonicalized" - }, - "p_type" : { - "type" : "string", - "description" : "Predicate type", - "enum" : [ "LT", "LE", "GE", "GT" ] - }, - "value" : { - "type" : "integer", - "format" : "int32", - "description" : "Predicate threshold value" - } - } - }, - "IndyKeyCorrectnessProof" : { - "type" : "object", - "required" : [ "c", "xr_cap", "xz_cap" ], - "properties" : { - "c" : { - "type" : "string", - "example" : "0", - "description" : "c in key correctness proof", - "pattern" : "^[0-9]*$" - }, - "xr_cap" : { - "type" : "array", - "description" : "xr_cap in key correctness proof", - "items" : { - "type" : "array", - "description" : "xr_cap components in key correctness proof", + "type" : "object" + }, + "ConnectionInvitation" : { + "properties" : { + "@id" : { + "description" : "Message identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "@type" : { + "description" : "Message type", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "readOnly" : true, + "type" : "string" + }, + "did" : { + "description" : "DID for connection invitation", + "example" : "WgWxqztrNooG92RXvxSTWv", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$", + "type" : "string" + }, + "imageUrl" : { + "description" : "Optional image URL for connection invitation", + "example" : "http://192.168.56.101/img/logo.jpg", + "format" : "url", + "nullable" : true, + "type" : "string" + }, + "label" : { + "description" : "Optional label for connection invitation", + "example" : "Bob", + "type" : "string" + }, + "recipientKeys" : { + "description" : "List of recipient keys", "items" : { - "type" : "string", - "description" : "xr_cap component values in key correctness proof" - } - } - }, - "xz_cap" : { - "type" : "string", - "example" : "0", - "description" : "xz_cap in key correctness proof", - "pattern" : "^[0-9]*$" - } - } - }, - "IndyNonRevocProof" : { - "type" : "object", - "properties" : { - "c_list" : { - "type" : "object", - "additionalProperties" : { - "type" : "string" - } - }, - "x_list" : { - "type" : "object", - "additionalProperties" : { - "type" : "string" - } - } - } - }, - "IndyNonRevocationInterval" : { - "type" : "object", - "properties" : { - "from" : { - "type" : "integer", - "format" : "int32", - "example" : 1640995199, - "description" : "Earliest time of interest in non-revocation interval", - "minimum" : 0, - "maximum" : 18446744073709551615 - }, - "to" : { - "type" : "integer", - "format" : "int32", - "example" : 1640995199, - "description" : "Latest time of interest in non-revocation interval", - "minimum" : 0, - "maximum" : 18446744073709551615 - } - } - }, - "IndyPresAttrSpec" : { - "type" : "object", - "required" : [ "name" ], - "properties" : { - "cred_def_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - }, - "mime-type" : { - "type" : "string", - "example" : "image/jpeg", - "description" : "MIME type (default null)" - }, - "name" : { - "type" : "string", - "example" : "favourite_drink", - "description" : "Attribute name" - }, - "referent" : { - "type" : "string", - "example" : "0", - "description" : "Credential referent" - }, - "value" : { - "type" : "string", - "example" : "martini", - "description" : "Attribute value" - } - } - }, - "IndyPresPredSpec" : { - "type" : "object", - "required" : [ "name", "predicate", "threshold" ], - "properties" : { - "cred_def_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", - "description" : "Credential definition identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - }, - "name" : { - "type" : "string", - "example" : "high_score", - "description" : "Attribute name" - }, - "predicate" : { - "type" : "string", - "example" : ">=", - "description" : "Predicate type ('<', '<=', '>=', or '>')", - "enum" : [ "<", "<=", ">=", ">" ] - }, - "threshold" : { - "type" : "integer", - "format" : "int32", - "description" : "Threshold value" - } - } - }, - "IndyPresPreview" : { - "type" : "object", - "required" : [ "attributes", "predicates" ], - "properties" : { - "@type" : { - "type" : "string", - "example" : "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/present-proof/1.0/presentation-preview", - "description" : "Message type identifier" - }, - "attributes" : { - "type" : "array", - "items" : { - "$ref" : "#/definitions/IndyPresAttrSpec" - } - }, - "predicates" : { - "type" : "array", - "items" : { - "$ref" : "#/definitions/IndyPresPredSpec" - } - } - } - }, - "IndyPresSpec" : { - "type" : "object", - "required" : [ "requested_attributes", "requested_predicates", "self_attested_attributes" ], - "properties" : { - "requested_attributes" : { - "type" : "object", - "description" : "Nested object mapping proof request attribute referents to requested-attribute specifiers", - "additionalProperties" : { - "$ref" : "#/definitions/IndyRequestedCredsRequestedAttr" - } - }, - "requested_predicates" : { - "type" : "object", - "description" : "Nested object mapping proof request predicate referents to requested-predicate specifiers", - "additionalProperties" : { - "$ref" : "#/definitions/IndyRequestedCredsRequestedPred" - } - }, - "self_attested_attributes" : { - "type" : "object", - "description" : "Self-attested attributes to build into proof", - "additionalProperties" : { - "type" : "string", - "example" : "self_attested_value", - "description" : "Self-attested attribute values to use in requested-credentials structure for proof construction" - } - }, - "trace" : { - "type" : "boolean", - "example" : false, - "description" : "Whether to trace event (default false)" - } - } - }, - "IndyPrimaryProof" : { - "type" : "object", - "properties" : { - "eq_proof" : { - "$ref" : "#/definitions/IndyPrimaryProof_eq_proof" - }, - "ge_proofs" : { - "type" : "array", - "description" : "Indy GE proofs", - "items" : { - "$ref" : "#/definitions/IndyGEProof" - }, - "x-nullable" : true - } - } - }, - "IndyProof" : { - "type" : "object", - "properties" : { - "identifiers" : { - "type" : "array", - "description" : "Indy proof.identifiers content", - "items" : { - "$ref" : "#/definitions/IndyProofIdentifier" - } - }, - "proof" : { - "$ref" : "#/definitions/IndyProof_proof" - }, - "requested_proof" : { - "$ref" : "#/definitions/IndyProof_requested_proof" - } - } - }, - "IndyProofIdentifier" : { - "type" : "object", - "properties" : { - "cred_def_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", - "description" : "Credential definition identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - }, - "rev_reg_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:4:WgWxqztrNooG92RXvxSTWv:3:CL:20:tag:CL_ACCUM:0", - "description" : "Revocation registry identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)", - "x-nullable" : true - }, - "schema_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", - "description" : "Schema identifier", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" - }, - "timestamp" : { - "type" : "integer", - "format" : "int32", - "example" : 1640995199, - "description" : "Timestamp epoch", - "minimum" : 0, - "maximum" : 18446744073709551615, - "x-nullable" : true - } - } - }, - "IndyProofProof" : { - "type" : "object", - "properties" : { - "aggregated_proof" : { - "$ref" : "#/definitions/IndyProofProof_aggregated_proof" - }, - "proofs" : { - "type" : "array", - "description" : "Indy proof proofs", - "items" : { - "$ref" : "#/definitions/IndyProofProofProofsProof" - } - } - } - }, - "IndyProofProofAggregatedProof" : { - "type" : "object", - "properties" : { - "c_hash" : { - "type" : "string", - "description" : "c_hash value" - }, - "c_list" : { - "type" : "array", - "description" : "c_list value", - "items" : { - "type" : "array", + "description" : "Recipient public key", + "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$", + "type" : "string" + }, + "type" : "array" + }, + "routingKeys" : { + "description" : "List of routing keys", "items" : { - "type" : "integer", - "format" : "int32" - } - } - } - } - }, - "IndyProofProofProofsProof" : { - "type" : "object", - "properties" : { - "non_revoc_proof" : { - "$ref" : "#/definitions/IndyProofProofProofsProof_non_revoc_proof" - }, - "primary_proof" : { - "$ref" : "#/definitions/IndyProofProofProofsProof_primary_proof" - } - } - }, - "IndyProofReqAttrSpec" : { - "type" : "object", - "properties" : { - "name" : { - "type" : "string", - "example" : "favouriteDrink", - "description" : "Attribute name" - }, - "names" : { - "type" : "array", - "description" : "Attribute name group", - "items" : { - "type" : "string", - "example" : "age" - } - }, - "non_revoked" : { - "$ref" : "#/definitions/IndyProofReqAttrSpec_non_revoked" - }, - "restrictions" : { - "type" : "array", - "description" : "If present, credential must satisfy one of given restrictions: specify schema_id, schema_issuer_did, schema_name, schema_version, issuer_did, cred_def_id, and/or attr::::value where represents a credential attribute name", - "items" : { - "type" : "object", - "additionalProperties" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag" - } - } - } - } - }, - "IndyProofReqAttrSpecNonRevoked" : { - "type" : "object", - "properties" : { - "from" : { - "type" : "integer", - "format" : "int32", - "example" : 1640995199, - "description" : "Earliest time of interest in non-revocation interval", - "minimum" : 0, - "maximum" : 18446744073709551615 - }, - "to" : { - "type" : "integer", - "format" : "int32", - "example" : 1640995199, - "description" : "Latest time of interest in non-revocation interval", - "minimum" : 0, - "maximum" : 18446744073709551615 - } - } - }, - "IndyProofReqPredSpec" : { - "type" : "object", - "required" : [ "name", "p_type", "p_value" ], - "properties" : { - "name" : { - "type" : "string", - "example" : "index", - "description" : "Attribute name" - }, - "non_revoked" : { - "$ref" : "#/definitions/IndyProofReqAttrSpec_non_revoked" - }, - "p_type" : { - "type" : "string", - "example" : ">=", - "description" : "Predicate type ('<', '<=', '>=', or '>')", - "enum" : [ "<", "<=", ">=", ">" ] - }, - "p_value" : { - "type" : "integer", - "format" : "int32", - "description" : "Threshold value" - }, - "restrictions" : { - "type" : "array", - "description" : "If present, credential must satisfy one of given restrictions: specify schema_id, schema_issuer_did, schema_name, schema_version, issuer_did, cred_def_id, and/or attr::::value where represents a credential attribute name", - "items" : { - "type" : "object", - "additionalProperties" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag" - } - } - } - } - }, - "IndyProofReqPredSpecNonRevoked" : { - "type" : "object", - "properties" : { - "from" : { - "type" : "integer", - "format" : "int32", - "example" : 1640995199, - "description" : "Earliest time of interest in non-revocation interval", - "minimum" : 0, - "maximum" : 18446744073709551615 - }, - "to" : { - "type" : "integer", - "format" : "int32", - "example" : 1640995199, - "description" : "Latest time of interest in non-revocation interval", - "minimum" : 0, - "maximum" : 18446744073709551615 - } - } - }, - "IndyProofRequest" : { - "type" : "object", - "properties" : { - "name" : { - "type" : "string", - "example" : "Proof request", - "description" : "Proof request name" - }, - "non_revoked" : { - "$ref" : "#/definitions/IndyProofReqAttrSpec_non_revoked" - }, - "nonce" : { - "type" : "string", - "example" : "1", - "description" : "Nonce", - "pattern" : "^[1-9][0-9]*$" - }, - "requested_attributes" : { - "type" : "object", - "description" : "Requested attribute specifications of proof request", - "additionalProperties" : { - "$ref" : "#/definitions/IndyProofReqAttrSpec" - } - }, - "requested_predicates" : { - "type" : "object", - "description" : "Requested predicate specifications of proof request", - "additionalProperties" : { - "$ref" : "#/definitions/IndyProofReqPredSpec" - } - }, - "version" : { - "type" : "string", - "example" : "1.0", - "description" : "Proof request version", - "pattern" : "^[0-9.]+$" - } - } - }, - "IndyProofRequestNonRevoked" : { - "type" : "object", - "properties" : { - "from" : { - "type" : "integer", - "format" : "int32", - "example" : 1640995199, - "description" : "Earliest time of interest in non-revocation interval", - "minimum" : 0, - "maximum" : 18446744073709551615 - }, - "to" : { - "type" : "integer", - "format" : "int32", - "example" : 1640995199, - "description" : "Latest time of interest in non-revocation interval", - "minimum" : 0, - "maximum" : 18446744073709551615 - } - } - }, - "IndyProofRequestedProof" : { - "type" : "object", - "properties" : { - "predicates" : { - "type" : "object", - "description" : "Proof requested proof predicates.", - "additionalProperties" : { - "$ref" : "#/definitions/IndyProofRequestedProofPredicate" - } - }, - "revealed_attr_groups" : { - "type" : "object", - "description" : "Proof requested proof revealed attribute groups", - "additionalProperties" : { - "$ref" : "#/definitions/IndyProofRequestedProofRevealedAttrGroup" - }, - "x-nullable" : true - }, - "revealed_attrs" : { - "type" : "object", - "description" : "Proof requested proof revealed attributes", - "additionalProperties" : { - "$ref" : "#/definitions/IndyProofRequestedProofRevealedAttr" - }, - "x-nullable" : true - }, - "self_attested_attrs" : { - "type" : "object", - "description" : "Proof requested proof self-attested attributes", - "properties" : { } - }, - "unrevealed_attrs" : { - "type" : "object", - "description" : "Unrevealed attributes", - "properties" : { } - } - } - }, - "IndyProofRequestedProofPredicate" : { - "type" : "object", - "properties" : { - "sub_proof_index" : { - "type" : "integer", - "format" : "int32", - "description" : "Sub-proof index" - } - } - }, - "IndyProofRequestedProofRevealedAttr" : { - "type" : "object", - "properties" : { - "encoded" : { - "type" : "string", - "example" : "0", - "description" : "Encoded value", - "pattern" : "^[0-9]*$" - }, - "raw" : { - "type" : "string", - "description" : "Raw value" - }, - "sub_proof_index" : { - "type" : "integer", - "format" : "int32", - "description" : "Sub-proof index" - } - } - }, - "IndyProofRequestedProofRevealedAttrGroup" : { - "type" : "object", - "properties" : { - "sub_proof_index" : { - "type" : "integer", - "format" : "int32", - "description" : "Sub-proof index" - }, - "values" : { - "type" : "object", - "description" : "Indy proof requested proof revealed attr groups group value", - "additionalProperties" : { - "$ref" : "#/definitions/RawEncoded" - } - } - } - }, - "IndyRequestedCredsRequestedAttr" : { - "type" : "object", - "required" : [ "cred_id" ], - "properties" : { - "cred_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Wallet credential identifier (typically but not necessarily a UUID)" - }, - "revealed" : { - "type" : "boolean", - "description" : "Whether to reveal attribute in proof (default true)" - } - } - }, - "IndyRequestedCredsRequestedPred" : { - "type" : "object", - "required" : [ "cred_id" ], - "properties" : { - "cred_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Wallet credential identifier (typically but not necessarily a UUID)" - }, - "timestamp" : { - "type" : "integer", - "format" : "int32", - "example" : 1640995199, - "description" : "Epoch timestamp of interest for non-revocation proof", - "minimum" : 0, - "maximum" : 18446744073709551615 - } - } - }, - "IndyRevRegDef" : { - "type" : "object", - "properties" : { - "credDefId" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", - "description" : "Credential definition identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - }, - "id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:4:WgWxqztrNooG92RXvxSTWv:3:CL:20:tag:CL_ACCUM:0", - "description" : "Indy revocation registry identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" - }, - "revocDefType" : { - "type" : "string", - "example" : "CL_ACCUM", - "description" : "Revocation registry type (specify CL_ACCUM)", - "enum" : [ "CL_ACCUM" ] - }, - "tag" : { - "type" : "string", - "description" : "Revocation registry tag" - }, - "value" : { - "$ref" : "#/definitions/IndyRevRegDef_value" - }, - "ver" : { - "type" : "string", - "example" : "1.0", - "description" : "Version of revocation registry definition", - "pattern" : "^[0-9.]+$" - } - } - }, - "IndyRevRegDefValue" : { - "type" : "object", - "properties" : { - "issuanceType" : { - "type" : "string", - "description" : "Issuance type", - "enum" : [ "ISSUANCE_ON_DEMAND", "ISSUANCE_BY_DEFAULT" ] - }, - "maxCredNum" : { - "type" : "integer", - "format" : "int32", - "example" : 10, - "description" : "Maximum number of credentials; registry size", - "minimum" : 1 - }, - "publicKeys" : { - "$ref" : "#/definitions/IndyRevRegDefValue_publicKeys" - }, - "tailsHash" : { - "type" : "string", - "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", - "description" : "Tails hash value", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" - }, - "tailsLocation" : { - "type" : "string", - "description" : "Tails file location" - } - } - }, - "IndyRevRegDefValuePublicKeys" : { - "type" : "object", - "properties" : { - "accumKey" : { - "$ref" : "#/definitions/IndyRevRegDefValuePublicKeysAccumKey" - } - } - }, - "IndyRevRegDefValuePublicKeysAccumKey" : { - "type" : "object", - "properties" : { - "z" : { - "type" : "string", - "example" : "1 120F522F81E6B7 1 09F7A59005C4939854", - "description" : "Value for z" - } - } - }, - "IndyRevRegEntry" : { - "type" : "object", - "properties" : { - "value" : { - "$ref" : "#/definitions/IndyRevRegEntry_value" - }, - "ver" : { - "type" : "string", - "example" : "1.0", - "description" : "Version of revocation registry entry", - "pattern" : "^[0-9.]+$" - } - } - }, - "IndyRevRegEntryValue" : { - "type" : "object", - "properties" : { - "accum" : { - "type" : "string", - "example" : "21 11792B036AED0AAA12A4 4 298B2571FFC63A737", - "description" : "Accumulator value" - }, - "prevAccum" : { - "type" : "string", - "example" : "21 137AC810975E4 6 76F0384B6F23", - "description" : "Previous accumulator value" - }, - "revoked" : { - "type" : "array", - "description" : "Revoked credential revocation identifiers", - "items" : { - "type" : "integer", - "format" : "int32" - } - } - } - }, - "InputDescriptors" : { - "type" : "object", - "properties" : { - "constraints" : { - "$ref" : "#/definitions/Constraints" - }, - "group" : { - "type" : "array", - "items" : { - "type" : "string", - "description" : "Group" - } - }, - "id" : { - "type" : "string", - "description" : "ID" - }, - "metadata" : { - "type" : "object", - "description" : "Metadata dictionary", - "properties" : { } - }, - "name" : { - "type" : "string", - "description" : "Name" - }, - "purpose" : { - "type" : "string", - "description" : "Purpose" - } - } - }, - "IntroModuleResponse" : { - "type" : "object" - }, - "InvitationCreateRequest" : { - "type" : "object", - "properties" : { - "alias" : { - "type" : "string", - "example" : "Barry", - "description" : "Alias for connection" - }, - "attachments" : { - "type" : "array", - "description" : "Optional invitation attachments", - "items" : { - "$ref" : "#/definitions/AttachmentDef" - } - }, - "handshake_protocols" : { - "type" : "array", - "items" : { - "type" : "string", - "example" : "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/didexchange/1.0", - "description" : "Handshake protocol to specify in invitation" - } - }, - "accept" : { - "type" : "array", - "items" : { - "type" : "string", - "example" : "didcomm/aip1", - "description" : "Mime type list in order of preference to be used in response" - } - }, - "mediation_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Identifier for active mediation record to be used", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - }, - "metadata" : { - "type" : "object", - "description" : "Optional metadata to attach to the connection created with the invitation", - "properties" : { } - }, - "my_label" : { - "type" : "string", - "example" : "Invitation to Barry", - "description" : "Label for connection invitation" - }, - "use_public_did" : { - "type" : "boolean", - "example" : false, - "description" : "Whether to use public DID in invitation" - } - } - }, - "InvitationMessage" : { - "type" : "object", - "properties" : { - "@id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Message identifier" - }, - "@type" : { - "type" : "string", - "example" : "https://didcomm.org/my-family/1.0/my-message-type", - "description" : "Message type", - "readOnly" : true - }, - "handshake_protocols" : { - "type" : "array", - "items" : { - "type" : "string", - "example" : "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/didexchange/1.0", - "description" : "Handshake protocol" - } - }, - "accept" : { - "type" : "array", - "items" : { - "type" : "string", - "example" : "didcomm/aip1", - "description" : "Mime type list in order of preference to be used in response" - } - }, - "label" : { - "type" : "string", - "example" : "Bob", - "description" : "Optional label" - }, - "requests~attach" : { - "type" : "array", - "description" : "Optional request attachment", - "items" : { - "$ref" : "#/definitions/AttachDecorator" - } - }, - "services" : { - "type" : "array", - "example" : [ { - "did" : "WgWxqztrNooG92RXvxSTWv", - "id" : "string", - "recipientKeys" : [ "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH" ], - "routingKeys" : [ "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH" ], - "serviceEndpoint" : "http://192.168.56.101:8020", - "type" : "string" - }, "did:sov:WgWxqztrNooG92RXvxSTWv" ], - "items" : { - "description" : "Either a DIDComm service object (as per RFC0067) or a DID string." - } - } - } - }, - "InvitationRecord" : { - "type" : "object", - "properties" : { - "created_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of record creation", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "invi_msg_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Invitation message identifier" - }, - "invitation" : { - "$ref" : "#/definitions/InvitationRecord_invitation" - }, - "invitation_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Invitation record identifier" - }, - "invitation_url" : { - "type" : "string", - "example" : "https://example.com/endpoint?c_i=eyJAdHlwZSI6ICIuLi4iLCAiLi4uIjogIi4uLiJ9XX0=", - "description" : "Invitation message URL" - }, - "state" : { - "type" : "string", - "example" : "await_response", - "description" : "Out of band message exchange state" - }, - "trace" : { - "type" : "boolean", - "description" : "Record trace information, based on agent configuration" - }, - "updated_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of last record update", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - } - } - }, - "InvitationResult" : { - "type" : "object", - "properties" : { - "connection_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Connection identifier" - }, - "invitation" : { - "$ref" : "#/definitions/ConnectionInvitation" - }, - "invitation_url" : { - "type" : "string", - "example" : "http://192.168.56.101:8020/invite?c_i=eyJAdHlwZSI6Li4ufQ==", - "description" : "Invitation URL" - } - } - }, - "IssueCredentialModuleResponse" : { - "type" : "object" - }, - "IssuerCredRevRecord" : { - "type" : "object", - "properties" : { - "created_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of record creation", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "cred_def_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", - "description" : "Credential definition identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - }, - "cred_ex_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Credential exchange record identifier at credential issue" - }, - "cred_rev_id" : { - "type" : "string", - "example" : "12345", - "description" : "Credential revocation identifier", - "pattern" : "^[1-9][0-9]*$" - }, - "record_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Issuer credential revocation record identifier" - }, - "rev_reg_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:4:WgWxqztrNooG92RXvxSTWv:3:CL:20:tag:CL_ACCUM:0", - "description" : "Revocation registry identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" - }, - "state" : { - "type" : "string", - "example" : "issued", - "description" : "Issue credential revocation record state" - }, - "updated_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of last record update", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - } - } - }, - "IssuerRevRegRecord" : { - "type" : "object", - "properties" : { - "created_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of record creation", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "cred_def_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", - "description" : "Credential definition identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - }, - "error_msg" : { - "type" : "string", - "example" : "Revocation registry undefined", - "description" : "Error message" - }, - "issuer_did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "Issuer DID", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "max_cred_num" : { - "type" : "integer", - "format" : "int32", - "example" : 1000, - "description" : "Maximum number of credentials for revocation registry" - }, - "pending_pub" : { - "type" : "array", - "description" : "Credential revocation identifier for credential revoked and pending publication to ledger", - "items" : { - "type" : "string", - "example" : "23" - } - }, - "record_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Issuer revocation registry record identifier" - }, - "revoc_def_type" : { - "type" : "string", - "example" : "CL_ACCUM", - "description" : "Revocation registry type (specify CL_ACCUM)", - "enum" : [ "CL_ACCUM" ] - }, - "revoc_reg_def" : { - "$ref" : "#/definitions/IssuerRevRegRecord_revoc_reg_def" - }, - "revoc_reg_entry" : { - "$ref" : "#/definitions/IssuerRevRegRecord_revoc_reg_entry" - }, - "revoc_reg_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:4:WgWxqztrNooG92RXvxSTWv:3:CL:20:tag:CL_ACCUM:0", - "description" : "Revocation registry identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" - }, - "state" : { - "type" : "string", - "example" : "active", - "description" : "Issue revocation registry record state" - }, - "tag" : { - "type" : "string", - "description" : "Tag within issuer revocation registry identifier" - }, - "tails_hash" : { - "type" : "string", - "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", - "description" : "Tails hash", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" - }, - "tails_local_path" : { - "type" : "string", - "description" : "Local path to tails file" - }, - "tails_public_uri" : { - "type" : "string", - "description" : "Public URI for tails file" - }, - "updated_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of last record update", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - } - } - }, - "Keylist" : { - "type" : "object", - "properties" : { - "results" : { - "type" : "array", - "description" : "List of keylist records", - "items" : { - "$ref" : "#/definitions/RouteRecord" - } - } - } - }, - "KeylistQuery" : { - "type" : "object", - "properties" : { - "@id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Message identifier" - }, - "@type" : { - "type" : "string", - "example" : "https://didcomm.org/my-family/1.0/my-message-type", - "description" : "Message type", - "readOnly" : true - }, - "filter" : { - "type" : "object", - "example" : { - "filter" : { } - }, - "description" : "Query dictionary object", - "properties" : { } - }, - "paginate" : { - "$ref" : "#/definitions/KeylistQuery_paginate" - } - } - }, - "KeylistQueryFilterRequest" : { - "type" : "object", - "properties" : { - "filter" : { - "type" : "object", - "description" : "Filter for keylist query", - "properties" : { } - } - } - }, - "KeylistQueryPaginate" : { - "type" : "object", - "properties" : { - "limit" : { - "type" : "integer", - "format" : "int32", - "example" : 30, - "description" : "Limit for keylist query" - }, - "offset" : { - "type" : "integer", - "format" : "int32", - "example" : 0, - "description" : "Offset value for query" - } - } - }, - "KeylistUpdate" : { - "type" : "object", - "properties" : { - "@id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Message identifier" - }, - "@type" : { - "type" : "string", - "example" : "https://didcomm.org/my-family/1.0/my-message-type", - "description" : "Message type", - "readOnly" : true - }, - "updates" : { - "type" : "array", - "description" : "List of update rules", - "items" : { - "$ref" : "#/definitions/KeylistUpdateRule" - } - } - } - }, - "KeylistUpdateRequest" : { - "type" : "object", - "properties" : { - "updates" : { - "type" : "array", - "items" : { - "$ref" : "#/definitions/KeylistUpdateRule" - } - } - } - }, - "KeylistUpdateRule" : { - "type" : "object", - "required" : [ "action", "recipient_key" ], - "properties" : { - "action" : { - "type" : "string", - "example" : "add", - "description" : "Action for specific key", - "enum" : [ "add", "remove" ] - }, - "recipient_key" : { - "type" : "string", - "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", - "description" : "Key to remove or add", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" - } - } - }, - "LDProofVCDetail" : { - "type" : "object", - "required" : [ "credential", "options" ], - "properties" : { - "credential" : { - "$ref" : "#/definitions/LDProofVCDetail_credential" - }, - "options" : { - "$ref" : "#/definitions/LDProofVCDetail_options" - } - } - }, - "LDProofVCDetailOptions" : { - "type" : "object", - "required" : [ "proofType" ], - "properties" : { - "challenge" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "A challenge to include in the proof. SHOULD be provided by the requesting party of the credential (=holder)" - }, - "created" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "The date and time of the proof (with a maximum accuracy in seconds). Defaults to current system time", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "credentialStatus" : { - "$ref" : "#/definitions/LDProofVCDetailOptions_credentialStatus" - }, - "domain" : { - "type" : "string", - "example" : "example.com", - "description" : "The intended domain of validity for the proof" - }, - "proofPurpose" : { - "type" : "string", - "example" : "assertionMethod", - "description" : "The proof purpose used for the proof. Should match proof purposes registered in the Linked Data Proofs Specification" - }, - "proofType" : { - "type" : "string", - "example" : "Ed25519Signature2018", - "description" : "The proof type used for the proof. Should match suites registered in the Linked Data Cryptographic Suite Registry" - } - } - }, - "LedgerModulesResult" : { - "type" : "object" - }, - "LinkedDataProof" : { - "type" : "object", - "required" : [ "created", "proofPurpose", "type", "verificationMethod" ], - "properties" : { - "challenge" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Associates a challenge with a proof, for use with a proofPurpose such as authentication" - }, - "created" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "The string value of an ISO8601 combined date and time string generated by the Signature Algorithm", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "domain" : { - "type" : "string", - "example" : "example.com", - "description" : "A string value specifying the restricted domain of the signature.", - "pattern" : "\\w+:(\\/?\\/?)[^\\s]+" - }, - "jws" : { - "type" : "string", - "example" : "eyJhbGciOiAiRWREUc2UsICJjcml0IjogWyJiNjQiXX0..lKJU0Df_keblRKhZAS9Qq6zybm-HqUXNVZ8vgEPNTAjQ1Ch6YBKY7UBAjg6iBX5qBQ", - "description" : "Associates a Detached Json Web Signature with a proof" - }, - "nonce" : { - "type" : "string", - "example" : "CF69iO3nfvqRsRBNElE8b4wO39SyJHPM7Gg1nExltW5vSfQA1lvDCR/zXX1To0/4NLo==", - "description" : "The nonce" - }, - "proofPurpose" : { - "type" : "string", - "example" : "assertionMethod", - "description" : "Proof purpose" - }, - "proofValue" : { - "type" : "string", - "example" : "sy1AahqbzJQ63n9RtekmwzqZeVj494VppdAVJBnMYrTwft6cLJJGeTSSxCCJ6HKnRtwE7jjDh6sB2z2AAiZY9BBnCD8wUVgwqH3qchGRCuC2RugA4eQ9fUrR4Yuycac3caiaaay", - "description" : "The proof value of a proof" - }, - "type" : { - "type" : "string", - "example" : "Ed25519Signature2018", - "description" : "Identifies the digital signature suite that was used to create the signature" - }, - "verificationMethod" : { - "type" : "string", - "example" : "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", - "description" : "Information used for proof verification", - "pattern" : "\\w+:(\\/?\\/?)[^\\s]+" - } - } - }, - "MediationCreateRequest" : { - "type" : "object", - "properties" : { - "mediator_terms" : { - "type" : "array", - "description" : "List of mediator rules for recipient", - "items" : { - "type" : "string", - "description" : "Indicate terms to which the mediator requires the recipient to agree" - } - }, - "recipient_terms" : { - "type" : "array", - "description" : "List of recipient rules for mediation", - "items" : { - "type" : "string", - "description" : "Indicate terms to which the recipient requires the mediator to agree" + "description" : "Routing key", + "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$", + "type" : "string" + }, + "type" : "array" + }, + "serviceEndpoint" : { + "description" : "Service endpoint at which to reach this agent", + "example" : "http://192.168.56.101:8020", + "type" : "string" } - } - } - }, - "MediationDeny" : { - "type" : "object", - "properties" : { - "@id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Message identifier" }, - "@type" : { - "type" : "string", - "example" : "https://didcomm.org/my-family/1.0/my-message-type", - "description" : "Message type", - "readOnly" : true - }, - "mediator_terms" : { - "type" : "array", - "items" : { - "type" : "string", - "description" : "Terms for mediator to agree" + "type" : "object" + }, + "ConnectionList" : { + "properties" : { + "results" : { + "description" : "List of connection records", + "items" : { + "$ref" : "#/components/schemas/ConnRecord" + }, + "type" : "array" } }, - "recipient_terms" : { - "type" : "array", - "items" : { - "type" : "string", - "description" : "Terms for recipient to agree" + "type" : "object" + }, + "ConnectionMetadata" : { + "properties" : { + "results" : { + "description" : "Dictionary of metadata associated with connection.", + "properties" : { }, + "type" : "object" } - } - } - }, - "MediationGrant" : { - "type" : "object", - "properties" : { - "@id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Message identifier" - }, - "@type" : { - "type" : "string", - "example" : "https://didcomm.org/my-family/1.0/my-message-type", - "description" : "Message type", - "readOnly" : true }, - "endpoint" : { - "type" : "string", - "example" : "http://192.168.56.102:8020/", - "description" : "endpoint on which messages destined for the recipient are received." - }, - "routing_keys" : { - "type" : "array", - "items" : { - "type" : "string", - "description" : "Keys to use for forward message packaging" - } - } - } - }, - "MediationList" : { - "type" : "object", - "properties" : { - "results" : { - "type" : "array", - "description" : "List of mediation records", - "items" : { - "$ref" : "#/definitions/MediationRecord" + "type" : "object" + }, + "ConnectionMetadataSetRequest" : { + "properties" : { + "metadata" : { + "description" : "Dictionary of metadata to set for connection.", + "properties" : { }, + "type" : "object" } - } - } - }, - "MediationRecord" : { - "type" : "object", - "required" : [ "connection_id", "role" ], - "properties" : { - "connection_id" : { - "type" : "string" - }, - "created_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of record creation", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "endpoint" : { - "type" : "string" - }, - "mediation_id" : { - "type" : "string" }, - "mediator_terms" : { - "type" : "array", - "items" : { + "required" : [ "metadata" ], + "type" : "object" + }, + "ConnectionModuleResponse" : { + "type" : "object" + }, + "ConnectionStaticRequest" : { + "properties" : { + "alias" : { + "description" : "Alias to assign to this connection", "type" : "string" - } - }, - "recipient_terms" : { - "type" : "array", - "items" : { + }, + "my_did" : { + "description" : "Local DID", + "example" : "WgWxqztrNooG92RXvxSTWv", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$", + "type" : "string" + }, + "my_seed" : { + "description" : "Seed to use for the local DID", + "type" : "string" + }, + "their_did" : { + "description" : "Remote DID", + "example" : "WgWxqztrNooG92RXvxSTWv", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$", + "type" : "string" + }, + "their_endpoint" : { + "description" : "URL endpoint for other party", + "example" : "https://myhost:8021", + "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$", + "type" : "string" + }, + "their_label" : { + "description" : "Other party's label for this connection", + "type" : "string" + }, + "their_seed" : { + "description" : "Seed to use for the remote DID", + "type" : "string" + }, + "their_verkey" : { + "description" : "Remote verification key", "type" : "string" } }, - "role" : { - "type" : "string" - }, - "routing_keys" : { - "type" : "array", - "items" : { - "type" : "string", + "type" : "object" + }, + "ConnectionStaticResult" : { + "properties" : { + "my_did" : { + "description" : "Local DID", + "example" : "WgWxqztrNooG92RXvxSTWv", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$", + "type" : "string" + }, + "my_endpoint" : { + "description" : "My URL endpoint", + "example" : "https://myhost:8021", + "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$", + "type" : "string" + }, + "my_verkey" : { + "description" : "My verification key", "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" - } - }, - "state" : { - "type" : "string", - "example" : "active", - "description" : "Current record state" - }, - "updated_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of last record update", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - } - } - }, - "Menu" : { - "type" : "object", - "required" : [ "options" ], - "properties" : { - "@id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Message identifier" - }, - "@type" : { - "type" : "string", - "example" : "https://didcomm.org/my-family/1.0/my-message-type", - "description" : "Message type", - "readOnly" : true - }, - "description" : { - "type" : "string", - "example" : "This menu presents options", - "description" : "Introductory text for the menu" - }, - "errormsg" : { - "type" : "string", - "example" : "Error: item not found", - "description" : "An optional error message to display in menu header" - }, - "options" : { - "type" : "array", - "description" : "List of menu options", - "items" : { - "$ref" : "#/definitions/MenuOption" - } - }, - "title" : { - "type" : "string", - "example" : "My Menu", - "description" : "Menu title" - } - } - }, - "MenuForm" : { - "type" : "object", - "properties" : { - "description" : { - "type" : "string", - "example" : "Window preference settings", - "description" : "Additional descriptive text for menu form" - }, - "params" : { - "type" : "array", - "description" : "List of form parameters", - "items" : { - "$ref" : "#/definitions/MenuFormParam" - } - }, - "submit-label" : { - "type" : "string", - "example" : "Send", - "description" : "Alternative label for form submit button" - }, - "title" : { - "type" : "string", - "example" : "Preferences", - "description" : "Menu form title" - } - } - }, - "MenuFormParam" : { - "type" : "object", - "required" : [ "name", "title" ], - "properties" : { - "default" : { - "type" : "string", - "example" : "0", - "description" : "Default parameter value" - }, - "description" : { - "type" : "string", - "example" : "Delay in seconds before starting", - "description" : "Additional descriptive text for menu form parameter" - }, - "name" : { - "type" : "string", - "example" : "delay", - "description" : "Menu parameter name" - }, - "required" : { - "type" : "boolean", - "example" : false, - "description" : "Whether parameter is required" - }, - "title" : { - "type" : "string", - "example" : "Delay in seconds", - "description" : "Menu parameter title" - }, - "type" : { - "type" : "string", - "example" : "int", - "description" : "Menu form parameter input type" - } - } - }, - "MenuJson" : { - "type" : "object", - "required" : [ "options" ], - "properties" : { - "description" : { - "type" : "string", - "example" : "User preferences for window settings", - "description" : "Introductory text for the menu" - }, - "errormsg" : { - "type" : "string", - "example" : "Error: item not present", - "description" : "Optional error message to display in menu header" - }, - "options" : { - "type" : "array", - "description" : "List of menu options", - "items" : { - "$ref" : "#/definitions/MenuOption" - } - }, - "title" : { - "type" : "string", - "example" : "My Menu", - "description" : "Menu title" - } - } - }, - "MenuOption" : { - "type" : "object", - "required" : [ "name", "title" ], - "properties" : { - "description" : { - "type" : "string", - "example" : "Window display preferences", - "description" : "Additional descriptive text for menu option" - }, - "disabled" : { - "type" : "boolean", - "example" : false, - "description" : "Whether to show option as disabled" - }, - "form" : { - "$ref" : "#/definitions/MenuForm" - }, - "name" : { - "type" : "string", - "example" : "window_prefs", - "description" : "Menu option name (unique identifier)" - }, - "title" : { - "type" : "string", - "example" : "Window Preferences", - "description" : "Menu option title" - } - } - }, - "MultitenantModuleResponse" : { - "type" : "object" - }, - "PerformRequest" : { - "type" : "object", - "properties" : { - "name" : { - "type" : "string", - "example" : "Query", - "description" : "Menu option name" - }, - "params" : { - "type" : "object", - "description" : "Input parameter values", - "additionalProperties" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6" - } - } - } - }, - "PingRequest" : { - "type" : "object", - "properties" : { - "comment" : { - "type" : "string", - "description" : "Comment for the ping message", - "x-nullable" : true - } - } - }, - "PingRequestResponse" : { - "type" : "object", - "properties" : { - "thread_id" : { - "type" : "string", - "description" : "Thread ID of the ping message" - } - } - }, - "PresentationDefinition" : { - "type" : "object", - "properties" : { - "format" : { - "$ref" : "#/definitions/ClaimFormat" - }, - "id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Unique Resource Identifier", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - }, - "input_descriptors" : { - "type" : "array", - "items" : { - "$ref" : "#/definitions/InputDescriptors" - } - }, - "name" : { - "type" : "string", - "description" : "Human-friendly name that describes what the presentation definition pertains to" - }, - "purpose" : { - "type" : "string", - "description" : "Describes the purpose for which the Presentation Definition's inputs are being requested" - }, - "submission_requirements" : { - "type" : "array", - "items" : { - "$ref" : "#/definitions/SubmissionRequirements" - } - } - } - }, - "PresentationProposal" : { - "type" : "object", - "required" : [ "presentation_proposal" ], - "properties" : { - "@id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Message identifier" - }, - "@type" : { - "type" : "string", - "example" : "https://didcomm.org/my-family/1.0/my-message-type", - "description" : "Message type", - "readOnly" : true - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - }, - "presentation_proposal" : { - "$ref" : "#/definitions/IndyPresPreview" - } - } - }, - "PresentationRequest" : { - "type" : "object", - "required" : [ "request_presentations~attach" ], - "properties" : { - "@id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Message identifier" - }, - "@type" : { - "type" : "string", - "example" : "https://didcomm.org/my-family/1.0/my-message-type", - "description" : "Message type", - "readOnly" : true - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - }, - "request_presentations~attach" : { - "type" : "array", - "items" : { - "$ref" : "#/definitions/AttachDecorator" - } - } - } - }, - "PublishRevocations" : { - "type" : "object", - "properties" : { - "rrid2crid" : { - "type" : "object", - "description" : "Credential revocation ids by revocation registry id", - "additionalProperties" : { - "type" : "array", - "items" : { - "type" : "string", - "example" : "12345", - "description" : "Credential revocation identifier", - "pattern" : "^[1-9][0-9]*$" - } - } - } - } - }, - "QueryResult" : { - "type" : "object", - "properties" : { - "results" : { - "type" : "object", - "description" : "Query results keyed by protocol", - "additionalProperties" : { - "type" : "object", - "description" : "Protocol descriptor", - "properties" : { } - } - } - } - }, - "RawEncoded" : { - "type" : "object", - "properties" : { - "encoded" : { - "type" : "string", - "example" : "0", - "description" : "Encoded value", - "pattern" : "^[0-9]*$" - }, - "raw" : { - "type" : "string", - "description" : "Raw value" - } - } - }, - "ReceiveInvitationRequest" : { - "type" : "object", - "properties" : { - "@id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Message identifier" - }, - "@type" : { - "type" : "string", - "example" : "https://didcomm.org/my-family/1.0/my-message-type", - "description" : "Message type", - "readOnly" : true - }, - "did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "DID for connection invitation", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "imageUrl" : { - "type" : "string", - "format" : "url", - "example" : "http://192.168.56.101/img/logo.jpg", - "description" : "Optional image URL for connection invitation", - "x-nullable" : true - }, - "label" : { - "type" : "string", - "example" : "Bob", - "description" : "Optional label for connection invitation" - }, - "recipientKeys" : { - "type" : "array", - "description" : "List of recipient keys", - "items" : { - "type" : "string", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$", + "type" : "string" + }, + "record" : { + "$ref" : "#/components/schemas/ConnRecord" + }, + "their_did" : { + "description" : "Remote DID", + "example" : "WgWxqztrNooG92RXvxSTWv", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$", + "type" : "string" + }, + "their_verkey" : { + "description" : "Remote verification key", "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", - "description" : "Recipient public key", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$", + "type" : "string" } }, - "routingKeys" : { - "type" : "array", - "description" : "List of routing keys", - "items" : { - "type" : "string", - "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", - "description" : "Routing key", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" - } - }, - "serviceEndpoint" : { - "type" : "string", - "example" : "http://192.168.56.101:8020", - "description" : "Service endpoint at which to reach this agent" - } - } - }, - "RegisterLedgerNymResponse" : { - "type" : "object", - "properties" : { - "success" : { - "type" : "boolean", - "example" : true, - "description" : "Success of nym registration operation" - } - } - }, - "RemoveWalletRequest" : { - "type" : "object", - "properties" : { - "wallet_key" : { - "type" : "string", - "example" : "MySecretKey123", - "description" : "Master key used for key derivation. Only required for unmanaged wallets." - } - } - }, - "ResolutionResult" : { - "type" : "object", - "required" : [ "did_doc", "metadata" ], - "properties" : { - "did_doc" : { - "type" : "object", - "description" : "DID Document", - "properties" : { } - }, - "metadata" : { - "type" : "object", - "description" : "Resolution metadata", - "properties" : { } - } - } - }, - "RevRegCreateRequest" : { - "type" : "object", - "properties" : { - "credential_definition_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", - "description" : "Credential definition identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - }, - "max_cred_num" : { - "type" : "integer", - "format" : "int32", - "example" : 1000, - "description" : "Revocation registry size", - "minimum" : 4, - "maximum" : 32768 - } - } - }, - "RevRegIssuedResult" : { - "type" : "object", - "properties" : { - "result" : { - "type" : "integer", - "format" : "int32", - "example" : 0, - "description" : "Number of credentials issued against revocation registry", - "minimum" : 0 - } - } - }, - "RevRegResult" : { - "type" : "object", - "properties" : { - "result" : { - "$ref" : "#/definitions/IssuerRevRegRecord" - } - } - }, - "RevRegUpdateTailsFileUri" : { - "type" : "object", - "required" : [ "tails_public_uri" ], - "properties" : { - "tails_public_uri" : { - "type" : "string", - "format" : "url", - "example" : "http://192.168.56.133:6543/revocation/registry/WgWxqztrNooG92RXvxSTWv:4:WgWxqztrNooG92RXvxSTWv:3:CL:20:tag:CL_ACCUM:0/tails-file", - "description" : "Public URI to the tails file" - } - } - }, - "RevRegsCreated" : { - "type" : "object", - "properties" : { - "rev_reg_ids" : { - "type" : "array", - "items" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:4:WgWxqztrNooG92RXvxSTWv:3:CL:20:tag:CL_ACCUM:0", - "description" : "Revocation registry identifiers", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" + "required" : [ "my_did", "my_endpoint", "my_verkey", "record", "their_did", "their_verkey" ], + "type" : "object" + }, + "Constraints" : { + "properties" : { + "fields" : { + "items" : { + "$ref" : "#/components/schemas/DIFField" + }, + "type" : "array" + }, + "is_holder" : { + "items" : { + "$ref" : "#/components/schemas/DIFHolder" + }, + "type" : "array" + }, + "limit_disclosure" : { + "description" : "LimitDisclosure", + "type" : "string" + }, + "status_active" : { + "enum" : [ "required", "allowed", "disallowed" ], + "type" : "string" + }, + "status_revoked" : { + "enum" : [ "required", "allowed", "disallowed" ], + "type" : "string" + }, + "status_suspended" : { + "enum" : [ "required", "allowed", "disallowed" ], + "type" : "string" + }, + "subject_is_issuer" : { + "description" : "SubjectIsIssuer", + "enum" : [ "required", "preferred" ], + "type" : "string" } - } - } - }, - "RevocationModuleResponse" : { - "type" : "object" - }, - "RevokeRequest" : { - "type" : "object", - "properties" : { - "cred_ex_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Credential exchange identifier", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - }, - "cred_rev_id" : { - "type" : "string", - "example" : "12345", - "description" : "Credential revocation identifier", - "pattern" : "^[1-9][0-9]*$" - }, - "publish" : { - "type" : "boolean", - "description" : "(True) publish revocation to ledger immediately, or (default, False) mark it pending" - }, - "rev_reg_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:4:WgWxqztrNooG92RXvxSTWv:3:CL:20:tag:CL_ACCUM:0", - "description" : "Revocation registry identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" - } - } - }, - "RouteRecord" : { - "type" : "object", - "required" : [ "recipient_key" ], - "properties" : { - "connection_id" : { - "type" : "string" - }, - "created_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of record creation", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "recipient_key" : { - "type" : "string" - }, - "record_id" : { - "type" : "string" - }, - "role" : { - "type" : "string" - }, - "state" : { - "type" : "string", - "example" : "active", - "description" : "Current record state" - }, - "updated_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of last record update", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "wallet_id" : { - "type" : "string" - } - } - }, - "Schema" : { - "type" : "object", - "properties" : { - "attrNames" : { - "type" : "array", - "description" : "Schema attribute names", - "items" : { - "type" : "string", - "example" : "score", - "description" : "Attribute name" - } - }, - "id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", - "description" : "Schema identifier", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" - }, - "name" : { - "type" : "string", - "example" : "schema_name", - "description" : "Schema name" - }, - "seqNo" : { - "type" : "integer", - "format" : "int32", - "example" : 10, - "description" : "Schema sequence number", - "minimum" : 1 - }, - "ver" : { - "type" : "string", - "example" : "1.0", - "description" : "Node protocol version", - "pattern" : "^[0-9.]+$" - }, - "version" : { - "type" : "string", - "example" : "1.0", - "description" : "Schema version", - "pattern" : "^[0-9.]+$" - } - } - }, - "SchemaGetResult" : { - "type" : "object", - "properties" : { - "schema" : { - "$ref" : "#/definitions/Schema" - } - } - }, - "SchemaInputDescriptor" : { - "type" : "object", - "properties" : { - "required" : { - "type" : "boolean", - "description" : "Required" - }, - "uri" : { - "type" : "string", - "description" : "URI" - } - } - }, - "SchemaSendRequest" : { - "type" : "object", - "required" : [ "attributes", "schema_name", "schema_version" ], - "properties" : { - "attributes" : { - "type" : "array", - "description" : "List of schema attributes", - "items" : { - "type" : "string", - "example" : "score", - "description" : "attribute name" - } - }, - "schema_name" : { - "type" : "string", - "example" : "prefs", - "description" : "Schema name" - }, - "schema_version" : { - "type" : "string", - "example" : "1.0", - "description" : "Schema version", - "pattern" : "^[0-9.]+$" - } - } - }, - "SchemaSendResult" : { - "type" : "object", - "required" : [ "schema_id" ], - "properties" : { - "schema" : { - "$ref" : "#/definitions/SchemaSendResult_schema" - }, - "schema_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", - "description" : "Schema identifier", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" - } - } - }, - "SchemasCreatedResult" : { - "type" : "object", - "properties" : { - "schema_ids" : { - "type" : "array", - "items" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", - "description" : "Schema identifiers", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" - } - } - } - }, - "SendMenu" : { - "type" : "object", - "required" : [ "menu" ], - "properties" : { - "menu" : { - "$ref" : "#/definitions/SendMenu_menu" - } - } - }, - "SendMessage" : { - "type" : "object", - "properties" : { - "content" : { - "type" : "string", - "example" : "Hello", - "description" : "Message content" - } - } - }, - "SignRequest" : { - "type" : "object", - "required" : [ "doc", "verkey" ], - "properties" : { - "doc" : { - "$ref" : "#/definitions/Doc" - }, - "verkey" : { - "type" : "string", - "description" : "Verkey to use for signing" - } - } - }, - "SignResponse" : { - "type" : "object", - "properties" : { - "error" : { - "type" : "string", - "description" : "Error text" - }, - "signed_doc" : { - "type" : "object", - "description" : "Signed document", - "properties" : { } - } - } - }, - "SignatureOptions" : { - "type" : "object", - "required" : [ "proofPurpose", "verificationMethod" ], - "properties" : { - "challenge" : { - "type" : "string" - }, - "domain" : { - "type" : "string" - }, - "proofPurpose" : { - "type" : "string" - }, - "type" : { - "type" : "string" - }, - "verificationMethod" : { - "type" : "string" - } - } - }, - "SignedDoc" : { - "type" : "object", - "required" : [ "proof" ], - "properties" : { - "proof" : { - "$ref" : "#/definitions/SignedDoc_proof" - } - } - }, - "SubmissionRequirements" : { - "type" : "object", - "properties" : { - "count" : { - "type" : "integer", - "format" : "int32", - "example" : 1234, - "description" : "Count Value" - }, - "from" : { - "type" : "string", - "description" : "From" }, - "from_nested" : { - "type" : "array", - "items" : { - "$ref" : "#/definitions/SubmissionRequirements" - } - }, - "max" : { - "type" : "integer", - "format" : "int32", - "example" : 1234, - "description" : "Max Value" - }, - "min" : { - "type" : "integer", - "format" : "int32", - "example" : 1234, - "description" : "Min Value" - }, - "name" : { - "type" : "string", - "description" : "Name" - }, - "purpose" : { - "type" : "string", - "description" : "Purpose" - }, - "rule" : { - "type" : "string", - "description" : "Selection", - "enum" : [ "all", "pick" ] - } - } - }, - "TAAAccept" : { - "type" : "object", - "properties" : { - "mechanism" : { - "type" : "string" - }, - "text" : { - "type" : "string" - }, - "version" : { - "type" : "string" - } - } - }, - "TAAAcceptance" : { - "type" : "object", - "properties" : { - "mechanism" : { - "type" : "string" - }, - "time" : { - "type" : "integer", - "format" : "int32", - "example" : 1640995199, - "minimum" : 0, - "maximum" : 18446744073709551615 - } - } - }, - "TAAInfo" : { - "type" : "object", - "properties" : { - "aml_record" : { - "$ref" : "#/definitions/AMLRecord" - }, - "taa_accepted" : { - "$ref" : "#/definitions/TAAAcceptance" - }, - "taa_record" : { - "$ref" : "#/definitions/TAARecord" - }, - "taa_required" : { - "type" : "boolean" - } - } - }, - "TAARecord" : { - "type" : "object", - "properties" : { - "digest" : { - "type" : "string" - }, - "text" : { - "type" : "string" - }, - "version" : { - "type" : "string" - } - } - }, - "TAAResult" : { - "type" : "object", - "properties" : { - "result" : { - "$ref" : "#/definitions/TAAInfo" - } - } - }, - "TransactionJobs" : { - "type" : "object", - "properties" : { - "transaction_my_job" : { - "type" : "string", - "description" : "My transaction related job", - "enum" : [ "TRANSACTION_AUTHOR", "TRANSACTION_ENDORSER", "reset" ] - }, - "transaction_their_job" : { - "type" : "string", - "description" : "Their transaction related job", - "enum" : [ "TRANSACTION_AUTHOR", "TRANSACTION_ENDORSER", "reset" ] - } - } - }, - "TransactionList" : { - "type" : "object", - "properties" : { - "results" : { - "type" : "array", - "description" : "List of transaction records", - "items" : { - "$ref" : "#/definitions/TransactionRecord" - } - } - } - }, - "TransactionRecord" : { - "type" : "object", - "properties" : { - "_type" : { - "type" : "string", - "example" : "101", - "description" : "Transaction type" - }, - "connection_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "The connection identifier for thie particular transaction record" - }, - "created_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of record creation", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "endorser_write_txn" : { - "type" : "boolean", - "example" : true, - "description" : "If True, Endorser will write the transaction after endorsing it" - }, - "formats" : { - "type" : "array", - "items" : { - "type" : "object", - "example" : { - "attach_id" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "format" : "dif/endorse-transaction/request@v1.0" - }, - "additionalProperties" : { + "type" : "object" + }, + "CreateInvitationRequest" : { + "properties" : { + "mediation_id" : { + "description" : "Identifier for active mediation record to be used", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", + "type" : "string" + }, + "metadata" : { + "description" : "Optional metadata to attach to the connection created with the invitation", + "properties" : { }, + "type" : "object" + }, + "my_label" : { + "description" : "Optional label for connection invitation", + "example" : "Bob", + "type" : "string" + }, + "recipient_keys" : { + "description" : "List of recipient keys", + "items" : { + "description" : "Recipient public key", + "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$", "type" : "string" - } - } - }, - "messages_attach" : { - "type" : "array", - "items" : { - "type" : "object", - "example" : { - "@id" : "143c458d-1b1c-40c7-ab85-4d16808ddf0a", - "data" : { - "json" : "{\"endorser\": \"V4SGRU86Z58d6TV7PBUe6f\",\"identifier\": \"LjgpST2rjsoxYegQDRm7EL\",\"operation\": {\"data\": {\"attr_names\": [\"first_name\", \"last_name\"],\"name\": \"test_schema\",\"version\": \"2.1\",},\"type\": \"101\",},\"protocolVersion\": 2,\"reqId\": 1597766666168851000,\"signatures\": {\"LjgpST2rjsox\": \"4ATKMn6Y9sTgwqaGTm7py2c2M8x1EVDTWKZArwyuPgjU\"},\"taaAcceptance\": {\"mechanism\": \"manual\",\"taaDigest\": \"f50fe2c2ab977006761d36bd6f23e4c6a7e0fc2feb9f62\",\"time\": 1597708800,}}" - }, - "mime-type" : "application/json" }, - "properties" : { } - } - }, - "signature_request" : { - "type" : "array", - "items" : { - "type" : "object", - "example" : { - "author_goal_code" : "transaction.ledger.write", - "context" : "did:sov", - "method" : "add-signature", - "signature_type" : "", - "signer_goal_code" : "transaction.endorse" + "type" : "array" + }, + "routing_keys" : { + "description" : "List of routing keys", + "items" : { + "description" : "Routing key", + "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$", + "type" : "string" }, - "properties" : { } + "type" : "array" + }, + "service_endpoint" : { + "description" : "Connection endpoint", + "example" : "http://192.168.56.102:8020", + "type" : "string" } }, - "signature_response" : { - "type" : "array", - "items" : { - "type" : "object", - "example" : { - "context" : "did:sov", - "message_id" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "method" : "add-signature", - "signer_goal_code" : "transaction.refuse" + "type" : "object" + }, + "CreateWalletRequest" : { + "properties" : { + "image_url" : { + "description" : "Image url for this wallet. This image url is publicized (self-attested) to other agents as part of forming a connection.", + "example" : "https://aries.ca/images/sample.png", + "type" : "string" + }, + "key_management_mode" : { + "description" : "Key management method to use for this wallet.", + "enum" : [ "managed" ], + "example" : "managed", + "type" : "string" + }, + "label" : { + "description" : "Label for this wallet. This label is publicized (self-attested) to other agents as part of forming a connection.", + "example" : "Alice", + "type" : "string" + }, + "wallet_dispatch_type" : { + "description" : "Webhook target dispatch type for this wallet. default - Dispatch only to webhooks associated with this wallet. base - Dispatch only to webhooks associated with the base wallet. both - Dispatch to both webhook targets.", + "enum" : [ "default", "both", "base" ], + "example" : "default", + "type" : "string" + }, + "wallet_key" : { + "description" : "Master key used for key derivation.", + "example" : "MySecretKey123", + "type" : "string" + }, + "wallet_key_derivation" : { + "description" : "Key derivation", + "enum" : [ "ARGON2I_MOD", "ARGON2I_INT", "RAW" ], + "example" : "RAW", + "type" : "string" + }, + "wallet_name" : { + "description" : "Wallet name", + "example" : "MyNewWallet", + "type" : "string" + }, + "wallet_type" : { + "description" : "Type of the wallet to create", + "enum" : [ "askar", "in_memory", "indy" ], + "example" : "indy", + "type" : "string" + }, + "wallet_webhook_urls" : { + "description" : "List of Webhook URLs associated with this subwallet", + "items" : { + "description" : "Optional webhook URL to receive webhook messages", + "example" : "http://localhost:8022/webhooks", + "type" : "string" }, - "properties" : { } + "type" : "array" } }, - "state" : { - "type" : "string", - "example" : "active", - "description" : "Current record state" - }, - "thread_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Thread Identifier" - }, - "timing" : { - "type" : "object", - "example" : { - "expires_time" : "2020-12-13T17:29:06+0000" + "type" : "object" + }, + "CreateWalletResponse" : { + "properties" : { + "created_at" : { + "description" : "Time of record creation", + "example" : "2021-12-31T23:59:59Z", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$", + "type" : "string" }, - "properties" : { } - }, - "trace" : { - "type" : "boolean", - "description" : "Record trace information, based on agent configuration" - }, - "transaction_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Transaction identifier" - }, - "updated_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of last record update", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - } - } - }, - "TxnOrCredentialDefinitionSendResult" : { - "type" : "object", - "properties" : { - "sent" : { - "$ref" : "#/definitions/CredentialDefinitionSendResult" - }, - "txn" : { - "$ref" : "#/definitions/TxnOrCredentialDefinitionSendResult_txn" - } - } - }, - "TxnOrPublishRevocationsResult" : { - "type" : "object", - "properties" : { - "sent" : { - "$ref" : "#/definitions/PublishRevocations" - }, - "txn" : { - "$ref" : "#/definitions/TxnOrPublishRevocationsResult_txn" - } - } - }, - "TxnOrRevRegResult" : { - "type" : "object", - "properties" : { - "sent" : { - "$ref" : "#/definitions/RevRegResult" - }, - "txn" : { - "$ref" : "#/definitions/TxnOrRevRegResult_txn" - } - } - }, - "TxnOrSchemaSendResult" : { - "type" : "object", - "properties" : { - "sent" : { - "$ref" : "#/definitions/TxnOrSchemaSendResult_sent" - }, - "txn" : { - "$ref" : "#/definitions/TxnOrSchemaSendResult_txn" - } - } - }, - "UpdateWalletRequest" : { - "type" : "object", - "properties" : { - "image_url" : { - "type" : "string", - "example" : "https://aries.ca/images/sample.png", - "description" : "Image url for this wallet. This image url is publicized (self-attested) to other agents as part of forming a connection." - }, - "label" : { - "type" : "string", - "example" : "Alice", - "description" : "Label for this wallet. This label is publicized (self-attested) to other agents as part of forming a connection." - }, - "wallet_dispatch_type" : { - "type" : "string", - "example" : "default", - "description" : "Webhook target dispatch type for this wallet. default - Dispatch only to webhooks associated with this wallet. base - Dispatch only to webhooks associated with the base wallet. both - Dispatch to both webhook targets.", - "enum" : [ "default", "both", "base" ] - }, - "wallet_webhook_urls" : { - "type" : "array", - "description" : "List of Webhook URLs associated with this subwallet", - "items" : { - "type" : "string", - "example" : "http://localhost:8022/webhooks", - "description" : "Optional webhook URL to receive webhook messages" - } - } - } - }, - "V10CredentialBoundOfferRequest" : { - "type" : "object", - "properties" : { - "counter_proposal" : { - "$ref" : "#/definitions/V10CredentialBoundOfferRequest_counter_proposal" - } - } - }, - "V10CredentialConnFreeOfferRequest" : { - "type" : "object", - "required" : [ "cred_def_id", "credential_preview" ], - "properties" : { - "auto_issue" : { - "type" : "boolean", - "description" : "Whether to respond automatically to credential requests, creating and issuing requested credentials" - }, - "auto_remove" : { - "type" : "boolean", - "description" : "Whether to remove the credential exchange record on completion (overrides --preserve-exchange-records configuration setting)" - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - }, - "cred_def_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", - "description" : "Credential definition identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - }, - "credential_preview" : { - "$ref" : "#/definitions/CredentialPreview" - }, - "trace" : { - "type" : "boolean", - "description" : "Record trace information, based on agent configuration" - } - } - }, - "V10CredentialCreate" : { - "type" : "object", - "required" : [ "credential_proposal" ], - "properties" : { - "auto_remove" : { - "type" : "boolean", - "description" : "Whether to remove the credential exchange record on completion (overrides --preserve-exchange-records configuration setting)" - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - }, - "cred_def_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", - "description" : "Credential definition identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - }, - "credential_proposal" : { - "$ref" : "#/definitions/CredentialPreview" - }, - "issuer_did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "Credential issuer DID", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "schema_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", - "description" : "Schema identifier", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" - }, - "schema_issuer_did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "Schema issuer DID", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "schema_name" : { - "type" : "string", - "example" : "preferences", - "description" : "Schema name" - }, - "schema_version" : { - "type" : "string", - "example" : "1.0", - "description" : "Schema version", - "pattern" : "^[0-9.]+$" - }, - "trace" : { - "type" : "boolean", - "description" : "Record trace information, based on agent configuration" - } - } - }, - "V10CredentialExchange" : { - "type" : "object", - "properties" : { - "auto_issue" : { - "type" : "boolean", - "example" : false, - "description" : "Issuer choice to issue to request in this credential exchange" - }, - "auto_offer" : { - "type" : "boolean", - "example" : false, - "description" : "Holder choice to accept offer in this credential exchange" - }, - "auto_remove" : { - "type" : "boolean", - "example" : false, - "description" : "Issuer choice to remove this credential exchange record when complete" - }, - "connection_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Connection identifier" - }, - "created_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of record creation", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "credential" : { - "$ref" : "#/definitions/V10CredentialExchange_credential" - }, - "credential_definition_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", - "description" : "Credential definition identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - }, - "credential_exchange_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Credential exchange identifier" - }, - "credential_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Credential identifier" - }, - "credential_offer" : { - "$ref" : "#/definitions/V10CredentialExchange_credential_offer" - }, - "credential_offer_dict" : { - "$ref" : "#/definitions/V10CredentialExchange_credential_offer_dict" - }, - "credential_proposal_dict" : { - "$ref" : "#/definitions/V10CredentialExchange_credential_proposal_dict" - }, - "credential_request" : { - "$ref" : "#/definitions/V10CredentialExchange_credential_request" - }, - "credential_request_metadata" : { - "type" : "object", - "description" : "(Indy) credential request metadata", - "properties" : { } - }, - "error_msg" : { - "type" : "string", - "example" : "Credential definition identifier is not set in proposal", - "description" : "Error message" - }, - "initiator" : { - "type" : "string", - "example" : "self", - "description" : "Issue-credential exchange initiator: self or external", - "enum" : [ "self", "external" ] - }, - "parent_thread_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Parent thread identifier" - }, - "raw_credential" : { - "$ref" : "#/definitions/V10CredentialExchange_raw_credential" - }, - "revoc_reg_id" : { - "type" : "string", - "description" : "Revocation registry identifier" - }, - "revocation_id" : { - "type" : "string", - "description" : "Credential identifier within revocation registry" - }, - "role" : { - "type" : "string", - "example" : "issuer", - "description" : "Issue-credential exchange role: holder or issuer", - "enum" : [ "holder", "issuer" ] - }, - "schema_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", - "description" : "Schema identifier", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" - }, - "state" : { - "type" : "string", - "example" : "credential_acked", - "description" : "Issue-credential exchange state" - }, - "thread_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Thread identifier" - }, - "trace" : { - "type" : "boolean", - "description" : "Record trace information, based on agent configuration" - }, - "updated_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of last record update", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - } - } - }, - "V10CredentialExchangeListResult" : { - "type" : "object", - "properties" : { - "results" : { - "type" : "array", - "description" : "Aries#0036 v1.0 credential exchange records", - "items" : { - "$ref" : "#/definitions/V10CredentialExchange" - } - } - } - }, - "V10CredentialFreeOfferRequest" : { - "type" : "object", - "required" : [ "connection_id", "cred_def_id", "credential_preview" ], - "properties" : { - "auto_issue" : { - "type" : "boolean", - "description" : "Whether to respond automatically to credential requests, creating and issuing requested credentials" - }, - "auto_remove" : { - "type" : "boolean", - "description" : "Whether to remove the credential exchange record on completion (overrides --preserve-exchange-records configuration setting)" - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - }, - "connection_id" : { - "type" : "string", - "format" : "uuid", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Connection identifier" - }, - "cred_def_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", - "description" : "Credential definition identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - }, - "credential_preview" : { - "$ref" : "#/definitions/CredentialPreview" - }, - "trace" : { - "type" : "boolean", - "description" : "Record trace information, based on agent configuration" - } - } - }, - "V10CredentialIssueRequest" : { - "type" : "object", - "properties" : { - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - } - } - }, - "V10CredentialProblemReportRequest" : { - "type" : "object", - "required" : [ "description" ], - "properties" : { - "description" : { - "type" : "string" - } - } - }, - "V10CredentialProposalRequestMand" : { - "type" : "object", - "required" : [ "connection_id", "credential_proposal" ], - "properties" : { - "auto_remove" : { - "type" : "boolean", - "description" : "Whether to remove the credential exchange record on completion (overrides --preserve-exchange-records configuration setting)" - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - }, - "connection_id" : { - "type" : "string", - "format" : "uuid", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Connection identifier" - }, - "cred_def_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", - "description" : "Credential definition identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - }, - "credential_proposal" : { - "$ref" : "#/definitions/CredentialPreview" - }, - "issuer_did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "Credential issuer DID", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "schema_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", - "description" : "Schema identifier", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" - }, - "schema_issuer_did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "Schema issuer DID", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "schema_name" : { - "type" : "string", - "example" : "preferences", - "description" : "Schema name" - }, - "schema_version" : { - "type" : "string", - "example" : "1.0", - "description" : "Schema version", - "pattern" : "^[0-9.]+$" - }, - "trace" : { - "type" : "boolean", - "description" : "Record trace information, based on agent configuration" - } - } - }, - "V10CredentialProposalRequestOpt" : { - "type" : "object", - "required" : [ "connection_id" ], - "properties" : { - "auto_remove" : { - "type" : "boolean", - "description" : "Whether to remove the credential exchange record on completion (overrides --preserve-exchange-records configuration setting)" - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - }, - "connection_id" : { - "type" : "string", - "format" : "uuid", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Connection identifier" - }, - "cred_def_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", - "description" : "Credential definition identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - }, - "credential_proposal" : { - "$ref" : "#/definitions/CredentialPreview" - }, - "issuer_did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "Credential issuer DID", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "schema_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", - "description" : "Schema identifier", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" - }, - "schema_issuer_did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "Schema issuer DID", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "schema_name" : { - "type" : "string", - "example" : "preferences", - "description" : "Schema name" - }, - "schema_version" : { - "type" : "string", - "example" : "1.0", - "description" : "Schema version", - "pattern" : "^[0-9.]+$" - }, - "trace" : { - "type" : "boolean", - "description" : "Record trace information, based on agent configuration" - } - } - }, - "V10CredentialStoreRequest" : { - "type" : "object", - "properties" : { - "credential_id" : { - "type" : "string" - } - } - }, - "V10PresentProofModuleResponse" : { - "type" : "object" - }, - "V10PresentationCreateRequestRequest" : { - "type" : "object", - "required" : [ "proof_request" ], - "properties" : { - "comment" : { - "type" : "string", - "x-nullable" : true - }, - "proof_request" : { - "$ref" : "#/definitions/IndyProofRequest" - }, - "trace" : { - "type" : "boolean", - "example" : false, - "description" : "Whether to trace event (default false)" - } - } - }, - "V10PresentationExchange" : { - "type" : "object", - "properties" : { - "auto_present" : { - "type" : "boolean", - "example" : false, - "description" : "Prover choice to auto-present proof as verifier requests" - }, - "connection_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Connection identifier" - }, - "created_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of record creation", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "error_msg" : { - "type" : "string", - "example" : "Invalid structure", - "description" : "Error message" - }, - "initiator" : { - "type" : "string", - "example" : "self", - "description" : "Present-proof exchange initiator: self or external", - "enum" : [ "self", "external" ] - }, - "presentation" : { - "$ref" : "#/definitions/V10PresentationExchange_presentation" - }, - "presentation_exchange_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Presentation exchange identifier" - }, - "presentation_proposal_dict" : { - "$ref" : "#/definitions/V10PresentationExchange_presentation_proposal_dict" - }, - "presentation_request" : { - "$ref" : "#/definitions/V10PresentationExchange_presentation_request" - }, - "presentation_request_dict" : { - "$ref" : "#/definitions/V10PresentationExchange_presentation_request_dict" - }, - "role" : { - "type" : "string", - "example" : "prover", - "description" : "Present-proof exchange role: prover or verifier", - "enum" : [ "prover", "verifier" ] - }, - "state" : { - "type" : "string", - "example" : "verified", - "description" : "Present-proof exchange state" - }, - "thread_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Thread identifier" - }, - "trace" : { - "type" : "boolean", - "description" : "Record trace information, based on agent configuration" - }, - "updated_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of last record update", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "verified" : { - "type" : "string", - "example" : "true", - "description" : "Whether presentation is verified: true or false", - "enum" : [ "true", "false" ] - } - } - }, - "V10PresentationExchangeList" : { - "type" : "object", - "properties" : { - "results" : { - "type" : "array", - "description" : "Aries RFC 37 v1.0 presentation exchange records", - "items" : { - "$ref" : "#/definitions/V10PresentationExchange" - } - } - } - }, - "V10PresentationProblemReportRequest" : { - "type" : "object", - "required" : [ "description" ], - "properties" : { - "description" : { - "type" : "string" - } - } - }, - "V10PresentationProposalRequest" : { - "type" : "object", - "required" : [ "connection_id", "presentation_proposal" ], - "properties" : { - "auto_present" : { - "type" : "boolean", - "description" : "Whether to respond automatically to presentation requests, building and presenting requested proof" - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - }, - "connection_id" : { - "type" : "string", - "format" : "uuid", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Connection identifier" - }, - "presentation_proposal" : { - "$ref" : "#/definitions/IndyPresPreview" - }, - "trace" : { - "type" : "boolean", - "example" : false, - "description" : "Whether to trace event (default false)" - } - } - }, - "V10PresentationSendRequestRequest" : { - "type" : "object", - "required" : [ "connection_id", "proof_request" ], - "properties" : { - "comment" : { - "type" : "string", - "x-nullable" : true - }, - "connection_id" : { - "type" : "string", - "format" : "uuid", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Connection identifier" - }, - "proof_request" : { - "$ref" : "#/definitions/IndyProofRequest" - }, - "trace" : { - "type" : "boolean", - "example" : false, - "description" : "Whether to trace event (default false)" - } - } - }, - "V20CredAttrSpec" : { - "type" : "object", - "required" : [ "name", "value" ], - "properties" : { - "mime-type" : { - "type" : "string", - "example" : "image/jpeg", - "description" : "MIME type: omit for (null) default", - "x-nullable" : true - }, - "name" : { - "type" : "string", - "example" : "favourite_drink", - "description" : "Attribute name" - }, - "value" : { - "type" : "string", - "example" : "martini", - "description" : "Attribute value: base64-encode if MIME type is present" - } - } - }, - "V20CredBoundOfferRequest" : { - "type" : "object", - "properties" : { - "counter_preview" : { - "$ref" : "#/definitions/V20CredBoundOfferRequest_counter_preview" - }, - "filter" : { - "$ref" : "#/definitions/V20CredBoundOfferRequest_filter" - } - } - }, - "V20CredExFree" : { - "type" : "object", - "required" : [ "connection_id", "filter" ], - "properties" : { - "auto_remove" : { - "type" : "boolean", - "description" : "Whether to remove the credential exchange record on completion (overrides --preserve-exchange-records configuration setting)" - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - }, - "connection_id" : { - "type" : "string", - "format" : "uuid", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Connection identifier" - }, - "credential_preview" : { - "$ref" : "#/definitions/V20CredPreview" - }, - "filter" : { - "$ref" : "#/definitions/V20CredBoundOfferRequest_filter" - }, - "trace" : { - "type" : "boolean", - "description" : "Record trace information, based on agent configuration" - } - } - }, - "V20CredExRecord" : { - "type" : "object", - "properties" : { - "auto_issue" : { - "type" : "boolean", - "example" : false, - "description" : "Issuer choice to issue to request in this credential exchange" - }, - "auto_offer" : { - "type" : "boolean", - "example" : false, - "description" : "Holder choice to accept offer in this credential exchange" - }, - "auto_remove" : { - "type" : "boolean", - "example" : false, - "description" : "Issuer choice to remove this credential exchange record when complete" - }, - "by_format" : { - "$ref" : "#/definitions/V20CredExRecord_by_format" - }, - "connection_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Connection identifier" - }, - "created_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of record creation", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "cred_ex_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Credential exchange identifier" - }, - "cred_issue" : { - "$ref" : "#/definitions/V20CredExRecord_cred_issue" - }, - "cred_offer" : { - "$ref" : "#/definitions/V10CredentialExchange_credential_offer_dict" - }, - "cred_preview" : { - "$ref" : "#/definitions/V20CredExRecord_cred_preview" - }, - "cred_proposal" : { - "$ref" : "#/definitions/V10CredentialExchange_credential_proposal_dict" - }, - "cred_request" : { - "$ref" : "#/definitions/V20CredExRecord_cred_request" - }, - "error_msg" : { - "type" : "string", - "example" : "The front fell off", - "description" : "Error message" - }, - "initiator" : { - "type" : "string", - "example" : "self", - "description" : "Issue-credential exchange initiator: self or external", - "enum" : [ "self", "external" ] - }, - "parent_thread_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Parent thread identifier" - }, - "role" : { - "type" : "string", - "example" : "issuer", - "description" : "Issue-credential exchange role: holder or issuer", - "enum" : [ "issuer", "holder" ] - }, - "state" : { - "type" : "string", - "example" : "done", - "description" : "Issue-credential exchange state", - "enum" : [ "proposal-sent", "proposal-received", "offer-sent", "offer-received", "request-sent", "request-received", "credential-issued", "credential-received", "done" ] - }, - "thread_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Thread identifier" - }, - "trace" : { - "type" : "boolean", - "description" : "Record trace information, based on agent configuration" - }, - "updated_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of last record update", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - } - } - }, - "V20CredExRecordByFormat" : { - "type" : "object", - "properties" : { - "cred_issue" : { - "type" : "object", - "properties" : { } - }, - "cred_offer" : { - "type" : "object", - "properties" : { } - }, - "cred_proposal" : { - "type" : "object", - "properties" : { } - }, - "cred_request" : { - "type" : "object", - "properties" : { } - } - } - }, - "V20CredExRecordDetail" : { - "type" : "object", - "properties" : { - "cred_ex_record" : { - "$ref" : "#/definitions/V20CredExRecordDetail_cred_ex_record" - }, - "indy" : { - "$ref" : "#/definitions/V20CredExRecordIndy" - }, - "ld_proof" : { - "$ref" : "#/definitions/V20CredExRecordLDProof" - } - } - }, - "V20CredExRecordIndy" : { - "type" : "object", - "properties" : { - "created_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of record creation", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "cred_ex_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Corresponding v2.0 credential exchange record identifier" - }, - "cred_ex_indy_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Record identifier" - }, - "cred_id_stored" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Credential identifier stored in wallet" - }, - "cred_request_metadata" : { - "type" : "object", - "description" : "Credential request metadata for indy holder", - "properties" : { } - }, - "cred_rev_id" : { - "type" : "string", - "example" : "12345", - "description" : "Credential revocation identifier within revocation registry", - "pattern" : "^[1-9][0-9]*$" - }, - "rev_reg_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:4:WgWxqztrNooG92RXvxSTWv:3:CL:20:tag:CL_ACCUM:0", - "description" : "Revocation registry identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" - }, - "state" : { - "type" : "string", - "example" : "active", - "description" : "Current record state" - }, - "updated_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of last record update", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - } - } - }, - "V20CredExRecordLDProof" : { - "type" : "object", - "properties" : { - "created_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of record creation", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "cred_ex_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Corresponding v2.0 credential exchange record identifier" - }, - "cred_ex_ld_proof_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Record identifier" - }, - "cred_id_stored" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Credential identifier stored in wallet" - }, - "state" : { - "type" : "string", - "example" : "active", - "description" : "Current record state" - }, - "updated_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of last record update", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - } - } - }, - "V20CredExRecordListResult" : { - "type" : "object", - "properties" : { - "results" : { - "type" : "array", - "description" : "Credential exchange records and corresponding detail records", - "items" : { - "$ref" : "#/definitions/V20CredExRecordDetail" - } - } - } - }, - "V20CredFilter" : { - "type" : "object", - "properties" : { - "indy" : { - "$ref" : "#/definitions/V20CredFilter_indy" - }, - "ld_proof" : { - "$ref" : "#/definitions/V20CredFilter_ld_proof" - } - } - }, - "V20CredFilterIndy" : { - "type" : "object", - "properties" : { - "cred_def_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", - "description" : "Credential definition identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - }, - "issuer_did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "Credential issuer DID", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "schema_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", - "description" : "Schema identifier", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" - }, - "schema_issuer_did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "Schema issuer DID", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "schema_name" : { - "type" : "string", - "example" : "preferences", - "description" : "Schema name" - }, - "schema_version" : { - "type" : "string", - "example" : "1.0", - "description" : "Schema version", - "pattern" : "^[0-9.]+$" - } - } - }, - "V20CredFilterLDProof" : { - "type" : "object", - "required" : [ "ld_proof" ], - "properties" : { - "ld_proof" : { - "$ref" : "#/definitions/V20CredFilter_ld_proof" - } - } - }, - "V20CredFormat" : { - "type" : "object", - "required" : [ "attach_id", "format" ], - "properties" : { - "attach_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Attachment identifier" - }, - "format" : { - "type" : "string", - "example" : "aries/ld-proof-vc-detail@v1.0", - "description" : "Attachment format specifier" - } - } - }, - "V20CredIssue" : { - "type" : "object", - "required" : [ "credentials~attach", "formats" ], - "properties" : { - "@id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Message identifier" - }, - "@type" : { - "type" : "string", - "example" : "https://didcomm.org/my-family/1.0/my-message-type", - "description" : "Message type", - "readOnly" : true - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - }, - "credentials~attach" : { - "type" : "array", - "description" : "Credential attachments", - "items" : { - "$ref" : "#/definitions/AttachDecorator" - } - }, - "formats" : { - "type" : "array", - "description" : "Acceptable attachment formats", - "items" : { - "$ref" : "#/definitions/V20CredFormat" - } - }, - "replacement_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Issuer-unique identifier to coordinate credential replacement" - } - } - }, - "V20CredIssueProblemReportRequest" : { - "type" : "object", - "required" : [ "description" ], - "properties" : { - "description" : { - "type" : "string" - } - } - }, - "V20CredIssueRequest" : { - "type" : "object", - "properties" : { - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - } - } - }, - "V20CredOffer" : { - "type" : "object", - "required" : [ "formats", "offers~attach" ], - "properties" : { - "@id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Message identifier" - }, - "@type" : { - "type" : "string", - "example" : "https://didcomm.org/my-family/1.0/my-message-type", - "description" : "Message type", - "readOnly" : true - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - }, - "credential_preview" : { - "$ref" : "#/definitions/V20CredPreview" - }, - "formats" : { - "type" : "array", - "description" : "Acceptable credential formats", - "items" : { - "$ref" : "#/definitions/V20CredFormat" - } - }, - "offers~attach" : { - "type" : "array", - "description" : "Offer attachments", - "items" : { - "$ref" : "#/definitions/AttachDecorator" - } - }, - "replacement_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Issuer-unique identifier to coordinate credential replacement" - } - } - }, - "V20CredOfferConnFreeRequest" : { - "type" : "object", - "required" : [ "filter" ], - "properties" : { - "auto_issue" : { - "type" : "boolean", - "description" : "Whether to respond automatically to credential requests, creating and issuing requested credentials" - }, - "auto_remove" : { - "type" : "boolean", - "description" : "Whether to remove the credential exchange record on completion (overrides --preserve-exchange-records configuration setting)" - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - }, - "credential_preview" : { - "$ref" : "#/definitions/V20CredPreview" - }, - "filter" : { - "$ref" : "#/definitions/V20CredBoundOfferRequest_filter" - }, - "trace" : { - "type" : "boolean", - "description" : "Record trace information, based on agent configuration" - } - } - }, - "V20CredOfferRequest" : { - "type" : "object", - "required" : [ "connection_id", "filter" ], - "properties" : { - "auto_issue" : { - "type" : "boolean", - "description" : "Whether to respond automatically to credential requests, creating and issuing requested credentials" - }, - "auto_remove" : { - "type" : "boolean", - "description" : "Whether to remove the credential exchange record on completion (overrides --preserve-exchange-records configuration setting)" - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - }, - "connection_id" : { - "type" : "string", - "format" : "uuid", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Connection identifier" - }, - "credential_preview" : { - "$ref" : "#/definitions/V20CredPreview" - }, - "filter" : { - "$ref" : "#/definitions/V20CredBoundOfferRequest_filter" - }, - "trace" : { - "type" : "boolean", - "description" : "Record trace information, based on agent configuration" - } - } - }, - "V20CredPreview" : { - "type" : "object", - "required" : [ "attributes" ], - "properties" : { - "@type" : { - "type" : "string", - "example" : "issue-credential/2.0/credential-preview", - "description" : "Message type identifier" - }, - "attributes" : { - "type" : "array", - "items" : { - "$ref" : "#/definitions/V20CredAttrSpec" - } - } - } - }, - "V20CredProposal" : { - "type" : "object", - "required" : [ "filters~attach", "formats" ], - "properties" : { - "@id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Message identifier" - }, - "@type" : { - "type" : "string", - "example" : "https://didcomm.org/my-family/1.0/my-message-type", - "description" : "Message type", - "readOnly" : true - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - }, - "credential_preview" : { - "$ref" : "#/definitions/V20CredProposal_credential_preview" - }, - "filters~attach" : { - "type" : "array", - "description" : "Credential filter per acceptable format on corresponding identifier", - "items" : { - "$ref" : "#/definitions/AttachDecorator" - } - }, - "formats" : { - "type" : "array", - "description" : "Attachment formats", - "items" : { - "$ref" : "#/definitions/V20CredFormat" - } - } - } - }, - "V20CredRequest" : { - "type" : "object", - "required" : [ "formats", "requests~attach" ], - "properties" : { - "@id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Message identifier" - }, - "@type" : { - "type" : "string", - "example" : "https://didcomm.org/my-family/1.0/my-message-type", - "description" : "Message type", - "readOnly" : true - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - }, - "formats" : { - "type" : "array", - "description" : "Acceptable attachment formats", - "items" : { - "$ref" : "#/definitions/V20CredFormat" - } - }, - "requests~attach" : { - "type" : "array", - "description" : "Request attachments", - "items" : { - "$ref" : "#/definitions/AttachDecorator" - } - } - } - }, - "V20CredRequestFree" : { - "type" : "object", - "required" : [ "connection_id", "filter" ], - "properties" : { - "auto_remove" : { - "type" : "boolean", - "description" : "Whether to remove the credential exchange record on completion (overrides --preserve-exchange-records configuration setting)" - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - }, - "connection_id" : { - "type" : "string", - "format" : "uuid", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Connection identifier" - }, - "filter" : { - "$ref" : "#/definitions/V20CredBoundOfferRequest_filter" - }, - "holder_did" : { - "type" : "string", - "example" : "did:key:ahsdkjahsdkjhaskjdhakjshdkajhsdkjahs", - "description" : "Holder DID to substitute for the credentialSubject.id", - "x-nullable" : true - }, - "trace" : { - "type" : "boolean", - "example" : false, - "description" : "Whether to trace event (default false)" - } - } - }, - "V20CredRequestRequest" : { - "type" : "object", - "properties" : { - "holder_did" : { - "type" : "string", - "example" : "did:key:ahsdkjahsdkjhaskjdhakjshdkajhsdkjahs", - "description" : "Holder DID to substitute for the credentialSubject.id", - "x-nullable" : true - } - } - }, - "V20CredStoreRequest" : { - "type" : "object", - "properties" : { - "credential_id" : { - "type" : "string" - } - } - }, - "V20IssueCredSchemaCore" : { - "type" : "object", - "required" : [ "filter" ], - "properties" : { - "auto_remove" : { - "type" : "boolean", - "description" : "Whether to remove the credential exchange record on completion (overrides --preserve-exchange-records configuration setting)" - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - }, - "credential_preview" : { - "$ref" : "#/definitions/V20CredPreview" - }, - "filter" : { - "$ref" : "#/definitions/V20CredBoundOfferRequest_filter" - }, - "trace" : { - "type" : "boolean", - "description" : "Record trace information, based on agent configuration" - } - } - }, - "V20IssueCredentialModuleResponse" : { - "type" : "object" - }, - "V20Pres" : { - "type" : "object", - "required" : [ "formats", "presentations~attach" ], - "properties" : { - "@id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Message identifier" - }, - "@type" : { - "type" : "string", - "example" : "https://didcomm.org/my-family/1.0/my-message-type", - "description" : "Message type", - "readOnly" : true - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - }, - "formats" : { - "type" : "array", - "description" : "Acceptable attachment formats", - "items" : { - "$ref" : "#/definitions/V20PresFormat" - } - }, - "presentations~attach" : { - "type" : "array", - "items" : { - "$ref" : "#/definitions/AttachDecorator" - } - } - } - }, - "V20PresCreateRequestRequest" : { - "type" : "object", - "required" : [ "presentation_request" ], - "properties" : { - "comment" : { - "type" : "string", - "x-nullable" : true - }, - "presentation_request" : { - "$ref" : "#/definitions/V20PresRequestByFormat" - }, - "trace" : { - "type" : "boolean", - "example" : false, - "description" : "Whether to trace event (default false)" - } - } - }, - "V20PresExRecord" : { - "type" : "object", - "properties" : { - "auto_present" : { - "type" : "boolean", - "example" : false, - "description" : "Prover choice to auto-present proof as verifier requests" - }, - "by_format" : { - "$ref" : "#/definitions/V20PresExRecord_by_format" - }, - "connection_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Connection identifier" - }, - "created_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of record creation", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "error_msg" : { - "type" : "string", - "example" : "Invalid structure", - "description" : "Error message" - }, - "initiator" : { - "type" : "string", - "example" : "self", - "description" : "Present-proof exchange initiator: self or external", - "enum" : [ "self", "external" ] - }, - "pres" : { - "$ref" : "#/definitions/V20PresExRecord_pres" - }, - "pres_ex_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Presentation exchange identifier" - }, - "pres_proposal" : { - "$ref" : "#/definitions/V10PresentationExchange_presentation_proposal_dict" - }, - "pres_request" : { - "$ref" : "#/definitions/V10PresentationExchange_presentation_request_dict" - }, - "role" : { - "type" : "string", - "example" : "prover", - "description" : "Present-proof exchange role: prover or verifier", - "enum" : [ "prover", "verifier" ] - }, - "state" : { - "type" : "string", - "description" : "Present-proof exchange state", - "enum" : [ "proposal-sent", "proposal-received", "request-sent", "request-received", "presentation-sent", "presentation-received", "done", "abandoned" ] - }, - "thread_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Thread identifier" - }, - "trace" : { - "type" : "boolean", - "description" : "Record trace information, based on agent configuration" - }, - "updated_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of last record update", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "verified" : { - "type" : "string", - "example" : "true", - "description" : "Whether presentation is verified: 'true' or 'false'", - "enum" : [ "true", "false" ] - } - } - }, - "V20PresExRecordByFormat" : { - "type" : "object", - "properties" : { - "pres" : { - "type" : "object", - "properties" : { } - }, - "pres_proposal" : { - "type" : "object", - "properties" : { } - }, - "pres_request" : { - "type" : "object", - "properties" : { } - } - } - }, - "V20PresExRecordList" : { - "type" : "object", - "properties" : { - "results" : { - "type" : "array", - "description" : "Presentation exchange records", - "items" : { - "$ref" : "#/definitions/V20PresExRecord" - } - } - } - }, - "V20PresFormat" : { - "type" : "object", - "required" : [ "attach_id", "format" ], - "properties" : { - "attach_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Attachment identifier" - }, - "format" : { - "type" : "string", - "example" : "dif/presentation-exchange/submission@v1.0", - "description" : "Attachment format specifier" - } - } - }, - "V20PresProblemReportRequest" : { - "type" : "object", - "required" : [ "description" ], - "properties" : { - "description" : { - "type" : "string" - } - } - }, - "V20PresProposal" : { - "type" : "object", - "required" : [ "formats", "proposals~attach" ], - "properties" : { - "@id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Message identifier" - }, - "@type" : { - "type" : "string", - "example" : "https://didcomm.org/my-family/1.0/my-message-type", - "description" : "Message type", - "readOnly" : true - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment" - }, - "formats" : { - "type" : "array", - "items" : { - "$ref" : "#/definitions/V20PresFormat" - } - }, - "proposals~attach" : { - "type" : "array", - "description" : "Attachment per acceptable format on corresponding identifier", - "items" : { - "$ref" : "#/definitions/AttachDecorator" - } - } - } - }, - "V20PresProposalByFormat" : { - "type" : "object", - "properties" : { - "dif" : { - "$ref" : "#/definitions/V20PresProposalByFormat_dif" - }, - "indy" : { - "$ref" : "#/definitions/V20PresProposalByFormat_indy" - } - } - }, - "V20PresProposalRequest" : { - "type" : "object", - "required" : [ "connection_id", "presentation_proposal" ], - "properties" : { - "auto_present" : { - "type" : "boolean", - "description" : "Whether to respond automatically to presentation requests, building and presenting requested proof" - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - }, - "connection_id" : { - "type" : "string", - "format" : "uuid", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Connection identifier" - }, - "presentation_proposal" : { - "$ref" : "#/definitions/V20PresProposalByFormat" - }, - "trace" : { - "type" : "boolean", - "example" : false, - "description" : "Whether to trace event (default false)" - } - } - }, - "V20PresRequest" : { - "type" : "object", - "required" : [ "formats", "request_presentations~attach" ], - "properties" : { - "@id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Message identifier" - }, - "@type" : { - "type" : "string", - "example" : "https://didcomm.org/my-family/1.0/my-message-type", - "description" : "Message type", - "readOnly" : true - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment" - }, - "formats" : { - "type" : "array", - "items" : { - "$ref" : "#/definitions/V20PresFormat" - } - }, - "request_presentations~attach" : { - "type" : "array", - "description" : "Attachment per acceptable format on corresponding identifier", - "items" : { - "$ref" : "#/definitions/AttachDecorator" - } - }, - "will_confirm" : { - "type" : "boolean", - "description" : "Whether verifier will send confirmation ack" - } - } - }, - "V20PresRequestByFormat" : { - "type" : "object", - "properties" : { - "dif" : { - "$ref" : "#/definitions/V20PresRequestByFormat_dif" - }, - "indy" : { - "$ref" : "#/definitions/V20PresRequestByFormat_indy" - } - } - }, - "V20PresSendRequestRequest" : { - "type" : "object", - "required" : [ "connection_id", "presentation_request" ], - "properties" : { - "comment" : { - "type" : "string", - "x-nullable" : true - }, - "connection_id" : { - "type" : "string", - "format" : "uuid", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Connection identifier" - }, - "presentation_request" : { - "$ref" : "#/definitions/V20PresRequestByFormat" - }, - "trace" : { - "type" : "boolean", - "example" : false, - "description" : "Whether to trace event (default false)" - } - } - }, - "V20PresSpecByFormatRequest" : { - "type" : "object", - "properties" : { - "dif" : { - "$ref" : "#/definitions/V20PresSpecByFormatRequest_dif" - }, - "indy" : { - "$ref" : "#/definitions/V20PresSpecByFormatRequest_indy" - }, - "trace" : { - "type" : "boolean", - "description" : "Record trace information, based on agent configuration" - } - } - }, - "V20PresentProofModuleResponse" : { - "type" : "object" - }, - "VCRecord" : { - "type" : "object", - "properties" : { - "contexts" : { - "type" : "array", - "items" : { - "type" : "string", - "example" : "https://myhost:8021", - "description" : "Context", - "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" + "key_management_mode" : { + "description" : "Mode regarding management of wallet key", + "enum" : [ "managed", "unmanaged" ], + "type" : "string" + }, + "settings" : { + "description" : "Settings for this wallet.", + "properties" : { }, + "type" : "object" + }, + "state" : { + "description" : "Current record state", + "example" : "active", + "type" : "string" + }, + "token" : { + "description" : "Authorization token to authenticate wallet requests", + "example" : "eyJhbGciOiJFZERTQSJ9.eyJhIjogIjAifQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk", + "type" : "string" + }, + "updated_at" : { + "description" : "Time of last record update", + "example" : "2021-12-31T23:59:59Z", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$", + "type" : "string" + }, + "wallet_id" : { + "description" : "Wallet record ID", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" } }, - "cred_tags" : { - "type" : "object", - "additionalProperties" : { - "type" : "string", - "description" : "Retrieval tag value" + "required" : [ "key_management_mode", "wallet_id" ], + "type" : "object" + }, + "CreateWalletTokenRequest" : { + "properties" : { + "wallet_key" : { + "description" : "Master key used for key derivation. Only required for unamanged wallets.", + "example" : "MySecretKey123", + "type" : "string" } }, - "cred_value" : { - "type" : "object", - "description" : "(JSON-serializable) credential value", - "properties" : { } - }, - "expanded_types" : { - "type" : "array", - "items" : { - "type" : "string", - "example" : "https://w3id.org/citizenship#PermanentResidentCard", - "description" : "JSON-LD expanded type extracted from type and context" + "type" : "object" + }, + "CreateWalletTokenResponse" : { + "properties" : { + "token" : { + "description" : "Authorization token to authenticate wallet requests", + "example" : "eyJhbGciOiJFZERTQSJ9.eyJhIjogIjAifQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk", + "type" : "string" } }, - "given_id" : { - "type" : "string", - "example" : "http://example.edu/credentials/3732", - "description" : "Credential identifier" - }, - "issuer_id" : { - "type" : "string", - "example" : "https://example.edu/issuers/14", - "description" : "Issuer identifier" + "type" : "object" + }, + "CredAttrSpec" : { + "properties" : { + "mime-type" : { + "description" : "MIME type: omit for (null) default", + "example" : "image/jpeg", + "nullable" : true, + "type" : "string" + }, + "name" : { + "description" : "Attribute name", + "example" : "favourite_drink", + "type" : "string" + }, + "value" : { + "description" : "Attribute value: base64-encode if MIME type is present", + "example" : "martini", + "type" : "string" + } }, - "proof_types" : { - "type" : "array", - "items" : { - "type" : "string", - "example" : "Ed25519Signature2018", - "description" : "Signature suite used for proof" + "required" : [ "name", "value" ], + "type" : "object" + }, + "CredDefValue" : { + "properties" : { + "primary" : { + "$ref" : "#/components/schemas/CredDefValue_primary" + }, + "revocation" : { + "$ref" : "#/components/schemas/CredDefValue_revocation" } }, - "record_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Record identifier" + "type" : "object" + }, + "CredDefValuePrimary" : { + "properties" : { + "n" : { + "example" : "0", + "pattern" : "^[0-9]*$", + "type" : "string" + }, + "r" : { + "$ref" : "#/components/schemas/Generated" + }, + "rctxt" : { + "example" : "0", + "pattern" : "^[0-9]*$", + "type" : "string" + }, + "s" : { + "example" : "0", + "pattern" : "^[0-9]*$", + "type" : "string" + }, + "z" : { + "example" : "0", + "pattern" : "^[0-9]*$", + "type" : "string" + } }, - "schema_ids" : { - "type" : "array", - "items" : { - "type" : "string", - "example" : "https://example.org/examples/degree.json", - "description" : "Schema identifier" + "type" : "object" + }, + "CredDefValueRevocation" : { + "properties" : { + "g" : { + "example" : "1 1F14F&ECB578F 2 095E45DDF417D", + "type" : "string" + }, + "g_dash" : { + "example" : "1 1D64716fCDC00C 1 0C781960FA66E3D3 2 095E45DDF417D", + "type" : "string" + }, + "h" : { + "example" : "1 16675DAE54BFAE8 2 095E45DD417D", + "type" : "string" + }, + "h0" : { + "example" : "1 21E5EF9476EAF18 2 095E45DDF417D", + "type" : "string" + }, + "h1" : { + "example" : "1 236D1D99236090 2 095E45DDF417D", + "type" : "string" + }, + "h2" : { + "example" : "1 1C3AE8D1F1E277 2 095E45DDF417D", + "type" : "string" + }, + "h_cap" : { + "example" : "1 1B2A32CF3167 1 2490FEBF6EE55 1 0000000000000000", + "type" : "string" + }, + "htilde" : { + "example" : "1 1D8549E8C0F8 2 095E45DDF417D", + "type" : "string" + }, + "pk" : { + "example" : "1 142CD5E5A7DC 1 153885BD903312 2 095E45DDF417D", + "type" : "string" + }, + "u" : { + "example" : "1 0C430AAB2B4710 1 1CB3A0932EE7E 1 0000000000000000", + "type" : "string" + }, + "y" : { + "example" : "1 153558BD903312 2 095E45DDF417D 1 0000000000000000", + "type" : "string" } }, - "subject_ids" : { - "type" : "array", - "items" : { - "type" : "string", - "example" : "did:example:ebfeb1f712ebc6f1c276e12ec21", - "description" : "Subject identifier" + "type" : "object" + }, + "CredInfoList" : { + "properties" : { + "results" : { + "items" : { + "$ref" : "#/components/schemas/IndyCredInfo" + }, + "type" : "array" } - } - } - }, - "VCRecordList" : { - "type" : "object", - "properties" : { - "results" : { - "type" : "array", - "items" : { - "$ref" : "#/definitions/VCRecord" + }, + "type" : "object" + }, + "CredRevIndyRecordsResult" : { + "properties" : { + "rev_reg_delta" : { + "description" : "Indy revocation registry delta", + "properties" : { }, + "type" : "object" } - } - } - }, - "VerifyRequest" : { - "type" : "object", - "required" : [ "doc" ], - "properties" : { - "doc" : { - "$ref" : "#/definitions/VerifyRequest_doc" }, - "verkey" : { - "type" : "string", - "description" : "Verkey to use for doc verification" - } - } - }, - "VerifyResponse" : { - "type" : "object", - "required" : [ "valid" ], - "properties" : { - "error" : { - "type" : "string", - "description" : "Error text" + "type" : "object" + }, + "CredRevRecordDetailsResult" : { + "properties" : { + "results" : { + "items" : { + "$ref" : "#/components/schemas/IssuerCredRevRecord" + }, + "type" : "array" + } }, - "valid" : { - "type" : "boolean" - } - } - }, - "W3CCredentialsListRequest" : { - "type" : "object", - "properties" : { - "contexts" : { - "type" : "array", - "items" : { - "type" : "string", - "example" : "https://myhost:8021", - "description" : "Credential context to match", - "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" + "type" : "object" + }, + "CredRevRecordResult" : { + "properties" : { + "result" : { + "$ref" : "#/components/schemas/IssuerCredRevRecord" } }, - "given_id" : { - "type" : "string", - "description" : "Given credential id to match" + "type" : "object" + }, + "CredRevokedResult" : { + "properties" : { + "revoked" : { + "description" : "Whether credential is revoked on the ledger", + "type" : "boolean" + } }, - "issuer_id" : { - "type" : "string", - "description" : "Credential issuer identifier to match" + "type" : "object" + }, + "Credential" : { + "properties" : { + "@context" : { + "description" : "The JSON-LD context of the credential", + "example" : [ "https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1" ], + "items" : { + "type" : "object" + }, + "type" : "array" + }, + "credentialSubject" : { + "example" : "", + "type" : "object" + }, + "expirationDate" : { + "description" : "The expiration date", + "example" : "2010-01-01T19:23:24Z", + "pattern" : "^([0-9]{4})-([0-9]{2})-([0-9]{2})([Tt ]([0-9]{2}):([0-9]{2}):([0-9]{2})(\\.[0-9]+)?)?(([Zz]|([+-])([0-9]{2}):([0-9]{2})))?$", + "type" : "string" + }, + "id" : { + "example" : "http://example.edu/credentials/1872", + "pattern" : "\\w+:(\\/?\\/?)[^\\s]+", + "type" : "string" + }, + "issuanceDate" : { + "description" : "The issuance date", + "example" : "2010-01-01T19:23:24Z", + "pattern" : "^([0-9]{4})-([0-9]{2})-([0-9]{2})([Tt ]([0-9]{2}):([0-9]{2}):([0-9]{2})(\\.[0-9]+)?)?(([Zz]|([+-])([0-9]{2}):([0-9]{2})))?$", + "type" : "string" + }, + "issuer" : { + "description" : "The JSON-LD Verifiable Credential Issuer. Either string of object with id field.", + "example" : "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH", + "type" : "object" + }, + "proof" : { + "$ref" : "#/components/schemas/Credential_proof" + }, + "type" : { + "description" : "The JSON-LD type of the credential", + "example" : [ "VerifiableCredential", "AlumniCredential" ], + "items" : { + "type" : "string" + }, + "type" : "array" + } }, - "max_results" : { - "type" : "integer", - "format" : "int32", - "description" : "Maximum number of results to return" + "required" : [ "@context", "credentialSubject", "issuanceDate", "issuer", "type" ], + "type" : "object" + }, + "CredentialDefinition" : { + "properties" : { + "id" : { + "description" : "Credential definition identifier", + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$", + "type" : "string" + }, + "schemaId" : { + "description" : "Schema identifier within credential definition identifier", + "example" : "20", + "type" : "string" + }, + "tag" : { + "description" : "Tag within credential definition identifier", + "example" : "tag", + "type" : "string" + }, + "type" : { + "description" : "Signature type: CL for Camenisch-Lysyanskaya", + "example" : "CL", + "type" : "object" + }, + "value" : { + "$ref" : "#/components/schemas/CredentialDefinition_value" + }, + "ver" : { + "description" : "Node protocol version", + "example" : "1.0", + "pattern" : "^[0-9.]+$", + "type" : "string" + } }, - "proof_types" : { - "type" : "array", - "items" : { - "type" : "string", - "example" : "Ed25519Signature2018", - "description" : "Signature suite used for proof" + "type" : "object" + }, + "CredentialDefinitionGetResult" : { + "properties" : { + "credential_definition" : { + "$ref" : "#/components/schemas/CredentialDefinition" } }, - "schema_ids" : { - "type" : "array", - "description" : "Schema identifiers, all of which to match", - "items" : { - "type" : "string", - "example" : "https://myhost:8021", - "description" : "Credential schema identifier", - "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" + "type" : "object" + }, + "CredentialDefinitionSendRequest" : { + "properties" : { + "revocation_registry_size" : { + "description" : "Revocation registry size", + "example" : 1000, + "format" : "int32", + "maximum" : 32768, + "minimum" : 4, + "type" : "integer" + }, + "schema_id" : { + "description" : "Schema identifier", + "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$", + "type" : "string" + }, + "support_revocation" : { + "description" : "Revocation supported flag", + "type" : "boolean" + }, + "tag" : { + "description" : "Credential definition identifier tag", + "example" : "default", + "type" : "string" } }, - "subject_ids" : { - "type" : "array", - "description" : "Subject identifiers, all of which to match", - "items" : { - "type" : "string", - "description" : "Subject identifier" + "type" : "object" + }, + "CredentialDefinitionSendResult" : { + "properties" : { + "credential_definition_id" : { + "description" : "Credential definition identifier", + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$", + "type" : "string" } }, - "tag_query" : { - "type" : "object", - "description" : "Tag filter", - "additionalProperties" : { - "type" : "string", - "description" : "Tag value" + "type" : "object" + }, + "CredentialDefinitionsCreatedResult" : { + "properties" : { + "credential_definition_ids" : { + "items" : { + "description" : "Credential definition identifiers", + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$", + "type" : "string" + }, + "type" : "array" } }, - "types" : { - "type" : "array", - "items" : { - "type" : "string", - "example" : "https://myhost:8021", - "description" : "Credential type to match", - "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" - } - } - } - }, - "WalletList" : { - "type" : "object", - "properties" : { - "results" : { - "type" : "array", - "description" : "List of wallet records", - "items" : { - "$ref" : "#/definitions/WalletRecord" - } - } - } - }, - "WalletModuleResponse" : { - "type" : "object" - }, - "WalletRecord" : { - "type" : "object", - "required" : [ "key_management_mode", "wallet_id" ], - "properties" : { - "created_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of record creation", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "key_management_mode" : { - "type" : "string", - "description" : "Mode regarding management of wallet key", - "enum" : [ "managed", "unmanaged" ] - }, - "settings" : { - "type" : "object", - "description" : "Settings for this wallet.", - "properties" : { } - }, - "state" : { - "type" : "string", - "example" : "active", - "description" : "Current record state" - }, - "updated_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of last record update", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "wallet_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Wallet record ID" - } - } - }, - "ActionMenuFetchResult_result" : { - "type" : "object", - "description" : "Action menu" - }, - "AttachDecoratorData_jws" : { - "type" : "object", - "description" : "Detached Java Web Signature" - }, - "CredDefValue_primary" : { - "type" : "object", - "description" : "Primary value for credential definition" - }, - "CredDefValue_revocation" : { - "type" : "object", - "description" : "Revocation value for credential definition" - }, - "Credential_proof" : { - "type" : "object", - "description" : "The proof of the credential", - "example" : "{\"created\":\"2019-12-11T03:50:55\",\"jws\":\"eyJhbGciOiAiRWREU0EiLCAiYjY0IjogZmFsc2UsICJjcml0JiNjQiXX0..lKJU0Df_keblRKhZAS9Qq6zybm-HqUXNVZ8vgEPNTAjQKBhQDxvXNo7nvtUBb_Eq1Ch6YBKY5qBQ\",\"proofPurpose\":\"assertionMethod\",\"type\":\"Ed25519Signature2018\",\"verificationMethod\":\"did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL\"}" - }, - "CredentialDefinition_value" : { - "type" : "object", - "description" : "Credential definition primary and revocation values" - }, - "DIDCreate_options" : { - "type" : "object", - "description" : "To define a key type for a did:key" - }, - "DIDXRequest_did_docattach" : { - "type" : "object", - "description" : "As signed attachment, DID Doc associated with DID" - }, - "Doc_options" : { - "type" : "object", - "description" : "Signature options" - }, - "IndyCredAbstract_key_correctness_proof" : { - "type" : "object", - "description" : "Key correctness proof" - }, - "IndyCredPrecis_cred_info" : { - "type" : "object", - "description" : "Credential info" - }, - "IndyCredPrecis_interval" : { - "type" : "object", - "description" : "Non-revocation interval from presentation request" - }, - "IndyPrimaryProof_eq_proof" : { - "type" : "object", - "description" : "Indy equality proof", - "x-nullable" : true - }, - "IndyProof_proof" : { - "type" : "object", - "description" : "Indy proof.proof content" - }, - "IndyProof_requested_proof" : { - "type" : "object", - "description" : "Indy proof.requested_proof content" - }, - "IndyProofProof_aggregated_proof" : { - "type" : "object", - "description" : "Indy proof aggregated proof" - }, - "IndyProofProofProofsProof_non_revoc_proof" : { - "type" : "object", - "description" : "Indy non-revocation proof", - "x-nullable" : true - }, - "IndyProofProofProofsProof_primary_proof" : { - "type" : "object", - "description" : "Indy primary proof" - }, - "IndyProofReqAttrSpec_non_revoked" : { - "type" : "object", - "x-nullable" : true - }, - "IndyRevRegDef_value" : { - "type" : "object", - "description" : "Revocation registry definition value" - }, - "IndyRevRegDefValue_publicKeys" : { - "type" : "object", - "description" : "Public keys" - }, - "IndyRevRegEntry_value" : { - "type" : "object", - "description" : "Revocation registry entry value" - }, - "InvitationRecord_invitation" : { - "type" : "object", - "description" : "Out of band invitation message" - }, - "IssuerRevRegRecord_revoc_reg_def" : { - "type" : "object", - "description" : "Revocation registry definition" - }, - "IssuerRevRegRecord_revoc_reg_entry" : { - "type" : "object", - "description" : "Revocation registry entry" - }, - "KeylistQuery_paginate" : { - "type" : "object", - "description" : "Pagination info" - }, - "LDProofVCDetail_credential" : { - "type" : "object", - "description" : "Detail of the JSON-LD Credential to be issued", - "example" : "{\"@context\":[\"https://www.w3.org/2018/credentials/v1\",\"https://w3id.org/citizenship/v1\"],\"credentialSubject\":{\"familyName\":\"SMITH\",\"gender\":\"Male\",\"givenName\":\"JOHN\",\"type\":[\"PermanentResident\",\"Person\"]},\"description\":\"Government of Example Permanent Resident Card.\",\"identifier\":\"83627465\",\"issuanceDate\":\"2019-12-03T12:19:52Z\",\"issuer\":\"did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th\",\"name\":\"Permanent Resident Card\",\"type\":[\"VerifiableCredential\",\"PermanentResidentCard\"]}" - }, - "LDProofVCDetail_options" : { - "type" : "object", - "description" : "Options for specifying how the linked data proof is created.", - "example" : "{\"proofType\":\"Ed25519Signature2018\"}" - }, - "LDProofVCDetailOptions_credentialStatus" : { - "type" : "object", - "description" : "The credential status mechanism to use for the credential. Omitting the property indicates the issued credential will not include a credential status" - }, - "SchemaSendResult_schema" : { - "type" : "object", - "description" : "Schema definition" - }, - "SendMenu_menu" : { - "type" : "object", - "description" : "Menu to send to connection" - }, - "SignedDoc_proof" : { - "type" : "object", - "description" : "Linked data proof" - }, - "TxnOrCredentialDefinitionSendResult_txn" : { - "type" : "object", - "description" : "Credential definition transaction to endorse" - }, - "TxnOrPublishRevocationsResult_txn" : { - "type" : "object", - "description" : "Revocation registry revocations transaction to endorse" - }, - "TxnOrRevRegResult_txn" : { - "type" : "object", - "description" : "Revocation registry definition transaction to endorse" - }, - "TxnOrSchemaSendResult_sent" : { - "type" : "object", - "description" : "Content sent" - }, - "TxnOrSchemaSendResult_txn" : { - "type" : "object", - "description" : "Schema transaction to endorse" - }, - "V10CredentialBoundOfferRequest_counter_proposal" : { - "type" : "object", - "description" : "Optional counter-proposal" - }, - "V10CredentialExchange_credential" : { - "type" : "object", - "description" : "Credential as stored" - }, - "V10CredentialExchange_credential_offer" : { - "type" : "object", - "description" : "(Indy) credential offer" - }, - "V10CredentialExchange_credential_offer_dict" : { - "type" : "object", - "description" : "Credential offer message" - }, - "V10CredentialExchange_credential_proposal_dict" : { - "type" : "object", - "description" : "Credential proposal message" - }, - "V10CredentialExchange_credential_request" : { - "type" : "object", - "description" : "(Indy) credential request" - }, - "V10CredentialExchange_raw_credential" : { - "type" : "object", - "description" : "Credential as received, prior to storage in holder wallet" - }, - "V10PresentationExchange_presentation" : { - "type" : "object", - "description" : "(Indy) presentation (also known as proof)" - }, - "V10PresentationExchange_presentation_proposal_dict" : { - "type" : "object", - "description" : "Presentation proposal message" - }, - "V10PresentationExchange_presentation_request" : { - "type" : "object", - "description" : "(Indy) presentation request (also known as proof request)" - }, - "V10PresentationExchange_presentation_request_dict" : { - "type" : "object", - "description" : "Presentation request message" - }, - "V20CredBoundOfferRequest_counter_preview" : { - "type" : "object", - "description" : "Optional content for counter-proposal" - }, - "V20CredBoundOfferRequest_filter" : { - "type" : "object", - "description" : "Credential specification criteria by format" - }, - "V20CredExRecord_by_format" : { - "type" : "object", - "description" : "Attachment content by format for proposal, offer, request, and issue" - }, - "V20CredExRecord_cred_issue" : { - "type" : "object", - "description" : "Serialized credential issue message" - }, - "V20CredExRecord_cred_preview" : { - "type" : "object", - "description" : "Credential preview from credential proposal" - }, - "V20CredExRecord_cred_request" : { - "type" : "object", - "description" : "Serialized credential request message" - }, - "V20CredExRecordDetail_cred_ex_record" : { - "type" : "object", - "description" : "Credential exchange record" - }, - "V20CredFilter_indy" : { - "type" : "object", - "description" : "Credential filter for indy" - }, - "V20CredFilter_ld_proof" : { - "type" : "object", - "description" : "Credential filter for linked data proof" - }, - "V20CredProposal_credential_preview" : { - "type" : "object", - "description" : "Credential preview" - }, - "V20PresExRecord_by_format" : { - "type" : "object", - "description" : "Attachment content by format for proposal, request, and presentation" - }, - "V20PresExRecord_pres" : { - "type" : "object", - "description" : "Presentation message" - }, - "V20PresProposalByFormat_dif" : { - "type" : "object", - "description" : "Presentation proposal for DIF" - }, - "V20PresProposalByFormat_indy" : { - "type" : "object", - "description" : "Presentation proposal for indy" - }, - "V20PresRequestByFormat_dif" : { - "type" : "object", - "description" : "Presentation request for DIF" - }, - "V20PresRequestByFormat_indy" : { - "type" : "object", - "description" : "Presentation request for indy" - }, - "V20PresSpecByFormatRequest_dif" : { - "type" : "object", - "description" : "Optional Presentation specification for DIF, overrides the PresentionExchange record's PresRequest" - }, - "V20PresSpecByFormatRequest_indy" : { - "type" : "object", - "description" : "Presentation specification for indy" + "type" : "object" + }, + "CredentialOffer" : { + "properties" : { + "@id" : { + "description" : "Message identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "@type" : { + "description" : "Message type", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "readOnly" : true, + "type" : "string" + }, + "comment" : { + "description" : "Human-readable comment", + "nullable" : true, + "type" : "string" + }, + "credential_preview" : { + "$ref" : "#/components/schemas/CredentialPreview" + }, + "offers~attach" : { + "items" : { + "$ref" : "#/components/schemas/AttachDecorator" + }, + "type" : "array" + } + }, + "required" : [ "offers~attach" ], + "type" : "object" + }, + "CredentialPreview" : { + "properties" : { + "@type" : { + "description" : "Message type identifier", + "example" : "issue-credential/1.0/credential-preview", + "type" : "string" + }, + "attributes" : { + "items" : { + "$ref" : "#/components/schemas/CredAttrSpec" + }, + "type" : "array" + } + }, + "required" : [ "attributes" ], + "type" : "object" + }, + "CredentialProposal" : { + "properties" : { + "@id" : { + "description" : "Message identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "@type" : { + "description" : "Message type", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "readOnly" : true, + "type" : "string" + }, + "comment" : { + "description" : "Human-readable comment", + "nullable" : true, + "type" : "string" + }, + "cred_def_id" : { + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$", + "type" : "string" + }, + "credential_proposal" : { + "$ref" : "#/components/schemas/CredentialPreview" + }, + "issuer_did" : { + "example" : "WgWxqztrNooG92RXvxSTWv", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$", + "type" : "string" + }, + "schema_id" : { + "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$", + "type" : "string" + }, + "schema_issuer_did" : { + "example" : "WgWxqztrNooG92RXvxSTWv", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$", + "type" : "string" + }, + "schema_name" : { + "type" : "string" + }, + "schema_version" : { + "example" : "1.0", + "pattern" : "^[0-9.]+$", + "type" : "string" + } + }, + "type" : "object" + }, + "CredentialStatusOptions" : { + "properties" : { + "type" : { + "description" : "Credential status method type to use for the credential. Should match status method registered in the Verifiable Credential Extension Registry", + "example" : "CredentialStatusList2017", + "type" : "string" + } + }, + "required" : [ "type" ], + "type" : "object" + }, + "DID" : { + "properties" : { + "did" : { + "description" : "DID of interest", + "example" : "did:peer:WgWxqztrNooG92RXvxSTWv", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$|^did:([a-zA-Z0-9_]+):([a-zA-Z0-9_.%-]+(:[a-zA-Z0-9_.%-]+)*)((;[a-zA-Z0-9_.:%-]+=[a-zA-Z0-9_.:%-]*)*)(\\/[^#?]*)?([?][^#]*)?(\\#.*)?$$", + "type" : "string" + }, + "key_type" : { + "description" : "Key type associated with the DID", + "enum" : [ "ed25519", "bls12381g2" ], + "example" : "ed25519", + "type" : "string" + }, + "method" : { + "description" : "Did method associated with the DID", + "example" : "sov", + "type" : "string" + }, + "posture" : { + "description" : "Whether DID is current public DID, posted to ledger but not current public DID, or local to the wallet", + "enum" : [ "public", "posted", "wallet_only" ], + "example" : "wallet_only", + "type" : "string" + }, + "verkey" : { + "description" : "Public verification key", + "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$", + "type" : "string" + } + }, + "type" : "object" + }, + "DIDCreate" : { + "properties" : { + "method" : { + "description" : "Method for the requested DID.Supported methods are 'key', 'sov', and any other registered method.", + "example" : "sov", + "type" : "string" + }, + "options" : { + "$ref" : "#/components/schemas/DIDCreate_options" + }, + "seed" : { + "description" : "Optional seed to use for DID, Must beenabled in configuration before use.", + "example" : "000000000000000000000000Trustee1", + "type" : "string" + } + }, + "type" : "object" + }, + "DIDCreateOptions" : { + "properties" : { + "did" : { + "description" : "Specify final value of the did (including did:: prefix)if the method supports or requires so.", + "example" : "did:peer:WgWxqztrNooG92RXvxSTWv", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$|^did:([a-zA-Z0-9_]+):([a-zA-Z0-9_.%-]+(:[a-zA-Z0-9_.%-]+)*)((;[a-zA-Z0-9_.:%-]+=[a-zA-Z0-9_.:%-]*)*)(\\/[^#?]*)?([?][^#]*)?(\\#.*)?$$", + "type" : "string" + }, + "key_type" : { + "description" : "Key type to use for the DID keypair. Validated with the chosen DID method's supported key types.", + "enum" : [ "ed25519", "bls12381g2" ], + "example" : "ed25519", + "type" : "string" + } + }, + "required" : [ "key_type" ], + "type" : "object" + }, + "DIDEndpoint" : { + "properties" : { + "did" : { + "description" : "DID of interest", + "example" : "WgWxqztrNooG92RXvxSTWv", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$", + "type" : "string" + }, + "endpoint" : { + "description" : "Endpoint to set (omit to delete)", + "example" : "https://myhost:8021", + "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$", + "type" : "string" + } + }, + "required" : [ "did" ], + "type" : "object" + }, + "DIDEndpointWithType" : { + "properties" : { + "did" : { + "description" : "DID of interest", + "example" : "WgWxqztrNooG92RXvxSTWv", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$", + "type" : "string" + }, + "endpoint" : { + "description" : "Endpoint to set (omit to delete)", + "example" : "https://myhost:8021", + "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$", + "type" : "string" + }, + "endpoint_type" : { + "description" : "Endpoint type to set (default 'Endpoint'); affects only public or posted DIDs", + "enum" : [ "Endpoint", "Profile", "LinkedDomains" ], + "example" : "Endpoint", + "type" : "string" + } + }, + "required" : [ "did" ], + "type" : "object" + }, + "DIDList" : { + "properties" : { + "results" : { + "description" : "DID list", + "items" : { + "$ref" : "#/components/schemas/DID" + }, + "type" : "array" + } + }, + "type" : "object" + }, + "DIDResult" : { + "properties" : { + "result" : { + "$ref" : "#/components/schemas/DID" + } + }, + "type" : "object" + }, + "DIDXRequest" : { + "properties" : { + "@id" : { + "description" : "Message identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "@type" : { + "description" : "Message type", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "readOnly" : true, + "type" : "string" + }, + "did" : { + "description" : "DID of exchange", + "example" : "WgWxqztrNooG92RXvxSTWv", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$", + "type" : "string" + }, + "did_doc~attach" : { + "$ref" : "#/components/schemas/DIDXRequest_did_doc_attach" + }, + "label" : { + "description" : "Label for DID exchange request", + "example" : "Request to connect with Bob", + "type" : "string" + } + }, + "required" : [ "label" ], + "type" : "object" + }, + "DIFField" : { + "properties" : { + "filter" : { + "$ref" : "#/components/schemas/Filter" + }, + "id" : { + "description" : "ID", + "type" : "string" + }, + "path" : { + "items" : { + "description" : "Path", + "type" : "string" + }, + "type" : "array" + }, + "predicate" : { + "description" : "Preference", + "enum" : [ "required", "preferred" ], + "type" : "string" + }, + "purpose" : { + "description" : "Purpose", + "type" : "string" + } + }, + "type" : "object" + }, + "DIFHolder" : { + "properties" : { + "directive" : { + "description" : "Preference", + "enum" : [ "required", "preferred" ], + "type" : "string" + }, + "field_id" : { + "items" : { + "description" : "FieldID", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", + "type" : "string" + }, + "type" : "array" + } + }, + "type" : "object" + }, + "DIFOptions" : { + "properties" : { + "challenge" : { + "description" : "Challenge protect against replay attack", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", + "type" : "string" + }, + "domain" : { + "description" : "Domain protect against replay attack", + "example" : "4jt78h47fh47", + "type" : "string" + } + }, + "type" : "object" + }, + "DIFPresSpec" : { + "properties" : { + "issuer_id" : { + "description" : "Issuer identifier to sign the presentation, if different from current public DID", + "type" : "string" + }, + "presentation_definition" : { + "$ref" : "#/components/schemas/PresentationDefinition" + }, + "record_ids" : { + "description" : "Mapping of input_descriptor id to list of stored W3C credential record_id", + "example" : { + "" : [ "", "" ], + "" : [ "" ] + }, + "properties" : { }, + "type" : "object" + }, + "reveal_doc" : { + "description" : "reveal doc [JSON-LD frame] dict used to derive the credential when selective disclosure is required", + "example" : { + "@context" : [ "https://www.w3.org/2018/credentials/v1", "https://w3id.org/security/bbs/v1" ], + "@explicit" : true, + "@requireAll" : true, + "credentialSubject" : { + "@explicit" : true, + "@requireAll" : true, + "Observation" : [ { + "effectiveDateTime" : { }, + "@explicit" : true, + "@requireAll" : true + } ] + }, + "issuanceDate" : { }, + "issuer" : { }, + "type" : [ "VerifiableCredential", "LabReport" ] + }, + "properties" : { }, + "type" : "object" + } + }, + "type" : "object" + }, + "DIFProofProposal" : { + "properties" : { + "input_descriptors" : { + "items" : { + "$ref" : "#/components/schemas/InputDescriptors" + }, + "type" : "array" + }, + "options" : { + "$ref" : "#/components/schemas/DIFOptions" + } + }, + "type" : "object" + }, + "DIFProofRequest" : { + "properties" : { + "options" : { + "$ref" : "#/components/schemas/DIFOptions" + }, + "presentation_definition" : { + "$ref" : "#/components/schemas/PresentationDefinition" + } + }, + "required" : [ "presentation_definition" ], + "type" : "object" + }, + "Date" : { + "properties" : { + "expires_time" : { + "description" : "Expiry Date", + "example" : "2021-03-29T05:22:19Z", + "format" : "date-time", + "type" : "string" + } + }, + "required" : [ "expires_time" ], + "type" : "object" + }, + "Disclose" : { + "properties" : { + "@id" : { + "description" : "Message identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "@type" : { + "description" : "Message type", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "readOnly" : true, + "type" : "string" + }, + "protocols" : { + "description" : "List of protocol descriptors", + "items" : { + "$ref" : "#/components/schemas/ProtocolDescriptor" + }, + "type" : "array" + } + }, + "required" : [ "protocols" ], + "type" : "object" + }, + "Disclosures" : { + "properties" : { + "@id" : { + "description" : "Message identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "@type" : { + "description" : "Message type", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "readOnly" : true, + "type" : "string" + }, + "disclosures" : { + "description" : "List of protocol or goal_code descriptors", + "items" : { + "type" : "object" + }, + "type" : "array" + } + }, + "required" : [ "disclosures" ], + "type" : "object" + }, + "Doc" : { + "properties" : { + "credential" : { + "description" : "Credential to sign", + "properties" : { }, + "type" : "object" + }, + "options" : { + "$ref" : "#/components/schemas/Doc_options" + } + }, + "required" : [ "credential", "options" ], + "type" : "object" + }, + "EndorserInfo" : { + "properties" : { + "endorser_did" : { + "description" : "Endorser DID", + "type" : "string" + }, + "endorser_name" : { + "description" : "Endorser Name", + "type" : "string" + } + }, + "required" : [ "endorser_did" ], + "type" : "object" + }, + "EndpointsResult" : { + "properties" : { + "my_endpoint" : { + "description" : "My endpoint", + "example" : "https://myhost:8021", + "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$", + "type" : "string" + }, + "their_endpoint" : { + "description" : "Their endpoint", + "example" : "https://myhost:8021", + "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$", + "type" : "string" + } + }, + "type" : "object" + }, + "Filter" : { + "properties" : { + "const" : { + "description" : "Const", + "type" : "object" + }, + "enum" : { + "items" : { + "description" : "Enum", + "type" : "object" + }, + "type" : "array" + }, + "exclusiveMaximum" : { + "description" : "ExclusiveMaximum", + "type" : "object" + }, + "exclusiveMinimum" : { + "description" : "ExclusiveMinimum", + "type" : "object" + }, + "format" : { + "description" : "Format", + "type" : "string" + }, + "maxLength" : { + "description" : "Max Length", + "example" : 1234, + "format" : "int32", + "type" : "integer" + }, + "maximum" : { + "description" : "Maximum", + "type" : "object" + }, + "minLength" : { + "description" : "Min Length", + "example" : 1234, + "format" : "int32", + "type" : "integer" + }, + "minimum" : { + "description" : "Minimum", + "type" : "object" + }, + "not" : { + "description" : "Not", + "example" : false, + "type" : "boolean" + }, + "pattern" : { + "description" : "Pattern", + "type" : "string" + }, + "type" : { + "description" : "Type", + "type" : "string" + } + }, + "type" : "object" + }, + "Generated" : { + "properties" : { + "master_secret" : { + "example" : "0", + "pattern" : "^[0-9]*$", + "type" : "string" + }, + "number" : { + "example" : "0", + "pattern" : "^[0-9]*$", + "type" : "string" + }, + "remainder" : { + "example" : "0", + "pattern" : "^[0-9]*$", + "type" : "string" + } + }, + "type" : "object" + }, + "GetDIDEndpointResponse" : { + "properties" : { + "endpoint" : { + "description" : "Full verification key", + "example" : "https://myhost:8021", + "nullable" : true, + "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$", + "type" : "string" + } + }, + "type" : "object" + }, + "GetDIDVerkeyResponse" : { + "properties" : { + "verkey" : { + "description" : "Full verification key", + "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", + "nullable" : true, + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$", + "type" : "string" + } + }, + "type" : "object" + }, + "GetNymRoleResponse" : { + "properties" : { + "role" : { + "description" : "Ledger role", + "enum" : [ "STEWARD", "TRUSTEE", "ENDORSER", "NETWORK_MONITOR", "USER", "ROLE_REMOVE" ], + "example" : "ENDORSER", + "type" : "string" + } + }, + "type" : "object" + }, + "HolderModuleResponse" : { + "type" : "object" + }, + "IndyAttrValue" : { + "properties" : { + "encoded" : { + "description" : "Attribute encoded value", + "example" : "-1", + "pattern" : "^-?[0-9]*$", + "type" : "string" + }, + "raw" : { + "description" : "Attribute raw value", + "type" : "string" + } + }, + "required" : [ "encoded", "raw" ], + "type" : "object" + }, + "IndyCredAbstract" : { + "properties" : { + "cred_def_id" : { + "description" : "Credential definition identifier", + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$", + "type" : "string" + }, + "key_correctness_proof" : { + "$ref" : "#/components/schemas/IndyCredAbstract_key_correctness_proof" + }, + "nonce" : { + "description" : "Nonce in credential abstract", + "example" : "0", + "pattern" : "^[0-9]*$", + "type" : "string" + }, + "schema_id" : { + "description" : "Schema identifier", + "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$", + "type" : "string" + } + }, + "required" : [ "cred_def_id", "key_correctness_proof", "nonce", "schema_id" ], + "type" : "object" + }, + "IndyCredInfo" : { + "properties" : { + "attrs" : { + "additionalProperties" : { + "example" : "alice", + "type" : "string" + }, + "description" : "Attribute names and value", + "type" : "object" + }, + "cred_def_id" : { + "description" : "Credential definition identifier", + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$", + "type" : "string" + }, + "cred_rev_id" : { + "description" : "Credential revocation identifier", + "example" : "12345", + "nullable" : true, + "pattern" : "^[1-9][0-9]*$", + "type" : "string" + }, + "referent" : { + "description" : "Wallet referent", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "rev_reg_id" : { + "description" : "Revocation registry identifier", + "example" : "WgWxqztrNooG92RXvxSTWv:4:WgWxqztrNooG92RXvxSTWv:3:CL:20:tag:CL_ACCUM:0", + "nullable" : true, + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)", + "type" : "string" + }, + "schema_id" : { + "description" : "Schema identifier", + "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$", + "type" : "string" + } + }, + "type" : "object" + }, + "IndyCredPrecis" : { + "properties" : { + "cred_info" : { + "$ref" : "#/components/schemas/IndyCredPrecis_cred_info" + }, + "interval" : { + "$ref" : "#/components/schemas/IndyCredPrecis_interval" + }, + "presentation_referents" : { + "items" : { + "description" : "presentation referent", + "example" : "1_age_uuid", + "type" : "string" + }, + "type" : "array" + } + }, + "type" : "object" + }, + "IndyCredRequest" : { + "properties" : { + "blinded_ms" : { + "description" : "Blinded master secret", + "properties" : { }, + "type" : "object" + }, + "blinded_ms_correctness_proof" : { + "description" : "Blinded master secret correctness proof", + "properties" : { }, + "type" : "object" + }, + "cred_def_id" : { + "description" : "Credential definition identifier", + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$", + "type" : "string" + }, + "nonce" : { + "description" : "Nonce in credential request", + "example" : "0", + "pattern" : "^[0-9]*$", + "type" : "string" + }, + "prover_did" : { + "description" : "Prover DID", + "example" : "WgWxqztrNooG92RXvxSTWv", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$", + "type" : "string" + } + }, + "required" : [ "blinded_ms", "blinded_ms_correctness_proof", "cred_def_id", "nonce", "prover_did" ], + "type" : "object" + }, + "IndyCredential" : { + "properties" : { + "cred_def_id" : { + "description" : "Credential definition identifier", + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$", + "type" : "string" + }, + "rev_reg" : { + "description" : "Revocation registry state", + "nullable" : true, + "properties" : { }, + "type" : "object" + }, + "rev_reg_id" : { + "description" : "Revocation registry identifier", + "example" : "WgWxqztrNooG92RXvxSTWv:4:WgWxqztrNooG92RXvxSTWv:3:CL:20:tag:CL_ACCUM:0", + "nullable" : true, + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)", + "type" : "string" + }, + "schema_id" : { + "description" : "Schema identifier", + "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$", + "type" : "string" + }, + "signature" : { + "description" : "Credential signature", + "properties" : { }, + "type" : "object" + }, + "signature_correctness_proof" : { + "description" : "Credential signature correctness proof", + "properties" : { }, + "type" : "object" + }, + "values" : { + "additionalProperties" : { + "$ref" : "#/components/schemas/IndyCredential_values_value" + }, + "description" : "Credential attributes", + "type" : "object" + }, + "witness" : { + "description" : "Witness for revocation proof", + "nullable" : true, + "properties" : { }, + "type" : "object" + } + }, + "required" : [ "cred_def_id", "schema_id", "signature", "signature_correctness_proof", "values" ], + "type" : "object" + }, + "IndyEQProof" : { + "properties" : { + "a_prime" : { + "example" : "0", + "pattern" : "^[0-9]*$", + "type" : "string" + }, + "e" : { + "example" : "0", + "pattern" : "^[0-9]*$", + "type" : "string" + }, + "m" : { + "additionalProperties" : { + "example" : "0", + "pattern" : "^[0-9]*$", + "type" : "string" + }, + "type" : "object" + }, + "m2" : { + "example" : "0", + "pattern" : "^[0-9]*$", + "type" : "string" + }, + "revealed_attrs" : { + "additionalProperties" : { + "example" : "-1", + "pattern" : "^-?[0-9]*$", + "type" : "string" + }, + "type" : "object" + }, + "v" : { + "example" : "0", + "pattern" : "^[0-9]*$", + "type" : "string" + } + }, + "type" : "object" + }, + "IndyGEProof" : { + "properties" : { + "alpha" : { + "example" : "0", + "pattern" : "^[0-9]*$", + "type" : "string" + }, + "mj" : { + "example" : "0", + "pattern" : "^[0-9]*$", + "type" : "string" + }, + "predicate" : { + "$ref" : "#/components/schemas/IndyGEProofPred" + }, + "r" : { + "additionalProperties" : { + "example" : "0", + "pattern" : "^[0-9]*$", + "type" : "string" + }, + "type" : "object" + }, + "t" : { + "additionalProperties" : { + "example" : "0", + "pattern" : "^[0-9]*$", + "type" : "string" + }, + "type" : "object" + }, + "u" : { + "additionalProperties" : { + "example" : "0", + "pattern" : "^[0-9]*$", + "type" : "string" + }, + "type" : "object" + } + }, + "type" : "object" + }, + "IndyGEProofPred" : { + "properties" : { + "attr_name" : { + "description" : "Attribute name, indy-canonicalized", + "type" : "string" + }, + "p_type" : { + "description" : "Predicate type", + "enum" : [ "LT", "LE", "GE", "GT" ], + "type" : "string" + }, + "value" : { + "description" : "Predicate threshold value", + "format" : "int32", + "type" : "integer" + } + }, + "type" : "object" + }, + "IndyKeyCorrectnessProof" : { + "properties" : { + "c" : { + "description" : "c in key correctness proof", + "example" : "0", + "pattern" : "^[0-9]*$", + "type" : "string" + }, + "xr_cap" : { + "description" : "xr_cap in key correctness proof", + "items" : { + "description" : "xr_cap components in key correctness proof", + "items" : { + "description" : "xr_cap component values in key correctness proof", + "type" : "string" + }, + "type" : "array" + }, + "type" : "array" + }, + "xz_cap" : { + "description" : "xz_cap in key correctness proof", + "example" : "0", + "pattern" : "^[0-9]*$", + "type" : "string" + } + }, + "required" : [ "c", "xr_cap", "xz_cap" ], + "type" : "object" + }, + "IndyNonRevocProof" : { + "properties" : { + "c_list" : { + "additionalProperties" : { + "type" : "string" + }, + "type" : "object" + }, + "x_list" : { + "additionalProperties" : { + "type" : "string" + }, + "type" : "object" + } + }, + "type" : "object" + }, + "IndyNonRevocationInterval" : { + "properties" : { + "from" : { + "description" : "Earliest time of interest in non-revocation interval", + "example" : 1640995199, + "format" : "int32", + "maximum" : 18446744073709551615, + "minimum" : 0, + "type" : "integer" + }, + "to" : { + "description" : "Latest time of interest in non-revocation interval", + "example" : 1640995199, + "format" : "int32", + "maximum" : 18446744073709551615, + "minimum" : 0, + "type" : "integer" + } + }, + "type" : "object" + }, + "IndyPresAttrSpec" : { + "properties" : { + "cred_def_id" : { + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$", + "type" : "string" + }, + "mime-type" : { + "description" : "MIME type (default null)", + "example" : "image/jpeg", + "type" : "string" + }, + "name" : { + "description" : "Attribute name", + "example" : "favourite_drink", + "type" : "string" + }, + "referent" : { + "description" : "Credential referent", + "example" : "0", + "type" : "string" + }, + "value" : { + "description" : "Attribute value", + "example" : "martini", + "type" : "string" + } + }, + "required" : [ "name" ], + "type" : "object" + }, + "IndyPresPredSpec" : { + "properties" : { + "cred_def_id" : { + "description" : "Credential definition identifier", + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$", + "type" : "string" + }, + "name" : { + "description" : "Attribute name", + "example" : "high_score", + "type" : "string" + }, + "predicate" : { + "description" : "Predicate type ('<', '<=', '>=', or '>')", + "enum" : [ "<", "<=", ">=", ">" ], + "example" : ">=", + "type" : "string" + }, + "threshold" : { + "description" : "Threshold value", + "format" : "int32", + "type" : "integer" + } + }, + "required" : [ "name", "predicate", "threshold" ], + "type" : "object" + }, + "IndyPresPreview" : { + "properties" : { + "@type" : { + "description" : "Message type identifier", + "example" : "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/present-proof/1.0/presentation-preview", + "type" : "string" + }, + "attributes" : { + "items" : { + "$ref" : "#/components/schemas/IndyPresAttrSpec" + }, + "type" : "array" + }, + "predicates" : { + "items" : { + "$ref" : "#/components/schemas/IndyPresPredSpec" + }, + "type" : "array" + } + }, + "required" : [ "attributes", "predicates" ], + "type" : "object" + }, + "IndyPresSpec" : { + "properties" : { + "requested_attributes" : { + "additionalProperties" : { + "$ref" : "#/components/schemas/IndyRequestedCredsRequestedAttr" + }, + "description" : "Nested object mapping proof request attribute referents to requested-attribute specifiers", + "type" : "object" + }, + "requested_predicates" : { + "additionalProperties" : { + "$ref" : "#/components/schemas/IndyRequestedCredsRequestedPred" + }, + "description" : "Nested object mapping proof request predicate referents to requested-predicate specifiers", + "type" : "object" + }, + "self_attested_attributes" : { + "additionalProperties" : { + "description" : "Self-attested attribute values to use in requested-credentials structure for proof construction", + "example" : "self_attested_value", + "type" : "string" + }, + "description" : "Self-attested attributes to build into proof", + "type" : "object" + }, + "trace" : { + "description" : "Whether to trace event (default false)", + "example" : false, + "type" : "boolean" + } + }, + "required" : [ "requested_attributes", "requested_predicates", "self_attested_attributes" ], + "type" : "object" + }, + "IndyPrimaryProof" : { + "properties" : { + "eq_proof" : { + "$ref" : "#/components/schemas/IndyPrimaryProof_eq_proof" + }, + "ge_proofs" : { + "description" : "Indy GE proofs", + "items" : { + "$ref" : "#/components/schemas/IndyGEProof" + }, + "nullable" : true, + "type" : "array" + } + }, + "type" : "object" + }, + "IndyProof" : { + "properties" : { + "identifiers" : { + "description" : "Indy proof.identifiers content", + "items" : { + "$ref" : "#/components/schemas/IndyProofIdentifier" + }, + "type" : "array" + }, + "proof" : { + "$ref" : "#/components/schemas/IndyProof_proof" + }, + "requested_proof" : { + "$ref" : "#/components/schemas/IndyProof_requested_proof" + } + }, + "type" : "object" + }, + "IndyProofIdentifier" : { + "properties" : { + "cred_def_id" : { + "description" : "Credential definition identifier", + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$", + "type" : "string" + }, + "rev_reg_id" : { + "description" : "Revocation registry identifier", + "example" : "WgWxqztrNooG92RXvxSTWv:4:WgWxqztrNooG92RXvxSTWv:3:CL:20:tag:CL_ACCUM:0", + "nullable" : true, + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)", + "type" : "string" + }, + "schema_id" : { + "description" : "Schema identifier", + "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$", + "type" : "string" + }, + "timestamp" : { + "description" : "Timestamp epoch", + "example" : 1640995199, + "format" : "int32", + "maximum" : 18446744073709551615, + "minimum" : 0, + "nullable" : true, + "type" : "integer" + } + }, + "type" : "object" + }, + "IndyProofProof" : { + "properties" : { + "aggregated_proof" : { + "$ref" : "#/components/schemas/IndyProofProof_aggregated_proof" + }, + "proofs" : { + "description" : "Indy proof proofs", + "items" : { + "$ref" : "#/components/schemas/IndyProofProofProofsProof" + }, + "type" : "array" + } + }, + "type" : "object" + }, + "IndyProofProofAggregatedProof" : { + "properties" : { + "c_hash" : { + "description" : "c_hash value", + "type" : "string" + }, + "c_list" : { + "description" : "c_list value", + "items" : { + "items" : { + "format" : "int32", + "type" : "integer" + }, + "type" : "array" + }, + "type" : "array" + } + }, + "type" : "object" + }, + "IndyProofProofProofsProof" : { + "properties" : { + "non_revoc_proof" : { + "$ref" : "#/components/schemas/IndyProofProofProofsProof_non_revoc_proof" + }, + "primary_proof" : { + "$ref" : "#/components/schemas/IndyProofProofProofsProof_primary_proof" + } + }, + "type" : "object" + }, + "IndyProofReqAttrSpec" : { + "properties" : { + "name" : { + "description" : "Attribute name", + "example" : "favouriteDrink", + "type" : "string" + }, + "names" : { + "description" : "Attribute name group", + "items" : { + "example" : "age", + "type" : "string" + }, + "type" : "array" + }, + "non_revoked" : { + "$ref" : "#/components/schemas/IndyProofReqAttrSpec_non_revoked" + }, + "restrictions" : { + "description" : "If present, credential must satisfy one of given restrictions: specify schema_id, schema_issuer_did, schema_name, schema_version, issuer_did, cred_def_id, and/or attr::::value where represents a credential attribute name", + "items" : { + "additionalProperties" : { + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "type" : "string" + }, + "type" : "object" + }, + "type" : "array" + } + }, + "type" : "object" + }, + "IndyProofReqAttrSpecNonRevoked" : { + "properties" : { + "from" : { + "description" : "Earliest time of interest in non-revocation interval", + "example" : 1640995199, + "format" : "int32", + "maximum" : 18446744073709551615, + "minimum" : 0, + "type" : "integer" + }, + "to" : { + "description" : "Latest time of interest in non-revocation interval", + "example" : 1640995199, + "format" : "int32", + "maximum" : 18446744073709551615, + "minimum" : 0, + "type" : "integer" + } + }, + "type" : "object" + }, + "IndyProofReqPredSpec" : { + "properties" : { + "name" : { + "description" : "Attribute name", + "example" : "index", + "type" : "string" + }, + "non_revoked" : { + "$ref" : "#/components/schemas/IndyProofReqPredSpec_non_revoked" + }, + "p_type" : { + "description" : "Predicate type ('<', '<=', '>=', or '>')", + "enum" : [ "<", "<=", ">=", ">" ], + "example" : ">=", + "type" : "string" + }, + "p_value" : { + "description" : "Threshold value", + "format" : "int32", + "type" : "integer" + }, + "restrictions" : { + "description" : "If present, credential must satisfy one of given restrictions: specify schema_id, schema_issuer_did, schema_name, schema_version, issuer_did, cred_def_id, and/or attr::::value where represents a credential attribute name", + "items" : { + "additionalProperties" : { + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "type" : "string" + }, + "type" : "object" + }, + "type" : "array" + } + }, + "required" : [ "name", "p_type", "p_value" ], + "type" : "object" + }, + "IndyProofReqPredSpecNonRevoked" : { + "properties" : { + "from" : { + "description" : "Earliest time of interest in non-revocation interval", + "example" : 1640995199, + "format" : "int32", + "maximum" : 18446744073709551615, + "minimum" : 0, + "type" : "integer" + }, + "to" : { + "description" : "Latest time of interest in non-revocation interval", + "example" : 1640995199, + "format" : "int32", + "maximum" : 18446744073709551615, + "minimum" : 0, + "type" : "integer" + } + }, + "type" : "object" + }, + "IndyProofRequest" : { + "properties" : { + "name" : { + "description" : "Proof request name", + "example" : "Proof request", + "type" : "string" + }, + "non_revoked" : { + "$ref" : "#/components/schemas/IndyProofRequest_non_revoked" + }, + "nonce" : { + "description" : "Nonce", + "example" : "1", + "pattern" : "^[1-9][0-9]*$", + "type" : "string" + }, + "requested_attributes" : { + "additionalProperties" : { + "$ref" : "#/components/schemas/IndyProofReqAttrSpec" + }, + "description" : "Requested attribute specifications of proof request", + "type" : "object" + }, + "requested_predicates" : { + "additionalProperties" : { + "$ref" : "#/components/schemas/IndyProofReqPredSpec" + }, + "description" : "Requested predicate specifications of proof request", + "type" : "object" + }, + "version" : { + "description" : "Proof request version", + "example" : "1.0", + "pattern" : "^[0-9.]+$", + "type" : "string" + } + }, + "required" : [ "requested_attributes", "requested_predicates" ], + "type" : "object" + }, + "IndyProofRequestNonRevoked" : { + "properties" : { + "from" : { + "description" : "Earliest time of interest in non-revocation interval", + "example" : 1640995199, + "format" : "int32", + "maximum" : 18446744073709551615, + "minimum" : 0, + "type" : "integer" + }, + "to" : { + "description" : "Latest time of interest in non-revocation interval", + "example" : 1640995199, + "format" : "int32", + "maximum" : 18446744073709551615, + "minimum" : 0, + "type" : "integer" + } + }, + "type" : "object" + }, + "IndyProofRequestedProof" : { + "properties" : { + "predicates" : { + "additionalProperties" : { + "$ref" : "#/components/schemas/IndyProofRequestedProofPredicate" + }, + "description" : "Proof requested proof predicates.", + "type" : "object" + }, + "revealed_attr_groups" : { + "additionalProperties" : { + "$ref" : "#/components/schemas/IndyProofRequestedProofRevealedAttrGroup" + }, + "description" : "Proof requested proof revealed attribute groups", + "nullable" : true, + "type" : "object" + }, + "revealed_attrs" : { + "additionalProperties" : { + "$ref" : "#/components/schemas/IndyProofRequestedProofRevealedAttr" + }, + "description" : "Proof requested proof revealed attributes", + "nullable" : true, + "type" : "object" + }, + "self_attested_attrs" : { + "description" : "Proof requested proof self-attested attributes", + "properties" : { }, + "type" : "object" + }, + "unrevealed_attrs" : { + "description" : "Unrevealed attributes", + "properties" : { }, + "type" : "object" + } + }, + "type" : "object" + }, + "IndyProofRequestedProofPredicate" : { + "properties" : { + "sub_proof_index" : { + "description" : "Sub-proof index", + "format" : "int32", + "type" : "integer" + } + }, + "type" : "object" + }, + "IndyProofRequestedProofRevealedAttr" : { + "properties" : { + "encoded" : { + "description" : "Encoded value", + "example" : "-1", + "pattern" : "^-?[0-9]*$", + "type" : "string" + }, + "raw" : { + "description" : "Raw value", + "type" : "string" + }, + "sub_proof_index" : { + "description" : "Sub-proof index", + "format" : "int32", + "type" : "integer" + } + }, + "type" : "object" + }, + "IndyProofRequestedProofRevealedAttrGroup" : { + "properties" : { + "sub_proof_index" : { + "description" : "Sub-proof index", + "format" : "int32", + "type" : "integer" + }, + "values" : { + "additionalProperties" : { + "$ref" : "#/components/schemas/RawEncoded" + }, + "description" : "Indy proof requested proof revealed attr groups group value", + "type" : "object" + } + }, + "type" : "object" + }, + "IndyRequestedCredsRequestedAttr" : { + "properties" : { + "cred_id" : { + "description" : "Wallet credential identifier (typically but not necessarily a UUID)", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "revealed" : { + "description" : "Whether to reveal attribute in proof (default true)", + "type" : "boolean" + } + }, + "required" : [ "cred_id" ], + "type" : "object" + }, + "IndyRequestedCredsRequestedPred" : { + "properties" : { + "cred_id" : { + "description" : "Wallet credential identifier (typically but not necessarily a UUID)", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "timestamp" : { + "description" : "Epoch timestamp of interest for non-revocation proof", + "example" : 1640995199, + "format" : "int32", + "maximum" : 18446744073709551615, + "minimum" : 0, + "type" : "integer" + } + }, + "required" : [ "cred_id" ], + "type" : "object" + }, + "IndyRevRegDef" : { + "properties" : { + "credDefId" : { + "description" : "Credential definition identifier", + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$", + "type" : "string" + }, + "id" : { + "description" : "Indy revocation registry identifier", + "example" : "WgWxqztrNooG92RXvxSTWv:4:WgWxqztrNooG92RXvxSTWv:3:CL:20:tag:CL_ACCUM:0", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)", + "type" : "string" + }, + "revocDefType" : { + "description" : "Revocation registry type (specify CL_ACCUM)", + "enum" : [ "CL_ACCUM" ], + "example" : "CL_ACCUM", + "type" : "string" + }, + "tag" : { + "description" : "Revocation registry tag", + "type" : "string" + }, + "value" : { + "$ref" : "#/components/schemas/IndyRevRegDef_value" + }, + "ver" : { + "description" : "Version of revocation registry definition", + "example" : "1.0", + "pattern" : "^[0-9.]+$", + "type" : "string" + } + }, + "type" : "object" + }, + "IndyRevRegDefValue" : { + "properties" : { + "issuanceType" : { + "description" : "Issuance type", + "enum" : [ "ISSUANCE_ON_DEMAND", "ISSUANCE_BY_DEFAULT" ], + "type" : "string" + }, + "maxCredNum" : { + "description" : "Maximum number of credentials; registry size", + "example" : 10, + "format" : "int32", + "minimum" : 1, + "type" : "integer" + }, + "publicKeys" : { + "$ref" : "#/components/schemas/IndyRevRegDefValue_publicKeys" + }, + "tailsHash" : { + "description" : "Tails hash value", + "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$", + "type" : "string" + }, + "tailsLocation" : { + "description" : "Tails file location", + "type" : "string" + } + }, + "type" : "object" + }, + "IndyRevRegDefValuePublicKeys" : { + "properties" : { + "accumKey" : { + "$ref" : "#/components/schemas/IndyRevRegDefValuePublicKeysAccumKey" + } + }, + "type" : "object" + }, + "IndyRevRegDefValuePublicKeysAccumKey" : { + "properties" : { + "z" : { + "description" : "Value for z", + "example" : "1 120F522F81E6B7 1 09F7A59005C4939854", + "type" : "string" + } + }, + "type" : "object" + }, + "IndyRevRegEntry" : { + "properties" : { + "value" : { + "$ref" : "#/components/schemas/IndyRevRegEntry_value" + }, + "ver" : { + "description" : "Version of revocation registry entry", + "example" : "1.0", + "pattern" : "^[0-9.]+$", + "type" : "string" + } + }, + "type" : "object" + }, + "IndyRevRegEntryValue" : { + "properties" : { + "accum" : { + "description" : "Accumulator value", + "example" : "21 11792B036AED0AAA12A4 4 298B2571FFC63A737", + "type" : "string" + }, + "prevAccum" : { + "description" : "Previous accumulator value", + "example" : "21 137AC810975E4 6 76F0384B6F23", + "type" : "string" + }, + "revoked" : { + "description" : "Revoked credential revocation identifiers", + "items" : { + "format" : "int32", + "type" : "integer" + }, + "type" : "array" + } + }, + "type" : "object" + }, + "InputDescriptors" : { + "properties" : { + "constraints" : { + "$ref" : "#/components/schemas/Constraints" + }, + "group" : { + "items" : { + "description" : "Group", + "type" : "string" + }, + "type" : "array" + }, + "id" : { + "description" : "ID", + "type" : "string" + }, + "metadata" : { + "description" : "Metadata dictionary", + "properties" : { }, + "type" : "object" + }, + "name" : { + "description" : "Name", + "type" : "string" + }, + "purpose" : { + "description" : "Purpose", + "type" : "string" + }, + "schema" : { + "$ref" : "#/components/schemas/InputDescriptors_schema" + } + }, + "type" : "object" + }, + "IntroModuleResponse" : { + "type" : "object" + }, + "InvitationCreateRequest" : { + "properties" : { + "accept" : { + "description" : "List of mime type in order of preference that should be use in responding to the message", + "example" : [ "didcomm/aip1", "didcomm/aip2;env=rfc19" ], + "items" : { + "type" : "string" + }, + "type" : "array" + }, + "alias" : { + "description" : "Alias for connection", + "example" : "Barry", + "type" : "string" + }, + "attachments" : { + "description" : "Optional invitation attachments", + "items" : { + "$ref" : "#/components/schemas/AttachmentDef" + }, + "type" : "array" + }, + "handshake_protocols" : { + "items" : { + "description" : "Handshake protocol to specify in invitation", + "example" : "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/didexchange/1.0", + "type" : "string" + }, + "type" : "array" + }, + "mediation_id" : { + "description" : "Identifier for active mediation record to be used", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", + "type" : "string" + }, + "metadata" : { + "description" : "Optional metadata to attach to the connection created with the invitation", + "properties" : { }, + "type" : "object" + }, + "my_label" : { + "description" : "Label for connection invitation", + "example" : "Invitation to Barry", + "type" : "string" + }, + "protocol_version" : { + "description" : "OOB protocol version", + "example" : "1.1", + "type" : "string" + }, + "use_public_did" : { + "description" : "Whether to use public DID in invitation", + "example" : false, + "type" : "boolean" + } + }, + "type" : "object" + }, + "InvitationMessage" : { + "properties" : { + "@id" : { + "description" : "Message identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "@type" : { + "description" : "Message type", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "type" : "string" + }, + "accept" : { + "description" : "List of mime type in order of preference", + "example" : [ "didcomm/aip1", "didcomm/aip2;env=rfc19" ], + "items" : { + "type" : "string" + }, + "type" : "array" + }, + "handshake_protocols" : { + "items" : { + "description" : "Handshake protocol", + "example" : "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/didexchange/1.0", + "type" : "string" + }, + "type" : "array" + }, + "imageUrl" : { + "description" : "Optional image URL for out-of-band invitation", + "example" : "http://192.168.56.101/img/logo.jpg", + "format" : "url", + "nullable" : true, + "type" : "string" + }, + "label" : { + "description" : "Optional label", + "example" : "Bob", + "type" : "string" + }, + "requests~attach" : { + "description" : "Optional request attachment", + "items" : { + "$ref" : "#/components/schemas/AttachDecorator" + }, + "type" : "array" + }, + "services" : { + "example" : [ { + "did" : "WgWxqztrNooG92RXvxSTWv", + "id" : "string", + "recipientKeys" : [ "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH" ], + "routingKeys" : [ "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH" ], + "serviceEndpoint" : "http://192.168.56.101:8020", + "type" : "string" + }, "did:sov:WgWxqztrNooG92RXvxSTWv" ], + "items" : { + "description" : "Either a DIDComm service object (as per RFC0067) or a DID string.", + "type" : "object" + }, + "type" : "array" + } + }, + "type" : "object" + }, + "InvitationRecord" : { + "properties" : { + "created_at" : { + "description" : "Time of record creation", + "example" : "2021-12-31T23:59:59Z", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$", + "type" : "string" + }, + "invi_msg_id" : { + "description" : "Invitation message identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "invitation" : { + "$ref" : "#/components/schemas/InvitationRecord_invitation" + }, + "invitation_id" : { + "description" : "Invitation record identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "invitation_url" : { + "description" : "Invitation message URL", + "example" : "https://example.com/endpoint?c_i=eyJAdHlwZSI6ICIuLi4iLCAiLi4uIjogIi4uLiJ9XX0=", + "type" : "string" + }, + "oob_id" : { + "description" : "Out of band record identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "state" : { + "description" : "Out of band message exchange state", + "example" : "await_response", + "type" : "string" + }, + "trace" : { + "description" : "Record trace information, based on agent configuration", + "type" : "boolean" + }, + "updated_at" : { + "description" : "Time of last record update", + "example" : "2021-12-31T23:59:59Z", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$", + "type" : "string" + } + }, + "type" : "object" + }, + "InvitationResult" : { + "properties" : { + "connection_id" : { + "description" : "Connection identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "invitation" : { + "$ref" : "#/components/schemas/ConnectionInvitation" + }, + "invitation_url" : { + "description" : "Invitation URL", + "example" : "http://192.168.56.101:8020/invite?c_i=eyJAdHlwZSI6Li4ufQ==", + "type" : "string" + } + }, + "type" : "object" + }, + "IssueCredentialModuleResponse" : { + "type" : "object" + }, + "IssuerCredRevRecord" : { + "properties" : { + "created_at" : { + "description" : "Time of record creation", + "example" : "2021-12-31T23:59:59Z", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$", + "type" : "string" + }, + "cred_def_id" : { + "description" : "Credential definition identifier", + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$", + "type" : "string" + }, + "cred_ex_id" : { + "description" : "Credential exchange record identifier at credential issue", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "cred_ex_version" : { + "description" : "Credential exchange version", + "type" : "string" + }, + "cred_rev_id" : { + "description" : "Credential revocation identifier", + "example" : "12345", + "pattern" : "^[1-9][0-9]*$", + "type" : "string" + }, + "record_id" : { + "description" : "Issuer credential revocation record identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "rev_reg_id" : { + "description" : "Revocation registry identifier", + "example" : "WgWxqztrNooG92RXvxSTWv:4:WgWxqztrNooG92RXvxSTWv:3:CL:20:tag:CL_ACCUM:0", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)", + "type" : "string" + }, + "state" : { + "description" : "Issue credential revocation record state", + "example" : "issued", + "type" : "string" + }, + "updated_at" : { + "description" : "Time of last record update", + "example" : "2021-12-31T23:59:59Z", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$", + "type" : "string" + } + }, + "type" : "object" + }, + "IssuerRevRegRecord" : { + "properties" : { + "created_at" : { + "description" : "Time of record creation", + "example" : "2021-12-31T23:59:59Z", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$", + "type" : "string" + }, + "cred_def_id" : { + "description" : "Credential definition identifier", + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$", + "type" : "string" + }, + "error_msg" : { + "description" : "Error message", + "example" : "Revocation registry undefined", + "type" : "string" + }, + "issuer_did" : { + "description" : "Issuer DID", + "example" : "WgWxqztrNooG92RXvxSTWv", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$", + "type" : "string" + }, + "max_cred_num" : { + "description" : "Maximum number of credentials for revocation registry", + "example" : 1000, + "format" : "int32", + "type" : "integer" + }, + "pending_pub" : { + "description" : "Credential revocation identifier for credential revoked and pending publication to ledger", + "items" : { + "example" : "23", + "type" : "string" + }, + "type" : "array" + }, + "record_id" : { + "description" : "Issuer revocation registry record identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "revoc_def_type" : { + "description" : "Revocation registry type (specify CL_ACCUM)", + "enum" : [ "CL_ACCUM" ], + "example" : "CL_ACCUM", + "type" : "string" + }, + "revoc_reg_def" : { + "$ref" : "#/components/schemas/IssuerRevRegRecord_revoc_reg_def" + }, + "revoc_reg_entry" : { + "$ref" : "#/components/schemas/IssuerRevRegRecord_revoc_reg_entry" + }, + "revoc_reg_id" : { + "description" : "Revocation registry identifier", + "example" : "WgWxqztrNooG92RXvxSTWv:4:WgWxqztrNooG92RXvxSTWv:3:CL:20:tag:CL_ACCUM:0", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)", + "type" : "string" + }, + "state" : { + "description" : "Issue revocation registry record state", + "example" : "active", + "type" : "string" + }, + "tag" : { + "description" : "Tag within issuer revocation registry identifier", + "type" : "string" + }, + "tails_hash" : { + "description" : "Tails hash", + "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$", + "type" : "string" + }, + "tails_local_path" : { + "description" : "Local path to tails file", + "type" : "string" + }, + "tails_public_uri" : { + "description" : "Public URI for tails file", + "type" : "string" + }, + "updated_at" : { + "description" : "Time of last record update", + "example" : "2021-12-31T23:59:59Z", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$", + "type" : "string" + } + }, + "type" : "object" + }, + "Keylist" : { + "properties" : { + "results" : { + "description" : "List of keylist records", + "items" : { + "$ref" : "#/components/schemas/RouteRecord" + }, + "type" : "array" + } + }, + "type" : "object" + }, + "KeylistQuery" : { + "properties" : { + "@id" : { + "description" : "Message identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "@type" : { + "description" : "Message type", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "readOnly" : true, + "type" : "string" + }, + "filter" : { + "description" : "Query dictionary object", + "example" : { + "filter" : { } + }, + "properties" : { }, + "type" : "object" + }, + "paginate" : { + "$ref" : "#/components/schemas/KeylistQuery_paginate" + } + }, + "type" : "object" + }, + "KeylistQueryFilterRequest" : { + "properties" : { + "filter" : { + "description" : "Filter for keylist query", + "properties" : { }, + "type" : "object" + } + }, + "type" : "object" + }, + "KeylistQueryPaginate" : { + "properties" : { + "limit" : { + "description" : "Limit for keylist query", + "example" : 30, + "format" : "int32", + "type" : "integer" + }, + "offset" : { + "description" : "Offset value for query", + "example" : 0, + "format" : "int32", + "type" : "integer" + } + }, + "type" : "object" + }, + "KeylistUpdate" : { + "properties" : { + "@id" : { + "description" : "Message identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "@type" : { + "description" : "Message type", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "readOnly" : true, + "type" : "string" + }, + "updates" : { + "description" : "List of update rules", + "items" : { + "$ref" : "#/components/schemas/KeylistUpdateRule" + }, + "type" : "array" + } + }, + "type" : "object" + }, + "KeylistUpdateRequest" : { + "properties" : { + "updates" : { + "items" : { + "$ref" : "#/components/schemas/KeylistUpdateRule" + }, + "type" : "array" + } + }, + "type" : "object" + }, + "KeylistUpdateRule" : { + "properties" : { + "action" : { + "description" : "Action for specific key", + "enum" : [ "add", "remove" ], + "example" : "add", + "type" : "string" + }, + "recipient_key" : { + "description" : "Key to remove or add", + "example" : "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH", + "pattern" : "^did:key:z[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+$|^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$", + "type" : "string" + } + }, + "required" : [ "action", "recipient_key" ], + "type" : "object" + }, + "LDProofVCDetail" : { + "properties" : { + "credential" : { + "$ref" : "#/components/schemas/LDProofVCDetail_credential" + }, + "options" : { + "$ref" : "#/components/schemas/LDProofVCDetail_options" + } + }, + "required" : [ "credential", "options" ], + "type" : "object" + }, + "LDProofVCDetailOptions" : { + "properties" : { + "challenge" : { + "description" : "A challenge to include in the proof. SHOULD be provided by the requesting party of the credential (=holder)", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "created" : { + "description" : "The date and time of the proof (with a maximum accuracy in seconds). Defaults to current system time", + "example" : "2021-12-31T23:59:59Z", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$", + "type" : "string" + }, + "credentialStatus" : { + "$ref" : "#/components/schemas/LDProofVCDetailOptions_credentialStatus" + }, + "domain" : { + "description" : "The intended domain of validity for the proof", + "example" : "example.com", + "type" : "string" + }, + "proofPurpose" : { + "description" : "The proof purpose used for the proof. Should match proof purposes registered in the Linked Data Proofs Specification", + "example" : "assertionMethod", + "type" : "string" + }, + "proofType" : { + "description" : "The proof type used for the proof. Should match suites registered in the Linked Data Cryptographic Suite Registry", + "example" : "Ed25519Signature2018", + "type" : "string" + } + }, + "required" : [ "proofType" ], + "type" : "object" + }, + "LedgerConfigInstance" : { + "properties" : { + "genesis_file" : { + "description" : "genesis_file", + "type" : "string" + }, + "genesis_transactions" : { + "description" : "genesis_transactions", + "type" : "string" + }, + "genesis_url" : { + "description" : "genesis_url", + "type" : "string" + }, + "id" : { + "description" : "ledger_id", + "type" : "string" + }, + "is_production" : { + "description" : "is_production", + "type" : "boolean" + } + }, + "type" : "object" + }, + "LedgerConfigList" : { + "properties" : { + "ledger_config_list" : { + "items" : { + "$ref" : "#/components/schemas/LedgerConfigInstance" + }, + "type" : "array" + } + }, + "required" : [ "ledger_config_list" ], + "type" : "object" + }, + "LedgerModulesResult" : { + "type" : "object" + }, + "LinkedDataProof" : { + "properties" : { + "challenge" : { + "description" : "Associates a challenge with a proof, for use with a proofPurpose such as authentication", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "created" : { + "description" : "The string value of an ISO8601 combined date and time string generated by the Signature Algorithm", + "example" : "2021-12-31T23:59:59Z", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$", + "type" : "string" + }, + "domain" : { + "description" : "A string value specifying the restricted domain of the signature.", + "example" : "example.com", + "pattern" : "\\w+:(\\/?\\/?)[^\\s]+", + "type" : "string" + }, + "jws" : { + "description" : "Associates a Detached Json Web Signature with a proof", + "example" : "eyJhbGciOiAiRWREUc2UsICJjcml0IjogWyJiNjQiXX0..lKJU0Df_keblRKhZAS9Qq6zybm-HqUXNVZ8vgEPNTAjQ1Ch6YBKY7UBAjg6iBX5qBQ", + "type" : "string" + }, + "nonce" : { + "description" : "The nonce", + "example" : "CF69iO3nfvqRsRBNElE8b4wO39SyJHPM7Gg1nExltW5vSfQA1lvDCR/zXX1To0/4NLo==", + "type" : "string" + }, + "proofPurpose" : { + "description" : "Proof purpose", + "example" : "assertionMethod", + "type" : "string" + }, + "proofValue" : { + "description" : "The proof value of a proof", + "example" : "sy1AahqbzJQ63n9RtekmwzqZeVj494VppdAVJBnMYrTwft6cLJJGeTSSxCCJ6HKnRtwE7jjDh6sB2z2AAiZY9BBnCD8wUVgwqH3qchGRCuC2RugA4eQ9fUrR4Yuycac3caiaaay", + "type" : "string" + }, + "type" : { + "description" : "Identifies the digital signature suite that was used to create the signature", + "example" : "Ed25519Signature2018", + "type" : "string" + }, + "verificationMethod" : { + "description" : "Information used for proof verification", + "example" : "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", + "pattern" : "\\w+:(\\/?\\/?)[^\\s]+", + "type" : "string" + } + }, + "required" : [ "created", "proofPurpose", "type", "verificationMethod" ], + "type" : "object" + }, + "MediationCreateRequest" : { + "properties" : { + "mediator_terms" : { + "description" : "List of mediator rules for recipient", + "items" : { + "description" : "Indicate terms to which the mediator requires the recipient to agree", + "type" : "string" + }, + "type" : "array" + }, + "recipient_terms" : { + "description" : "List of recipient rules for mediation", + "items" : { + "description" : "Indicate terms to which the recipient requires the mediator to agree", + "type" : "string" + }, + "type" : "array" + } + }, + "type" : "object" + }, + "MediationDeny" : { + "properties" : { + "@id" : { + "description" : "Message identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "@type" : { + "description" : "Message type", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "readOnly" : true, + "type" : "string" + }, + "mediator_terms" : { + "items" : { + "description" : "Terms for mediator to agree", + "type" : "string" + }, + "type" : "array" + }, + "recipient_terms" : { + "items" : { + "description" : "Terms for recipient to agree", + "type" : "string" + }, + "type" : "array" + } + }, + "type" : "object" + }, + "MediationGrant" : { + "properties" : { + "@id" : { + "description" : "Message identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "@type" : { + "description" : "Message type", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "readOnly" : true, + "type" : "string" + }, + "endpoint" : { + "description" : "endpoint on which messages destined for the recipient are received.", + "example" : "http://192.168.56.102:8020/", + "type" : "string" + }, + "routing_keys" : { + "items" : { + "description" : "Keys to use for forward message packaging", + "type" : "string" + }, + "type" : "array" + } + }, + "type" : "object" + }, + "MediationIdMatchInfo" : { + "properties" : { + "mediation_id" : { + "description" : "Mediation record identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "format" : "uuid", + "type" : "string" + } + }, + "type" : "object" + }, + "MediationList" : { + "properties" : { + "results" : { + "description" : "List of mediation records", + "items" : { + "$ref" : "#/components/schemas/MediationRecord" + }, + "type" : "array" + } + }, + "type" : "object" + }, + "MediationRecord" : { + "properties" : { + "connection_id" : { + "type" : "string" + }, + "created_at" : { + "description" : "Time of record creation", + "example" : "2021-12-31T23:59:59Z", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$", + "type" : "string" + }, + "endpoint" : { + "type" : "string" + }, + "mediation_id" : { + "type" : "string" + }, + "mediator_terms" : { + "items" : { + "type" : "string" + }, + "type" : "array" + }, + "recipient_terms" : { + "items" : { + "type" : "string" + }, + "type" : "array" + }, + "role" : { + "type" : "string" + }, + "routing_keys" : { + "items" : { + "example" : "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH", + "pattern" : "^did:key:z[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+$", + "type" : "string" + }, + "type" : "array" + }, + "state" : { + "description" : "Current record state", + "example" : "active", + "type" : "string" + }, + "updated_at" : { + "description" : "Time of last record update", + "example" : "2021-12-31T23:59:59Z", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$", + "type" : "string" + } + }, + "required" : [ "connection_id", "role" ], + "type" : "object" + }, + "Menu" : { + "properties" : { + "@id" : { + "description" : "Message identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "@type" : { + "description" : "Message type", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "readOnly" : true, + "type" : "string" + }, + "description" : { + "description" : "Introductory text for the menu", + "example" : "This menu presents options", + "type" : "string" + }, + "errormsg" : { + "description" : "An optional error message to display in menu header", + "example" : "Error: item not found", + "type" : "string" + }, + "options" : { + "description" : "List of menu options", + "items" : { + "$ref" : "#/components/schemas/MenuOption" + }, + "type" : "array" + }, + "title" : { + "description" : "Menu title", + "example" : "My Menu", + "type" : "string" + } + }, + "required" : [ "options" ], + "type" : "object" + }, + "MenuForm" : { + "properties" : { + "description" : { + "description" : "Additional descriptive text for menu form", + "example" : "Window preference settings", + "type" : "string" + }, + "params" : { + "description" : "List of form parameters", + "items" : { + "$ref" : "#/components/schemas/MenuFormParam" + }, + "type" : "array" + }, + "submit-label" : { + "description" : "Alternative label for form submit button", + "example" : "Send", + "type" : "string" + }, + "title" : { + "description" : "Menu form title", + "example" : "Preferences", + "type" : "string" + } + }, + "type" : "object" + }, + "MenuFormParam" : { + "properties" : { + "default" : { + "description" : "Default parameter value", + "example" : "0", + "type" : "string" + }, + "description" : { + "description" : "Additional descriptive text for menu form parameter", + "example" : "Delay in seconds before starting", + "type" : "string" + }, + "name" : { + "description" : "Menu parameter name", + "example" : "delay", + "type" : "string" + }, + "required" : { + "description" : "Whether parameter is required", + "example" : false, + "type" : "boolean" + }, + "title" : { + "description" : "Menu parameter title", + "example" : "Delay in seconds", + "type" : "string" + }, + "type" : { + "description" : "Menu form parameter input type", + "example" : "int", + "type" : "string" + } + }, + "required" : [ "name", "title" ], + "type" : "object" + }, + "MenuJson" : { + "properties" : { + "description" : { + "description" : "Introductory text for the menu", + "example" : "User preferences for window settings", + "type" : "string" + }, + "errormsg" : { + "description" : "Optional error message to display in menu header", + "example" : "Error: item not present", + "type" : "string" + }, + "options" : { + "description" : "List of menu options", + "items" : { + "$ref" : "#/components/schemas/MenuOption" + }, + "type" : "array" + }, + "title" : { + "description" : "Menu title", + "example" : "My Menu", + "type" : "string" + } + }, + "required" : [ "options" ], + "type" : "object" + }, + "MenuOption" : { + "properties" : { + "description" : { + "description" : "Additional descriptive text for menu option", + "example" : "Window display preferences", + "type" : "string" + }, + "disabled" : { + "description" : "Whether to show option as disabled", + "example" : false, + "type" : "boolean" + }, + "form" : { + "$ref" : "#/components/schemas/MenuForm" + }, + "name" : { + "description" : "Menu option name (unique identifier)", + "example" : "window_prefs", + "type" : "string" + }, + "title" : { + "description" : "Menu option title", + "example" : "Window Preferences", + "type" : "string" + } + }, + "required" : [ "name", "title" ], + "type" : "object" + }, + "MultitenantModuleResponse" : { + "type" : "object" + }, + "OobRecord" : { + "properties" : { + "attach_thread_id" : { + "description" : "Connection record identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "connection_id" : { + "description" : "Connection record identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "created_at" : { + "description" : "Time of record creation", + "example" : "2021-12-31T23:59:59Z", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$", + "type" : "string" + }, + "invi_msg_id" : { + "description" : "Invitation message identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "invitation" : { + "$ref" : "#/components/schemas/InvitationRecord_invitation" + }, + "oob_id" : { + "description" : "Oob record identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "our_recipient_key" : { + "description" : "Recipient key used for oob invitation", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "role" : { + "description" : "OOB Role", + "enum" : [ "sender", "receiver" ], + "example" : "receiver", + "type" : "string" + }, + "state" : { + "description" : "Out of band message exchange state", + "enum" : [ "initial", "prepare-response", "await-response", "reuse-not-accepted", "reuse-accepted", "done", "deleted" ], + "example" : "await-response", + "type" : "string" + }, + "their_service" : { + "$ref" : "#/components/schemas/ServiceDecorator" + }, + "trace" : { + "description" : "Record trace information, based on agent configuration", + "type" : "boolean" + }, + "updated_at" : { + "description" : "Time of last record update", + "example" : "2021-12-31T23:59:59Z", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$", + "type" : "string" + } + }, + "required" : [ "invi_msg_id", "invitation", "oob_id", "state" ], + "type" : "object" + }, + "PerformRequest" : { + "properties" : { + "name" : { + "description" : "Menu option name", + "example" : "Query", + "type" : "string" + }, + "params" : { + "additionalProperties" : { + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "description" : "Input parameter values", + "type" : "object" + } + }, + "type" : "object" + }, + "PingRequest" : { + "properties" : { + "comment" : { + "description" : "Comment for the ping message", + "nullable" : true, + "type" : "string" + } + }, + "type" : "object" + }, + "PingRequestResponse" : { + "properties" : { + "thread_id" : { + "description" : "Thread ID of the ping message", + "type" : "string" + } + }, + "type" : "object" + }, + "PresentationDefinition" : { + "properties" : { + "format" : { + "$ref" : "#/components/schemas/ClaimFormat" + }, + "id" : { + "description" : "Unique Resource Identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", + "type" : "string" + }, + "input_descriptors" : { + "items" : { + "$ref" : "#/components/schemas/InputDescriptors" + }, + "type" : "array" + }, + "name" : { + "description" : "Human-friendly name that describes what the presentation definition pertains to", + "type" : "string" + }, + "purpose" : { + "description" : "Describes the purpose for which the Presentation Definition's inputs are being requested", + "type" : "string" + }, + "submission_requirements" : { + "items" : { + "$ref" : "#/components/schemas/SubmissionRequirements" + }, + "type" : "array" + } + }, + "type" : "object" + }, + "PresentationProposal" : { + "properties" : { + "@id" : { + "description" : "Message identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "@type" : { + "description" : "Message type", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "readOnly" : true, + "type" : "string" + }, + "comment" : { + "description" : "Human-readable comment", + "nullable" : true, + "type" : "string" + }, + "presentation_proposal" : { + "$ref" : "#/components/schemas/IndyPresPreview" + } + }, + "required" : [ "presentation_proposal" ], + "type" : "object" + }, + "PresentationRequest" : { + "properties" : { + "@id" : { + "description" : "Message identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "@type" : { + "description" : "Message type", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "readOnly" : true, + "type" : "string" + }, + "comment" : { + "description" : "Human-readable comment", + "nullable" : true, + "type" : "string" + }, + "request_presentations~attach" : { + "items" : { + "$ref" : "#/components/schemas/AttachDecorator" + }, + "type" : "array" + } + }, + "required" : [ "request_presentations~attach" ], + "type" : "object" + }, + "ProtocolDescriptor" : { + "properties" : { + "pid" : { + "type" : "string" + }, + "roles" : { + "description" : "List of roles", + "items" : { + "description" : "Role: requester or responder", + "example" : "requester", + "type" : "string" + }, + "nullable" : true, + "type" : "array" + } + }, + "required" : [ "pid" ], + "type" : "object" + }, + "PublishRevocations" : { + "properties" : { + "rrid2crid" : { + "additionalProperties" : { + "items" : { + "description" : "Credential revocation identifier", + "example" : "12345", + "pattern" : "^[1-9][0-9]*$", + "type" : "string" + }, + "type" : "array" + }, + "description" : "Credential revocation ids by revocation registry id", + "type" : "object" + } + }, + "type" : "object" + }, + "Queries" : { + "properties" : { + "@id" : { + "description" : "Message identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "@type" : { + "description" : "Message type", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "readOnly" : true, + "type" : "string" + }, + "queries" : { + "items" : { + "$ref" : "#/components/schemas/QueryItem" + }, + "type" : "array" + } + }, + "type" : "object" + }, + "Query" : { + "properties" : { + "@id" : { + "description" : "Message identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "@type" : { + "description" : "Message type", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "readOnly" : true, + "type" : "string" + }, + "comment" : { + "nullable" : true, + "type" : "string" + }, + "query" : { + "type" : "string" + } + }, + "required" : [ "query" ], + "type" : "object" + }, + "QueryItem" : { + "properties" : { + "feature-type" : { + "description" : "feature type", + "enum" : [ "protocol", "goal-code" ], + "type" : "string" + }, + "match" : { + "description" : "match", + "type" : "string" + } + }, + "required" : [ "feature-type", "match" ], + "type" : "object" + }, + "RawEncoded" : { + "properties" : { + "encoded" : { + "description" : "Encoded value", + "example" : "-1", + "pattern" : "^-?[0-9]*$", + "type" : "string" + }, + "raw" : { + "description" : "Raw value", + "type" : "string" + } + }, + "type" : "object" + }, + "ReceiveInvitationRequest" : { + "properties" : { + "@id" : { + "description" : "Message identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "@type" : { + "description" : "Message type", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "readOnly" : true, + "type" : "string" + }, + "did" : { + "description" : "DID for connection invitation", + "example" : "WgWxqztrNooG92RXvxSTWv", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$", + "type" : "string" + }, + "imageUrl" : { + "description" : "Optional image URL for connection invitation", + "example" : "http://192.168.56.101/img/logo.jpg", + "format" : "url", + "nullable" : true, + "type" : "string" + }, + "label" : { + "description" : "Optional label for connection invitation", + "example" : "Bob", + "type" : "string" + }, + "recipientKeys" : { + "description" : "List of recipient keys", + "items" : { + "description" : "Recipient public key", + "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$", + "type" : "string" + }, + "type" : "array" + }, + "routingKeys" : { + "description" : "List of routing keys", + "items" : { + "description" : "Routing key", + "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$", + "type" : "string" + }, + "type" : "array" + }, + "serviceEndpoint" : { + "description" : "Service endpoint at which to reach this agent", + "example" : "http://192.168.56.101:8020", + "type" : "string" + } + }, + "type" : "object" + }, + "RemoveWalletRequest" : { + "properties" : { + "wallet_key" : { + "description" : "Master key used for key derivation. Only required for unmanaged wallets.", + "example" : "MySecretKey123", + "type" : "string" + } + }, + "type" : "object" + }, + "ResolutionResult" : { + "properties" : { + "did_document" : { + "description" : "DID Document", + "properties" : { }, + "type" : "object" + }, + "metadata" : { + "description" : "Resolution metadata", + "properties" : { }, + "type" : "object" + } + }, + "required" : [ "did_document", "metadata" ], + "type" : "object" + }, + "RevRegCreateRequest" : { + "properties" : { + "credential_definition_id" : { + "description" : "Credential definition identifier", + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$", + "type" : "string" + }, + "max_cred_num" : { + "description" : "Revocation registry size", + "example" : 1000, + "format" : "int32", + "maximum" : 32768, + "minimum" : 4, + "type" : "integer" + } + }, + "type" : "object" + }, + "RevRegIssuedResult" : { + "properties" : { + "result" : { + "description" : "Number of credentials issued against revocation registry", + "example" : 0, + "format" : "int32", + "minimum" : 0, + "type" : "integer" + } + }, + "type" : "object" + }, + "RevRegResult" : { + "properties" : { + "result" : { + "$ref" : "#/components/schemas/IssuerRevRegRecord" + } + }, + "type" : "object" + }, + "RevRegUpdateTailsFileUri" : { + "properties" : { + "tails_public_uri" : { + "description" : "Public URI to the tails file", + "example" : "http://192.168.56.133:6543/revocation/registry/WgWxqztrNooG92RXvxSTWv:4:WgWxqztrNooG92RXvxSTWv:3:CL:20:tag:CL_ACCUM:0/tails-file", + "format" : "url", + "type" : "string" + } + }, + "required" : [ "tails_public_uri" ], + "type" : "object" + }, + "RevRegWalletUpdatedResult" : { + "properties" : { + "accum_calculated" : { + "description" : "Calculated accumulator for phantom revocations", + "properties" : { }, + "type" : "object" + }, + "accum_fixed" : { + "description" : "Applied ledger transaction to fix revocations", + "properties" : { }, + "type" : "object" + }, + "rev_reg_delta" : { + "description" : "Indy revocation registry delta", + "properties" : { }, + "type" : "object" + } + }, + "type" : "object" + }, + "RevRegsCreated" : { + "properties" : { + "rev_reg_ids" : { + "items" : { + "description" : "Revocation registry identifiers", + "example" : "WgWxqztrNooG92RXvxSTWv:4:WgWxqztrNooG92RXvxSTWv:3:CL:20:tag:CL_ACCUM:0", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)", + "type" : "string" + }, + "type" : "array" + } + }, + "type" : "object" + }, + "RevocationModuleResponse" : { + "type" : "object" + }, + "RevokeRequest" : { + "properties" : { + "comment" : { + "description" : "Optional comment to include in revocation notification", + "type" : "string" + }, + "connection_id" : { + "description" : "Connection ID to which the revocation notification will be sent; required if notify is true", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", + "type" : "string" + }, + "cred_ex_id" : { + "description" : "Credential exchange identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", + "type" : "string" + }, + "cred_rev_id" : { + "description" : "Credential revocation identifier", + "example" : "12345", + "pattern" : "^[1-9][0-9]*$", + "type" : "string" + }, + "notify" : { + "description" : "Send a notification to the credential recipient", + "type" : "boolean" + }, + "notify_version" : { + "description" : "Specify which version of the revocation notification should be sent", + "enum" : [ "v1_0", "v2_0" ], + "type" : "string" + }, + "publish" : { + "description" : "(True) publish revocation to ledger immediately, or (default, False) mark it pending", + "type" : "boolean" + }, + "rev_reg_id" : { + "description" : "Revocation registry identifier", + "example" : "WgWxqztrNooG92RXvxSTWv:4:WgWxqztrNooG92RXvxSTWv:3:CL:20:tag:CL_ACCUM:0", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)", + "type" : "string" + }, + "thread_id" : { + "description" : "Thread ID of the credential exchange message thread resulting in the credential now being revoked; required if notify is true", + "type" : "string" + } + }, + "type" : "object" + }, + "RouteRecord" : { + "properties" : { + "connection_id" : { + "type" : "string" + }, + "created_at" : { + "description" : "Time of record creation", + "example" : "2021-12-31T23:59:59Z", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$", + "type" : "string" + }, + "recipient_key" : { + "type" : "string" + }, + "record_id" : { + "type" : "string" + }, + "role" : { + "type" : "string" + }, + "state" : { + "description" : "Current record state", + "example" : "active", + "type" : "string" + }, + "updated_at" : { + "description" : "Time of last record update", + "example" : "2021-12-31T23:59:59Z", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$", + "type" : "string" + }, + "wallet_id" : { + "type" : "string" + } + }, + "required" : [ "recipient_key" ], + "type" : "object" + }, + "Schema" : { + "properties" : { + "attrNames" : { + "description" : "Schema attribute names", + "items" : { + "description" : "Attribute name", + "example" : "score", + "type" : "string" + }, + "type" : "array" + }, + "id" : { + "description" : "Schema identifier", + "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$", + "type" : "string" + }, + "name" : { + "description" : "Schema name", + "example" : "schema_name", + "type" : "string" + }, + "seqNo" : { + "description" : "Schema sequence number", + "example" : 10, + "format" : "int32", + "minimum" : 1, + "type" : "integer" + }, + "ver" : { + "description" : "Node protocol version", + "example" : "1.0", + "pattern" : "^[0-9.]+$", + "type" : "string" + }, + "version" : { + "description" : "Schema version", + "example" : "1.0", + "pattern" : "^[0-9.]+$", + "type" : "string" + } + }, + "type" : "object" + }, + "SchemaGetResult" : { + "properties" : { + "schema" : { + "$ref" : "#/components/schemas/Schema" + } + }, + "type" : "object" + }, + "SchemaInputDescriptor" : { + "properties" : { + "required" : { + "description" : "Required", + "type" : "boolean" + }, + "uri" : { + "description" : "URI", + "type" : "string" + } + }, + "type" : "object" + }, + "SchemaSendRequest" : { + "properties" : { + "attributes" : { + "description" : "List of schema attributes", + "items" : { + "description" : "attribute name", + "example" : "score", + "type" : "string" + }, + "type" : "array" + }, + "schema_name" : { + "description" : "Schema name", + "example" : "prefs", + "type" : "string" + }, + "schema_version" : { + "description" : "Schema version", + "example" : "1.0", + "pattern" : "^[0-9.]+$", + "type" : "string" + } + }, + "required" : [ "attributes", "schema_name", "schema_version" ], + "type" : "object" + }, + "SchemaSendResult" : { + "properties" : { + "schema" : { + "$ref" : "#/components/schemas/SchemaSendResult_schema" + }, + "schema_id" : { + "description" : "Schema identifier", + "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$", + "type" : "string" + } + }, + "required" : [ "schema_id" ], + "type" : "object" + }, + "SchemasCreatedResult" : { + "properties" : { + "schema_ids" : { + "items" : { + "description" : "Schema identifiers", + "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$", + "type" : "string" + }, + "type" : "array" + } + }, + "type" : "object" + }, + "SchemasInputDescriptorFilter" : { + "properties" : { + "oneof_filter" : { + "description" : "oneOf", + "type" : "boolean" + }, + "uri_groups" : { + "items" : { + "items" : { + "$ref" : "#/components/schemas/SchemaInputDescriptor" + }, + "type" : "array" + }, + "type" : "array" + } + }, + "type" : "object" + }, + "SendMenu" : { + "properties" : { + "menu" : { + "$ref" : "#/components/schemas/SendMenu_menu" + } + }, + "required" : [ "menu" ], + "type" : "object" + }, + "SendMessage" : { + "properties" : { + "content" : { + "description" : "Message content", + "example" : "Hello", + "type" : "string" + } + }, + "type" : "object" + }, + "ServiceDecorator" : { + "properties" : { + "recipientKeys" : { + "description" : "List of recipient keys", + "items" : { + "description" : "Recipient public key", + "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$", + "type" : "string" + }, + "type" : "array" + }, + "routingKeys" : { + "description" : "List of routing keys", + "items" : { + "description" : "Routing key", + "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$", + "type" : "string" + }, + "type" : "array" + }, + "serviceEndpoint" : { + "description" : "Service endpoint at which to reach this agent", + "example" : "http://192.168.56.101:8020", + "type" : "string" + } + }, + "required" : [ "recipientKeys", "serviceEndpoint" ], + "type" : "object" + }, + "SignRequest" : { + "properties" : { + "doc" : { + "$ref" : "#/components/schemas/Doc" + }, + "verkey" : { + "description" : "Verkey to use for signing", + "type" : "string" + } + }, + "required" : [ "doc", "verkey" ], + "type" : "object" + }, + "SignResponse" : { + "properties" : { + "error" : { + "description" : "Error text", + "type" : "string" + }, + "signed_doc" : { + "description" : "Signed document", + "properties" : { }, + "type" : "object" + } + }, + "type" : "object" + }, + "SignatureOptions" : { + "properties" : { + "challenge" : { + "type" : "string" + }, + "domain" : { + "type" : "string" + }, + "proofPurpose" : { + "type" : "string" + }, + "type" : { + "type" : "string" + }, + "verificationMethod" : { + "type" : "string" + } + }, + "required" : [ "proofPurpose", "verificationMethod" ], + "type" : "object" + }, + "SignedDoc" : { + "properties" : { + "proof" : { + "$ref" : "#/components/schemas/SignedDoc_proof" + } + }, + "required" : [ "proof" ], + "type" : "object" + }, + "SubmissionRequirements" : { + "properties" : { + "count" : { + "description" : "Count Value", + "example" : 1234, + "format" : "int32", + "type" : "integer" + }, + "from" : { + "description" : "From", + "type" : "string" + }, + "from_nested" : { + "items" : { + "$ref" : "#/components/schemas/SubmissionRequirements" + }, + "type" : "array" + }, + "max" : { + "description" : "Max Value", + "example" : 1234, + "format" : "int32", + "type" : "integer" + }, + "min" : { + "description" : "Min Value", + "example" : 1234, + "format" : "int32", + "type" : "integer" + }, + "name" : { + "description" : "Name", + "type" : "string" + }, + "purpose" : { + "description" : "Purpose", + "type" : "string" + }, + "rule" : { + "description" : "Selection", + "enum" : [ "all", "pick" ], + "type" : "string" + } + }, + "type" : "object" + }, + "TAAAccept" : { + "properties" : { + "mechanism" : { + "type" : "string" + }, + "text" : { + "type" : "string" + }, + "version" : { + "type" : "string" + } + }, + "type" : "object" + }, + "TAAAcceptance" : { + "properties" : { + "mechanism" : { + "type" : "string" + }, + "time" : { + "example" : 1640995199, + "format" : "int32", + "maximum" : 18446744073709551615, + "minimum" : 0, + "type" : "integer" + } + }, + "type" : "object" + }, + "TAAInfo" : { + "properties" : { + "aml_record" : { + "$ref" : "#/components/schemas/AMLRecord" + }, + "taa_accepted" : { + "$ref" : "#/components/schemas/TAAAcceptance" + }, + "taa_record" : { + "$ref" : "#/components/schemas/TAARecord" + }, + "taa_required" : { + "type" : "boolean" + } + }, + "type" : "object" + }, + "TAARecord" : { + "properties" : { + "digest" : { + "type" : "string" + }, + "text" : { + "type" : "string" + }, + "version" : { + "type" : "string" + } + }, + "type" : "object" + }, + "TAAResult" : { + "properties" : { + "result" : { + "$ref" : "#/components/schemas/TAAInfo" + } + }, + "type" : "object" + }, + "TailsDeleteResponse" : { + "properties" : { + "message" : { + "type" : "string" + } + }, + "type" : "object" + }, + "TransactionJobs" : { + "properties" : { + "transaction_my_job" : { + "description" : "My transaction related job", + "enum" : [ "TRANSACTION_AUTHOR", "TRANSACTION_ENDORSER", "reset" ], + "type" : "string" + }, + "transaction_their_job" : { + "description" : "Their transaction related job", + "enum" : [ "TRANSACTION_AUTHOR", "TRANSACTION_ENDORSER", "reset" ], + "type" : "string" + } + }, + "type" : "object" + }, + "TransactionList" : { + "properties" : { + "results" : { + "description" : "List of transaction records", + "items" : { + "$ref" : "#/components/schemas/TransactionRecord" + }, + "type" : "array" + } + }, + "type" : "object" + }, + "TransactionRecord" : { + "properties" : { + "_type" : { + "description" : "Transaction type", + "example" : "101", + "type" : "string" + }, + "connection_id" : { + "description" : "The connection identifier for thie particular transaction record", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "created_at" : { + "description" : "Time of record creation", + "example" : "2021-12-31T23:59:59Z", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$", + "type" : "string" + }, + "endorser_write_txn" : { + "description" : "If True, Endorser will write the transaction after endorsing it", + "example" : true, + "type" : "boolean" + }, + "formats" : { + "items" : { + "additionalProperties" : { + "type" : "string" + }, + "example" : { + "attach_id" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "format" : "dif/endorse-transaction/request@v1.0" + }, + "type" : "object" + }, + "type" : "array" + }, + "messages_attach" : { + "items" : { + "example" : { + "@id" : "143c458d-1b1c-40c7-ab85-4d16808ddf0a", + "data" : { + "json" : "{\"endorser\": \"V4SGRU86Z58d6TV7PBUe6f\",\"identifier\": \"LjgpST2rjsoxYegQDRm7EL\",\"operation\": {\"data\": {\"attr_names\": [\"first_name\", \"last_name\"],\"name\": \"test_schema\",\"version\": \"2.1\",},\"type\": \"101\",},\"protocolVersion\": 2,\"reqId\": 1597766666168851000,\"signatures\": {\"LjgpST2rjsox\": \"4ATKMn6Y9sTgwqaGTm7py2c2M8x1EVDTWKZArwyuPgjU\"},\"taaAcceptance\": {\"mechanism\": \"manual\",\"taaDigest\": \"f50fe2c2ab977006761d36bd6f23e4c6a7e0fc2feb9f62\",\"time\": 1597708800,}}" + }, + "mime-type" : "application/json" + }, + "properties" : { }, + "type" : "object" + }, + "type" : "array" + }, + "meta_data" : { + "example" : { + "context" : { + "param1" : "param1_value", + "param2" : "param2_value" + }, + "post_process" : [ { + "topic" : "topic_value", + "other" : "other_value" + } ] + }, + "properties" : { }, + "type" : "object" + }, + "signature_request" : { + "items" : { + "example" : { + "author_goal_code" : "aries.transaction.ledger.write", + "context" : "did:sov", + "method" : "add-signature", + "signature_type" : "", + "signer_goal_code" : "aries.transaction.endorse" + }, + "properties" : { }, + "type" : "object" + }, + "type" : "array" + }, + "signature_response" : { + "items" : { + "example" : { + "context" : "did:sov", + "message_id" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "method" : "add-signature", + "signer_goal_code" : "aries.transaction.refuse" + }, + "properties" : { }, + "type" : "object" + }, + "type" : "array" + }, + "state" : { + "description" : "Current record state", + "example" : "active", + "type" : "string" + }, + "thread_id" : { + "description" : "Thread Identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "timing" : { + "example" : { + "expires_time" : "2020-12-13T17:29:06+0000" + }, + "properties" : { }, + "type" : "object" + }, + "trace" : { + "description" : "Record trace information, based on agent configuration", + "type" : "boolean" + }, + "transaction_id" : { + "description" : "Transaction identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "updated_at" : { + "description" : "Time of last record update", + "example" : "2021-12-31T23:59:59Z", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$", + "type" : "string" + } + }, + "type" : "object" + }, + "TxnOrCredentialDefinitionSendResult" : { + "properties" : { + "sent" : { + "$ref" : "#/components/schemas/CredentialDefinitionSendResult" + }, + "txn" : { + "$ref" : "#/components/schemas/TxnOrCredentialDefinitionSendResult_txn" + } + }, + "type" : "object" + }, + "TxnOrPublishRevocationsResult" : { + "properties" : { + "sent" : { + "$ref" : "#/components/schemas/PublishRevocations" + }, + "txn" : { + "$ref" : "#/components/schemas/TxnOrPublishRevocationsResult_txn" + } + }, + "type" : "object" + }, + "TxnOrRegisterLedgerNymResponse" : { + "properties" : { + "success" : { + "description" : "Success of nym registration operation", + "example" : true, + "type" : "boolean" + }, + "txn" : { + "$ref" : "#/components/schemas/TxnOrRegisterLedgerNymResponse_txn" + } + }, + "type" : "object" + }, + "TxnOrRevRegResult" : { + "properties" : { + "sent" : { + "$ref" : "#/components/schemas/RevRegResult" + }, + "txn" : { + "$ref" : "#/components/schemas/TxnOrRevRegResult_txn" + } + }, + "type" : "object" + }, + "TxnOrSchemaSendResult" : { + "properties" : { + "sent" : { + "$ref" : "#/components/schemas/TxnOrSchemaSendResult_sent" + }, + "txn" : { + "$ref" : "#/components/schemas/TxnOrSchemaSendResult_txn" + } + }, + "type" : "object" + }, + "UpdateWalletRequest" : { + "properties" : { + "image_url" : { + "description" : "Image url for this wallet. This image url is publicized (self-attested) to other agents as part of forming a connection.", + "example" : "https://aries.ca/images/sample.png", + "type" : "string" + }, + "label" : { + "description" : "Label for this wallet. This label is publicized (self-attested) to other agents as part of forming a connection.", + "example" : "Alice", + "type" : "string" + }, + "wallet_dispatch_type" : { + "description" : "Webhook target dispatch type for this wallet. default - Dispatch only to webhooks associated with this wallet. base - Dispatch only to webhooks associated with the base wallet. both - Dispatch to both webhook targets.", + "enum" : [ "default", "both", "base" ], + "example" : "default", + "type" : "string" + }, + "wallet_webhook_urls" : { + "description" : "List of Webhook URLs associated with this subwallet", + "items" : { + "description" : "Optional webhook URL to receive webhook messages", + "example" : "http://localhost:8022/webhooks", + "type" : "string" + }, + "type" : "array" + } + }, + "type" : "object" + }, + "V10CredentialBoundOfferRequest" : { + "properties" : { + "counter_proposal" : { + "$ref" : "#/components/schemas/V10CredentialBoundOfferRequest_counter_proposal" + } + }, + "type" : "object" + }, + "V10CredentialConnFreeOfferRequest" : { + "properties" : { + "auto_issue" : { + "description" : "Whether to respond automatically to credential requests, creating and issuing requested credentials", + "type" : "boolean" + }, + "auto_remove" : { + "description" : "Whether to remove the credential exchange record on completion (overrides --preserve-exchange-records configuration setting)", + "type" : "boolean" + }, + "comment" : { + "description" : "Human-readable comment", + "nullable" : true, + "type" : "string" + }, + "cred_def_id" : { + "description" : "Credential definition identifier", + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$", + "type" : "string" + }, + "credential_preview" : { + "$ref" : "#/components/schemas/CredentialPreview" + }, + "trace" : { + "description" : "Record trace information, based on agent configuration", + "type" : "boolean" + } + }, + "required" : [ "cred_def_id", "credential_preview" ], + "type" : "object" + }, + "V10CredentialCreate" : { + "properties" : { + "auto_remove" : { + "description" : "Whether to remove the credential exchange record on completion (overrides --preserve-exchange-records configuration setting)", + "type" : "boolean" + }, + "comment" : { + "description" : "Human-readable comment", + "nullable" : true, + "type" : "string" + }, + "cred_def_id" : { + "description" : "Credential definition identifier", + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$", + "type" : "string" + }, + "credential_proposal" : { + "$ref" : "#/components/schemas/CredentialPreview" + }, + "issuer_did" : { + "description" : "Credential issuer DID", + "example" : "WgWxqztrNooG92RXvxSTWv", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$", + "type" : "string" + }, + "schema_id" : { + "description" : "Schema identifier", + "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$", + "type" : "string" + }, + "schema_issuer_did" : { + "description" : "Schema issuer DID", + "example" : "WgWxqztrNooG92RXvxSTWv", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$", + "type" : "string" + }, + "schema_name" : { + "description" : "Schema name", + "example" : "preferences", + "type" : "string" + }, + "schema_version" : { + "description" : "Schema version", + "example" : "1.0", + "pattern" : "^[0-9.]+$", + "type" : "string" + }, + "trace" : { + "description" : "Record trace information, based on agent configuration", + "type" : "boolean" + } + }, + "required" : [ "credential_proposal" ], + "type" : "object" + }, + "V10CredentialExchange" : { + "properties" : { + "auto_issue" : { + "description" : "Issuer choice to issue to request in this credential exchange", + "example" : false, + "type" : "boolean" + }, + "auto_offer" : { + "description" : "Holder choice to accept offer in this credential exchange", + "example" : false, + "type" : "boolean" + }, + "auto_remove" : { + "description" : "Issuer choice to remove this credential exchange record when complete", + "example" : false, + "type" : "boolean" + }, + "connection_id" : { + "description" : "Connection identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "created_at" : { + "description" : "Time of record creation", + "example" : "2021-12-31T23:59:59Z", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$", + "type" : "string" + }, + "credential" : { + "$ref" : "#/components/schemas/V10CredentialExchange_credential" + }, + "credential_definition_id" : { + "description" : "Credential definition identifier", + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$", + "type" : "string" + }, + "credential_exchange_id" : { + "description" : "Credential exchange identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "credential_id" : { + "description" : "Credential identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "credential_offer" : { + "$ref" : "#/components/schemas/V10CredentialExchange_credential_offer" + }, + "credential_offer_dict" : { + "$ref" : "#/components/schemas/V10CredentialExchange_credential_offer_dict" + }, + "credential_proposal_dict" : { + "$ref" : "#/components/schemas/V10CredentialExchange_credential_proposal_dict" + }, + "credential_request" : { + "$ref" : "#/components/schemas/V10CredentialExchange_credential_request" + }, + "credential_request_metadata" : { + "description" : "(Indy) credential request metadata", + "properties" : { }, + "type" : "object" + }, + "error_msg" : { + "description" : "Error message", + "example" : "Credential definition identifier is not set in proposal", + "type" : "string" + }, + "initiator" : { + "description" : "Issue-credential exchange initiator: self or external", + "enum" : [ "self", "external" ], + "example" : "self", + "type" : "string" + }, + "parent_thread_id" : { + "description" : "Parent thread identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "raw_credential" : { + "$ref" : "#/components/schemas/V10CredentialExchange_raw_credential" + }, + "revoc_reg_id" : { + "description" : "Revocation registry identifier", + "type" : "string" + }, + "revocation_id" : { + "description" : "Credential identifier within revocation registry", + "type" : "string" + }, + "role" : { + "description" : "Issue-credential exchange role: holder or issuer", + "enum" : [ "holder", "issuer" ], + "example" : "issuer", + "type" : "string" + }, + "schema_id" : { + "description" : "Schema identifier", + "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$", + "type" : "string" + }, + "state" : { + "description" : "Issue-credential exchange state", + "example" : "credential_acked", + "type" : "string" + }, + "thread_id" : { + "description" : "Thread identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "trace" : { + "description" : "Record trace information, based on agent configuration", + "type" : "boolean" + }, + "updated_at" : { + "description" : "Time of last record update", + "example" : "2021-12-31T23:59:59Z", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$", + "type" : "string" + } + }, + "type" : "object" + }, + "V10CredentialExchangeListResult" : { + "properties" : { + "results" : { + "description" : "Aries#0036 v1.0 credential exchange records", + "items" : { + "$ref" : "#/components/schemas/V10CredentialExchange" + }, + "type" : "array" + } + }, + "type" : "object" + }, + "V10CredentialFreeOfferRequest" : { + "properties" : { + "auto_issue" : { + "description" : "Whether to respond automatically to credential requests, creating and issuing requested credentials", + "type" : "boolean" + }, + "auto_remove" : { + "description" : "Whether to remove the credential exchange record on completion (overrides --preserve-exchange-records configuration setting)", + "type" : "boolean" + }, + "comment" : { + "description" : "Human-readable comment", + "nullable" : true, + "type" : "string" + }, + "connection_id" : { + "description" : "Connection identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "format" : "uuid", + "type" : "string" + }, + "cred_def_id" : { + "description" : "Credential definition identifier", + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$", + "type" : "string" + }, + "credential_preview" : { + "$ref" : "#/components/schemas/CredentialPreview" + }, + "trace" : { + "description" : "Record trace information, based on agent configuration", + "type" : "boolean" + } + }, + "required" : [ "connection_id", "cred_def_id", "credential_preview" ], + "type" : "object" + }, + "V10CredentialIssueRequest" : { + "properties" : { + "comment" : { + "description" : "Human-readable comment", + "nullable" : true, + "type" : "string" + } + }, + "type" : "object" + }, + "V10CredentialProblemReportRequest" : { + "properties" : { + "description" : { + "type" : "string" + } + }, + "required" : [ "description" ], + "type" : "object" + }, + "V10CredentialProposalRequestMand" : { + "properties" : { + "auto_remove" : { + "description" : "Whether to remove the credential exchange record on completion (overrides --preserve-exchange-records configuration setting)", + "type" : "boolean" + }, + "comment" : { + "description" : "Human-readable comment", + "nullable" : true, + "type" : "string" + }, + "connection_id" : { + "description" : "Connection identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "format" : "uuid", + "type" : "string" + }, + "cred_def_id" : { + "description" : "Credential definition identifier", + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$", + "type" : "string" + }, + "credential_proposal" : { + "$ref" : "#/components/schemas/CredentialPreview" + }, + "issuer_did" : { + "description" : "Credential issuer DID", + "example" : "WgWxqztrNooG92RXvxSTWv", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$", + "type" : "string" + }, + "schema_id" : { + "description" : "Schema identifier", + "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$", + "type" : "string" + }, + "schema_issuer_did" : { + "description" : "Schema issuer DID", + "example" : "WgWxqztrNooG92RXvxSTWv", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$", + "type" : "string" + }, + "schema_name" : { + "description" : "Schema name", + "example" : "preferences", + "type" : "string" + }, + "schema_version" : { + "description" : "Schema version", + "example" : "1.0", + "pattern" : "^[0-9.]+$", + "type" : "string" + }, + "trace" : { + "description" : "Record trace information, based on agent configuration", + "type" : "boolean" + } + }, + "required" : [ "connection_id", "credential_proposal" ], + "type" : "object" + }, + "V10CredentialProposalRequestOpt" : { + "properties" : { + "auto_remove" : { + "description" : "Whether to remove the credential exchange record on completion (overrides --preserve-exchange-records configuration setting)", + "type" : "boolean" + }, + "comment" : { + "description" : "Human-readable comment", + "nullable" : true, + "type" : "string" + }, + "connection_id" : { + "description" : "Connection identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "format" : "uuid", + "type" : "string" + }, + "cred_def_id" : { + "description" : "Credential definition identifier", + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$", + "type" : "string" + }, + "credential_proposal" : { + "$ref" : "#/components/schemas/CredentialPreview" + }, + "issuer_did" : { + "description" : "Credential issuer DID", + "example" : "WgWxqztrNooG92RXvxSTWv", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$", + "type" : "string" + }, + "schema_id" : { + "description" : "Schema identifier", + "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$", + "type" : "string" + }, + "schema_issuer_did" : { + "description" : "Schema issuer DID", + "example" : "WgWxqztrNooG92RXvxSTWv", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$", + "type" : "string" + }, + "schema_name" : { + "description" : "Schema name", + "example" : "preferences", + "type" : "string" + }, + "schema_version" : { + "description" : "Schema version", + "example" : "1.0", + "pattern" : "^[0-9.]+$", + "type" : "string" + }, + "trace" : { + "description" : "Record trace information, based on agent configuration", + "type" : "boolean" + } + }, + "required" : [ "connection_id" ], + "type" : "object" + }, + "V10CredentialStoreRequest" : { + "properties" : { + "credential_id" : { + "type" : "string" + } + }, + "type" : "object" + }, + "V10DiscoveryExchangeListResult" : { + "properties" : { + "results" : { + "items" : { + "$ref" : "#/components/schemas/V10DiscoveryExchangeListResult_results_inner" + }, + "type" : "array" + } + }, + "type" : "object" + }, + "V10DiscoveryRecord" : { + "properties" : { + "connection_id" : { + "description" : "Connection identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "created_at" : { + "description" : "Time of record creation", + "example" : "2021-12-31T23:59:59Z", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$", + "type" : "string" + }, + "disclose" : { + "$ref" : "#/components/schemas/V10DiscoveryRecord_disclose" + }, + "discovery_exchange_id" : { + "description" : "Credential exchange identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "query_msg" : { + "$ref" : "#/components/schemas/V10DiscoveryRecord_query_msg" + }, + "state" : { + "description" : "Current record state", + "example" : "active", + "type" : "string" + }, + "thread_id" : { + "description" : "Thread identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "trace" : { + "description" : "Record trace information, based on agent configuration", + "type" : "boolean" + }, + "updated_at" : { + "description" : "Time of last record update", + "example" : "2021-12-31T23:59:59Z", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$", + "type" : "string" + } + }, + "type" : "object" + }, + "V10PresentProofModuleResponse" : { + "type" : "object" + }, + "V10PresentationCreateRequestRequest" : { + "properties" : { + "auto_verify" : { + "description" : "Verifier choice to auto-verify proof presentation", + "example" : false, + "type" : "boolean" + }, + "comment" : { + "nullable" : true, + "type" : "string" + }, + "proof_request" : { + "$ref" : "#/components/schemas/IndyProofRequest" + }, + "trace" : { + "description" : "Whether to trace event (default false)", + "example" : false, + "type" : "boolean" + } + }, + "required" : [ "proof_request" ], + "type" : "object" + }, + "V10PresentationExchange" : { + "properties" : { + "auto_present" : { + "description" : "Prover choice to auto-present proof as verifier requests", + "example" : false, + "type" : "boolean" + }, + "auto_verify" : { + "description" : "Verifier choice to auto-verify proof presentation", + "type" : "boolean" + }, + "connection_id" : { + "description" : "Connection identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "created_at" : { + "description" : "Time of record creation", + "example" : "2021-12-31T23:59:59Z", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$", + "type" : "string" + }, + "error_msg" : { + "description" : "Error message", + "example" : "Invalid structure", + "type" : "string" + }, + "initiator" : { + "description" : "Present-proof exchange initiator: self or external", + "enum" : [ "self", "external" ], + "example" : "self", + "type" : "string" + }, + "presentation" : { + "$ref" : "#/components/schemas/V10PresentationExchange_presentation" + }, + "presentation_exchange_id" : { + "description" : "Presentation exchange identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "presentation_proposal_dict" : { + "$ref" : "#/components/schemas/V10PresentationExchange_presentation_proposal_dict" + }, + "presentation_request" : { + "$ref" : "#/components/schemas/V10PresentationExchange_presentation_request" + }, + "presentation_request_dict" : { + "$ref" : "#/components/schemas/V10PresentationExchange_presentation_request_dict" + }, + "role" : { + "description" : "Present-proof exchange role: prover or verifier", + "enum" : [ "prover", "verifier" ], + "example" : "prover", + "type" : "string" + }, + "state" : { + "description" : "Present-proof exchange state", + "example" : "verified", + "type" : "string" + }, + "thread_id" : { + "description" : "Thread identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "trace" : { + "description" : "Record trace information, based on agent configuration", + "type" : "boolean" + }, + "updated_at" : { + "description" : "Time of last record update", + "example" : "2021-12-31T23:59:59Z", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$", + "type" : "string" + }, + "verified" : { + "description" : "Whether presentation is verified: true or false", + "enum" : [ "true", "false" ], + "example" : "true", + "type" : "string" + }, + "verified_msgs" : { + "items" : { + "description" : "Proof verification warning or error information", + "type" : "string" + }, + "type" : "array" + } + }, + "type" : "object" + }, + "V10PresentationExchangeList" : { + "properties" : { + "results" : { + "description" : "Aries RFC 37 v1.0 presentation exchange records", + "items" : { + "$ref" : "#/components/schemas/V10PresentationExchange" + }, + "type" : "array" + } + }, + "type" : "object" + }, + "V10PresentationProblemReportRequest" : { + "properties" : { + "description" : { + "type" : "string" + } + }, + "required" : [ "description" ], + "type" : "object" + }, + "V10PresentationProposalRequest" : { + "properties" : { + "auto_present" : { + "description" : "Whether to respond automatically to presentation requests, building and presenting requested proof", + "type" : "boolean" + }, + "comment" : { + "description" : "Human-readable comment", + "nullable" : true, + "type" : "string" + }, + "connection_id" : { + "description" : "Connection identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "format" : "uuid", + "type" : "string" + }, + "presentation_proposal" : { + "$ref" : "#/components/schemas/IndyPresPreview" + }, + "trace" : { + "description" : "Whether to trace event (default false)", + "example" : false, + "type" : "boolean" + } + }, + "required" : [ "connection_id", "presentation_proposal" ], + "type" : "object" + }, + "V10PresentationSendRequestRequest" : { + "properties" : { + "auto_verify" : { + "description" : "Verifier choice to auto-verify proof presentation", + "example" : false, + "type" : "boolean" + }, + "comment" : { + "nullable" : true, + "type" : "string" + }, + "connection_id" : { + "description" : "Connection identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "format" : "uuid", + "type" : "string" + }, + "proof_request" : { + "$ref" : "#/components/schemas/IndyProofRequest" + }, + "trace" : { + "description" : "Whether to trace event (default false)", + "example" : false, + "type" : "boolean" + } + }, + "required" : [ "connection_id", "proof_request" ], + "type" : "object" + }, + "V10PresentationSendRequestToProposal" : { + "properties" : { + "auto_verify" : { + "description" : "Verifier choice to auto-verify proof presentation", + "example" : false, + "type" : "boolean" + }, + "trace" : { + "description" : "Whether to trace event (default false)", + "example" : false, + "type" : "boolean" + } + }, + "type" : "object" + }, + "V20CredAttrSpec" : { + "properties" : { + "mime-type" : { + "description" : "MIME type: omit for (null) default", + "example" : "image/jpeg", + "nullable" : true, + "type" : "string" + }, + "name" : { + "description" : "Attribute name", + "example" : "favourite_drink", + "type" : "string" + }, + "value" : { + "description" : "Attribute value: base64-encode if MIME type is present", + "example" : "martini", + "type" : "string" + } + }, + "required" : [ "name", "value" ], + "type" : "object" + }, + "V20CredBoundOfferRequest" : { + "properties" : { + "counter_preview" : { + "$ref" : "#/components/schemas/V20CredBoundOfferRequest_counter_preview" + }, + "filter" : { + "$ref" : "#/components/schemas/V20CredBoundOfferRequest_filter" + } + }, + "type" : "object" + }, + "V20CredExFree" : { + "properties" : { + "auto_remove" : { + "description" : "Whether to remove the credential exchange record on completion (overrides --preserve-exchange-records configuration setting)", + "type" : "boolean" + }, + "comment" : { + "description" : "Human-readable comment", + "nullable" : true, + "type" : "string" + }, + "connection_id" : { + "description" : "Connection identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "format" : "uuid", + "type" : "string" + }, + "credential_preview" : { + "$ref" : "#/components/schemas/V20CredPreview" + }, + "filter" : { + "$ref" : "#/components/schemas/V20CredBoundOfferRequest_filter" + }, + "trace" : { + "description" : "Record trace information, based on agent configuration", + "type" : "boolean" + }, + "verification_method" : { + "description" : "For ld-proofs. Verification method for signing.", + "nullable" : true, + "type" : "string" + } + }, + "required" : [ "connection_id", "filter" ], + "type" : "object" + }, + "V20CredExRecord" : { + "properties" : { + "auto_issue" : { + "description" : "Issuer choice to issue to request in this credential exchange", + "example" : false, + "type" : "boolean" + }, + "auto_offer" : { + "description" : "Holder choice to accept offer in this credential exchange", + "example" : false, + "type" : "boolean" + }, + "auto_remove" : { + "description" : "Issuer choice to remove this credential exchange record when complete", + "example" : false, + "type" : "boolean" + }, + "by_format" : { + "$ref" : "#/components/schemas/V20CredExRecord_by_format" + }, + "connection_id" : { + "description" : "Connection identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "created_at" : { + "description" : "Time of record creation", + "example" : "2021-12-31T23:59:59Z", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$", + "type" : "string" + }, + "cred_ex_id" : { + "description" : "Credential exchange identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "cred_issue" : { + "$ref" : "#/components/schemas/V20CredExRecord_cred_issue" + }, + "cred_offer" : { + "$ref" : "#/components/schemas/V20CredExRecord_cred_offer" + }, + "cred_preview" : { + "$ref" : "#/components/schemas/V20CredExRecord_cred_preview" + }, + "cred_proposal" : { + "$ref" : "#/components/schemas/V20CredExRecord_cred_proposal" + }, + "cred_request" : { + "$ref" : "#/components/schemas/V20CredExRecord_cred_request" + }, + "error_msg" : { + "description" : "Error message", + "example" : "The front fell off", + "type" : "string" + }, + "initiator" : { + "description" : "Issue-credential exchange initiator: self or external", + "enum" : [ "self", "external" ], + "example" : "self", + "type" : "string" + }, + "parent_thread_id" : { + "description" : "Parent thread identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "role" : { + "description" : "Issue-credential exchange role: holder or issuer", + "enum" : [ "issuer", "holder" ], + "example" : "issuer", + "type" : "string" + }, + "state" : { + "description" : "Issue-credential exchange state", + "enum" : [ "proposal-sent", "proposal-received", "offer-sent", "offer-received", "request-sent", "request-received", "credential-issued", "credential-received", "done", "credential-revoked", "abandoned", "deleted" ], + "example" : "done", + "type" : "string" + }, + "thread_id" : { + "description" : "Thread identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "trace" : { + "description" : "Record trace information, based on agent configuration", + "type" : "boolean" + }, + "updated_at" : { + "description" : "Time of last record update", + "example" : "2021-12-31T23:59:59Z", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$", + "type" : "string" + } + }, + "type" : "object" + }, + "V20CredExRecordByFormat" : { + "properties" : { + "cred_issue" : { + "properties" : { }, + "type" : "object" + }, + "cred_offer" : { + "properties" : { }, + "type" : "object" + }, + "cred_proposal" : { + "properties" : { }, + "type" : "object" + }, + "cred_request" : { + "properties" : { }, + "type" : "object" + } + }, + "type" : "object" + }, + "V20CredExRecordDetail" : { + "properties" : { + "cred_ex_record" : { + "$ref" : "#/components/schemas/V20CredExRecordDetail_cred_ex_record" + }, + "indy" : { + "$ref" : "#/components/schemas/V20CredExRecordIndy" + }, + "ld_proof" : { + "$ref" : "#/components/schemas/V20CredExRecordLDProof" + } + }, + "type" : "object" + }, + "V20CredExRecordIndy" : { + "properties" : { + "created_at" : { + "description" : "Time of record creation", + "example" : "2021-12-31T23:59:59Z", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$", + "type" : "string" + }, + "cred_ex_id" : { + "description" : "Corresponding v2.0 credential exchange record identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "cred_ex_indy_id" : { + "description" : "Record identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "cred_id_stored" : { + "description" : "Credential identifier stored in wallet", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "cred_request_metadata" : { + "description" : "Credential request metadata for indy holder", + "properties" : { }, + "type" : "object" + }, + "cred_rev_id" : { + "description" : "Credential revocation identifier within revocation registry", + "example" : "12345", + "pattern" : "^[1-9][0-9]*$", + "type" : "string" + }, + "rev_reg_id" : { + "description" : "Revocation registry identifier", + "example" : "WgWxqztrNooG92RXvxSTWv:4:WgWxqztrNooG92RXvxSTWv:3:CL:20:tag:CL_ACCUM:0", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)", + "type" : "string" + }, + "state" : { + "description" : "Current record state", + "example" : "active", + "type" : "string" + }, + "updated_at" : { + "description" : "Time of last record update", + "example" : "2021-12-31T23:59:59Z", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$", + "type" : "string" + } + }, + "type" : "object" + }, + "V20CredExRecordLDProof" : { + "properties" : { + "created_at" : { + "description" : "Time of record creation", + "example" : "2021-12-31T23:59:59Z", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$", + "type" : "string" + }, + "cred_ex_id" : { + "description" : "Corresponding v2.0 credential exchange record identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "cred_ex_ld_proof_id" : { + "description" : "Record identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "cred_id_stored" : { + "description" : "Credential identifier stored in wallet", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "state" : { + "description" : "Current record state", + "example" : "active", + "type" : "string" + }, + "updated_at" : { + "description" : "Time of last record update", + "example" : "2021-12-31T23:59:59Z", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$", + "type" : "string" + } + }, + "type" : "object" + }, + "V20CredExRecordListResult" : { + "properties" : { + "results" : { + "description" : "Credential exchange records and corresponding detail records", + "items" : { + "$ref" : "#/components/schemas/V20CredExRecordDetail" + }, + "type" : "array" + } + }, + "type" : "object" + }, + "V20CredFilter" : { + "properties" : { + "indy" : { + "$ref" : "#/components/schemas/V20CredFilter_indy" + }, + "ld_proof" : { + "$ref" : "#/components/schemas/V20CredFilter_ld_proof" + } + }, + "type" : "object" + }, + "V20CredFilterIndy" : { + "properties" : { + "cred_def_id" : { + "description" : "Credential definition identifier", + "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", + "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$", + "type" : "string" + }, + "issuer_did" : { + "description" : "Credential issuer DID", + "example" : "WgWxqztrNooG92RXvxSTWv", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$", + "type" : "string" + }, + "schema_id" : { + "description" : "Schema identifier", + "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$", + "type" : "string" + }, + "schema_issuer_did" : { + "description" : "Schema issuer DID", + "example" : "WgWxqztrNooG92RXvxSTWv", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$", + "type" : "string" + }, + "schema_name" : { + "description" : "Schema name", + "example" : "preferences", + "type" : "string" + }, + "schema_version" : { + "description" : "Schema version", + "example" : "1.0", + "pattern" : "^[0-9.]+$", + "type" : "string" + } + }, + "type" : "object" + }, + "V20CredFilterLDProof" : { + "properties" : { + "ld_proof" : { + "$ref" : "#/components/schemas/V20CredFilter_ld_proof" + } + }, + "required" : [ "ld_proof" ], + "type" : "object" + }, + "V20CredFormat" : { + "properties" : { + "attach_id" : { + "description" : "Attachment identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "format" : { + "description" : "Attachment format specifier", + "example" : "aries/ld-proof-vc-detail@v1.0", + "type" : "string" + } + }, + "required" : [ "attach_id", "format" ], + "type" : "object" + }, + "V20CredIssue" : { + "properties" : { + "@id" : { + "description" : "Message identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "@type" : { + "description" : "Message type", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "readOnly" : true, + "type" : "string" + }, + "comment" : { + "description" : "Human-readable comment", + "nullable" : true, + "type" : "string" + }, + "credentials~attach" : { + "description" : "Credential attachments", + "items" : { + "$ref" : "#/components/schemas/AttachDecorator" + }, + "type" : "array" + }, + "formats" : { + "description" : "Acceptable attachment formats", + "items" : { + "$ref" : "#/components/schemas/V20CredFormat" + }, + "type" : "array" + }, + "replacement_id" : { + "description" : "Issuer-unique identifier to coordinate credential replacement", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + } + }, + "required" : [ "credentials~attach", "formats" ], + "type" : "object" + }, + "V20CredIssueProblemReportRequest" : { + "properties" : { + "description" : { + "type" : "string" + } + }, + "required" : [ "description" ], + "type" : "object" + }, + "V20CredIssueRequest" : { + "properties" : { + "comment" : { + "description" : "Human-readable comment", + "nullable" : true, + "type" : "string" + } + }, + "type" : "object" + }, + "V20CredOffer" : { + "properties" : { + "@id" : { + "description" : "Message identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "@type" : { + "description" : "Message type", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "readOnly" : true, + "type" : "string" + }, + "comment" : { + "description" : "Human-readable comment", + "nullable" : true, + "type" : "string" + }, + "credential_preview" : { + "$ref" : "#/components/schemas/V20CredPreview" + }, + "formats" : { + "description" : "Acceptable credential formats", + "items" : { + "$ref" : "#/components/schemas/V20CredFormat" + }, + "type" : "array" + }, + "offers~attach" : { + "description" : "Offer attachments", + "items" : { + "$ref" : "#/components/schemas/AttachDecorator" + }, + "type" : "array" + }, + "replacement_id" : { + "description" : "Issuer-unique identifier to coordinate credential replacement", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + } + }, + "required" : [ "formats", "offers~attach" ], + "type" : "object" + }, + "V20CredOfferConnFreeRequest" : { + "properties" : { + "auto_issue" : { + "description" : "Whether to respond automatically to credential requests, creating and issuing requested credentials", + "type" : "boolean" + }, + "auto_remove" : { + "description" : "Whether to remove the credential exchange record on completion (overrides --preserve-exchange-records configuration setting)", + "type" : "boolean" + }, + "comment" : { + "description" : "Human-readable comment", + "nullable" : true, + "type" : "string" + }, + "credential_preview" : { + "$ref" : "#/components/schemas/V20CredPreview" + }, + "filter" : { + "$ref" : "#/components/schemas/V20CredBoundOfferRequest_filter" + }, + "trace" : { + "description" : "Record trace information, based on agent configuration", + "type" : "boolean" + } + }, + "required" : [ "filter" ], + "type" : "object" + }, + "V20CredOfferRequest" : { + "properties" : { + "auto_issue" : { + "description" : "Whether to respond automatically to credential requests, creating and issuing requested credentials", + "type" : "boolean" + }, + "auto_remove" : { + "description" : "Whether to remove the credential exchange record on completion (overrides --preserve-exchange-records configuration setting)", + "type" : "boolean" + }, + "comment" : { + "description" : "Human-readable comment", + "nullable" : true, + "type" : "string" + }, + "connection_id" : { + "description" : "Connection identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "format" : "uuid", + "type" : "string" + }, + "credential_preview" : { + "$ref" : "#/components/schemas/V20CredPreview" + }, + "filter" : { + "$ref" : "#/components/schemas/V20CredBoundOfferRequest_filter" + }, + "trace" : { + "description" : "Record trace information, based on agent configuration", + "type" : "boolean" + } + }, + "required" : [ "connection_id", "filter" ], + "type" : "object" + }, + "V20CredPreview" : { + "properties" : { + "@type" : { + "description" : "Message type identifier", + "example" : "issue-credential/2.0/credential-preview", + "type" : "string" + }, + "attributes" : { + "items" : { + "$ref" : "#/components/schemas/V20CredAttrSpec" + }, + "type" : "array" + } + }, + "required" : [ "attributes" ], + "type" : "object" + }, + "V20CredProposal" : { + "properties" : { + "@id" : { + "description" : "Message identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "@type" : { + "description" : "Message type", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "readOnly" : true, + "type" : "string" + }, + "comment" : { + "description" : "Human-readable comment", + "nullable" : true, + "type" : "string" + }, + "credential_preview" : { + "$ref" : "#/components/schemas/V20CredProposal_credential_preview" + }, + "filters~attach" : { + "description" : "Credential filter per acceptable format on corresponding identifier", + "items" : { + "$ref" : "#/components/schemas/AttachDecorator" + }, + "type" : "array" + }, + "formats" : { + "description" : "Attachment formats", + "items" : { + "$ref" : "#/components/schemas/V20CredFormat" + }, + "type" : "array" + } + }, + "required" : [ "filters~attach", "formats" ], + "type" : "object" + }, + "V20CredRequest" : { + "properties" : { + "@id" : { + "description" : "Message identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "@type" : { + "description" : "Message type", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "readOnly" : true, + "type" : "string" + }, + "comment" : { + "description" : "Human-readable comment", + "nullable" : true, + "type" : "string" + }, + "formats" : { + "description" : "Acceptable attachment formats", + "items" : { + "$ref" : "#/components/schemas/V20CredFormat" + }, + "type" : "array" + }, + "requests~attach" : { + "description" : "Request attachments", + "items" : { + "$ref" : "#/components/schemas/AttachDecorator" + }, + "type" : "array" + } + }, + "required" : [ "formats", "requests~attach" ], + "type" : "object" + }, + "V20CredRequestFree" : { + "properties" : { + "auto_remove" : { + "description" : "Whether to remove the credential exchange record on completion (overrides --preserve-exchange-records configuration setting)", + "type" : "boolean" + }, + "comment" : { + "description" : "Human-readable comment", + "nullable" : true, + "type" : "string" + }, + "connection_id" : { + "description" : "Connection identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "format" : "uuid", + "type" : "string" + }, + "filter" : { + "$ref" : "#/components/schemas/V20CredRequestFree_filter" + }, + "holder_did" : { + "description" : "Holder DID to substitute for the credentialSubject.id", + "example" : "did:key:ahsdkjahsdkjhaskjdhakjshdkajhsdkjahs", + "nullable" : true, + "type" : "string" + }, + "trace" : { + "description" : "Whether to trace event (default false)", + "example" : false, + "type" : "boolean" + } + }, + "required" : [ "connection_id", "filter" ], + "type" : "object" + }, + "V20CredRequestRequest" : { + "properties" : { + "holder_did" : { + "description" : "Holder DID to substitute for the credentialSubject.id", + "example" : "did:key:ahsdkjahsdkjhaskjdhakjshdkajhsdkjahs", + "nullable" : true, + "type" : "string" + } + }, + "type" : "object" + }, + "V20CredStoreRequest" : { + "properties" : { + "credential_id" : { + "type" : "string" + } + }, + "type" : "object" + }, + "V20DiscoveryExchangeListResult" : { + "properties" : { + "results" : { + "items" : { + "$ref" : "#/components/schemas/V20DiscoveryExchangeListResult_results_inner" + }, + "type" : "array" + } + }, + "type" : "object" + }, + "V20DiscoveryExchangeResult" : { + "properties" : { + "results" : { + "$ref" : "#/components/schemas/V20DiscoveryExchangeListResult_results_inner" + } + }, + "type" : "object" + }, + "V20DiscoveryRecord" : { + "properties" : { + "connection_id" : { + "description" : "Connection identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "created_at" : { + "description" : "Time of record creation", + "example" : "2021-12-31T23:59:59Z", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$", + "type" : "string" + }, + "disclosures" : { + "$ref" : "#/components/schemas/V20DiscoveryRecord_disclosures" + }, + "discovery_exchange_id" : { + "description" : "Credential exchange identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "queries_msg" : { + "$ref" : "#/components/schemas/V20DiscoveryRecord_queries_msg" + }, + "state" : { + "description" : "Current record state", + "example" : "active", + "type" : "string" + }, + "thread_id" : { + "description" : "Thread identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "trace" : { + "description" : "Record trace information, based on agent configuration", + "type" : "boolean" + }, + "updated_at" : { + "description" : "Time of last record update", + "example" : "2021-12-31T23:59:59Z", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$", + "type" : "string" + } + }, + "type" : "object" + }, + "V20IssueCredSchemaCore" : { + "properties" : { + "auto_remove" : { + "description" : "Whether to remove the credential exchange record on completion (overrides --preserve-exchange-records configuration setting)", + "type" : "boolean" + }, + "comment" : { + "description" : "Human-readable comment", + "nullable" : true, + "type" : "string" + }, + "credential_preview" : { + "$ref" : "#/components/schemas/V20CredPreview" + }, + "filter" : { + "$ref" : "#/components/schemas/V20CredBoundOfferRequest_filter" + }, + "trace" : { + "description" : "Record trace information, based on agent configuration", + "type" : "boolean" + } + }, + "required" : [ "filter" ], + "type" : "object" + }, + "V20IssueCredentialModuleResponse" : { + "type" : "object" + }, + "V20Pres" : { + "properties" : { + "@id" : { + "description" : "Message identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "@type" : { + "description" : "Message type", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "readOnly" : true, + "type" : "string" + }, + "comment" : { + "description" : "Human-readable comment", + "nullable" : true, + "type" : "string" + }, + "formats" : { + "description" : "Acceptable attachment formats", + "items" : { + "$ref" : "#/components/schemas/V20PresFormat" + }, + "type" : "array" + }, + "presentations~attach" : { + "items" : { + "$ref" : "#/components/schemas/AttachDecorator" + }, + "type" : "array" + } + }, + "required" : [ "formats", "presentations~attach" ], + "type" : "object" + }, + "V20PresCreateRequestRequest" : { + "properties" : { + "auto_verify" : { + "description" : "Verifier choice to auto-verify proof presentation", + "example" : false, + "type" : "boolean" + }, + "comment" : { + "nullable" : true, + "type" : "string" + }, + "presentation_request" : { + "$ref" : "#/components/schemas/V20PresRequestByFormat" + }, + "trace" : { + "description" : "Whether to trace event (default false)", + "example" : false, + "type" : "boolean" + } + }, + "required" : [ "presentation_request" ], + "type" : "object" + }, + "V20PresExRecord" : { + "properties" : { + "auto_present" : { + "description" : "Prover choice to auto-present proof as verifier requests", + "example" : false, + "type" : "boolean" + }, + "auto_verify" : { + "description" : "Verifier choice to auto-verify proof presentation", + "type" : "boolean" + }, + "by_format" : { + "$ref" : "#/components/schemas/V20PresExRecord_by_format" + }, + "connection_id" : { + "description" : "Connection identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "created_at" : { + "description" : "Time of record creation", + "example" : "2021-12-31T23:59:59Z", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$", + "type" : "string" + }, + "error_msg" : { + "description" : "Error message", + "example" : "Invalid structure", + "type" : "string" + }, + "initiator" : { + "description" : "Present-proof exchange initiator: self or external", + "enum" : [ "self", "external" ], + "example" : "self", + "type" : "string" + }, + "pres" : { + "$ref" : "#/components/schemas/V20PresExRecord_pres" + }, + "pres_ex_id" : { + "description" : "Presentation exchange identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "pres_proposal" : { + "$ref" : "#/components/schemas/V20PresExRecord_pres_proposal" + }, + "pres_request" : { + "$ref" : "#/components/schemas/V20PresExRecord_pres_request" + }, + "role" : { + "description" : "Present-proof exchange role: prover or verifier", + "enum" : [ "prover", "verifier" ], + "example" : "prover", + "type" : "string" + }, + "state" : { + "description" : "Present-proof exchange state", + "enum" : [ "proposal-sent", "proposal-received", "request-sent", "request-received", "presentation-sent", "presentation-received", "done", "abandoned", "deleted" ], + "type" : "string" + }, + "thread_id" : { + "description" : "Thread identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "trace" : { + "description" : "Record trace information, based on agent configuration", + "type" : "boolean" + }, + "updated_at" : { + "description" : "Time of last record update", + "example" : "2021-12-31T23:59:59Z", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$", + "type" : "string" + }, + "verified" : { + "description" : "Whether presentation is verified: 'true' or 'false'", + "enum" : [ "true", "false" ], + "example" : "true", + "type" : "string" + }, + "verified_msgs" : { + "items" : { + "description" : "Proof verification warning or error information", + "type" : "string" + }, + "type" : "array" + } + }, + "type" : "object" + }, + "V20PresExRecordByFormat" : { + "properties" : { + "pres" : { + "properties" : { }, + "type" : "object" + }, + "pres_proposal" : { + "properties" : { }, + "type" : "object" + }, + "pres_request" : { + "properties" : { }, + "type" : "object" + } + }, + "type" : "object" + }, + "V20PresExRecordList" : { + "properties" : { + "results" : { + "description" : "Presentation exchange records", + "items" : { + "$ref" : "#/components/schemas/V20PresExRecord" + }, + "type" : "array" + } + }, + "type" : "object" + }, + "V20PresFormat" : { + "properties" : { + "attach_id" : { + "description" : "Attachment identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "format" : { + "description" : "Attachment format specifier", + "example" : "dif/presentation-exchange/submission@v1.0", + "type" : "string" + } + }, + "required" : [ "attach_id", "format" ], + "type" : "object" + }, + "V20PresProblemReportRequest" : { + "properties" : { + "description" : { + "type" : "string" + } + }, + "required" : [ "description" ], + "type" : "object" + }, + "V20PresProposal" : { + "properties" : { + "@id" : { + "description" : "Message identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "@type" : { + "description" : "Message type", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "readOnly" : true, + "type" : "string" + }, + "comment" : { + "description" : "Human-readable comment", + "type" : "string" + }, + "formats" : { + "items" : { + "$ref" : "#/components/schemas/V20PresFormat" + }, + "type" : "array" + }, + "proposals~attach" : { + "description" : "Attachment per acceptable format on corresponding identifier", + "items" : { + "$ref" : "#/components/schemas/AttachDecorator" + }, + "type" : "array" + } + }, + "required" : [ "formats", "proposals~attach" ], + "type" : "object" + }, + "V20PresProposalByFormat" : { + "properties" : { + "dif" : { + "$ref" : "#/components/schemas/V20PresProposalByFormat_dif" + }, + "indy" : { + "$ref" : "#/components/schemas/V20PresProposalByFormat_indy" + } + }, + "type" : "object" + }, + "V20PresProposalRequest" : { + "properties" : { + "auto_present" : { + "description" : "Whether to respond automatically to presentation requests, building and presenting requested proof", + "type" : "boolean" + }, + "comment" : { + "description" : "Human-readable comment", + "nullable" : true, + "type" : "string" + }, + "connection_id" : { + "description" : "Connection identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "format" : "uuid", + "type" : "string" + }, + "presentation_proposal" : { + "$ref" : "#/components/schemas/V20PresProposalByFormat" + }, + "trace" : { + "description" : "Whether to trace event (default false)", + "example" : false, + "type" : "boolean" + } + }, + "required" : [ "connection_id", "presentation_proposal" ], + "type" : "object" + }, + "V20PresRequest" : { + "properties" : { + "@id" : { + "description" : "Message identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "@type" : { + "description" : "Message type", + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "readOnly" : true, + "type" : "string" + }, + "comment" : { + "description" : "Human-readable comment", + "type" : "string" + }, + "formats" : { + "items" : { + "$ref" : "#/components/schemas/V20PresFormat" + }, + "type" : "array" + }, + "request_presentations~attach" : { + "description" : "Attachment per acceptable format on corresponding identifier", + "items" : { + "$ref" : "#/components/schemas/AttachDecorator" + }, + "type" : "array" + }, + "will_confirm" : { + "description" : "Whether verifier will send confirmation ack", + "type" : "boolean" + } + }, + "required" : [ "formats", "request_presentations~attach" ], + "type" : "object" + }, + "V20PresRequestByFormat" : { + "properties" : { + "dif" : { + "$ref" : "#/components/schemas/V20PresRequestByFormat_dif" + }, + "indy" : { + "$ref" : "#/components/schemas/V20PresRequestByFormat_indy" + } + }, + "type" : "object" + }, + "V20PresSendRequestRequest" : { + "properties" : { + "auto_verify" : { + "description" : "Verifier choice to auto-verify proof presentation", + "example" : false, + "type" : "boolean" + }, + "comment" : { + "nullable" : true, + "type" : "string" + }, + "connection_id" : { + "description" : "Connection identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "format" : "uuid", + "type" : "string" + }, + "presentation_request" : { + "$ref" : "#/components/schemas/V20PresRequestByFormat" + }, + "trace" : { + "description" : "Whether to trace event (default false)", + "example" : false, + "type" : "boolean" + } + }, + "required" : [ "connection_id", "presentation_request" ], + "type" : "object" + }, + "V20PresSpecByFormatRequest" : { + "properties" : { + "dif" : { + "$ref" : "#/components/schemas/V20PresSpecByFormatRequest_dif" + }, + "indy" : { + "$ref" : "#/components/schemas/V20PresSpecByFormatRequest_indy" + }, + "trace" : { + "description" : "Record trace information, based on agent configuration", + "type" : "boolean" + } + }, + "type" : "object" + }, + "V20PresentProofModuleResponse" : { + "type" : "object" + }, + "V20PresentationSendRequestToProposal" : { + "properties" : { + "auto_verify" : { + "description" : "Verifier choice to auto-verify proof presentation", + "example" : false, + "type" : "boolean" + }, + "trace" : { + "description" : "Whether to trace event (default false)", + "example" : false, + "type" : "boolean" + } + }, + "type" : "object" + }, + "VCRecord" : { + "properties" : { + "contexts" : { + "items" : { + "description" : "Context", + "example" : "https://myhost:8021", + "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$", + "type" : "string" + }, + "type" : "array" + }, + "cred_tags" : { + "additionalProperties" : { + "description" : "Retrieval tag value", + "type" : "string" + }, + "type" : "object" + }, + "cred_value" : { + "description" : "(JSON-serializable) credential value", + "properties" : { }, + "type" : "object" + }, + "expanded_types" : { + "items" : { + "description" : "JSON-LD expanded type extracted from type and context", + "example" : "https://w3id.org/citizenship#PermanentResidentCard", + "type" : "string" + }, + "type" : "array" + }, + "given_id" : { + "description" : "Credential identifier", + "example" : "http://example.edu/credentials/3732", + "type" : "string" + }, + "issuer_id" : { + "description" : "Issuer identifier", + "example" : "https://example.edu/issuers/14", + "type" : "string" + }, + "proof_types" : { + "items" : { + "description" : "Signature suite used for proof", + "example" : "Ed25519Signature2018", + "type" : "string" + }, + "type" : "array" + }, + "record_id" : { + "description" : "Record identifier", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + }, + "schema_ids" : { + "items" : { + "description" : "Schema identifier", + "example" : "https://example.org/examples/degree.json", + "type" : "string" + }, + "type" : "array" + }, + "subject_ids" : { + "items" : { + "description" : "Subject identifier", + "example" : "did:example:ebfeb1f712ebc6f1c276e12ec21", + "type" : "string" + }, + "type" : "array" + } + }, + "type" : "object" + }, + "VCRecordList" : { + "properties" : { + "results" : { + "items" : { + "$ref" : "#/components/schemas/VCRecord" + }, + "type" : "array" + } + }, + "type" : "object" + }, + "VerifyRequest" : { + "properties" : { + "doc" : { + "$ref" : "#/components/schemas/VerifyRequest_doc" + }, + "verkey" : { + "description" : "Verkey to use for doc verification", + "type" : "string" + } + }, + "required" : [ "doc" ], + "type" : "object" + }, + "VerifyResponse" : { + "properties" : { + "error" : { + "description" : "Error text", + "type" : "string" + }, + "valid" : { + "type" : "boolean" + } + }, + "required" : [ "valid" ], + "type" : "object" + }, + "W3CCredentialsListRequest" : { + "properties" : { + "contexts" : { + "items" : { + "description" : "Credential context to match", + "example" : "https://myhost:8021", + "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$", + "type" : "string" + }, + "type" : "array" + }, + "given_id" : { + "description" : "Given credential id to match", + "type" : "string" + }, + "issuer_id" : { + "description" : "Credential issuer identifier to match", + "type" : "string" + }, + "max_results" : { + "description" : "Maximum number of results to return", + "format" : "int32", + "type" : "integer" + }, + "proof_types" : { + "items" : { + "description" : "Signature suite used for proof", + "example" : "Ed25519Signature2018", + "type" : "string" + }, + "type" : "array" + }, + "schema_ids" : { + "description" : "Schema identifiers, all of which to match", + "items" : { + "description" : "Credential schema identifier", + "example" : "https://myhost:8021", + "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$", + "type" : "string" + }, + "type" : "array" + }, + "subject_ids" : { + "description" : "Subject identifiers, all of which to match", + "items" : { + "description" : "Subject identifier", + "type" : "string" + }, + "type" : "array" + }, + "tag_query" : { + "additionalProperties" : { + "description" : "Tag value", + "type" : "string" + }, + "description" : "Tag filter", + "type" : "object" + }, + "types" : { + "items" : { + "description" : "Credential type to match", + "example" : "https://myhost:8021", + "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$", + "type" : "string" + }, + "type" : "array" + } + }, + "type" : "object" + }, + "WalletList" : { + "properties" : { + "results" : { + "description" : "List of wallet records", + "items" : { + "$ref" : "#/components/schemas/WalletRecord" + }, + "type" : "array" + } + }, + "type" : "object" + }, + "WalletModuleResponse" : { + "type" : "object" + }, + "WalletRecord" : { + "properties" : { + "created_at" : { + "description" : "Time of record creation", + "example" : "2021-12-31T23:59:59Z", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$", + "type" : "string" + }, + "key_management_mode" : { + "description" : "Mode regarding management of wallet key", + "enum" : [ "managed", "unmanaged" ], + "type" : "string" + }, + "settings" : { + "description" : "Settings for this wallet.", + "properties" : { }, + "type" : "object" + }, + "state" : { + "description" : "Current record state", + "example" : "active", + "type" : "string" + }, + "updated_at" : { + "description" : "Time of last record update", + "example" : "2021-12-31T23:59:59Z", + "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$", + "type" : "string" + }, + "wallet_id" : { + "description" : "Wallet record ID", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type" : "string" + } + }, + "required" : [ "key_management_mode", "wallet_id" ], + "type" : "object" + }, + "WriteLedgerRequest" : { + "properties" : { + "ledger_id" : { + "type" : "string" + } + }, + "type" : "object" + }, + "ActionMenuFetchResult_result" : { + "allOf" : [ { + "$ref" : "#/components/schemas/Menu" + } ], + "description" : "Action menu", + "type" : "object" + }, + "AttachDecoratorData_jws" : { + "allOf" : [ { + "$ref" : "#/components/schemas/AttachDecoratorDataJWS" + } ], + "description" : "Detached Java Web Signature", + "type" : "object" + }, + "CredDefValue_primary" : { + "allOf" : [ { + "$ref" : "#/components/schemas/CredDefValuePrimary" + } ], + "description" : "Primary value for credential definition", + "type" : "object" + }, + "CredDefValue_revocation" : { + "allOf" : [ { + "$ref" : "#/components/schemas/CredDefValueRevocation" + } ], + "description" : "Revocation value for credential definition", + "type" : "object" + }, + "Credential_proof" : { + "allOf" : [ { + "$ref" : "#/components/schemas/LinkedDataProof" + } ], + "description" : "The proof of the credential", + "example" : { + "created" : "2019-12-11T03:50:55", + "jws" : "eyJhbGciOiAiRWREU0EiLCAiYjY0IjogZmFsc2UsICJjcml0JiNjQiXX0..lKJU0Df_keblRKhZAS9Qq6zybm-HqUXNVZ8vgEPNTAjQKBhQDxvXNo7nvtUBb_Eq1Ch6YBKY5qBQ", + "proofPurpose" : "assertionMethod", + "type" : "Ed25519Signature2018", + "verificationMethod" : "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL" + }, + "type" : "object" + }, + "CredentialDefinition_value" : { + "allOf" : [ { + "$ref" : "#/components/schemas/CredDefValue" + } ], + "description" : "Credential definition primary and revocation values", + "type" : "object" + }, + "DIDCreate_options" : { + "allOf" : [ { + "$ref" : "#/components/schemas/DIDCreateOptions" + } ], + "description" : "To define a key type and/or a did depending on chosen DID method.", + "type" : "object" + }, + "DIDXRequest_did_doc_attach" : { + "allOf" : [ { + "$ref" : "#/components/schemas/AttachDecorator" + } ], + "description" : "As signed attachment, DID Doc associated with DID", + "type" : "object" + }, + "Doc_options" : { + "allOf" : [ { + "$ref" : "#/components/schemas/SignatureOptions" + } ], + "description" : "Signature options", + "type" : "object" + }, + "IndyCredAbstract_key_correctness_proof" : { + "allOf" : [ { + "$ref" : "#/components/schemas/IndyKeyCorrectnessProof" + } ], + "description" : "Key correctness proof", + "type" : "object" + }, + "IndyCredPrecis_cred_info" : { + "allOf" : [ { + "$ref" : "#/components/schemas/IndyCredInfo" + } ], + "description" : "Credential info", + "type" : "object" + }, + "IndyCredPrecis_interval" : { + "allOf" : [ { + "$ref" : "#/components/schemas/IndyNonRevocationInterval" + } ], + "description" : "Non-revocation interval from presentation request", + "type" : "object" + }, + "IndyCredential_values_value" : { + "allOf" : [ { + "$ref" : "#/definitions/IndyAttrValue" + } ], + "description" : "Attribute value", + "type" : "object" + }, + "IndyPrimaryProof_eq_proof" : { + "allOf" : [ { + "$ref" : "#/components/schemas/IndyEQProof" + } ], + "description" : "Indy equality proof", + "nullable" : true, + "type" : "object" + }, + "IndyProof_proof" : { + "allOf" : [ { + "$ref" : "#/components/schemas/IndyProofProof" + } ], + "description" : "Indy proof.proof content", + "type" : "object" + }, + "IndyProof_requested_proof" : { + "allOf" : [ { + "$ref" : "#/components/schemas/IndyProofRequestedProof" + } ], + "description" : "Indy proof.requested_proof content", + "type" : "object" + }, + "IndyProofProof_aggregated_proof" : { + "allOf" : [ { + "$ref" : "#/components/schemas/IndyProofProofAggregatedProof" + } ], + "description" : "Indy proof aggregated proof", + "type" : "object" + }, + "IndyProofProofProofsProof_non_revoc_proof" : { + "allOf" : [ { + "$ref" : "#/components/schemas/IndyNonRevocProof" + } ], + "description" : "Indy non-revocation proof", + "nullable" : true, + "type" : "object" + }, + "IndyProofProofProofsProof_primary_proof" : { + "allOf" : [ { + "$ref" : "#/components/schemas/IndyPrimaryProof" + } ], + "description" : "Indy primary proof", + "type" : "object" + }, + "IndyProofReqAttrSpec_non_revoked" : { + "allOf" : [ { + "$ref" : "#/components/schemas/IndyProofReqAttrSpecNonRevoked" + } ], + "nullable" : true, + "type" : "object" + }, + "IndyProofReqPredSpec_non_revoked" : { + "allOf" : [ { + "$ref" : "#/components/schemas/IndyProofReqPredSpecNonRevoked" + } ], + "nullable" : true, + "type" : "object" + }, + "IndyProofRequest_non_revoked" : { + "allOf" : [ { + "$ref" : "#/components/schemas/IndyProofRequestNonRevoked" + } ], + "nullable" : true, + "type" : "object" + }, + "IndyRevRegDef_value" : { + "allOf" : [ { + "$ref" : "#/components/schemas/IndyRevRegDefValue" + } ], + "description" : "Revocation registry definition value", + "type" : "object" + }, + "IndyRevRegDefValue_publicKeys" : { + "allOf" : [ { + "$ref" : "#/components/schemas/IndyRevRegDefValuePublicKeys" + } ], + "description" : "Public keys", + "type" : "object" + }, + "IndyRevRegEntry_value" : { + "allOf" : [ { + "$ref" : "#/components/schemas/IndyRevRegEntryValue" + } ], + "description" : "Revocation registry entry value", + "type" : "object" + }, + "InputDescriptors_schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/SchemasInputDescriptorFilter" + } ], + "description" : "Accepts a list of schema or a dict containing filters like oneof_filter.", + "example" : { + "oneof_filter" : [ [ { + "uri" : "https://www.w3.org/Test1#Test1" + }, { + "uri" : "https://www.w3.org/Test2#Test2" + } ], { + "oneof_filter" : [ [ { + "uri" : "https://www.w3.org/Test1#Test1" + } ], [ { + "uri" : "https://www.w3.org/Test2#Test2" + } ] ] + } ] + }, + "type" : "object" + }, + "InvitationRecord_invitation" : { + "allOf" : [ { + "$ref" : "#/components/schemas/InvitationMessage" + } ], + "description" : "Out of band invitation message", + "type" : "object" + }, + "IssuerRevRegRecord_revoc_reg_def" : { + "allOf" : [ { + "$ref" : "#/components/schemas/IndyRevRegDef" + } ], + "description" : "Revocation registry definition", + "type" : "object" + }, + "IssuerRevRegRecord_revoc_reg_entry" : { + "allOf" : [ { + "$ref" : "#/components/schemas/IndyRevRegEntry" + } ], + "description" : "Revocation registry entry", + "type" : "object" + }, + "KeylistQuery_paginate" : { + "allOf" : [ { + "$ref" : "#/components/schemas/KeylistQueryPaginate" + } ], + "description" : "Pagination info", + "type" : "object" + }, + "LDProofVCDetail_credential" : { + "allOf" : [ { + "$ref" : "#/components/schemas/Credential" + } ], + "description" : "Detail of the JSON-LD Credential to be issued", + "example" : { + "@context" : [ "https://www.w3.org/2018/credentials/v1", "https://w3id.org/citizenship/v1" ], + "credentialSubject" : { + "familyName" : "SMITH", + "gender" : "Male", + "givenName" : "JOHN", + "type" : [ "PermanentResident", "Person" ] + }, + "description" : "Government of Example Permanent Resident Card.", + "identifier" : "83627465", + "issuanceDate" : "2019-12-03T12:19:52Z", + "issuer" : "did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th", + "name" : "Permanent Resident Card", + "type" : [ "VerifiableCredential", "PermanentResidentCard" ] + }, + "type" : "object" + }, + "LDProofVCDetail_options" : { + "allOf" : [ { + "$ref" : "#/components/schemas/LDProofVCDetailOptions" + } ], + "description" : "Options for specifying how the linked data proof is created.", + "example" : { + "proofType" : "Ed25519Signature2018" + }, + "type" : "object" + }, + "LDProofVCDetailOptions_credentialStatus" : { + "allOf" : [ { + "$ref" : "#/components/schemas/CredentialStatusOptions" + } ], + "description" : "The credential status mechanism to use for the credential. Omitting the property indicates the issued credential will not include a credential status", + "type" : "object" + }, + "SchemaSendResult_schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/Schema" + } ], + "description" : "Schema definition", + "type" : "object" + }, + "SendMenu_menu" : { + "allOf" : [ { + "$ref" : "#/components/schemas/MenuJson" + } ], + "description" : "Menu to send to connection", + "type" : "object" + }, + "SignedDoc_proof" : { + "allOf" : [ { + "$ref" : "#/components/schemas/SignatureOptions" + } ], + "description" : "Linked data proof", + "type" : "object" + }, + "TxnOrCredentialDefinitionSendResult_txn" : { + "allOf" : [ { + "$ref" : "#/components/schemas/TransactionRecord" + } ], + "description" : "Credential definition transaction to endorse", + "type" : "object" + }, + "TxnOrPublishRevocationsResult_txn" : { + "allOf" : [ { + "$ref" : "#/components/schemas/TransactionRecord" + } ], + "description" : "Revocation registry revocations transaction to endorse", + "type" : "object" + }, + "TxnOrRegisterLedgerNymResponse_txn" : { + "allOf" : [ { + "$ref" : "#/components/schemas/TransactionRecord" + } ], + "description" : "DID transaction to endorse", + "type" : "object" + }, + "TxnOrRevRegResult_txn" : { + "allOf" : [ { + "$ref" : "#/components/schemas/TransactionRecord" + } ], + "description" : "Revocation registry definition transaction to endorse", + "type" : "object" + }, + "TxnOrSchemaSendResult_sent" : { + "allOf" : [ { + "$ref" : "#/components/schemas/SchemaSendResult" + } ], + "description" : "Content sent", + "type" : "object" + }, + "TxnOrSchemaSendResult_txn" : { + "allOf" : [ { + "$ref" : "#/components/schemas/TransactionRecord" + } ], + "description" : "Schema transaction to endorse", + "type" : "object" + }, + "V10CredentialBoundOfferRequest_counter_proposal" : { + "allOf" : [ { + "$ref" : "#/components/schemas/CredentialProposal" + } ], + "description" : "Optional counter-proposal", + "type" : "object" + }, + "V10CredentialExchange_credential" : { + "allOf" : [ { + "$ref" : "#/components/schemas/IndyCredInfo" + } ], + "description" : "Credential as stored", + "type" : "object" + }, + "V10CredentialExchange_credential_offer" : { + "allOf" : [ { + "$ref" : "#/components/schemas/IndyCredAbstract" + } ], + "description" : "(Indy) credential offer", + "type" : "object" + }, + "V10CredentialExchange_credential_offer_dict" : { + "allOf" : [ { + "$ref" : "#/components/schemas/CredentialOffer" + } ], + "description" : "Credential offer message", + "type" : "object" + }, + "V10CredentialExchange_credential_proposal_dict" : { + "allOf" : [ { + "$ref" : "#/components/schemas/CredentialProposal" + } ], + "description" : "Credential proposal message", + "type" : "object" + }, + "V10CredentialExchange_credential_request" : { + "allOf" : [ { + "$ref" : "#/components/schemas/IndyCredRequest" + } ], + "description" : "(Indy) credential request", + "type" : "object" + }, + "V10CredentialExchange_raw_credential" : { + "allOf" : [ { + "$ref" : "#/components/schemas/IndyCredential" + } ], + "description" : "Credential as received, prior to storage in holder wallet", + "type" : "object" + }, + "V10DiscoveryExchangeListResult_results_inner" : { + "allOf" : [ { + "$ref" : "#/components/schemas/V10DiscoveryRecord" + } ], + "description" : "Discover Features v1.0 exchange record", + "type" : "object" + }, + "V10DiscoveryRecord_disclose" : { + "allOf" : [ { + "$ref" : "#/components/schemas/Disclose" + } ], + "description" : "Disclose message", + "type" : "object" + }, + "V10DiscoveryRecord_query_msg" : { + "allOf" : [ { + "$ref" : "#/components/schemas/Query" + } ], + "description" : "Query message", + "type" : "object" + }, + "V10PresentationExchange_presentation" : { + "allOf" : [ { + "$ref" : "#/components/schemas/IndyProof" + } ], + "description" : "(Indy) presentation (also known as proof)", + "type" : "object" + }, + "V10PresentationExchange_presentation_proposal_dict" : { + "allOf" : [ { + "$ref" : "#/components/schemas/PresentationProposal" + } ], + "description" : "Presentation proposal message", + "type" : "object" + }, + "V10PresentationExchange_presentation_request" : { + "allOf" : [ { + "$ref" : "#/components/schemas/IndyProofRequest" + } ], + "description" : "(Indy) presentation request (also known as proof request)", + "type" : "object" + }, + "V10PresentationExchange_presentation_request_dict" : { + "allOf" : [ { + "$ref" : "#/components/schemas/PresentationRequest" + } ], + "description" : "Presentation request message", + "type" : "object" + }, + "V20CredBoundOfferRequest_counter_preview" : { + "allOf" : [ { + "$ref" : "#/components/schemas/V20CredPreview" + } ], + "description" : "Optional content for counter-proposal", + "type" : "object" + }, + "V20CredBoundOfferRequest_filter" : { + "allOf" : [ { + "$ref" : "#/components/schemas/V20CredFilter" + } ], + "description" : "Credential specification criteria by format", + "type" : "object" + }, + "V20CredExRecord_by_format" : { + "allOf" : [ { + "$ref" : "#/components/schemas/V20CredExRecordByFormat" + } ], + "description" : "Attachment content by format for proposal, offer, request, and issue", + "type" : "object" + }, + "V20CredExRecord_cred_issue" : { + "allOf" : [ { + "$ref" : "#/components/schemas/V20CredIssue" + } ], + "description" : "Serialized credential issue message", + "type" : "object" + }, + "V20CredExRecord_cred_offer" : { + "allOf" : [ { + "$ref" : "#/components/schemas/V20CredOffer" + } ], + "description" : "Credential offer message", + "type" : "object" + }, + "V20CredExRecord_cred_preview" : { + "allOf" : [ { + "$ref" : "#/components/schemas/V20CredPreview" + } ], + "description" : "Credential preview from credential proposal", + "type" : "object" + }, + "V20CredExRecord_cred_proposal" : { + "allOf" : [ { + "$ref" : "#/components/schemas/V20CredProposal" + } ], + "description" : "Credential proposal message", + "type" : "object" + }, + "V20CredExRecord_cred_request" : { + "allOf" : [ { + "$ref" : "#/components/schemas/V20CredRequest" + } ], + "description" : "Serialized credential request message", + "type" : "object" + }, + "V20CredExRecordDetail_cred_ex_record" : { + "allOf" : [ { + "$ref" : "#/components/schemas/V20CredExRecord" + } ], + "description" : "Credential exchange record", + "type" : "object" + }, + "V20CredFilter_indy" : { + "allOf" : [ { + "$ref" : "#/components/schemas/V20CredFilterIndy" + } ], + "description" : "Credential filter for indy", + "type" : "object" + }, + "V20CredFilter_ld_proof" : { + "allOf" : [ { + "$ref" : "#/components/schemas/LDProofVCDetail" + } ], + "description" : "Credential filter for linked data proof", + "type" : "object" + }, + "V20CredProposal_credential_preview" : { + "allOf" : [ { + "$ref" : "#/components/schemas/V20CredPreview" + } ], + "description" : "Credential preview", + "type" : "object" + }, + "V20CredRequestFree_filter" : { + "allOf" : [ { + "$ref" : "#/components/schemas/V20CredFilterLDProof" + } ], + "description" : "Credential specification criteria by format", + "type" : "object" + }, + "V20DiscoveryExchangeListResult_results_inner" : { + "allOf" : [ { + "$ref" : "#/components/schemas/V20DiscoveryRecord" + } ], + "description" : "Discover Features v2.0 exchange record", + "type" : "object" + }, + "V20DiscoveryRecord_disclosures" : { + "allOf" : [ { + "$ref" : "#/components/schemas/Disclosures" + } ], + "description" : "Disclosures message", + "type" : "object" + }, + "V20DiscoveryRecord_queries_msg" : { + "allOf" : [ { + "$ref" : "#/components/schemas/Queries" + } ], + "description" : "Queries message", + "type" : "object" + }, + "V20PresExRecord_by_format" : { + "allOf" : [ { + "$ref" : "#/components/schemas/V20PresExRecordByFormat" + } ], + "description" : "Attachment content by format for proposal, request, and presentation", + "type" : "object" + }, + "V20PresExRecord_pres" : { + "allOf" : [ { + "$ref" : "#/components/schemas/V20Pres" + } ], + "description" : "Presentation message", + "type" : "object" + }, + "V20PresExRecord_pres_proposal" : { + "allOf" : [ { + "$ref" : "#/components/schemas/V20PresProposal" + } ], + "description" : "Presentation proposal message", + "type" : "object" + }, + "V20PresExRecord_pres_request" : { + "allOf" : [ { + "$ref" : "#/components/schemas/V20PresRequest" + } ], + "description" : "Presentation request message", + "type" : "object" + }, + "V20PresProposalByFormat_dif" : { + "allOf" : [ { + "$ref" : "#/components/schemas/DIFProofProposal" + } ], + "description" : "Presentation proposal for DIF", + "type" : "object" + }, + "V20PresProposalByFormat_indy" : { + "allOf" : [ { + "$ref" : "#/components/schemas/IndyProofRequest" + } ], + "description" : "Presentation proposal for indy", + "type" : "object" + }, + "V20PresRequestByFormat_dif" : { + "allOf" : [ { + "$ref" : "#/components/schemas/DIFProofRequest" + } ], + "description" : "Presentation request for DIF", + "type" : "object" + }, + "V20PresRequestByFormat_indy" : { + "allOf" : [ { + "$ref" : "#/components/schemas/IndyProofRequest" + } ], + "description" : "Presentation request for indy", + "type" : "object" + }, + "V20PresSpecByFormatRequest_dif" : { + "allOf" : [ { + "$ref" : "#/components/schemas/DIFPresSpec" + } ], + "description" : "Optional Presentation specification for DIF, overrides the PresentionExchange record's PresRequest", + "type" : "object" + }, + "V20PresSpecByFormatRequest_indy" : { + "allOf" : [ { + "$ref" : "#/components/schemas/IndyPresSpec" + } ], + "description" : "Presentation specification for indy", + "type" : "object" + }, + "VerifyRequest_doc" : { + "allOf" : [ { + "$ref" : "#/components/schemas/SignedDoc" + } ], + "description" : "Signed document", + "type" : "object" + } }, - "VerifyRequest_doc" : { - "type" : "object", - "description" : "Signed document" + "securitySchemes" : { + "AuthorizationHeader" : { + "description" : "Bearer token. Be sure to preprend token with 'Bearer '", + "in" : "header", + "name" : "Authorization", + "type" : "apiKey" + } } - } + }, + "x-original-swagger-version" : "2.0" } \ No newline at end of file From 898cdc9dbc28cbc185b83115b3b067e82bf2a696 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Wed, 24 May 2023 09:52:23 -0400 Subject: [PATCH 808/872] fix: only cache completed connection targets Signed-off-by: Daniel Bluhm --- aries_cloudagent/protocols/connections/v1_0/manager.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/protocols/connections/v1_0/manager.py b/aries_cloudagent/protocols/connections/v1_0/manager.py index 6e87aeb96f..8487819a85 100644 --- a/aries_cloudagent/protocols/connections/v1_0/manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/manager.py @@ -1088,7 +1088,13 @@ async def get_connection_targets( targets = await self.fetch_connection_targets(connection) - await entry.set_result([row.serialize() for row in targets], 3600) + if connection.state == ConnRecord.State.COMPLETED.rfc160: + # Only set cache if connection has reached completed state + # Otherwise, a replica that participated early in exchange + # may have bad data set in cache. + await entry.set_result( + [row.serialize() for row in targets], 3600 + ) else: targets = await self.fetch_connection_targets(connection) return targets From 490da979760ef1741bede00926a8fc0e579f16dd Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Tue, 21 Mar 2023 15:05:03 -0700 Subject: [PATCH 809/872] pass document loader to jsonld.expand Signed-off-by: Andrew Whitehead --- .../issue_credential/v2_0/formats/ld_proof/handler.py | 2 +- .../protocols/present_proof/dif/pres_exch_handler.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py index 0ea8aabf93..95cf55e017 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py @@ -596,7 +596,7 @@ async def store_credential( raise V20CredFormatError(f"Received invalid credential: {result}") # Saving expanded type as a cred_tag - expanded = jsonld.expand(cred_dict) + expanded = jsonld.expand(cred_dict, options={"documentLoader": document_loader}) types = JsonLdProcessor.get_values( expanded[0], "@type", diff --git a/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py b/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py index 191b9676bb..0d2ac468de 100644 --- a/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py +++ b/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py @@ -490,7 +490,8 @@ def create_vcrecord(self, cred_dict: dict) -> VCRecord: if type(schemas) is dict: schemas = [schemas] schema_ids = [schema.get("id") for schema in schemas] - expanded = jsonld.expand(cred_dict) + document_loader = self.profile.inject(DocumentLoader) + expanded = jsonld.expand(cred_dict, options={"documentLoader": document_loader}) types = JsonLdProcessor.get_values( expanded[0], "@type", From a0ab49588ae7da3ef45c85e9b1b76f3429251f1d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 May 2023 07:14:39 +0000 Subject: [PATCH 810/872] Bump requests from 2.30.0 to 2.31.0 in /demo/playground/scripts Bumps [requests](https://github.com/psf/requests) from 2.30.0 to 2.31.0. - [Release notes](https://github.com/psf/requests/releases) - [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md) - [Commits](https://github.com/psf/requests/compare/v2.30.0...v2.31.0) --- updated-dependencies: - dependency-name: requests dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- demo/playground/scripts/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/playground/scripts/requirements.txt b/demo/playground/scripts/requirements.txt index 7bf7952fe8..8550dc88e5 100644 --- a/demo/playground/scripts/requirements.txt +++ b/demo/playground/scripts/requirements.txt @@ -16,7 +16,7 @@ pytest==7.3.1; python_version >= "3.7" pyyaml==6.0; python_version >= "3.6" random-word==1.0.11; python_version >= "3" repoze.lru==0.7 -requests==2.30.0; python_version >= "3.7" +requests==2.31.0; python_version >= "3.7" routes==2.5.1 six==1.16.0; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" tomli==2.0.1; python_version < "3.11" and python_version >= "3.7" From 762660f01dcd973c40af068098d59539d8a26342 Mon Sep 17 00:00:00 2001 From: Jason Sherman Date: Thu, 18 May 2023 10:38:50 -0700 Subject: [PATCH 811/872] Add updated ELK stack for demos. Remove outdated EFK stack and update documentation. Signed-off-by: Jason Sherman --- demo/AgentTracing.md | 12 +- demo/EFK-stack/README.md | 99 --- demo/EFK-stack/docker-compose.yml | 35 -- .../EFK-stack/examples/anotherapp.log.request | 41 -- demo/EFK-stack/examples/myapp.log.request | 41 -- demo/EFK-stack/fluentd/Dockerfile | 9 - demo/EFK-stack/fluentd/conf/fluent.conf | 65 -- demo/EFK-stack/kibana/conf/kibana.yml | 7 - demo/EFK-stack/requirements.txt | 5 - demo/EFK-stack/search.py | 94 --- demo/elk-stack/.env.sample | 47 ++ demo/{EFK-stack => elk-stack}/LICENSE | 5 +- demo/elk-stack/README.md | 573 ++++++++++++++++++ demo/elk-stack/docker-compose.yml | 110 ++++ demo/elk-stack/elasticsearch/.dockerignore | 6 + demo/elk-stack/elasticsearch/Dockerfile | 7 + .../elasticsearch/config/elasticsearch.yml | 12 + demo/elk-stack/extensions/README.md | 3 + .../extensions/curator/.dockerignore | 6 + demo/elk-stack/extensions/curator/Dockerfile | 9 + demo/elk-stack/extensions/curator/README.md | 20 + .../extensions/curator/config/curator.yml | 13 + .../config/delete_log_files_curator.yml | 21 + .../extensions/curator/curator-compose.yml | 16 + .../enterprise-search/.dockerignore | 6 + .../extensions/enterprise-search/Dockerfile | 4 + .../extensions/enterprise-search/README.md | 144 +++++ .../config/enterprise-search.yml | 28 + .../enterprise-search-compose.yml | 20 + .../extensions/filebeat/.dockerignore | 6 + demo/elk-stack/extensions/filebeat/Dockerfile | 3 + demo/elk-stack/extensions/filebeat/README.md | 42 ++ .../extensions/filebeat/config/filebeat.yml | 39 ++ .../extensions/filebeat/filebeat-compose.yml | 35 ++ demo/elk-stack/extensions/fleet/.dockerignore | 6 + demo/elk-stack/extensions/fleet/Dockerfile | 8 + demo/elk-stack/extensions/fleet/README.md | 69 +++ .../fleet/agent-apmserver-compose.yml | 45 ++ .../extensions/fleet/fleet-compose.yml | 36 ++ .../extensions/heartbeat/.dockerignore | 6 + .../elk-stack/extensions/heartbeat/Dockerfile | 3 + demo/elk-stack/extensions/heartbeat/README.md | 41 ++ .../extensions/heartbeat/config/heartbeat.yml | 40 ++ .../heartbeat/heartbeat-compose.yml | 24 + .../extensions/logspout/.dockerignore | 6 + demo/elk-stack/extensions/logspout/Dockerfile | 5 + demo/elk-stack/extensions/logspout/README.md | 28 + demo/elk-stack/extensions/logspout/build.sh | 13 + .../extensions/logspout/logspout-compose.yml | 19 + demo/elk-stack/extensions/logspout/modules.go | 10 + .../extensions/metricbeat/.dockerignore | 6 + .../extensions/metricbeat/Dockerfile | 3 + .../elk-stack/extensions/metricbeat/README.md | 49 ++ .../metricbeat/config/metricbeat.yml | 72 +++ .../metricbeat/metricbeat-compose.yml | 47 ++ demo/elk-stack/kibana/.dockerignore | 6 + demo/elk-stack/kibana/Dockerfile | 7 + demo/elk-stack/kibana/config/kibana.yml | 94 +++ demo/elk-stack/logstash/.dockerignore | 6 + demo/elk-stack/logstash/Dockerfile | 7 + demo/elk-stack/logstash/config/logstash.yml | 7 + .../elk-stack/logstash/pipeline/logstash.conf | 28 + demo/elk-stack/setup/.dockerignore | 12 + demo/elk-stack/setup/.gitignore | 1 + demo/elk-stack/setup/Dockerfile | 15 + demo/elk-stack/setup/entrypoint.sh | 134 ++++ demo/elk-stack/setup/lib.sh | 240 ++++++++ .../setup/roles/filebeat_writer.json | 19 + .../setup/roles/heartbeat_writer.json | 18 + .../setup/roles/logstash_writer.json | 33 + .../setup/roles/metricbeat_writer.json | 19 + demo/multi-demo/Dockerfile.acapy | 2 +- demo/multi-demo/README.md | 22 + demo/multi-demo/docker-compose.yml | 35 +- demo/multi-demo/max_conns.sql | 1 + demo/multi-demo/ngrok-wait.sh | 32 +- 76 files changed, 2433 insertions(+), 424 deletions(-) delete mode 100644 demo/EFK-stack/README.md delete mode 100644 demo/EFK-stack/docker-compose.yml delete mode 100644 demo/EFK-stack/examples/anotherapp.log.request delete mode 100644 demo/EFK-stack/examples/myapp.log.request delete mode 100644 demo/EFK-stack/fluentd/Dockerfile delete mode 100644 demo/EFK-stack/fluentd/conf/fluent.conf delete mode 100644 demo/EFK-stack/kibana/conf/kibana.yml delete mode 100644 demo/EFK-stack/requirements.txt delete mode 100644 demo/EFK-stack/search.py create mode 100644 demo/elk-stack/.env.sample rename demo/{EFK-stack => elk-stack}/LICENSE (94%) create mode 100644 demo/elk-stack/README.md create mode 100644 demo/elk-stack/docker-compose.yml create mode 100644 demo/elk-stack/elasticsearch/.dockerignore create mode 100644 demo/elk-stack/elasticsearch/Dockerfile create mode 100644 demo/elk-stack/elasticsearch/config/elasticsearch.yml create mode 100644 demo/elk-stack/extensions/README.md create mode 100644 demo/elk-stack/extensions/curator/.dockerignore create mode 100644 demo/elk-stack/extensions/curator/Dockerfile create mode 100644 demo/elk-stack/extensions/curator/README.md create mode 100644 demo/elk-stack/extensions/curator/config/curator.yml create mode 100644 demo/elk-stack/extensions/curator/config/delete_log_files_curator.yml create mode 100644 demo/elk-stack/extensions/curator/curator-compose.yml create mode 100644 demo/elk-stack/extensions/enterprise-search/.dockerignore create mode 100644 demo/elk-stack/extensions/enterprise-search/Dockerfile create mode 100644 demo/elk-stack/extensions/enterprise-search/README.md create mode 100644 demo/elk-stack/extensions/enterprise-search/config/enterprise-search.yml create mode 100644 demo/elk-stack/extensions/enterprise-search/enterprise-search-compose.yml create mode 100644 demo/elk-stack/extensions/filebeat/.dockerignore create mode 100644 demo/elk-stack/extensions/filebeat/Dockerfile create mode 100644 demo/elk-stack/extensions/filebeat/README.md create mode 100644 demo/elk-stack/extensions/filebeat/config/filebeat.yml create mode 100644 demo/elk-stack/extensions/filebeat/filebeat-compose.yml create mode 100644 demo/elk-stack/extensions/fleet/.dockerignore create mode 100644 demo/elk-stack/extensions/fleet/Dockerfile create mode 100644 demo/elk-stack/extensions/fleet/README.md create mode 100644 demo/elk-stack/extensions/fleet/agent-apmserver-compose.yml create mode 100644 demo/elk-stack/extensions/fleet/fleet-compose.yml create mode 100644 demo/elk-stack/extensions/heartbeat/.dockerignore create mode 100644 demo/elk-stack/extensions/heartbeat/Dockerfile create mode 100644 demo/elk-stack/extensions/heartbeat/README.md create mode 100644 demo/elk-stack/extensions/heartbeat/config/heartbeat.yml create mode 100644 demo/elk-stack/extensions/heartbeat/heartbeat-compose.yml create mode 100644 demo/elk-stack/extensions/logspout/.dockerignore create mode 100644 demo/elk-stack/extensions/logspout/Dockerfile create mode 100644 demo/elk-stack/extensions/logspout/README.md create mode 100755 demo/elk-stack/extensions/logspout/build.sh create mode 100644 demo/elk-stack/extensions/logspout/logspout-compose.yml create mode 100644 demo/elk-stack/extensions/logspout/modules.go create mode 100644 demo/elk-stack/extensions/metricbeat/.dockerignore create mode 100644 demo/elk-stack/extensions/metricbeat/Dockerfile create mode 100644 demo/elk-stack/extensions/metricbeat/README.md create mode 100644 demo/elk-stack/extensions/metricbeat/config/metricbeat.yml create mode 100644 demo/elk-stack/extensions/metricbeat/metricbeat-compose.yml create mode 100644 demo/elk-stack/kibana/.dockerignore create mode 100644 demo/elk-stack/kibana/Dockerfile create mode 100644 demo/elk-stack/kibana/config/kibana.yml create mode 100644 demo/elk-stack/logstash/.dockerignore create mode 100644 demo/elk-stack/logstash/Dockerfile create mode 100644 demo/elk-stack/logstash/config/logstash.yml create mode 100644 demo/elk-stack/logstash/pipeline/logstash.conf create mode 100644 demo/elk-stack/setup/.dockerignore create mode 100644 demo/elk-stack/setup/.gitignore create mode 100644 demo/elk-stack/setup/Dockerfile create mode 100755 demo/elk-stack/setup/entrypoint.sh create mode 100644 demo/elk-stack/setup/lib.sh create mode 100644 demo/elk-stack/setup/roles/filebeat_writer.json create mode 100644 demo/elk-stack/setup/roles/heartbeat_writer.json create mode 100644 demo/elk-stack/setup/roles/logstash_writer.json create mode 100644 demo/elk-stack/setup/roles/metricbeat_writer.json create mode 100644 demo/multi-demo/max_conns.sql diff --git a/demo/AgentTracing.md b/demo/AgentTracing.md index ccb009e19e..819d9af6b3 100644 --- a/demo/AgentTracing.md +++ b/demo/AgentTracing.md @@ -34,9 +34,9 @@ Environment variables: ``` TRACE_ENABLED Flag to enable tracing -TRACE_TARGET_URL Host:port of endpoint to log trace events (e.g. fluentd:8088) +TRACE_TARGET_URL Host:port of endpoint to log trace events (e.g. logstash:9700) -DOCKER_NET Docker network to join (must be used if EFK stack is running in docker) +DOCKER_NET Docker network to join (must be used if ELK stack is running in docker) TRACE_TAG Tag to be included in all logged trace events ``` @@ -83,16 +83,16 @@ Faber | Connected When `Exchange Tracing` is `ON`, all exchanges will include tracing. -## Logging Trace Events to an EFK Stack +## Logging Trace Events to an ELK Stack -You can use the `EFK` stack in the [EFK sub-directory](./EFK-stack) as a target for trace events, just start the EFK stack using the docker-compose file and then in two separate bash shells, startup the demo as follows: +You can use the `ELK` stack in the [ELK Stack sub-directory](./elk-stack) as a target for trace events, just start the ELK stack using the docker-compose file and then in two separate bash shells, startup the demo as follows: ```bash -DOCKER_NET=efk-stack_efk_net TRACE_TARGET_URL=fluentd:8088 ./run_demo faber --trace-http +DOCKER_NET=elknet TRACE_TARGET_URL=logstash:9700 ./run_demo faber --trace-http ``` ```bash -DOCKER_NET=efk-stack_efk_net TRACE_TARGET_URL=fluentd:8088 ./run_demo alice --trace-http +DOCKER_NET=elknet TRACE_TARGET_URL=logstash:9700 ./run_demo alice --trace-http ``` ## Hooking into event messaging diff --git a/demo/EFK-stack/README.md b/demo/EFK-stack/README.md deleted file mode 100644 index e7e956055e..0000000000 --- a/demo/EFK-stack/README.md +++ /dev/null @@ -1,99 +0,0 @@ -# EFK stack - -Note - this code was originally obtained from [https://github.com/giefferre/EFK-stack](https://github.com/giefferre/EFK-stack) - -A sample environment running an [EFK stack][efk] on your local machine. - -Includes: - -- [Elasticsearch][elasticsearch] -- [Fluentd][fluentd] -- [Kibana][kibana] - -## Introduction - -As software systems grow and become more and more decoupled, log aggregation is a key aspect to take care of. - -The issues to tackle down with logging are: - -- Having a centralized overview of all log events -- Normalizing different log types -- Automated processing of log messages -- Supporting several and very different event sources - -While [Elasticsearch][elasticsearch] and [Kibana][kibana] are the reference products *de facto* for log searching and visualization in the open source community, there's no such agreement for log collectors. - -The two most-popular data collectors are: - -- [Logstash][logstash], most known for being part of the [ELK Stack][elk] -- [Fluentd][fluentd], used by communities of users of software such as [Docker][docker-fluentd] and [GCP][gcp-fluentd] - -Logging systems using Fluentd as collector are usually referenced as [EFK stack][efk]. - -Aim of this repository is to run an EFK stack on your local machine using docker-compose. - -I'm not personally involved with companies supporting Logstash nor Fluentd. - -If you need help to choose between Logstash and Fluent, take a look to the [reference](#reference). - -## Launching the EFK stack - -### Requirements - -On your machine, make sure you have installed: - -- [Docker][docker] -- [Docker Compose][docker-compose] - -### Run - -```bash -docker-compose up -``` - -Please note: in this example Fluentd will run on port `8080` instead of the default `24224`. - -This settings has been changed to show how to configure Fluentd to listen on a different port. - -Kibana is exposed on port `5601`. - -### Testing with sample data - -If you are running macOS and you want to send sample data to test the EFK stack, you'll need [RESTed][rested]. - -Files are available in the [examples](examples) folder. - -Please note that RESTed is not strictly necessary as any other REST client application will work fine. - -## Running the aca-py Alice/Faber Demo Tracing using EFK - -In two separate bash shells, startup the demo as follows: - -```bash -DOCKER_NET=efk-stack_efk_net TRACE_TARGET_URL=fluentd:8088 ./run_demo faber --trace-http -``` - -```bash -DOCKER_NET=efk-stack_efk_net TRACE_TARGET_URL=fluentd:8088 ./run_demo alice --trace-http -``` - -## Reference - -- [Quora - What is the ELK stack](https://www.quora.com/What-is-the-ELK-stack) -- [Fluentd vs. LogStash: A Feature Comparison](https://www.loomsystems.com/blog/single-post/2017/01/30/a-comparison-of-fluentd-vs-logstash-log-collector) -- [Panda Strike: Fluentd vs Logstash](https://www.pandastrike.com/posts/20150807-fluentd-vs-logstash) -- [Log Aggregation with Fluentd, Elasticsearch and Kibana - Haufe-Lexware.github.io](http://work.haufegroup.io/log-aggregation/) -- [Fluentd vs Logstash, An unbiased comparison](https://techstricks.com/fluentd-vs-logstash/) -- [Fluentd vs. Logstash: A Comparison of Log Collectors | Logz.io](https://logz.io/blog/fluentd-logstash/) - -[elasticsearch]: https://www.elastic.co/products/elasticsearch -[fluentd]: https://www.fluentd.org/ -[kibana]: https://www.elastic.co/products/kibana -[logstash]: https://www.elastic.co/products/logstash -[elk]: https://www.elastic.co/videos/introduction-to-the-elk-stack -[docker-fluentd]: https://docs.docker.com/reference/logging/fluentd/ -[gcp-fluentd]: https://github.com/GoogleCloudPlatform/google-fluentd -[efk]: https://docs.openshift.com/enterprise/3.1/install_config/aggregate_logging.html#overview -[docker]: https://www.docker.com/ -[docker-compose]: https://docs.docker.com/compose/ -[rested]: https://itunes.apple.com/au/app/rested-simple-http-requests/id421879749?mt=12 \ No newline at end of file diff --git a/demo/EFK-stack/docker-compose.yml b/demo/EFK-stack/docker-compose.yml deleted file mode 100644 index 9434ff5ada..0000000000 --- a/demo/EFK-stack/docker-compose.yml +++ /dev/null @@ -1,35 +0,0 @@ -version: '3' -services: - elasticsearch: - image: docker.elastic.co/elasticsearch/elasticsearch:7.4.2 - environment: - discovery.type: single-node - ports: - - 9200:9200 - - 9300:9300 - networks: - - efk_net - - fluentd: - build: ./fluentd - volumes: - - ./fluentd/conf:/fluentd/etc - - ./logs:/logs - ports: - - 8088:8088 - networks: - - efk_net - - kibana: - image: docker.elastic.co/kibana/kibana:7.4.2 - environment: - ELASTICSEARCH_URL: http://elasticsearch:9200 - volumes: - - ./kibana/conf:/usr/share/kibana/config - ports: - - 5601:5601 - networks: - - efk_net - -networks: - efk_net: diff --git a/demo/EFK-stack/examples/anotherapp.log.request b/demo/EFK-stack/examples/anotherapp.log.request deleted file mode 100644 index e2532a34cd..0000000000 --- a/demo/EFK-stack/examples/anotherapp.log.request +++ /dev/null @@ -1,41 +0,0 @@ - - - - - baseURL - http://localhost:8080/anotherapp.log - bodyString - json={"action":"logout","userId":"5b07fbbb4e6b8"} - followRedirect - - handleJSONPCallbacks - - headers - - - header - Content-Type - inUse - - value - application/x-www-form-urlencoded - - - httpMethod - POST - jsonpScript - - paramBodyUIChoice - 0 - parameters - - parametersType - 0 - presentBeforeChallenge - - stringEncoding - 4 - usingHTTPBody - - - diff --git a/demo/EFK-stack/examples/myapp.log.request b/demo/EFK-stack/examples/myapp.log.request deleted file mode 100644 index c91f6f2423..0000000000 --- a/demo/EFK-stack/examples/myapp.log.request +++ /dev/null @@ -1,41 +0,0 @@ - - - - - baseURL - http://localhost:8080/myapp.log - bodyString - json={"action":"login","userId":"5b07fbbb4e6b8"} - followRedirect - - handleJSONPCallbacks - - headers - - - header - Content-Type - inUse - - value - application/x-www-form-urlencoded - - - httpMethod - POST - jsonpScript - - paramBodyUIChoice - 0 - parameters - - parametersType - 0 - presentBeforeChallenge - - stringEncoding - 4 - usingHTTPBody - - - diff --git a/demo/EFK-stack/fluentd/Dockerfile b/demo/EFK-stack/fluentd/Dockerfile deleted file mode 100644 index 8f8a4f94d3..0000000000 --- a/demo/EFK-stack/fluentd/Dockerfile +++ /dev/null @@ -1,9 +0,0 @@ -FROM fluent/fluentd:stable -RUN apk add --update --virtual .build-deps \ - sudo build-base ruby-dev \ - && sudo gem install \ - fluent-plugin-elasticsearch \ - && sudo gem sources --clear-all \ - && apk del .build-deps \ - && rm -rf /var/cache/apk/* \ - /home/fluent/.gem/ruby/2.3.0/cache/*.gem diff --git a/demo/EFK-stack/fluentd/conf/fluent.conf b/demo/EFK-stack/fluentd/conf/fluent.conf deleted file mode 100644 index 9c665c2a7c..0000000000 --- a/demo/EFK-stack/fluentd/conf/fluent.conf +++ /dev/null @@ -1,65 +0,0 @@ -# Fluentd main configuration file -# Reference: https://docs.fluentd.org/v1.0/articles/config-file - -# Set Fluentd to listen via http on port 8088, listening on all hosts - - @type http - port 8088 - bind 0.0.0.0 - - -# watch aca-py log files -# -# @type tail path /logs/acapy-*.log -# pos_file /logs/td-agent/acapy.log.pos -# tag acapy.event -# read_from_head true -# -# @type apache2 -# -# - - - @type copy - - @type elasticsearch - host elasticsearch - port 9200 - index_name fluentd - type_name fluentd - logstash_format true - logstash_prefix fluentd - logstash_dateformat %Y%m%d - include_tag_key true - tag_key @log_name - flush_interval 1s - - - -# Events having prefix 'myapp.' will be stored both on Elasticsearch and files. - - @type copy - - @type elasticsearch - host elasticsearch - port 9200 - index_name fluentd - type_name fluentd - logstash_format true - logstash_prefix fluentd - logstash_dateformat %Y%m%d - include_tag_key true - tag_key @log_name - flush_interval 1s - - - @type file - path /logs/myapp - flush_interval 30s - - - -# All other events will be printed to stdout - - @type stdout - \ No newline at end of file diff --git a/demo/EFK-stack/kibana/conf/kibana.yml b/demo/EFK-stack/kibana/conf/kibana.yml deleted file mode 100644 index cf19a76416..0000000000 --- a/demo/EFK-stack/kibana/conf/kibana.yml +++ /dev/null @@ -1,7 +0,0 @@ -# Default Kibana configuration for docker target -server.name: kibana -server.host: "0" -elasticsearch.hosts: [ "http://elasticsearch:9200" ] -xpack.monitoring.ui.container.elasticsearch.enabled: true -xpack.reporting.enabled: true -xpack.reporting.csv.maxSizeBytes: 10485760 diff --git a/demo/EFK-stack/requirements.txt b/demo/EFK-stack/requirements.txt deleted file mode 100644 index 78f00d2cf5..0000000000 --- a/demo/EFK-stack/requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -# Elasticsearch 7.x -elasticsearch>=7.0.0,<8.0.0 - -# Elasticsearch 7.x -elasticsearch-dsl>=7.0.0,<8.0.0 diff --git a/demo/EFK-stack/search.py b/demo/EFK-stack/search.py deleted file mode 100644 index db60979025..0000000000 --- a/demo/EFK-stack/search.py +++ /dev/null @@ -1,94 +0,0 @@ -import csv - -from elasticsearch_dsl import connections -from elasticsearch_dsl import Search - - -connections.create_connection(hosts=["localhost"], timeout=20) - -s = Search(index="fluentd-*") -# only return the selected fields -s = s.source( - [ - "str_time", - "timestamp", - "handler", - "ellapsed_milli", - "thread_id", - "msg_id", - "outcome", - "traced_type", - ] -) -s = s.sort("timestamp") -events = [] -for x in s.scan(): - events.append( - { - "str_time": x.str_time, - "timestamp": x.timestamp, - "handler": x.handler, - "ellapsed_milli": x.ellapsed_milli, - "thread_id": x.thread_id, - "msg_id": x.msg_id, - "outcome": x.outcome, - "traced_type": x.traced_type, - } - ) -sorted_events = sorted(events, key=lambda i: i["timestamp"]) - -threads = {} -thread_count = 0 -agents = {} -with open("agent-events.csv", "w", newline="") as csvfile: - spamwriter = csv.writer(csvfile) - i = 0 - spamwriter.writerow( - [ - "idx", - "str_time", - "timestamp", - "handler", - "ellapsed_milli", - "thread_id", - "msg_id", - "outcome", - "traced_type", - "delta_agent", - "delta_thread", - ] - ) - for x in sorted_events: - if x["handler"] in agents: - delta_agent = x["timestamp"] - agents[x["handler"]] - if delta_agent < 0: - print(i, delta_agent) - else: - delta_agent = 0 - agents[x["handler"]] = x["timestamp"] - if x["thread_id"] in threads: - delta_thread = x["timestamp"] - threads[x["thread_id"]] - if delta_thread < 0: - print(i, delta_thread) - else: - delta_thread = 0 - thread_count = thread_count + 1 - threads[x["thread_id"]] = x["timestamp"] - i = i + 1 - spamwriter.writerow( - [ - i, - x["str_time"], - x["timestamp"], - x["handler"], - x["ellapsed_milli"], - x["thread_id"], - x["msg_id"], - x["outcome"], - x["traced_type"], - delta_agent, - delta_thread, - ] - ) - -print("Total threads=", thread_count) diff --git a/demo/elk-stack/.env.sample b/demo/elk-stack/.env.sample new file mode 100644 index 0000000000..f0a50a5c35 --- /dev/null +++ b/demo/elk-stack/.env.sample @@ -0,0 +1,47 @@ +ELASTIC_VERSION=8.7.0 + +## Passwords for stack users +# + +# User 'elastic' (built-in) +# +# Superuser role, full access to cluster management and data indices. +# https://www.elastic.co/guide/en/elasticsearch/reference/current/built-in-users.html +ELASTIC_PASSWORD='changeme' + +# User 'logstash_internal' (custom) +# +# The user Logstash uses to connect and send data to Elasticsearch. +# https://www.elastic.co/guide/en/logstash/current/ls-security.html +LOGSTASH_INTERNAL_PASSWORD='changeme' + +# User 'kibana_system' (built-in) +# +# The user Kibana uses to connect and communicate with Elasticsearch. +# https://www.elastic.co/guide/en/elasticsearch/reference/current/built-in-users.html +KIBANA_SYSTEM_PASSWORD='changeme' + +# Users 'metricbeat_internal', 'filebeat_internal' and 'heartbeat_internal' (custom) +# +# The users Beats use to connect and send data to Elasticsearch. +# https://www.elastic.co/guide/en/beats/metricbeat/current/feature-roles.html +METRICBEAT_INTERNAL_PASSWORD='' +FILEBEAT_INTERNAL_PASSWORD='' +HEARTBEAT_INTERNAL_PASSWORD='' + +# User 'monitoring_internal' (custom) +# +# The user Metricbeat uses to collect monitoring data from stack components. +# https://www.elastic.co/guide/en/elasticsearch/reference/current/how-monitoring-works.html +MONITORING_INTERNAL_PASSWORD='' + +# User 'beats_system' (built-in) +# +# The user the Beats use when storing monitoring information in Elasticsearch. +# https://www.elastic.co/guide/en/elasticsearch/reference/current/built-in-users.html +BEATS_SYSTEM_PASSWORD='' + +# Docker Network Name for docker-elk +# +# +ELK_NETWORK_NAME=elknet \ No newline at end of file diff --git a/demo/EFK-stack/LICENSE b/demo/elk-stack/LICENSE similarity index 94% rename from demo/EFK-stack/LICENSE rename to demo/elk-stack/LICENSE index 54de4db429..0dbd69f8e2 100644 --- a/demo/EFK-stack/LICENSE +++ b/demo/elk-stack/LICENSE @@ -1,6 +1,6 @@ -MIT License +The MIT License (MIT) -Copyright (c) 2018 Gianfranco Reppucci +Copyright (c) 2015 Anthony Lapenna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -19,4 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - diff --git a/demo/elk-stack/README.md b/demo/elk-stack/README.md new file mode 100644 index 0000000000..8e0fae75cf --- /dev/null +++ b/demo/elk-stack/README.md @@ -0,0 +1,573 @@ +# ACA-Py ELK Stack for demos + +Note - this code was originally obtained from [https://github.com/deviantony/docker-elk](https://github.com/deviantony/docker-elk). + +The following changes were made to better incorporate this stack into ACA-Py demos and tracing. + +- renamed the network to `elknet` and added environment variable `ELK_NETWORK_NAME` in `.env` to change the name of the docker network. +- set [elasticsearch](./elasticsearch/config/elasticsearch.yml) license to `basic` +- [logstash.conf](./logstash/pipeline/logstash.conf) set an http port (9700) and exposed this for pushing agent traces into ELK. + +## run + +``` +cp .env.sample .env +docker compose build +docker compose up +``` + +Using the default configuration, `elasticsearch`, `kibana` and `logstash` services will be started. Kibana can be accessed at [http://localhost:5601](http://localhost:5601), and you can log in with `elastic / changeme` as the username and password. + +A `log-*` index will be created, and you can refresh the [Discover Analytics](http://localhost:5601/app/discover#/?_g=(filters:!(),refreshInterval:(pause:!t,value:60000),time:(from:now-15m,to:now))&_a=(columns:!(traced_type,handler,ellapsed_milli,outcome,thread_id,msg_id),filters:!(),index:'logs-*',interval:auto,query:(language:kuery,query:''),sort:!(!('@timestamp',desc)))) to see any logged events. + + +We can run demos to see agent tracing events and attach them to the `elknet` network to push events to ELK. + +## demos + +Assuming the elk stack is running from above... from your demos directory, in two separate bash shells, startup the demo as follows: + +```bash +DOCKER_NET=elknet TRACE_TARGET_URL=logstash:9700 LEDGER_URL=http://test.bcovrin.vonx.io ./run_demo faber --trace-http +``` + +```bash +DOCKER_NET=elknet TRACE_TARGET_URL=logstash:9700 LEDGER_URL=http://test.bcovrin.vonx.io ./run_demo alice --trace-http +``` + +And run the demo scenarios as you wish. + +**Note**: the `LEDGER_URL` override is unnecessary for tracing; it is only used so `faber` and `alice` are on the same ledger as the following `multi-demo`. + +## multi-demo + +The `multi-demo` (running ACA-Py in multitenant mode) requires a few edits to [docker-compose.yml](../multi-demo/docker-compose.yml). + + +Note the reference to `elknet`. + +``` +networks: + app-network: + name: ${APP_NETWORK_NAME:-appnet} + driver: bridge + elk-network: + name: ${ELK_NETWORK_NAME:-elknet} + driver: bridge +``` + + +And uncomment the tracing environment variables... + +```docker-compose.yml + environment: + - NGROK_NAME=ngrok-agent + - ACAPY_AGENT_ACCESS=${ACAPY_AGENT_ACCESS:-public} + - ACAPY_TRACE=${ACAPY_TRACE:-1} + - ACAPY_TRACE_TARGET=${ACAPY_TRACE_TARGET:-http://logstash:9700/} + - ACAPY_TRACE_TAG=${ACAPY_TRACE_TAG:-acapy.events} + - ACAPY_TRACE_LABEL=${ACAPY_TRACE_LABEL:-multi.agent.trace} +``` + +Another change you may wish to make is setting `ACAPY_AGENT_ACCESS` to `local`. Use this if there is no need for ngrok tunnelling. For instance, if all your agents are local to the docker network (ie not phones) and connect only to themselves (or with some "public" agents), then we can eliminate `ngrok` throwing errors if we have too many simultaneous http connections (ie hundreds of wallets in a load test). + +Assuming the elk stack is running from above... from your multi-demo directory, startup the agent as follows: + +```bash +docker compose build +docker compose up +``` + + +# Elastic stack (ELK) on Docker + +[![Elastic Stack version](https://img.shields.io/badge/Elastic%20Stack-8.7.0-00bfb3?style=flat&logo=elastic-stack)](https://www.elastic.co/blog/category/releases) +[![Build Status](https://github.com/deviantony/docker-elk/workflows/CI/badge.svg?branch=main)](https://github.com/deviantony/docker-elk/actions?query=workflow%3ACI+branch%3Amain) +[![Join the chat](https://badges.gitter.im/Join%20Chat.svg)](https://app.gitter.im/#/room/#deviantony_docker-elk:gitter.im) + +Run the latest version of the [Elastic stack][elk-stack] with Docker and Docker Compose. + +It gives you the ability to analyze any data set by using the searching/aggregation capabilities of Elasticsearch and +the visualization power of Kibana. + +![Animated demo](https://user-images.githubusercontent.com/3299086/155972072-0c89d6db-707a-47a1-818b-5f976565f95a.gif) + +> **Note** +> [Platinum][subscriptions] features are enabled by default for a [trial][license-mngmt] duration of **30 days**. After +> this evaluation period, you will retain access to all the free features included in the Open Basic license seamlessly, +> without manual intervention required, and without losing any data. Refer to the [How to disable paid +> features](#how-to-disable-paid-features) section to opt out of this behaviour. + +Based on the official Docker images from Elastic: + +* [Elasticsearch](https://github.com/elastic/elasticsearch/tree/main/distribution/docker) +* [Logstash](https://github.com/elastic/logstash/tree/main/docker) +* [Kibana](https://github.com/elastic/kibana/tree/main/src/dev/build/tasks/os_packages/docker_generator) + +Other available stack variants: + +* [`tls`](https://github.com/deviantony/docker-elk/tree/tls): TLS encryption enabled in Elasticsearch, Kibana (opt in), + and Fleet +* [`searchguard`](https://github.com/deviantony/docker-elk/tree/searchguard): Search Guard support + +--- + +## Philosophy + +We aim at providing the simplest possible entry into the Elastic stack for anybody who feels like experimenting with +this powerful combo of technologies. This project's default configuration is purposely minimal and unopinionated. It +does not rely on any external dependency, and uses as little custom automation as necessary to get things up and +running. + +Instead, we believe in good documentation so that you can use this repository as a template, tweak it, and make it _your +own_. [sherifabdlnaby/elastdocker][elastdocker] is one example among others of project that builds upon this idea. + +--- + +## Contents + +1. [Requirements](#requirements) + * [Host setup](#host-setup) + * [Docker Desktop](#docker-desktop) + * [Windows](#windows) + * [macOS](#macos) +1. [Usage](#usage) + * [Bringing up the stack](#bringing-up-the-stack) + * [Initial setup](#initial-setup) + * [Setting up user authentication](#setting-up-user-authentication) + * [Injecting data](#injecting-data) + * [Cleanup](#cleanup) + * [Version selection](#version-selection) +1. [Configuration](#configuration) + * [How to configure Elasticsearch](#how-to-configure-elasticsearch) + * [How to configure Kibana](#how-to-configure-kibana) + * [How to configure Logstash](#how-to-configure-logstash) + * [How to disable paid features](#how-to-disable-paid-features) + * [How to scale out the Elasticsearch cluster](#how-to-scale-out-the-elasticsearch-cluster) + * [How to re-execute the setup](#how-to-re-execute-the-setup) + * [How to reset a password programmatically](#how-to-reset-a-password-programmatically) +1. [Extensibility](#extensibility) + * [How to add plugins](#how-to-add-plugins) + * [How to enable the provided extensions](#how-to-enable-the-provided-extensions) +1. [JVM tuning](#jvm-tuning) + * [How to specify the amount of memory used by a service](#how-to-specify-the-amount-of-memory-used-by-a-service) + * [How to enable a remote JMX connection to a service](#how-to-enable-a-remote-jmx-connection-to-a-service) +1. [Going further](#going-further) + * [Plugins and integrations](#plugins-and-integrations) + +## Requirements + +### Host setup + +* [Docker Engine][docker-install] version **18.06.0** or newer +* [Docker Compose][compose-install] version **1.26.0** or newer (including [Compose V2][compose-v2]) +* 1.5 GB of RAM + +> **Warning** +> While Compose versions between **1.22.0** and **1.25.5** can technically run this stack as well, these versions have a +> [known issue](https://github.com/deviantony/docker-elk/pull/678#issuecomment-1055555368) which prevents them from +> parsing quoted values properly inside `.env` files. + +> **Note** +> Especially on Linux, make sure your user has the [required permissions][linux-postinstall] to interact with the Docker +> daemon. + +By default, the stack exposes the following ports: + +* 5044: Logstash Beats input +* 50000: Logstash TCP input +* 9600: Logstash monitoring API +* 9200: Elasticsearch HTTP +* 9300: Elasticsearch TCP transport +* 5601: Kibana + +> **Warning** +> Elasticsearch's [bootstrap checks][bootstrap-checks] were purposely disabled to facilitate the setup of the Elastic +> stack in development environments. For production setups, we recommend users to set up their host according to the +> instructions from the Elasticsearch documentation: [Important System Configuration][es-sys-config]. + +### Docker Desktop + +#### Windows + +If you are using the legacy Hyper-V mode of _Docker Desktop for Windows_, ensure [File Sharing][win-filesharing] is +enabled for the `C:` drive. + +#### macOS + +The default configuration of _Docker Desktop for Mac_ allows mounting files from `/Users/`, `/Volume/`, `/private/`, +`/tmp` and `/var/folders` exclusively. Make sure the repository is cloned in one of those locations or follow the +instructions from the [documentation][mac-filesharing] to add more locations. + +## Usage + +> **Warning** +> You must rebuild the stack images with `docker-compose build` whenever you switch branch or update the +> [version](#version-selection) of an already existing stack. + +### Bringing up the stack + +Clone this repository onto the Docker host that will run the stack with the command below: + +```sh +git clone https://github.com/deviantony/docker-elk.git +``` + +Then, start the stack components locally with Docker Compose: + +```sh +docker-compose up +``` + +> **Note** +> You can also run all services in the background (detached mode) by appending the `-d` flag to the above command. + +Give Kibana about a minute to initialize, then access the Kibana web UI by opening in a web +browser and use the following (default) credentials to log in: + +* user: *elastic* +* password: *changeme* + +> **Note** +> Upon the initial startup, the `elastic`, `logstash_internal` and `kibana_system` Elasticsearch users are intialized +> with the values of the passwords defined in the [`.env`](.env) file (_"changeme"_ by default). The first one is the +> [built-in superuser][builtin-users], the other two are used by Kibana and Logstash respectively to communicate with +> Elasticsearch. This task is only performed during the _initial_ startup of the stack. To change users' passwords +> _after_ they have been initialized, please refer to the instructions in the next section. + +### Initial setup + +#### Setting up user authentication + +> **Note** +> Refer to [Security settings in Elasticsearch][es-security] to disable authentication. + +> **Warning** +> Starting with Elastic v8.0.0, it is no longer possible to run Kibana using the bootstraped privileged `elastic` user. + +The _"changeme"_ password set by default for all aforementioned users is **unsecure**. For increased security, we will +reset the passwords of all aforementioned Elasticsearch users to random secrets. + +1. Reset passwords for default users + + The commands below reset the passwords of the `elastic`, `logstash_internal` and `kibana_system` users. Take note + of them. + + ```sh + docker-compose exec elasticsearch bin/elasticsearch-reset-password --batch --user elastic + ``` + + ```sh + docker-compose exec elasticsearch bin/elasticsearch-reset-password --batch --user logstash_internal + ``` + + ```sh + docker-compose exec elasticsearch bin/elasticsearch-reset-password --batch --user kibana_system + ``` + + If the need for it arises (e.g. if you want to [collect monitoring information][ls-monitoring] through Beats and + other components), feel free to repeat this operation at any time for the rest of the [built-in + users][builtin-users]. + +1. Replace usernames and passwords in configuration files + + Replace the password of the `elastic` user inside the `.env` file with the password generated in the previous step. + Its value isn't used by any core component, but [extensions](#how-to-enable-the-provided-extensions) use it to + connect to Elasticsearch. + + > **Note** + > In case you don't plan on using any of the provided [extensions](#how-to-enable-the-provided-extensions), or + > prefer to create your own roles and users to authenticate these services, it is safe to remove the + > `ELASTIC_PASSWORD` entry from the `.env` file altogether after the stack has been initialized. + + Replace the password of the `logstash_internal` user inside the `.env` file with the password generated in the + previous step. Its value is referenced inside the Logstash pipeline file (`logstash/pipeline/logstash.conf`). + + Replace the password of the `kibana_system` user inside the `.env` file with the password generated in the previous + step. Its value is referenced inside the Kibana configuration file (`kibana/config/kibana.yml`). + + See the [Configuration](#configuration) section below for more information about these configuration files. + +1. Restart Logstash and Kibana to re-connect to Elasticsearch using the new passwords + + ```sh + docker-compose up -d logstash kibana + ``` + +> **Note** +> Learn more about the security of the Elastic stack at [Secure the Elastic Stack][sec-cluster]. + +#### Injecting data + +Launch the Kibana web UI by opening in a web browser, and use the following credentials to log +in: + +* user: *elastic* +* password: *\* + +Now that the stack is fully configured, you can go ahead and inject some log entries. + +The shipped Logstash configuration allows you to send data over the TCP port 50000. For example, you can use one of the +following commands — depending on your installed version of `nc` (Netcat) — to ingest the content of the log file +`/path/to/logfile.log` in Elasticsearch, via Logstash: + +```sh +# Execute `nc -h` to determine your `nc` version + +cat /path/to/logfile.log | nc -q0 localhost 50000 # BSD +cat /path/to/logfile.log | nc -c localhost 50000 # GNU +cat /path/to/logfile.log | nc --send-only localhost 50000 # nmap +``` + +You can also load the sample data provided by your Kibana installation. + +### Cleanup + +Elasticsearch data is persisted inside a volume by default. + +In order to entirely shutdown the stack and remove all persisted data, use the following Docker Compose command: + +```sh +docker-compose down -v +``` + +### Version selection + +This repository stays aligned with the latest version of the Elastic stack. The `main` branch tracks the current major +version (8.x). + +To use a different version of the core Elastic components, simply change the version number inside the [`.env`](.env) +file. If you are upgrading an existing stack, remember to rebuild all container images using the `docker-compose build` +command. + +> **Warning** +> Always pay attention to the [official upgrade instructions][upgrade] for each individual component before performing a +> stack upgrade. + +Older major versions are also supported on separate branches: + +* [`release-7.x`](https://github.com/deviantony/docker-elk/tree/release-7.x): 7.x series +* [`release-6.x`](https://github.com/deviantony/docker-elk/tree/release-6.x): 6.x series (End-of-life) +* [`release-5.x`](https://github.com/deviantony/docker-elk/tree/release-5.x): 5.x series (End-of-life) + +## Configuration + +> **Note** +> Configuration is not dynamically reloaded, you will need to restart individual components after any configuration +> change. + +### How to configure Elasticsearch + +The Elasticsearch configuration is stored in [`elasticsearch/config/elasticsearch.yml`][config-es]. + +You can also specify the options you want to override by setting environment variables inside the Compose file: + +```yml +elasticsearch: + + environment: + network.host: _non_loopback_ + cluster.name: my-cluster +``` + +Please refer to the following documentation page for more details about how to configure Elasticsearch inside Docker +containers: [Install Elasticsearch with Docker][es-docker]. + +### How to configure Kibana + +The Kibana default configuration is stored in [`kibana/config/kibana.yml`][config-kbn]. + +You can also specify the options you want to override by setting environment variables inside the Compose file: + +```yml +kibana: + + environment: + SERVER_NAME: kibana.example.org +``` + +Please refer to the following documentation page for more details about how to configure Kibana inside Docker +containers: [Install Kibana with Docker][kbn-docker]. + +### How to configure Logstash + +The Logstash configuration is stored in [`logstash/config/logstash.yml`][config-ls]. + +You can also specify the options you want to override by setting environment variables inside the Compose file: + +```yml +logstash: + + environment: + LOG_LEVEL: debug +``` + +Please refer to the following documentation page for more details about how to configure Logstash inside Docker +containers: [Configuring Logstash for Docker][ls-docker]. + +### How to disable paid features + +Switch the value of Elasticsearch's `xpack.license.self_generated.type` setting from `trial` to `basic` (see [License +settings][license-settings]). + +You can also cancel an ongoing trial before its expiry date — and thus revert to a basic license — either from the +[License Management][license-mngmt] panel of Kibana, or using Elasticsearch's [Licensing APIs][license-apis]. + +### How to scale out the Elasticsearch cluster + +Follow the instructions from the Wiki: [Scaling out Elasticsearch](https://github.com/deviantony/docker-elk/wiki/Elasticsearch-cluster) + +### How to re-execute the setup + +To run the setup container again and re-initialize all users for which a password was defined inside the `.env` file, +delete its volume and "up" the `setup` Compose service again manually: + +```console +$ docker-compose rm -f setup + ⠿ Container docker-elk-setup-1 Removed +``` + +```console +$ docker volume rm docker-elk_setup +docker-elk_setup +``` + +```console +$ docker-compose up setup + ⠿ Volume "docker-elk_setup" Created + ⠿ Container docker-elk-elasticsearch-1 Running + ⠿ Container docker-elk-setup-1 Created +Attaching to docker-elk-setup-1 +... +docker-elk-setup-1 | [+] User 'monitoring_internal' +docker-elk-setup-1 | ⠿ User does not exist, creating +docker-elk-setup-1 | [+] User 'beats_system' +docker-elk-setup-1 | ⠿ User exists, setting password +docker-elk-setup-1 exited with code 0 +``` + +### How to reset a password programmatically + +If for any reason your are unable to use Kibana to change the password of your users (including [built-in +users][builtin-users]), you can use the Elasticsearch API instead and achieve the same result. + +In the example below, we reset the password of the `elastic` user (notice "/user/elastic" in the URL): + +```sh +curl -XPOST -D- 'http://localhost:9200/_security/user/elastic/_password' \ + -H 'Content-Type: application/json' \ + -u elastic: \ + -d '{"password" : ""}' +``` + +## Extensibility + +### How to add plugins + +To add plugins to any ELK component you have to: + +1. Add a `RUN` statement to the corresponding `Dockerfile` (eg. `RUN logstash-plugin install logstash-filter-json`) +1. Add the associated plugin code configuration to the service configuration (eg. Logstash input/output) +1. Rebuild the images using the `docker-compose build` command + +### How to enable the provided extensions + +A few extensions are available inside the [`extensions`](extensions) directory. These extensions provide features which +are not part of the standard Elastic stack, but can be used to enrich it with extra integrations. + +The documentation for these extensions is provided inside each individual subdirectory, on a per-extension basis. Some +of them require manual changes to the default ELK configuration. + +## JVM tuning + +### How to specify the amount of memory used by a service + +The startup scripts for Elasticsearch and Logstash can append extra JVM options from the value of an environment +variable, allowing the user to adjust the amount of memory that can be used by each component: + +| Service | Environment variable | +|---------------|----------------------| +| Elasticsearch | ES_JAVA_OPTS | +| Logstash | LS_JAVA_OPTS | + +To accomodate environments where memory is scarce (Docker Desktop for Mac has only 2 GB available by default), the Heap +Size allocation is capped by default in the `docker-compose.yml` file to 512 MB for Elasticsearch and 256 MB for +Logstash. If you want to override the default JVM configuration, edit the matching environment variable(s) in the +`docker-compose.yml` file. + +For example, to increase the maximum JVM Heap Size for Logstash: + +```yml +logstash: + + environment: + LS_JAVA_OPTS: -Xms1g -Xmx1g +``` + +When these options are not set: + +* Elasticsearch starts with a JVM Heap Size that is [determined automatically][es-heap]. +* Logstash starts with a fixed JVM Heap Size of 1 GB. + +### How to enable a remote JMX connection to a service + +As for the Java Heap memory (see above), you can specify JVM options to enable JMX and map the JMX port on the Docker +host. + +Update the `{ES,LS}_JAVA_OPTS` environment variable with the following content (I've mapped the JMX service on the port +18080, you can change that). Do not forget to update the `-Djava.rmi.server.hostname` option with the IP address of your +Docker host (replace **DOCKER_HOST_IP**): + +```yml +logstash: + + environment: + LS_JAVA_OPTS: -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.port=18080 -Dcom.sun.management.jmxremote.rmi.port=18080 -Djava.rmi.server.hostname=DOCKER_HOST_IP -Dcom.sun.management.jmxremote.local.only=false +``` + +## Going further + +### Plugins and integrations + +See the following Wiki pages: + +* [External applications](https://github.com/deviantony/docker-elk/wiki/External-applications) +* [Popular integrations](https://github.com/deviantony/docker-elk/wiki/Popular-integrations) + +[elk-stack]: https://www.elastic.co/what-is/elk-stack +[subscriptions]: https://www.elastic.co/subscriptions +[es-security]: https://www.elastic.co/guide/en/elasticsearch/reference/current/security-settings.html +[license-settings]: https://www.elastic.co/guide/en/elasticsearch/reference/current/license-settings.html +[license-mngmt]: https://www.elastic.co/guide/en/kibana/current/managing-licenses.html +[license-apis]: https://www.elastic.co/guide/en/elasticsearch/reference/current/licensing-apis.html + +[elastdocker]: https://github.com/sherifabdlnaby/elastdocker + +[docker-install]: https://docs.docker.com/get-docker/ +[compose-install]: https://docs.docker.com/compose/install/ +[compose-v2]: https://docs.docker.com/compose/compose-v2/ +[linux-postinstall]: https://docs.docker.com/engine/install/linux-postinstall/ + +[bootstrap-checks]: https://www.elastic.co/guide/en/elasticsearch/reference/current/bootstrap-checks.html +[es-sys-config]: https://www.elastic.co/guide/en/elasticsearch/reference/current/system-config.html +[es-heap]: https://www.elastic.co/guide/en/elasticsearch/reference/current/important-settings.html#heap-size-settings + +[win-filesharing]: https://docs.docker.com/desktop/settings/windows/#file-sharing +[mac-filesharing]: https://docs.docker.com/desktop/settings/mac/#file-sharing + +[builtin-users]: https://www.elastic.co/guide/en/elasticsearch/reference/current/built-in-users.html +[ls-monitoring]: https://www.elastic.co/guide/en/logstash/current/monitoring-with-metricbeat.html +[sec-cluster]: https://www.elastic.co/guide/en/elasticsearch/reference/current/secure-cluster.html + +[connect-kibana]: https://www.elastic.co/guide/en/kibana/current/connect-to-elasticsearch.html +[index-pattern]: https://www.elastic.co/guide/en/kibana/current/index-patterns.html + +[config-es]: ./elasticsearch/config/elasticsearch.yml +[config-kbn]: ./kibana/config/kibana.yml +[config-ls]: ./logstash/config/logstash.yml + +[es-docker]: https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html +[kbn-docker]: https://www.elastic.co/guide/en/kibana/current/docker.html +[ls-docker]: https://www.elastic.co/guide/en/logstash/current/docker-config.html + +[upgrade]: https://www.elastic.co/guide/en/elasticsearch/reference/current/setup-upgrade.html diff --git a/demo/elk-stack/docker-compose.yml b/demo/elk-stack/docker-compose.yml new file mode 100644 index 0000000000..eafecdbb72 --- /dev/null +++ b/demo/elk-stack/docker-compose.yml @@ -0,0 +1,110 @@ +version: '3.7' + +services: + + # The 'setup' service runs a one-off script which initializes users inside + # Elasticsearch — such as 'logstash_internal' and 'kibana_system' — with the + # values of the passwords defined in the '.env' file. + # + # This task is only performed during the *initial* startup of the stack. On all + # subsequent runs, the service simply returns immediately, without performing + # any modification to existing users. + setup: + build: + context: setup/ + args: + ELASTIC_VERSION: ${ELASTIC_VERSION} + init: true + volumes: + - ./setup/entrypoint.sh:/entrypoint.sh:ro,Z + - ./setup/lib.sh:/lib.sh:ro,Z + - ./setup/roles:/roles:ro,Z + - setup:/state:Z + environment: + ELASTIC_PASSWORD: ${ELASTIC_PASSWORD:-} + LOGSTASH_INTERNAL_PASSWORD: ${LOGSTASH_INTERNAL_PASSWORD:-} + KIBANA_SYSTEM_PASSWORD: ${KIBANA_SYSTEM_PASSWORD:-} + METRICBEAT_INTERNAL_PASSWORD: ${METRICBEAT_INTERNAL_PASSWORD:-} + FILEBEAT_INTERNAL_PASSWORD: ${FILEBEAT_INTERNAL_PASSWORD:-} + HEARTBEAT_INTERNAL_PASSWORD: ${HEARTBEAT_INTERNAL_PASSWORD:-} + MONITORING_INTERNAL_PASSWORD: ${MONITORING_INTERNAL_PASSWORD:-} + BEATS_SYSTEM_PASSWORD: ${BEATS_SYSTEM_PASSWORD:-} + networks: + - elknet + depends_on: + - elasticsearch + + elasticsearch: + build: + context: elasticsearch/ + args: + ELASTIC_VERSION: ${ELASTIC_VERSION} + volumes: + - ./elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml:ro,Z + - elasticsearch:/usr/share/elasticsearch/data:Z + ports: + - 9200:9200 + - 9300:9300 + environment: + node.name: elasticsearch + ES_JAVA_OPTS: -Xms512m -Xmx512m + # Bootstrap password. + # Used to initialize the keystore during the initial startup of + # Elasticsearch. Ignored on subsequent runs. + ELASTIC_PASSWORD: ${ELASTIC_PASSWORD:-} + # Use single node discovery in order to disable production mode and avoid bootstrap checks. + # see: https://www.elastic.co/guide/en/elasticsearch/reference/current/bootstrap-checks.html + discovery.type: single-node + networks: + - elknet + restart: unless-stopped + + logstash: + build: + context: logstash/ + args: + ELASTIC_VERSION: ${ELASTIC_VERSION} + volumes: + - ./logstash/config/logstash.yml:/usr/share/logstash/config/logstash.yml:ro,Z + - ./logstash/pipeline:/usr/share/logstash/pipeline:ro,Z + ports: + - 5044:5044 + - 50000:50000/tcp + - 50000:50000/udp + - 9600:9600 + - 9700:9700 + environment: + LS_JAVA_OPTS: -Xms256m -Xmx256m + LOGSTASH_INTERNAL_PASSWORD: ${LOGSTASH_INTERNAL_PASSWORD:-} + networks: + - elknet + depends_on: + - elasticsearch + restart: unless-stopped + + kibana: + build: + context: kibana/ + args: + ELASTIC_VERSION: ${ELASTIC_VERSION} + volumes: + - ./kibana/config/kibana.yml:/usr/share/kibana/config/kibana.yml:ro,Z + ports: + - 5601:5601 + environment: + KIBANA_SYSTEM_PASSWORD: ${KIBANA_SYSTEM_PASSWORD:-} + networks: + - elknet + depends_on: + - elasticsearch + restart: unless-stopped + +networks: + elknet: + driver: bridge + attachable: true + name: ${ELK_NETWORK_NAME} + +volumes: + setup: + elasticsearch: diff --git a/demo/elk-stack/elasticsearch/.dockerignore b/demo/elk-stack/elasticsearch/.dockerignore new file mode 100644 index 0000000000..37eef9d513 --- /dev/null +++ b/demo/elk-stack/elasticsearch/.dockerignore @@ -0,0 +1,6 @@ +# Ignore Docker build files +Dockerfile +.dockerignore + +# Ignore OS artifacts +**/.DS_Store diff --git a/demo/elk-stack/elasticsearch/Dockerfile b/demo/elk-stack/elasticsearch/Dockerfile new file mode 100644 index 0000000000..22528c6d7b --- /dev/null +++ b/demo/elk-stack/elasticsearch/Dockerfile @@ -0,0 +1,7 @@ +ARG ELASTIC_VERSION + +# https://www.docker.elastic.co/ +FROM docker.elastic.co/elasticsearch/elasticsearch:${ELASTIC_VERSION} + +# Add your elasticsearch plugins setup here +# Example: RUN elasticsearch-plugin install analysis-icu diff --git a/demo/elk-stack/elasticsearch/config/elasticsearch.yml b/demo/elk-stack/elasticsearch/config/elasticsearch.yml new file mode 100644 index 0000000000..d66f071aad --- /dev/null +++ b/demo/elk-stack/elasticsearch/config/elasticsearch.yml @@ -0,0 +1,12 @@ +--- +## Default Elasticsearch configuration from Elasticsearch base image. +## https://github.com/elastic/elasticsearch/blob/main/distribution/docker/src/docker/config/elasticsearch.yml +# +cluster.name: docker-cluster +network.host: 0.0.0.0 + +## X-Pack settings +## see https://www.elastic.co/guide/en/elasticsearch/reference/current/security-settings.html +# +xpack.license.self_generated.type: trial +xpack.security.enabled: true diff --git a/demo/elk-stack/extensions/README.md b/demo/elk-stack/extensions/README.md new file mode 100644 index 0000000000..50016fb6ce --- /dev/null +++ b/demo/elk-stack/extensions/README.md @@ -0,0 +1,3 @@ +# Extensions + +Third-party extensions that enable extra integrations with the Elastic stack. diff --git a/demo/elk-stack/extensions/curator/.dockerignore b/demo/elk-stack/extensions/curator/.dockerignore new file mode 100644 index 0000000000..37eef9d513 --- /dev/null +++ b/demo/elk-stack/extensions/curator/.dockerignore @@ -0,0 +1,6 @@ +# Ignore Docker build files +Dockerfile +.dockerignore + +# Ignore OS artifacts +**/.DS_Store diff --git a/demo/elk-stack/extensions/curator/Dockerfile b/demo/elk-stack/extensions/curator/Dockerfile new file mode 100644 index 0000000000..6cb8cdc681 --- /dev/null +++ b/demo/elk-stack/extensions/curator/Dockerfile @@ -0,0 +1,9 @@ +FROM untergeek/curator:8.0.2 + +USER root + +RUN >>/var/spool/cron/crontabs/nobody \ + echo '* * * * * /curator/curator /.curator/delete_log_files_curator.yml' + +ENTRYPOINT ["crond"] +CMD ["-f", "-d8"] diff --git a/demo/elk-stack/extensions/curator/README.md b/demo/elk-stack/extensions/curator/README.md new file mode 100644 index 0000000000..5c38786aac --- /dev/null +++ b/demo/elk-stack/extensions/curator/README.md @@ -0,0 +1,20 @@ +# Curator + +Elasticsearch Curator helps you curate or manage your indices. + +## Usage + +If you want to include the Curator extension, run Docker Compose from the root of the repository with an additional +command line argument referencing the `curator-compose.yml` file: + +```bash +$ docker-compose -f docker-compose.yml -f extensions/curator/curator-compose.yml up +``` + +This sample setup demonstrates how to run `curator` every minute using `cron`. + +All configuration files are available in the `config/` directory. + +## Documentation + +[Curator Reference](https://www.elastic.co/guide/en/elasticsearch/client/curator/current/index.html) diff --git a/demo/elk-stack/extensions/curator/config/curator.yml b/demo/elk-stack/extensions/curator/config/curator.yml new file mode 100644 index 0000000000..6777edc9cb --- /dev/null +++ b/demo/elk-stack/extensions/curator/config/curator.yml @@ -0,0 +1,13 @@ +# Curator configuration +# https://www.elastic.co/guide/en/elasticsearch/client/curator/current/configfile.html + +elasticsearch: + client: + hosts: [ http://elasticsearch:9200 ] + other_settings: + username: elastic + password: ${ELASTIC_PASSWORD} + +logging: + loglevel: INFO + logformat: default diff --git a/demo/elk-stack/extensions/curator/config/delete_log_files_curator.yml b/demo/elk-stack/extensions/curator/config/delete_log_files_curator.yml new file mode 100644 index 0000000000..779c67ac0d --- /dev/null +++ b/demo/elk-stack/extensions/curator/config/delete_log_files_curator.yml @@ -0,0 +1,21 @@ +actions: + 1: + action: delete_indices + description: >- + Delete indices. Find which to delete by first limiting the list to + logstash- prefixed indices. Then further filter those to prevent deletion + of anything less than the number of days specified by unit_count. + Ignore the error if the filter does not result in an actionable list of + indices (ignore_empty_list) and exit cleanly. + options: + ignore_empty_list: True + disable_action: False + filters: + - filtertype: pattern + kind: prefix + value: logstash- + - filtertype: age + source: creation_date + direction: older + unit: days + unit_count: 2 diff --git a/demo/elk-stack/extensions/curator/curator-compose.yml b/demo/elk-stack/extensions/curator/curator-compose.yml new file mode 100644 index 0000000000..1a4bb17e25 --- /dev/null +++ b/demo/elk-stack/extensions/curator/curator-compose.yml @@ -0,0 +1,16 @@ +version: '3.7' + +services: + curator: + build: + context: extensions/curator/ + init: true + volumes: + - ./extensions/curator/config/curator.yml:/.curator/curator.yml:ro,Z + - ./extensions/curator/config/delete_log_files_curator.yml:/.curator/delete_log_files_curator.yml:ro,Z + environment: + ELASTIC_PASSWORD: ${ELASTIC_PASSWORD:-} + networks: + - elk + depends_on: + - elasticsearch diff --git a/demo/elk-stack/extensions/enterprise-search/.dockerignore b/demo/elk-stack/extensions/enterprise-search/.dockerignore new file mode 100644 index 0000000000..37eef9d513 --- /dev/null +++ b/demo/elk-stack/extensions/enterprise-search/.dockerignore @@ -0,0 +1,6 @@ +# Ignore Docker build files +Dockerfile +.dockerignore + +# Ignore OS artifacts +**/.DS_Store diff --git a/demo/elk-stack/extensions/enterprise-search/Dockerfile b/demo/elk-stack/extensions/enterprise-search/Dockerfile new file mode 100644 index 0000000000..4f0752e55a --- /dev/null +++ b/demo/elk-stack/extensions/enterprise-search/Dockerfile @@ -0,0 +1,4 @@ +ARG ELASTIC_VERSION + +# https://www.docker.elastic.co/ +FROM docker.elastic.co/enterprise-search/enterprise-search:${ELASTIC_VERSION} diff --git a/demo/elk-stack/extensions/enterprise-search/README.md b/demo/elk-stack/extensions/enterprise-search/README.md new file mode 100644 index 0000000000..d6391dba67 --- /dev/null +++ b/demo/elk-stack/extensions/enterprise-search/README.md @@ -0,0 +1,144 @@ +# Enterprise Search extension + +Elastic Enterprise Search is a suite of products for search applications backed by the Elastic Stack. + +## Requirements + +* 2 GB of free RAM, on top of the resources required by the other stack components and extensions. + +The Enterprise Search web application is served on the TCP port `3002`. + +## Usage + +### Generate an encryption key + +Enterprise Search requires one or more [encryption keys][enterprisesearch-encryption] to be configured before the +initial startup. Failing to do so prevents the server from starting. + +Encryption keys can contain any series of characters. Elastic recommends using 256-bit keys for optimal security. + +Those encryption keys must be added manually to the [`config/enterprise-search.yml`][config-enterprisesearch] file. By +default, the list of encryption keys is empty and must be populated using one of the following formats: + +```yaml +secret_management.encryption_keys: + - my_first_encryption_key + - my_second_encryption_key + - ... +``` + +```yaml +secret_management.encryption_keys: [my_first_encryption_key, my_second_encryption_key, ...] +``` + +> **Note** +> To generate a strong random encryption key, you can use the OpenSSL utility or any other online/offline tool of your +> choice: +> +> ```console +> $ openssl rand -hex 32 +> 680f94e568c90364bedf927b2f0f49609702d3eab9098688585a375b14274546 +> ``` + +### Enable Elasticsearch's API key service + +Enterprise Search requires Elasticsearch's built-in [API key service][es-security] to be enabled in order to start. +Unless Elasticsearch is configured to enable TLS on the HTTP interface (disabled by default), this service is disabled +by default. + +To enable it, modify the Elasticsearch configuration file in [`elasticsearch/config/elasticsearch.yml`][config-es] and +add the following setting: + +```yaml +xpack.security.authc.api_key.enabled: true +``` + +### Configure the Enterprise Search host in Kibana + +Kibana acts as the [management interface][enterprisesearch-kb] to Enterprise Search. + +To enable the management experience for Enterprise Search, modify the Kibana configuration file in +[`kibana/config/kibana.yml`][config-kbn] and add the following setting: + +```yaml +enterpriseSearch.host: http://enterprise-search:3002 +``` + +### Start the server + +To include Enterprise Search in the stack, run Docker Compose from the root of the repository with an additional command +line argument referencing the `enterprise-search-compose.yml` file: + +```console +$ docker-compose -f docker-compose.yml -f extensions/enterprise-search/enterprise-search-compose.yml up +``` + +Allow a few minutes for the stack to start, then open your web browser at the address to see the +Enterprise Search home page. + +Enterprise Search is configured on first boot with the following default credentials: + +* user: *enterprise_search* +* password: *changeme* + +## Security + +The Enterprise Search password is defined inside the Compose file via the `ENT_SEARCH_DEFAULT_PASSWORD` environment +variable. We highly recommend choosing a more secure password than the default one for security reasons. + +To do so, change the value `ENT_SEARCH_DEFAULT_PASSWORD` environment variable inside the Compose file **before the first +boot**: + +```yaml +enterprise-search: + + environment: + ENT_SEARCH_DEFAULT_PASSWORD: {{some strong password}} +``` + +> **Warning** +> The default Enterprise Search password can only be set during the initial boot. Once the password is persisted in +> Elasticsearch, it can only be changed via the Elasticsearch API. + +For more information, please refer to [User Management and Security][enterprisesearch-security]. + +## Configuring Enterprise Search + +The Enterprise Search configuration is stored in [`config/enterprise-search.yml`][config-enterprisesearch]. You can +modify this file using the [Default Enterprise Search configuration][enterprisesearch-config] as a reference. + +You can also specify the options you want to override by setting environment variables inside the Compose file: + +```yaml +enterprise-search: + + environment: + ent_search.auth.source: standard + worker.threads: '6' +``` + +Any change to the Enterprise Search configuration requires a restart of the Enterprise Search container: + +```console +$ docker-compose -f docker-compose.yml -f extensions/enterprise-search/enterprise-search-compose.yml restart enterprise-search +``` + +Please refer to the following documentation page for more details about how to configure Enterprise Search inside a +Docker container: [Running Enterprise Search Using Docker][enterprisesearch-docker]. + +## See also + +[Enterprise Search documentation][enterprisesearch-docs] + +[config-enterprisesearch]: ./config/enterprise-search.yml + +[enterprisesearch-encryption]: https://www.elastic.co/guide/en/enterprise-search/current/encryption-keys.html +[enterprisesearch-security]: https://www.elastic.co/guide/en/workplace-search/current/workplace-search-security.html +[enterprisesearch-config]: https://www.elastic.co/guide/en/enterprise-search/current/configuration.html +[enterprisesearch-docker]: https://www.elastic.co/guide/en/enterprise-search/current/docker.html +[enterprisesearch-docs]: https://www.elastic.co/guide/en/enterprise-search/current/index.html +[enterprisesearch-kb]: https://www.elastic.co/guide/en/kibana/current/enterprise-search-settings-kb.html + +[es-security]: https://www.elastic.co/guide/en/elasticsearch/reference/current/security-settings.html#api-key-service-settings +[config-es]: ../../elasticsearch/config/elasticsearch.yml +[config-kbn]: ../../kibana/config/kibana.yml diff --git a/demo/elk-stack/extensions/enterprise-search/config/enterprise-search.yml b/demo/elk-stack/extensions/enterprise-search/config/enterprise-search.yml new file mode 100644 index 0000000000..a1f098dd2e --- /dev/null +++ b/demo/elk-stack/extensions/enterprise-search/config/enterprise-search.yml @@ -0,0 +1,28 @@ +--- +## Enterprise Search core configuration +## https://www.elastic.co/guide/en/enterprise-search/current/configuration.html +# + +## --------------------- REQUIRED --------------------- + +# Encryption keys to protect application secrets. +secret_management.encryption_keys: + # example: + #- 680f94e568c90364bedf927b2f0f49609702d3eab9098688585a375b14274546 + +## ---------------------------------------------------- + +# IP address Enterprise Search listens on +ent_search.listen_host: 0.0.0.0 + +# URL at which users reach Enterprise Search / Kibana +ent_search.external_url: http://localhost:3002 +kibana.host: http://localhost:5601 + +# Elasticsearch URL and credentials +elasticsearch.host: http://elasticsearch:9200 +elasticsearch.username: elastic +elasticsearch.password: ${ELASTIC_PASSWORD} + +# Allow Enterprise Search to modify Elasticsearch settings. Used to enable auto-creation of Elasticsearch indexes. +allow_es_settings_modification: true diff --git a/demo/elk-stack/extensions/enterprise-search/enterprise-search-compose.yml b/demo/elk-stack/extensions/enterprise-search/enterprise-search-compose.yml new file mode 100644 index 0000000000..585dda9380 --- /dev/null +++ b/demo/elk-stack/extensions/enterprise-search/enterprise-search-compose.yml @@ -0,0 +1,20 @@ +version: '3.7' + +services: + enterprise-search: + build: + context: extensions/enterprise-search/ + args: + ELASTIC_VERSION: ${ELASTIC_VERSION} + volumes: + - ./extensions/enterprise-search/config/enterprise-search.yml:/usr/share/enterprise-search/config/enterprise-search.yml:ro,Z + environment: + JAVA_OPTS: -Xms2g -Xmx2g + ENT_SEARCH_DEFAULT_PASSWORD: 'changeme' + ELASTIC_PASSWORD: ${ELASTIC_PASSWORD:-} + ports: + - 3002:3002 + networks: + - elk + depends_on: + - elasticsearch diff --git a/demo/elk-stack/extensions/filebeat/.dockerignore b/demo/elk-stack/extensions/filebeat/.dockerignore new file mode 100644 index 0000000000..37eef9d513 --- /dev/null +++ b/demo/elk-stack/extensions/filebeat/.dockerignore @@ -0,0 +1,6 @@ +# Ignore Docker build files +Dockerfile +.dockerignore + +# Ignore OS artifacts +**/.DS_Store diff --git a/demo/elk-stack/extensions/filebeat/Dockerfile b/demo/elk-stack/extensions/filebeat/Dockerfile new file mode 100644 index 0000000000..b8dd5f3f5a --- /dev/null +++ b/demo/elk-stack/extensions/filebeat/Dockerfile @@ -0,0 +1,3 @@ +ARG ELASTIC_VERSION + +FROM docker.elastic.co/beats/filebeat:${ELASTIC_VERSION} diff --git a/demo/elk-stack/extensions/filebeat/README.md b/demo/elk-stack/extensions/filebeat/README.md new file mode 100644 index 0000000000..f2bfd20608 --- /dev/null +++ b/demo/elk-stack/extensions/filebeat/README.md @@ -0,0 +1,42 @@ +# Filebeat + +Filebeat is a lightweight shipper for forwarding and centralizing log data. Installed as an agent on your servers, +Filebeat monitors the log files or locations that you specify, collects log events, and forwards them either to +Elasticsearch or Logstash for indexing. + +## Usage + +**This extension requires the `filebeat_internal` and `beats_system` users to be created and initialized with a +password.** In case you haven't done that during the initial startup of the stack, please refer to [How to re-execute +the setup][setup] to run the setup container again and initialize these users. + +To include Filebeat in the stack, run Docker Compose from the root of the repository with an additional command line +argument referencing the `filebeat-compose.yml` file: + +```console +$ docker-compose -f docker-compose.yml -f extensions/filebeat/filebeat-compose.yml up +``` + +## Configuring Filebeat + +The Filebeat configuration is stored in [`config/filebeat.yml`](./config/filebeat.yml). You can modify this file with +the help of the [Configuration reference][filebeat-config]. + +Any change to the Filebeat configuration requires a restart of the Filebeat container: + +```console +$ docker-compose -f docker-compose.yml -f extensions/filebeat/filebeat-compose.yml restart filebeat +``` + +Please refer to the following documentation page for more details about how to configure Filebeat inside a Docker +container: [Run Filebeat on Docker][filebeat-docker]. + +## See also + +[Filebeat documentation][filebeat-doc] + +[filebeat-config]: https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-reference-yml.html +[filebeat-docker]: https://www.elastic.co/guide/en/beats/filebeat/current/running-on-docker.html +[filebeat-doc]: https://www.elastic.co/guide/en/beats/filebeat/current/index.html + +[setup]: ../../README.md#how-to-re-execute-the-setup diff --git a/demo/elk-stack/extensions/filebeat/config/filebeat.yml b/demo/elk-stack/extensions/filebeat/config/filebeat.yml new file mode 100644 index 0000000000..da8e2ea39f --- /dev/null +++ b/demo/elk-stack/extensions/filebeat/config/filebeat.yml @@ -0,0 +1,39 @@ +## Filebeat configuration +## https://github.com/elastic/beats/blob/main/deploy/docker/filebeat.docker.yml +# + +name: filebeat + +filebeat.config: + modules: + path: ${path.config}/modules.d/*.yml + reload.enabled: false + +filebeat.autodiscover: + providers: + # The Docker autodiscover provider automatically retrieves logs from Docker + # containers as they start and stop. + - type: docker + hints.enabled: true + +processors: + - add_cloud_metadata: ~ + +monitoring: + enabled: true + elasticsearch: + username: beats_system + password: ${BEATS_SYSTEM_PASSWORD} + +output.elasticsearch: + hosts: [ http://elasticsearch:9200 ] + username: filebeat_internal + password: ${FILEBEAT_INTERNAL_PASSWORD} + +## HTTP endpoint for health checking +## https://www.elastic.co/guide/en/beats/filebeat/current/http-endpoint.html +# + +http: + enabled: true + host: 0.0.0.0 diff --git a/demo/elk-stack/extensions/filebeat/filebeat-compose.yml b/demo/elk-stack/extensions/filebeat/filebeat-compose.yml new file mode 100644 index 0000000000..5c5960efe9 --- /dev/null +++ b/demo/elk-stack/extensions/filebeat/filebeat-compose.yml @@ -0,0 +1,35 @@ +version: '3.7' + +services: + filebeat: + build: + context: extensions/filebeat/ + args: + ELASTIC_VERSION: ${ELASTIC_VERSION} + # Run as 'root' instead of 'filebeat' (uid 1000) to allow reading + # 'docker.sock' and the host's filesystem. + user: root + command: + # Log to stderr. + - -e + # Disable config file permissions checks. Allows mounting + # 'config/filebeat.yml' even if it's not owned by root. + # see: https://www.elastic.co/guide/en/beats/libbeat/current/config-file-permissions.html + - --strict.perms=false + volumes: + - ./extensions/filebeat/config/filebeat.yml:/usr/share/filebeat/filebeat.yml:ro,Z + - type: bind + source: /var/lib/docker/containers + target: /var/lib/docker/containers + read_only: true + - type: bind + source: /var/run/docker.sock + target: /var/run/docker.sock + read_only: true + environment: + FILEBEAT_INTERNAL_PASSWORD: ${FILEBEAT_INTERNAL_PASSWORD:-} + BEATS_SYSTEM_PASSWORD: ${BEATS_SYSTEM_PASSWORD:-} + networks: + - elk + depends_on: + - elasticsearch diff --git a/demo/elk-stack/extensions/fleet/.dockerignore b/demo/elk-stack/extensions/fleet/.dockerignore new file mode 100644 index 0000000000..37eef9d513 --- /dev/null +++ b/demo/elk-stack/extensions/fleet/.dockerignore @@ -0,0 +1,6 @@ +# Ignore Docker build files +Dockerfile +.dockerignore + +# Ignore OS artifacts +**/.DS_Store diff --git a/demo/elk-stack/extensions/fleet/Dockerfile b/demo/elk-stack/extensions/fleet/Dockerfile new file mode 100644 index 0000000000..0b5a691dd0 --- /dev/null +++ b/demo/elk-stack/extensions/fleet/Dockerfile @@ -0,0 +1,8 @@ +ARG ELASTIC_VERSION + +FROM docker.elastic.co/beats/elastic-agent:${ELASTIC_VERSION} + +# Ensure the 'state' directory exists and is owned by the 'elastic-agent' user, +# otherwise mounting a named volume in that location creates a directory owned +# by root:root which the 'elastic-agent' user isn't allowed to write to. +RUN mkdir state diff --git a/demo/elk-stack/extensions/fleet/README.md b/demo/elk-stack/extensions/fleet/README.md new file mode 100644 index 0000000000..de800857ad --- /dev/null +++ b/demo/elk-stack/extensions/fleet/README.md @@ -0,0 +1,69 @@ +# Fleet Server + +> **Warning** +> This extension currently exists for preview purposes and should be considered **EXPERIMENTAL**. Expect regular changes +> to the default Fleet settings, both in the Elastic Agent and Kibana. +> +> See [Known Issues](#known-issues) for a list of issues that need to be addressed before this extension can be +> considered functional. + +Fleet provides central management capabilities for [Elastic Agents][fleet-doc] via an API and web UI served by Kibana, +with Elasticsearch acting as the communication layer. +Fleet Server is the central component which allows connecting Elastic Agents to the Fleet. + +## Requirements + +The Fleet Server exposes the TCP port `8220` for Agent to Server communications. + +## Usage + +To include Fleet Server in the stack, run Docker Compose from the root of the repository with an additional command line +argument referencing the `fleet-compose.yml` file: + +```console +$ docker-compose -f docker-compose.yml -f extensions/fleet/fleet-compose.yml up +``` + +## Configuring Fleet Server + +Fleet Server — like any Elastic Agent — is configured via [Agent Policies][fleet-pol] which can be either managed +through the Fleet management UI in Kibana, or statically pre-configured inside the Kibana configuration file. + +To ease the enrollment of Fleet Server in this extension, docker-elk comes with a pre-configured Agent Policy for Fleet +Server defined inside [`kibana/config/kibana.yml`][config-kbn]. + +Please refer to the following documentation page for more details about configuring Fleet Server through the Fleet +management UI: [Fleet UI Settings][fleet-cfg]. + +## Known Issues + +- Logs and metrics are only collected within the Fleet Server's container. Ultimately, we want to emulate the behaviour + of the existing Metricsbeat and Filebeat extensions, and collect logs and metrics from all ELK containers + out-of-the-box. Unfortunately, this kind of use-case isn't (yet) well supported by Fleet, and most advanced + configurations currently require running Elastic Agents in [standalone mode][fleet-standalone]. + (Relevant resource: [Migrate from Beats to Elastic Agent][fleet-beats]) +- The Elastic Agent auto-enrolls using the `elastic` super-user. With this approach, you do not need to generate a + service token — either using the Fleet management UI or [CLI utility][es-svc-token] — prior to starting this + extension. However convenient that is, this approach _does not follow security best practices_, and we recommend + generating a service token for Fleet Server instead. + +## See also + +[Fleet and Elastic Agent Guide][fleet-doc] + +## Screenshots + +![fleet-agents](https://user-images.githubusercontent.com/3299086/202701399-27518fe4-17b7-49d1-aefb-868dffeaa68a.png +"Fleet Agents") +![elastic-agent-dashboard](https://user-images.githubusercontent.com/3299086/202701404-958f8d80-a7a0-4044-bbf9-bf73f3bdd17a.png +"Elastic Agent Dashboard") + +[fleet-doc]: https://www.elastic.co/guide/en/fleet/current/fleet-overview.html +[fleet-pol]: https://www.elastic.co/guide/en/fleet/current/agent-policy.html +[fleet-cfg]: https://www.elastic.co/guide/en/fleet/current/fleet-settings.html + +[config-kbn]: ../../kibana/config/kibana.yml + +[fleet-standalone]: https://www.elastic.co/guide/en/fleet/current/elastic-agent-configuration.html +[fleet-beats]: https://www.elastic.co/guide/en/fleet/current/migrate-beats-to-agent.html +[es-svc-token]: https://www.elastic.co/guide/en/elasticsearch/reference/current/service-tokens-command.html diff --git a/demo/elk-stack/extensions/fleet/agent-apmserver-compose.yml b/demo/elk-stack/extensions/fleet/agent-apmserver-compose.yml new file mode 100644 index 0000000000..06e201a9ee --- /dev/null +++ b/demo/elk-stack/extensions/fleet/agent-apmserver-compose.yml @@ -0,0 +1,45 @@ +version: '3.7' + +# Example of Fleet-enrolled Elastic Agent pre-configured with an agent policy +# for running the APM Server integration (see kibana.yml). +# +# Run with +# docker-compose \ +# -f docker-compose.yml \ +# -f extensions/fleet/fleet-compose.yml \ +# -f extensions/fleet/agent-apmserver-compose.yml \ +# up + +services: + apm-server: + build: + context: extensions/fleet/ + args: + ELASTIC_VERSION: ${ELASTIC_VERSION} + volumes: + - apm-server:/usr/share/elastic-agent/state:Z + environment: + FLEET_ENROLL: '1' + FLEET_TOKEN_POLICY_NAME: Agent Policy APM Server + FLEET_INSECURE: '1' + FLEET_URL: http://fleet-server:8220 + # Enrollment. + # (a) Auto-enroll using basic authentication + ELASTICSEARCH_USERNAME: elastic + ELASTICSEARCH_PASSWORD: ${ELASTIC_PASSWORD:-} + # (b) Enroll using a pre-generated enrollment token + #FLEET_ENROLLMENT_TOKEN: + ports: + - 8200:8200 + hostname: apm-server + # Elastic Agent does not retry failed connections to Kibana upon the initial enrollment phase. + restart: on-failure + networks: + - elk + depends_on: + - elasticsearch + - kibana + - fleet-server + +volumes: + apm-server: diff --git a/demo/elk-stack/extensions/fleet/fleet-compose.yml b/demo/elk-stack/extensions/fleet/fleet-compose.yml new file mode 100644 index 0000000000..e33f47b0e6 --- /dev/null +++ b/demo/elk-stack/extensions/fleet/fleet-compose.yml @@ -0,0 +1,36 @@ +version: '3.7' + +services: + fleet-server: + build: + context: extensions/fleet/ + args: + ELASTIC_VERSION: ${ELASTIC_VERSION} + volumes: + - fleet-server:/usr/share/elastic-agent/state:Z + environment: + FLEET_SERVER_ENABLE: '1' + FLEET_SERVER_INSECURE_HTTP: '1' + FLEET_SERVER_HOST: 0.0.0.0 + FLEET_SERVER_POLICY_ID: fleet-server-policy + # Fleet plugin in Kibana + KIBANA_FLEET_SETUP: '1' + # Enrollment. + # (a) Auto-enroll using basic authentication + ELASTICSEARCH_USERNAME: elastic + ELASTICSEARCH_PASSWORD: ${ELASTIC_PASSWORD:-} + # (b) Enroll using a pre-generated service token + #FLEET_SERVER_SERVICE_TOKEN: + ports: + - 8220:8220 + hostname: fleet-server + # Elastic Agent does not retry failed connections to Kibana upon the initial enrollment phase. + restart: on-failure + networks: + - elk + depends_on: + - elasticsearch + - kibana + +volumes: + fleet-server: diff --git a/demo/elk-stack/extensions/heartbeat/.dockerignore b/demo/elk-stack/extensions/heartbeat/.dockerignore new file mode 100644 index 0000000000..37eef9d513 --- /dev/null +++ b/demo/elk-stack/extensions/heartbeat/.dockerignore @@ -0,0 +1,6 @@ +# Ignore Docker build files +Dockerfile +.dockerignore + +# Ignore OS artifacts +**/.DS_Store diff --git a/demo/elk-stack/extensions/heartbeat/Dockerfile b/demo/elk-stack/extensions/heartbeat/Dockerfile new file mode 100644 index 0000000000..0d7de1964a --- /dev/null +++ b/demo/elk-stack/extensions/heartbeat/Dockerfile @@ -0,0 +1,3 @@ +ARG ELASTIC_VERSION + +FROM docker.elastic.co/beats/heartbeat:${ELASTIC_VERSION} diff --git a/demo/elk-stack/extensions/heartbeat/README.md b/demo/elk-stack/extensions/heartbeat/README.md new file mode 100644 index 0000000000..82c938f512 --- /dev/null +++ b/demo/elk-stack/extensions/heartbeat/README.md @@ -0,0 +1,41 @@ +# Heartbeat + +Heartbeat is a lightweight daemon that periodically checks the status of your services and determines whether they are +available. + +## Usage + +**This extension requires the `heartbeat_internal` and `beats_system` users to be created and initialized with a +password.** In case you haven't done that during the initial startup of the stack, please refer to [How to re-execute +the setup][setup] to run the setup container again and initialize these users. + +To include Heartbeat in the stack, run Docker Compose from the root of the repository with an additional command line +argument referencing the `heartbeat-compose.yml` file: + +```console +$ docker-compose -f docker-compose.yml -f extensions/heartbeat/heartbeat-compose.yml up +``` + +## Configuring Heartbeat + +The Heartbeat configuration is stored in [`config/heartbeat.yml`](./config/heartbeat.yml). You can modify this file +with the help of the [Configuration reference][heartbeat-config]. + +Any change to the Heartbeat configuration requires a restart of the Heartbeat container: + +```console +$ docker-compose -f docker-compose.yml -f extensions/heartbeat/heartbeat-compose.yml restart heartbeat +``` + +Please refer to the following documentation page for more details about how to configure Heartbeat inside a +Docker container: [Run Heartbeat on Docker][heartbeat-docker]. + +## See also + +[Heartbeat documentation][heartbeat-doc] + +[heartbeat-config]: https://www.elastic.co/guide/en/beats/heartbeat/current/heartbeat-reference-yml.html +[heartbeat-docker]: https://www.elastic.co/guide/en/beats/heartbeat/current/running-on-docker.html +[heartbeat-doc]: https://www.elastic.co/guide/en/beats/heartbeat/current/index.html + +[setup]: ../../README.md#how-to-re-execute-the-setup diff --git a/demo/elk-stack/extensions/heartbeat/config/heartbeat.yml b/demo/elk-stack/extensions/heartbeat/config/heartbeat.yml new file mode 100644 index 0000000000..b1416ea4a9 --- /dev/null +++ b/demo/elk-stack/extensions/heartbeat/config/heartbeat.yml @@ -0,0 +1,40 @@ +## Heartbeat configuration +## https://github.com/elastic/beats/blob/main/deploy/docker/heartbeat.docker.yml +# + +name: heartbeat + +heartbeat.monitors: +- type: http + schedule: '@every 5s' + urls: + - http://elasticsearch:9200 + username: heartbeat_internal + password: ${HEARTBEAT_INTERNAL_PASSWORD} + +- type: icmp + schedule: '@every 5s' + hosts: + - elasticsearch + +processors: +- add_cloud_metadata: ~ + +monitoring: + enabled: true + elasticsearch: + username: beats_system + password: ${BEATS_SYSTEM_PASSWORD} + +output.elasticsearch: + hosts: [ http://elasticsearch:9200 ] + username: heartbeat_internal + password: ${HEARTBEAT_INTERNAL_PASSWORD} + +## HTTP endpoint for health checking +## https://www.elastic.co/guide/en/beats/heartbeat/current/http-endpoint.html +# + +http: + enabled: true + host: 0.0.0.0 diff --git a/demo/elk-stack/extensions/heartbeat/heartbeat-compose.yml b/demo/elk-stack/extensions/heartbeat/heartbeat-compose.yml new file mode 100644 index 0000000000..47e07084ed --- /dev/null +++ b/demo/elk-stack/extensions/heartbeat/heartbeat-compose.yml @@ -0,0 +1,24 @@ +version: '3.7' + +services: + heartbeat: + build: + context: extensions/heartbeat/ + args: + ELASTIC_VERSION: ${ELASTIC_VERSION} + command: + # Log to stderr. + - -e + # Disable config file permissions checks. Allows mounting + # 'config/heartbeat.yml' even if it's not owned by root. + # see: https://www.elastic.co/guide/en/beats/libbeat/current/config-file-permissions.html + - --strict.perms=false + volumes: + - ./extensions/heartbeat/config/heartbeat.yml:/usr/share/heartbeat/heartbeat.yml:ro,Z + environment: + HEARTBEAT_INTERNAL_PASSWORD: ${HEARTBEAT_INTERNAL_PASSWORD:-} + BEATS_SYSTEM_PASSWORD: ${BEATS_SYSTEM_PASSWORD:-} + networks: + - elk + depends_on: + - elasticsearch diff --git a/demo/elk-stack/extensions/logspout/.dockerignore b/demo/elk-stack/extensions/logspout/.dockerignore new file mode 100644 index 0000000000..37eef9d513 --- /dev/null +++ b/demo/elk-stack/extensions/logspout/.dockerignore @@ -0,0 +1,6 @@ +# Ignore Docker build files +Dockerfile +.dockerignore + +# Ignore OS artifacts +**/.DS_Store diff --git a/demo/elk-stack/extensions/logspout/Dockerfile b/demo/elk-stack/extensions/logspout/Dockerfile new file mode 100644 index 0000000000..9591df53b0 --- /dev/null +++ b/demo/elk-stack/extensions/logspout/Dockerfile @@ -0,0 +1,5 @@ +# uses ONBUILD instructions described here: +# https://github.com/gliderlabs/logspout/tree/master/custom + +FROM gliderlabs/logspout:master +ENV SYSLOG_FORMAT rfc3164 diff --git a/demo/elk-stack/extensions/logspout/README.md b/demo/elk-stack/extensions/logspout/README.md new file mode 100644 index 0000000000..2e34648568 --- /dev/null +++ b/demo/elk-stack/extensions/logspout/README.md @@ -0,0 +1,28 @@ +# Logspout extension + +Logspout collects all Docker logs using the Docker logs API, and forwards them to Logstash without any additional +configuration. + +## Usage + +If you want to include the Logspout extension, run Docker Compose from the root of the repository with an additional +command line argument referencing the `logspout-compose.yml` file: + +```bash +$ docker-compose -f docker-compose.yml -f extensions/logspout/logspout-compose.yml up +``` + +In your Logstash pipeline configuration, enable the `udp` input and set the input codec to `json`: + +```logstash +input { + udp { + port => 50000 + codec => json + } +} +``` + +## Documentation + + diff --git a/demo/elk-stack/extensions/logspout/build.sh b/demo/elk-stack/extensions/logspout/build.sh new file mode 100755 index 0000000000..c3ff938845 --- /dev/null +++ b/demo/elk-stack/extensions/logspout/build.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +# source: https://github.com/gliderlabs/logspout/blob/621524e/custom/build.sh + +set -e +apk add --update go build-base git mercurial ca-certificates +cd /src +go build -ldflags "-X main.Version=$1" -o /bin/logspout +apk del go git mercurial build-base +rm -rf /root/go /var/cache/apk/* + +# backwards compatibility +ln -fs /tmp/docker.sock /var/run/docker.sock diff --git a/demo/elk-stack/extensions/logspout/logspout-compose.yml b/demo/elk-stack/extensions/logspout/logspout-compose.yml new file mode 100644 index 0000000000..8af149df36 --- /dev/null +++ b/demo/elk-stack/extensions/logspout/logspout-compose.yml @@ -0,0 +1,19 @@ +version: '3.7' + +services: + logspout: + build: + context: extensions/logspout + volumes: + - type: bind + source: /var/run/docker.sock + target: /var/run/docker.sock + read_only: true + environment: + ROUTE_URIS: logstash://logstash:50000 + LOGSTASH_TAGS: docker-elk + networks: + - elk + depends_on: + - logstash + restart: on-failure diff --git a/demo/elk-stack/extensions/logspout/modules.go b/demo/elk-stack/extensions/logspout/modules.go new file mode 100644 index 0000000000..f1a2258641 --- /dev/null +++ b/demo/elk-stack/extensions/logspout/modules.go @@ -0,0 +1,10 @@ +package main + +// installs the Logstash adapter for Logspout, and required dependencies +// https://github.com/looplab/logspout-logstash +import ( + _ "github.com/gliderlabs/logspout/healthcheck" + _ "github.com/gliderlabs/logspout/transports/tcp" + _ "github.com/gliderlabs/logspout/transports/udp" + _ "github.com/looplab/logspout-logstash" +) diff --git a/demo/elk-stack/extensions/metricbeat/.dockerignore b/demo/elk-stack/extensions/metricbeat/.dockerignore new file mode 100644 index 0000000000..37eef9d513 --- /dev/null +++ b/demo/elk-stack/extensions/metricbeat/.dockerignore @@ -0,0 +1,6 @@ +# Ignore Docker build files +Dockerfile +.dockerignore + +# Ignore OS artifacts +**/.DS_Store diff --git a/demo/elk-stack/extensions/metricbeat/Dockerfile b/demo/elk-stack/extensions/metricbeat/Dockerfile new file mode 100644 index 0000000000..6d05bf55f2 --- /dev/null +++ b/demo/elk-stack/extensions/metricbeat/Dockerfile @@ -0,0 +1,3 @@ +ARG ELASTIC_VERSION + +FROM docker.elastic.co/beats/metricbeat:${ELASTIC_VERSION} diff --git a/demo/elk-stack/extensions/metricbeat/README.md b/demo/elk-stack/extensions/metricbeat/README.md new file mode 100644 index 0000000000..1da1eaa216 --- /dev/null +++ b/demo/elk-stack/extensions/metricbeat/README.md @@ -0,0 +1,49 @@ +# Metricbeat + +Metricbeat is a lightweight shipper that you can install on your servers to periodically collect metrics from the +operating system and from services running on the server. Metricbeat takes the metrics and statistics that it collects +and ships them to the output that you specify, such as Elasticsearch or Logstash. + +## Usage + +**This extension requires the `metricbeat_internal`, `monitoring_internal` and `beats_system` users to be created and +initialized with a password.** In case you haven't done that during the initial startup of the stack, please refer to +[How to re-execute the setup][setup] to run the setup container again and initialize these users. + +To include Metricbeat in the stack, run Docker Compose from the root of the repository with an additional command line +argument referencing the `metricbeat-compose.yml` file: + +```console +$ docker-compose -f docker-compose.yml -f extensions/metricbeat/metricbeat-compose.yml up +``` + +## Configuring Metricbeat + +The Metricbeat configuration is stored in [`config/metricbeat.yml`](./config/metricbeat.yml). You can modify this file +with the help of the [Configuration reference][metricbeat-config]. + +Any change to the Metricbeat configuration requires a restart of the Metricbeat container: + +```console +$ docker-compose -f docker-compose.yml -f extensions/metricbeat/metricbeat-compose.yml restart metricbeat +``` + +Please refer to the following documentation page for more details about how to configure Metricbeat inside a +Docker container: [Run Metricbeat on Docker][metricbeat-docker]. + +## See also + +[Metricbeat documentation][metricbeat-doc] + +## Screenshots + +![stack-monitoring](https://user-images.githubusercontent.com/3299086/202710574-32a3d419-86ea-4334-b6f7-62d7826df18d.png +"Stack Monitoring") +![host-dashboard](https://user-images.githubusercontent.com/3299086/202710594-0deccf40-3a9a-4e63-8411-2e0d9cc6ad3a.png +"Host Overview Dashboard") + +[metricbeat-config]: https://www.elastic.co/guide/en/beats/metricbeat/current/metricbeat-reference-yml.html +[metricbeat-docker]: https://www.elastic.co/guide/en/beats/metricbeat/current/running-on-docker.html +[metricbeat-doc]: https://www.elastic.co/guide/en/beats/metricbeat/current/index.html + +[setup]: ../../README.md#how-to-re-execute-the-setup diff --git a/demo/elk-stack/extensions/metricbeat/config/metricbeat.yml b/demo/elk-stack/extensions/metricbeat/config/metricbeat.yml new file mode 100644 index 0000000000..1c2b6cb87d --- /dev/null +++ b/demo/elk-stack/extensions/metricbeat/config/metricbeat.yml @@ -0,0 +1,72 @@ +## Metricbeat configuration +## https://github.com/elastic/beats/blob/main/deploy/docker/metricbeat.docker.yml +# + +name: metricbeat + +metricbeat.config: + modules: + path: ${path.config}/modules.d/*.yml + # Reload module configs as they change: + reload.enabled: false + +metricbeat.autodiscover: + providers: + - type: docker + hints.enabled: true + +metricbeat.modules: +- module: elasticsearch + hosts: [ http://elasticsearch:9200 ] + username: monitoring_internal + password: ${MONITORING_INTERNAL_PASSWORD} + xpack.enabled: true + period: 10s + enabled: true +- module: logstash + hosts: [ http://logstash:9600 ] + xpack.enabled: true + period: 10s + enabled: true +- module: kibana + hosts: [ http://kibana:5601 ] + username: monitoring_internal + password: ${MONITORING_INTERNAL_PASSWORD} + xpack.enabled: true + period: 10s + enabled: true +- module: docker + metricsets: + - container + - cpu + - diskio + - healthcheck + - info + #- image + - memory + - network + hosts: [ unix:///var/run/docker.sock ] + period: 10s + enabled: true + +processors: + - add_cloud_metadata: ~ + +monitoring: + enabled: true + elasticsearch: + username: beats_system + password: ${BEATS_SYSTEM_PASSWORD} + +output.elasticsearch: + hosts: [ http://elasticsearch:9200 ] + username: metricbeat_internal + password: ${METRICBEAT_INTERNAL_PASSWORD} + +## HTTP endpoint for health checking +## https://www.elastic.co/guide/en/beats/metricbeat/current/http-endpoint.html +# + +http: + enabled: true + host: 0.0.0.0 diff --git a/demo/elk-stack/extensions/metricbeat/metricbeat-compose.yml b/demo/elk-stack/extensions/metricbeat/metricbeat-compose.yml new file mode 100644 index 0000000000..5b37a66c42 --- /dev/null +++ b/demo/elk-stack/extensions/metricbeat/metricbeat-compose.yml @@ -0,0 +1,47 @@ +version: '3.7' + +services: + metricbeat: + build: + context: extensions/metricbeat/ + args: + ELASTIC_VERSION: ${ELASTIC_VERSION} + # Run as 'root' instead of 'metricbeat' (uid 1000) to allow reading + # 'docker.sock' and the host's filesystem. + user: root + command: + # Log to stderr. + - -e + # Disable config file permissions checks. Allows mounting + # 'config/metricbeat.yml' even if it's not owned by root. + # see: https://www.elastic.co/guide/en/beats/libbeat/current/config-file-permissions.html + - --strict.perms=false + # Mount point of the host’s filesystem. Required to monitor the host + # from within a container. + - --system.hostfs=/hostfs + volumes: + - ./extensions/metricbeat/config/metricbeat.yml:/usr/share/metricbeat/metricbeat.yml:ro,Z + - type: bind + source: / + target: /hostfs + read_only: true + - type: bind + source: /sys/fs/cgroup + target: /hostfs/sys/fs/cgroup + read_only: true + - type: bind + source: /proc + target: /hostfs/proc + read_only: true + - type: bind + source: /var/run/docker.sock + target: /var/run/docker.sock + read_only: true + environment: + METRICBEAT_INTERNAL_PASSWORD: ${METRICBEAT_INTERNAL_PASSWORD:-} + MONITORING_INTERNAL_PASSWORD: ${MONITORING_INTERNAL_PASSWORD:-} + BEATS_SYSTEM_PASSWORD: ${BEATS_SYSTEM_PASSWORD:-} + networks: + - elk + depends_on: + - elasticsearch diff --git a/demo/elk-stack/kibana/.dockerignore b/demo/elk-stack/kibana/.dockerignore new file mode 100644 index 0000000000..37eef9d513 --- /dev/null +++ b/demo/elk-stack/kibana/.dockerignore @@ -0,0 +1,6 @@ +# Ignore Docker build files +Dockerfile +.dockerignore + +# Ignore OS artifacts +**/.DS_Store diff --git a/demo/elk-stack/kibana/Dockerfile b/demo/elk-stack/kibana/Dockerfile new file mode 100644 index 0000000000..9a075bedb9 --- /dev/null +++ b/demo/elk-stack/kibana/Dockerfile @@ -0,0 +1,7 @@ +ARG ELASTIC_VERSION + +# https://www.docker.elastic.co/ +FROM docker.elastic.co/kibana/kibana:${ELASTIC_VERSION} + +# Add your kibana plugins setup here +# Example: RUN kibana-plugin install diff --git a/demo/elk-stack/kibana/config/kibana.yml b/demo/elk-stack/kibana/config/kibana.yml new file mode 100644 index 0000000000..9d4e79ab44 --- /dev/null +++ b/demo/elk-stack/kibana/config/kibana.yml @@ -0,0 +1,94 @@ +--- +## Default Kibana configuration from Kibana base image. +## https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/templates/kibana_yml.template.ts +# +server.name: kibana +server.host: 0.0.0.0 +elasticsearch.hosts: [ http://elasticsearch:9200 ] + +monitoring.ui.container.elasticsearch.enabled: true +monitoring.ui.container.logstash.enabled: true + +## X-Pack security credentials +# +elasticsearch.username: kibana_system +elasticsearch.password: ${KIBANA_SYSTEM_PASSWORD} + +## Encryption keys (optional but highly recommended) +## +## Generate with either +## $ docker container run --rm docker.elastic.co/kibana/kibana:8.6.2 bin/kibana-encryption-keys generate +## $ openssl rand -hex 32 +## +## https://www.elastic.co/guide/en/kibana/current/using-kibana-with-security.html +## https://www.elastic.co/guide/en/kibana/current/kibana-encryption-keys.html +# +#xpack.security.encryptionKey: +#xpack.encryptedSavedObjects.encryptionKey: +#xpack.reporting.encryptionKey: + +## Fleet +## https://www.elastic.co/guide/en/kibana/current/fleet-settings-kb.html +# +xpack.fleet.agents.fleet_server.hosts: [ http://fleet-server:8220 ] + +xpack.fleet.outputs: + - id: fleet-default-output + name: default + type: elasticsearch + hosts: [ http://elasticsearch:9200 ] + is_default: true + is_default_monitoring: true + +xpack.fleet.packages: + - name: fleet_server + version: latest + - name: system + version: latest + - name: elastic_agent + version: latest + - name: apm + version: latest + +xpack.fleet.agentPolicies: + - name: Fleet Server Policy + id: fleet-server-policy + description: Static agent policy for Fleet Server + monitoring_enabled: + - logs + - metrics + package_policies: + - name: fleet_server-1 + package: + name: fleet_server + - name: system-1 + package: + name: system + - name: elastic_agent-1 + package: + name: elastic_agent + - name: Agent Policy APM Server + id: agent-policy-apm-server + description: Static agent policy for the APM Server integration + monitoring_enabled: + - logs + - metrics + package_policies: + - name: system-1 + package: + name: system + - name: elastic_agent-1 + package: + name: elastic_agent + - name: apm-1 + package: + name: apm + # See the APM package manifest for a list of possible inputs. + # https://github.com/elastic/apm-server/blob/v8.5.0/apmpackage/apm/manifest.yml#L41-L168 + inputs: + - type: apm + vars: + - name: host + value: 0.0.0.0:8200 + - name: url + value: http://apm-server:8200 diff --git a/demo/elk-stack/logstash/.dockerignore b/demo/elk-stack/logstash/.dockerignore new file mode 100644 index 0000000000..37eef9d513 --- /dev/null +++ b/demo/elk-stack/logstash/.dockerignore @@ -0,0 +1,6 @@ +# Ignore Docker build files +Dockerfile +.dockerignore + +# Ignore OS artifacts +**/.DS_Store diff --git a/demo/elk-stack/logstash/Dockerfile b/demo/elk-stack/logstash/Dockerfile new file mode 100644 index 0000000000..bde5808d98 --- /dev/null +++ b/demo/elk-stack/logstash/Dockerfile @@ -0,0 +1,7 @@ +ARG ELASTIC_VERSION + +# https://www.docker.elastic.co/ +FROM docker.elastic.co/logstash/logstash:${ELASTIC_VERSION} + +# Add your logstash plugins setup here +# Example: RUN logstash-plugin install logstash-filter-json diff --git a/demo/elk-stack/logstash/config/logstash.yml b/demo/elk-stack/logstash/config/logstash.yml new file mode 100644 index 0000000000..a81b89bc79 --- /dev/null +++ b/demo/elk-stack/logstash/config/logstash.yml @@ -0,0 +1,7 @@ +--- +## Default Logstash configuration from Logstash base image. +## https://github.com/elastic/logstash/blob/main/docker/data/logstash/config/logstash-full.yml +# +http.host: 0.0.0.0 + +node.name: logstash diff --git a/demo/elk-stack/logstash/pipeline/logstash.conf b/demo/elk-stack/logstash/pipeline/logstash.conf new file mode 100644 index 0000000000..4f5006373f --- /dev/null +++ b/demo/elk-stack/logstash/pipeline/logstash.conf @@ -0,0 +1,28 @@ +input { + beats { + port => 5044 + } + + tcp { + port => 50000 + } + + udp { + port => 50000 + codec => json + } + + http { + port => 9700 + } +} + +## Add your filters / logstash plugins configuration here + +output { + elasticsearch { + hosts => "elasticsearch:9200" + user => "logstash_internal" + password => "${LOGSTASH_INTERNAL_PASSWORD}" + } +} diff --git a/demo/elk-stack/setup/.dockerignore b/demo/elk-stack/setup/.dockerignore new file mode 100644 index 0000000000..02f2244078 --- /dev/null +++ b/demo/elk-stack/setup/.dockerignore @@ -0,0 +1,12 @@ +# Ignore Docker build files +Dockerfile +.dockerignore + +# Ignore OS artifacts +**/.DS_Store + +# Ignore Git files +.gitignore + +# Ignore setup state +state/ diff --git a/demo/elk-stack/setup/.gitignore b/demo/elk-stack/setup/.gitignore new file mode 100644 index 0000000000..a27475ad10 --- /dev/null +++ b/demo/elk-stack/setup/.gitignore @@ -0,0 +1 @@ +/state/ diff --git a/demo/elk-stack/setup/Dockerfile b/demo/elk-stack/setup/Dockerfile new file mode 100644 index 0000000000..5365a99d1d --- /dev/null +++ b/demo/elk-stack/setup/Dockerfile @@ -0,0 +1,15 @@ +ARG ELASTIC_VERSION + +# https://www.docker.elastic.co/ +FROM docker.elastic.co/elasticsearch/elasticsearch:${ELASTIC_VERSION} + +USER root + +RUN set -eux; \ + mkdir /state; \ + chmod 0775 /state; \ + chown elasticsearch:root /state + +USER elasticsearch:root + +ENTRYPOINT ["/entrypoint.sh"] diff --git a/demo/elk-stack/setup/entrypoint.sh b/demo/elk-stack/setup/entrypoint.sh new file mode 100755 index 0000000000..ec1e1ff411 --- /dev/null +++ b/demo/elk-stack/setup/entrypoint.sh @@ -0,0 +1,134 @@ +#!/usr/bin/env bash + +set -eu +set -o pipefail + +source "${BASH_SOURCE[0]%/*}"/lib.sh + + +# -------------------------------------------------------- +# Users declarations + +declare -A users_passwords +users_passwords=( + [logstash_internal]="${LOGSTASH_INTERNAL_PASSWORD:-}" + [kibana_system]="${KIBANA_SYSTEM_PASSWORD:-}" + [metricbeat_internal]="${METRICBEAT_INTERNAL_PASSWORD:-}" + [filebeat_internal]="${FILEBEAT_INTERNAL_PASSWORD:-}" + [heartbeat_internal]="${HEARTBEAT_INTERNAL_PASSWORD:-}" + [monitoring_internal]="${MONITORING_INTERNAL_PASSWORD:-}" + [beats_system]="${BEATS_SYSTEM_PASSWORD=:-}" +) + +declare -A users_roles +users_roles=( + [logstash_internal]='logstash_writer' + [metricbeat_internal]='metricbeat_writer' + [filebeat_internal]='filebeat_writer' + [heartbeat_internal]='heartbeat_writer' + [monitoring_internal]='remote_monitoring_collector' +) + +# -------------------------------------------------------- +# Roles declarations + +declare -A roles_files +roles_files=( + [logstash_writer]='logstash_writer.json' + [metricbeat_writer]='metricbeat_writer.json' + [filebeat_writer]='filebeat_writer.json' + [heartbeat_writer]='heartbeat_writer.json' +) + +# -------------------------------------------------------- + + +echo "-------- $(date --rfc-3339=seconds) --------" + +state_file="${BASH_SOURCE[0]%/*}"/state/.done +if [[ -e "$state_file" ]]; then + declare state_birthtime + state_birthtime="$(stat -c '%Y' "$state_file")" + state_birthtime="$(date --rfc-3339=seconds --date="@${state_birthtime}")" + + log "Setup has already run successfully on ${state_birthtime}. Skipping" + exit 0 +fi + +log 'Waiting for availability of Elasticsearch. This can take several minutes.' + +declare -i exit_code=0 +wait_for_elasticsearch || exit_code=$? + +if ((exit_code)); then + case $exit_code in + 6) + suberr 'Could not resolve host. Is Elasticsearch running?' + ;; + 7) + suberr 'Failed to connect to host. Is Elasticsearch healthy?' + ;; + 28) + suberr 'Timeout connecting to host. Is Elasticsearch healthy?' + ;; + *) + suberr "Connection to Elasticsearch failed. Exit code: ${exit_code}" + ;; + esac + + exit $exit_code +fi + +sublog 'Elasticsearch is running' + +log 'Waiting for initialization of built-in users' + +wait_for_builtin_users || exit_code=$? + +if ((exit_code)); then + suberr 'Timed out waiting for condition' + exit $exit_code +fi + +sublog 'Built-in users were initialized' + +for role in "${!roles_files[@]}"; do + log "Role '$role'" + + declare body_file + body_file="${BASH_SOURCE[0]%/*}/roles/${roles_files[$role]:-}" + if [[ ! -f "${body_file:-}" ]]; then + sublog "No role body found at '${body_file}', skipping" + continue + fi + + sublog 'Creating/updating' + ensure_role "$role" "$(<"${body_file}")" +done + +for user in "${!users_passwords[@]}"; do + log "User '$user'" + if [[ -z "${users_passwords[$user]:-}" ]]; then + sublog 'No password defined, skipping' + continue + fi + + declare -i user_exists=0 + user_exists="$(check_user_exists "$user")" + + if ((user_exists)); then + sublog 'User exists, setting password' + set_user_password "$user" "${users_passwords[$user]}" + else + if [[ -z "${users_roles[$user]:-}" ]]; then + suberr ' No role defined, skipping creation' + continue + fi + + sublog 'User does not exist, creating' + create_user "$user" "${users_passwords[$user]}" "${users_roles[$user]}" + fi +done + +mkdir -p "${state_file%/*}" +touch "$state_file" diff --git a/demo/elk-stack/setup/lib.sh b/demo/elk-stack/setup/lib.sh new file mode 100644 index 0000000000..7e635c6a8b --- /dev/null +++ b/demo/elk-stack/setup/lib.sh @@ -0,0 +1,240 @@ +#!/usr/bin/env bash + +# Log a message. +function log { + echo "[+] $1" +} + +# Log a message at a sub-level. +function sublog { + echo " ⠿ $1" +} + +# Log an error. +function err { + echo "[x] $1" >&2 +} + +# Log an error at a sub-level. +function suberr { + echo " ⠍ $1" >&2 +} + +# Poll the 'elasticsearch' service until it responds with HTTP code 200. +function wait_for_elasticsearch { + local elasticsearch_host="${ELASTICSEARCH_HOST:-elasticsearch}" + + local -a args=( '-s' '-D-' '-m15' '-w' '%{http_code}' "http://${elasticsearch_host}:9200/" ) + + if [[ -n "${ELASTIC_PASSWORD:-}" ]]; then + args+=( '-u' "elastic:${ELASTIC_PASSWORD}" ) + fi + + local -i result=1 + local output + + # retry for max 300s (60*5s) + for _ in $(seq 1 60); do + local -i exit_code=0 + output="$(curl "${args[@]}")" || exit_code=$? + + if ((exit_code)); then + result=$exit_code + fi + + if [[ "${output: -3}" -eq 200 ]]; then + result=0 + break + fi + + sleep 5 + done + + if ((result)) && [[ "${output: -3}" -ne 000 ]]; then + echo -e "\n${output::-3}" + fi + + return $result +} + +# Poll the Elasticsearch users API until it returns users. +function wait_for_builtin_users { + local elasticsearch_host="${ELASTICSEARCH_HOST:-elasticsearch}" + + local -a args=( '-s' '-D-' '-m15' "http://${elasticsearch_host}:9200/_security/user?pretty" ) + + if [[ -n "${ELASTIC_PASSWORD:-}" ]]; then + args+=( '-u' "elastic:${ELASTIC_PASSWORD}" ) + fi + + local -i result=1 + + local line + local -i exit_code + local -i num_users + + # retry for max 30s (30*1s) + for _ in $(seq 1 30); do + num_users=0 + + # read exits with a non-zero code if the last read input doesn't end + # with a newline character. The printf without newline that follows the + # curl command ensures that the final input not only contains curl's + # exit code, but causes read to fail so we can capture the return value. + # Ref. https://unix.stackexchange.com/a/176703/152409 + while IFS= read -r line || ! exit_code="$line"; do + if [[ "$line" =~ _reserved.+true ]]; then + (( num_users++ )) + fi + done < <(curl "${args[@]}"; printf '%s' "$?") + + if ((exit_code)); then + result=$exit_code + fi + + # we expect more than just the 'elastic' user in the result + if (( num_users > 1 )); then + result=0 + break + fi + + sleep 1 + done + + return $result +} + +# Verify that the given Elasticsearch user exists. +function check_user_exists { + local username=$1 + + local elasticsearch_host="${ELASTICSEARCH_HOST:-elasticsearch}" + + local -a args=( '-s' '-D-' '-m15' '-w' '%{http_code}' + "http://${elasticsearch_host}:9200/_security/user/${username}" + ) + + if [[ -n "${ELASTIC_PASSWORD:-}" ]]; then + args+=( '-u' "elastic:${ELASTIC_PASSWORD}" ) + fi + + local -i result=1 + local -i exists=0 + local output + + output="$(curl "${args[@]}")" + if [[ "${output: -3}" -eq 200 || "${output: -3}" -eq 404 ]]; then + result=0 + fi + if [[ "${output: -3}" -eq 200 ]]; then + exists=1 + fi + + if ((result)); then + echo -e "\n${output::-3}" + else + echo "$exists" + fi + + return $result +} + +# Set password of a given Elasticsearch user. +function set_user_password { + local username=$1 + local password=$2 + + local elasticsearch_host="${ELASTICSEARCH_HOST:-elasticsearch}" + + local -a args=( '-s' '-D-' '-m15' '-w' '%{http_code}' + "http://${elasticsearch_host}:9200/_security/user/${username}/_password" + '-X' 'POST' + '-H' 'Content-Type: application/json' + '-d' "{\"password\" : \"${password}\"}" + ) + + if [[ -n "${ELASTIC_PASSWORD:-}" ]]; then + args+=( '-u' "elastic:${ELASTIC_PASSWORD}" ) + fi + + local -i result=1 + local output + + output="$(curl "${args[@]}")" + if [[ "${output: -3}" -eq 200 ]]; then + result=0 + fi + + if ((result)); then + echo -e "\n${output::-3}\n" + fi + + return $result +} + +# Create the given Elasticsearch user. +function create_user { + local username=$1 + local password=$2 + local role=$3 + + local elasticsearch_host="${ELASTICSEARCH_HOST:-elasticsearch}" + + local -a args=( '-s' '-D-' '-m15' '-w' '%{http_code}' + "http://${elasticsearch_host}:9200/_security/user/${username}" + '-X' 'POST' + '-H' 'Content-Type: application/json' + '-d' "{\"password\":\"${password}\",\"roles\":[\"${role}\"]}" + ) + + if [[ -n "${ELASTIC_PASSWORD:-}" ]]; then + args+=( '-u' "elastic:${ELASTIC_PASSWORD}" ) + fi + + local -i result=1 + local output + + output="$(curl "${args[@]}")" + if [[ "${output: -3}" -eq 200 ]]; then + result=0 + fi + + if ((result)); then + echo -e "\n${output::-3}\n" + fi + + return $result +} + +# Ensure that the given Elasticsearch role is up-to-date, create it if required. +function ensure_role { + local name=$1 + local body=$2 + + local elasticsearch_host="${ELASTICSEARCH_HOST:-elasticsearch}" + + local -a args=( '-s' '-D-' '-m15' '-w' '%{http_code}' + "http://${elasticsearch_host}:9200/_security/role/${name}" + '-X' 'POST' + '-H' 'Content-Type: application/json' + '-d' "$body" + ) + + if [[ -n "${ELASTIC_PASSWORD:-}" ]]; then + args+=( '-u' "elastic:${ELASTIC_PASSWORD}" ) + fi + + local -i result=1 + local output + + output="$(curl "${args[@]}")" + if [[ "${output: -3}" -eq 200 ]]; then + result=0 + fi + + if ((result)); then + echo -e "\n${output::-3}\n" + fi + + return $result +} diff --git a/demo/elk-stack/setup/roles/filebeat_writer.json b/demo/elk-stack/setup/roles/filebeat_writer.json new file mode 100644 index 0000000000..118614bee8 --- /dev/null +++ b/demo/elk-stack/setup/roles/filebeat_writer.json @@ -0,0 +1,19 @@ +{ + "cluster": [ + "manage_ilm", + "manage_index_templates", + "monitor", + "read_pipeline" + ], + "indices": [ + { + "names": [ + "filebeat-*" + ], + "privileges": [ + "create_doc", + "manage" + ] + } + ] +} diff --git a/demo/elk-stack/setup/roles/heartbeat_writer.json b/demo/elk-stack/setup/roles/heartbeat_writer.json new file mode 100644 index 0000000000..9f64fa86a7 --- /dev/null +++ b/demo/elk-stack/setup/roles/heartbeat_writer.json @@ -0,0 +1,18 @@ +{ + "cluster": [ + "manage_ilm", + "manage_index_templates", + "monitor" + ], + "indices": [ + { + "names": [ + "heartbeat-*" + ], + "privileges": [ + "create_doc", + "manage" + ] + } + ] +} diff --git a/demo/elk-stack/setup/roles/logstash_writer.json b/demo/elk-stack/setup/roles/logstash_writer.json new file mode 100644 index 0000000000..b43861fed9 --- /dev/null +++ b/demo/elk-stack/setup/roles/logstash_writer.json @@ -0,0 +1,33 @@ +{ + "cluster": [ + "manage_index_templates", + "monitor", + "manage_ilm" + ], + "indices": [ + { + "names": [ + "logs-generic-default", + "logstash-*", + "ecs-logstash-*" + ], + "privileges": [ + "write", + "create", + "create_index", + "manage", + "manage_ilm" + ] + }, + { + "names": [ + "logstash", + "ecs-logstash" + ], + "privileges": [ + "write", + "manage" + ] + } + ] +} diff --git a/demo/elk-stack/setup/roles/metricbeat_writer.json b/demo/elk-stack/setup/roles/metricbeat_writer.json new file mode 100644 index 0000000000..279308c625 --- /dev/null +++ b/demo/elk-stack/setup/roles/metricbeat_writer.json @@ -0,0 +1,19 @@ +{ + "cluster": [ + "manage_ilm", + "manage_index_templates", + "monitor" + ], + "indices": [ + { + "names": [ + ".monitoring-*-mb", + "metricbeat-*" + ], + "privileges": [ + "create_doc", + "manage" + ] + } + ] +} diff --git a/demo/multi-demo/Dockerfile.acapy b/demo/multi-demo/Dockerfile.acapy index a8eee30ae0..963a19012f 100644 --- a/demo/multi-demo/Dockerfile.acapy +++ b/demo/multi-demo/Dockerfile.acapy @@ -1,4 +1,4 @@ -FROM bcgovimages/aries-cloudagent:py36-1.16-1_1.0.0-rc0 +FROM local:acapy-081 USER root diff --git a/demo/multi-demo/README.md b/demo/multi-demo/README.md index 90274cb259..2f08e70b16 100644 --- a/demo/multi-demo/README.md +++ b/demo/multi-demo/README.md @@ -38,3 +38,25 @@ This will leave the agent's wallet data, so if you restart the agent it will mai ```bash docker volume rm multi-demo_wallet-db-data ``` + +# Run without NGrok +[Ngrok](https://ngrok.com) provides a tunneling service and a way to provide a public IP to your locally running instance of Aca-Py. There are restrictions with Ngrok most notably regarding inbound connections. + +``` +Too many connections! The tunnel session SESSION has violated the rate-limit policy of THRESHOLD connections per minute by initiating COUNT connections in the last SECONDS seconds. Please decrease your inbound connection volume or upgrade to a paid plan for additional capacity. + +ngrok limits the number of inbound connections to your tunnels. Limits are imposed on connections, not requests. If your HTTP clients use persistent connections aka HTTP keep-alive (most modern ones do), you'll likely never hit this limit. ngrok will return a 429 response to HTTP connections that exceed the rate limit. Connections to TCP and TLS tunnels violating the rate limit will be closed without a response. +``` + +If you do not require external access to your instance, consider turning NGrok off. NGrok tunneling can be disabled by changing the environment variable `ACAPY_AGENT_ACCESS` from "public" to "local". See [docker-compose file](docker-compose.yml). + +``` + environment: + - NGROK_NAME=ngrok-agent + - ACAPY_AGENT_ACCESS=local +``` + + +# ELK Stack / Tracing logging + +Please see [ELK Stack Readme](../elk-stack/README.md) to run the `multi-demo` with tracing enabled and pushing into an ELK Stack. \ No newline at end of file diff --git a/demo/multi-demo/docker-compose.yml b/demo/multi-demo/docker-compose.yml index 80021ab211..d268cfb081 100644 --- a/demo/multi-demo/docker-compose.yml +++ b/demo/multi-demo/docker-compose.yml @@ -1,15 +1,29 @@ # Sample docker-compose to start a local aca-py multitenancy agent -# To start aca-py and the postgres database, just run `docker-compose up` -# To shut down the services run `docker-compose rm` - this will retain the postgres database, so you can change aca-py startup parameters +# To start aca-py and the postgres database, just run `docker compose up` +# To shut down the services run `docker compose rm` - this will retain the postgres database, so you can change aca-py startup parameters # and restart the docker containers without losing your wallet data # If you want to delete your wallet data just run `docker volume ls -q | xargs docker volume rm` +# +# If you want to enable tracing, see elk-stack. Ensure elk-stack is running, uncomment the ACAPY_TRACE environement variables and run `docker compose up` version: "3" + + +networks: + app-network: + name: ${APP_NETWORK_NAME:-appnet} + driver: bridge + elk-network: + name: ${ELK_NETWORK_NAME:-elknet} + driver: bridge + services: ngrok-agent: image: wernight/ngrok ports: - 4067:4040 command: ngrok http multi-agent:8001 --log stdout + networks: + - app-network multi-agent: build: @@ -17,6 +31,15 @@ services: dockerfile: Dockerfile.acapy environment: - NGROK_NAME=ngrok-agent + - ACAPY_AGENT_ACCESS=${ACAPY_AGENT_ACCESS:-local} + - ACAPY_ENDPOINT=http://multi-agent:8001 + # - ACAPY_TRACE=${ACAPY_TRACE:-1} + # - ACAPY_TRACE_TARGET=${ACAPY_TRACE_TARGET:-http://logstash:9700/} + # - ACAPY_TRACE_TAG=${ACAPY_TRACE_TAG:-acapy.events} + # - ACAPY_TRACE_LABEL=${ACAPY_TRACE_LABEL:-multi.agent.trace} + - ACAPY_AUTO_ACCEPT_INVITES=true + - ACAPY_LOG_LEVEL=${LOG_LEVEL:-INFO} + - RUST_LOG=${RUST_LOG:-ERROR} ports: - 8010:8010 - 8001:8001 @@ -30,9 +53,12 @@ services: ] volumes: - ./ngrok-wait.sh:/home/indy/ngrok-wait.sh + networks: + - app-network + - elk-network wallet-db: - image: postgres:12 + image: postgres:15 environment: - POSTGRES_USER=DB_USER - POSTGRES_PASSWORD=DB_PASSWORD @@ -40,6 +66,9 @@ services: - 5433:5432 volumes: - wallet-db-data:/var/lib/pgsql/data + - ./max_conns.sql:/docker-entrypoint-initdb.d/max_conns.sql + networks: + - app-network volumes: wallet-db-data: diff --git a/demo/multi-demo/max_conns.sql b/demo/multi-demo/max_conns.sql new file mode 100644 index 0000000000..c49d0e4b0e --- /dev/null +++ b/demo/multi-demo/max_conns.sql @@ -0,0 +1 @@ +ALTER SYSTEM SET max_connections = 500; \ No newline at end of file diff --git a/demo/multi-demo/ngrok-wait.sh b/demo/multi-demo/ngrok-wait.sh index 6800ec5cd1..6a5b5e7571 100755 --- a/demo/multi-demo/ngrok-wait.sh +++ b/demo/multi-demo/ngrok-wait.sh @@ -2,21 +2,24 @@ # based on code developed by Sovrin: https://github.com/hyperledger/aries-acapy-plugin-toolbox -echo "using ngrok end point [$NGROK_NAME]" +if [[ "${ACAPY_AGENT_ACCESS}" == "public" ]]; then + echo "using ngrok end point [$NGROK_NAME]" -NGROK_ENDPOINT=null -while [ -z "$NGROK_ENDPOINT" ] || [ "$NGROK_ENDPOINT" = "null" ] -do - echo "Fetching end point from ngrok service" - NGROK_ENDPOINT=$(curl --silent $NGROK_NAME:4040/api/tunnels | ./jq -r '.tunnels[] | select(.proto=="https") | .public_url') + NGROK_ENDPOINT=null + while [ -z "$NGROK_ENDPOINT" ] || [ "$NGROK_ENDPOINT" = "null" ] + do + echo "Fetching end point from ngrok service" + NGROK_ENDPOINT=$(curl --silent $NGROK_NAME:4040/api/tunnels | ./jq -r '.tunnels[] | select(.proto=="https") | .public_url') - if [ -z "$NGROK_ENDPOINT" ] || [ "$NGROK_ENDPOINT" = "null" ]; then - echo "ngrok not ready, sleeping 5 seconds...." - sleep 5 - fi -done + if [ -z "$NGROK_ENDPOINT" ] || [ "$NGROK_ENDPOINT" = "null" ]; then + echo "ngrok not ready, sleeping 5 seconds...." + sleep 5 + fi + done + + export ACAPY_ENDPOINT=$NGROK_ENDPOINT +fi -export ACAPY_ENDPOINT=$NGROK_ENDPOINT echo "Starting aca-py agent with endpoint [$ACAPY_ENDPOINT]" @@ -36,12 +39,11 @@ exec aca-py start \ --wallet-name "test_multi" \ --wallet-key "secret_key" \ --wallet-storage-type "postgres_storage" \ - --wallet-storage-config "{\"url\":\"wallet-db:5432\",\"max_connections\":5,\"scheme\":\"MultiWalletSingleTable\"}" \ + --wallet-storage-config "{\"url\":\"wallet-db:5432\",\"max_connections\":20,\"scheme\":\"DatabasePerWallet\"}" \ --wallet-storage-creds "{\"account\":\"DB_USER\",\"password\":\"DB_PASSWORD\",\"admin_account\":\"DB_USER\",\"admin_password\":\"DB_PASSWORD\"}" \ --admin '0.0.0.0' 8010 \ --label "test_multi" \ --admin-insecure-mode \ --multitenant \ --multitenant-admin \ - --jwt-secret "very_secret_secret" \ - --log-level "error" + --jwt-secret "very_secret_secret" From 19bcdda8c8d11b77ab2e2ba0d9be01206c22aeec Mon Sep 17 00:00:00 2001 From: Jason Sherman Date: Tue, 30 May 2023 11:22:55 -0700 Subject: [PATCH 812/872] revert `multi-demo` to previous configuration (image, db config). change postgres to version 14. change playground to also use postgres 14 and change acapy image to not be indy based. Signed-off-by: Jason Sherman --- demo/multi-demo/Dockerfile.acapy | 2 +- demo/multi-demo/docker-compose.yml | 2 +- demo/multi-demo/ngrok-wait.sh | 2 +- demo/playground/Dockerfile.acapy | 2 +- demo/playground/docker-compose.yml | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/demo/multi-demo/Dockerfile.acapy b/demo/multi-demo/Dockerfile.acapy index 963a19012f..a8eee30ae0 100644 --- a/demo/multi-demo/Dockerfile.acapy +++ b/demo/multi-demo/Dockerfile.acapy @@ -1,4 +1,4 @@ -FROM local:acapy-081 +FROM bcgovimages/aries-cloudagent:py36-1.16-1_1.0.0-rc0 USER root diff --git a/demo/multi-demo/docker-compose.yml b/demo/multi-demo/docker-compose.yml index d268cfb081..1d5fbc0a53 100644 --- a/demo/multi-demo/docker-compose.yml +++ b/demo/multi-demo/docker-compose.yml @@ -58,7 +58,7 @@ services: - elk-network wallet-db: - image: postgres:15 + image: postgres:14 environment: - POSTGRES_USER=DB_USER - POSTGRES_PASSWORD=DB_PASSWORD diff --git a/demo/multi-demo/ngrok-wait.sh b/demo/multi-demo/ngrok-wait.sh index 6a5b5e7571..61d253a72e 100755 --- a/demo/multi-demo/ngrok-wait.sh +++ b/demo/multi-demo/ngrok-wait.sh @@ -39,7 +39,7 @@ exec aca-py start \ --wallet-name "test_multi" \ --wallet-key "secret_key" \ --wallet-storage-type "postgres_storage" \ - --wallet-storage-config "{\"url\":\"wallet-db:5432\",\"max_connections\":20,\"scheme\":\"DatabasePerWallet\"}" \ + --wallet-storage-config "{\"url\":\"wallet-db:5432\",\"max_connections\":5,\"scheme\":\"MultiWalletSingleTable\"}" \ --wallet-storage-creds "{\"account\":\"DB_USER\",\"password\":\"DB_PASSWORD\",\"admin_account\":\"DB_USER\",\"admin_password\":\"DB_PASSWORD\"}" \ --admin '0.0.0.0' 8010 \ --label "test_multi" \ diff --git a/demo/playground/Dockerfile.acapy b/demo/playground/Dockerfile.acapy index c9d5b50f5d..21b804aa0f 100644 --- a/demo/playground/Dockerfile.acapy +++ b/demo/playground/Dockerfile.acapy @@ -1,4 +1,4 @@ -FROM ghcr.io/hyperledger/aries-cloudagent-python:py3.9-indy-1.16.0-0.8.1 +FROM ghcr.io/hyperledger/aries-cloudagent-python:py3.9-0.8.1 USER root diff --git a/demo/playground/docker-compose.yml b/demo/playground/docker-compose.yml index d74688df62..da74281568 100644 --- a/demo/playground/docker-compose.yml +++ b/demo/playground/docker-compose.yml @@ -234,7 +234,7 @@ services: - ${MULTI_AGENT_HTTP_IN_PORT}:${MULTI_AGENT_HTTP_IN_PORT} networks: - app-network - - elk-network + # - elk-network healthcheck: test: /bin/bash -c " Date: Fri, 26 May 2023 14:46:58 +0200 Subject: [PATCH 813/872] fix: route multitenant connectionless oob invitation Signed-off-by: Timo Glastra --- aries_cloudagent/multitenant/base.py | 2 +- aries_cloudagent/protocols/connections/v1_0/manager.py | 2 +- .../protocols/coordinate_mediation/v1_0/route_manager.py | 9 ++++++++- .../v1_0/tests/test_route_manager.py | 8 ++++++++ aries_cloudagent/protocols/out_of_band/v1_0/manager.py | 9 +++++++++ aries_cloudagent/wallet/routes.py | 2 +- 6 files changed, 28 insertions(+), 4 deletions(-) diff --git a/aries_cloudagent/multitenant/base.py b/aries_cloudagent/multitenant/base.py index 9e242d3351..2afec03b00 100644 --- a/aries_cloudagent/multitenant/base.py +++ b/aries_cloudagent/multitenant/base.py @@ -199,7 +199,7 @@ async def create_wallet( public_did_info = await wallet.get_public_did() if public_did_info: - await profile.inject(RouteManager).route_public_did( + await profile.inject(RouteManager).route_verkey( profile, public_did_info.verkey ) except Exception: diff --git a/aries_cloudagent/protocols/connections/v1_0/manager.py b/aries_cloudagent/protocols/connections/v1_0/manager.py index 8487819a85..a1514388f3 100644 --- a/aries_cloudagent/protocols/connections/v1_0/manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/manager.py @@ -196,7 +196,7 @@ async def create_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) + await self._route_manager.route_verkey(self.profile, public_did.verkey) else: # Create connection record diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py index 0acf22e30b..645ca45adf 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py @@ -197,10 +197,17 @@ async def route_invitation( raise ValueError("Expected connection to have invitation_key") - async def route_public_did(self, profile: Profile, verkey: str): + async def route_verkey(self, profile: Profile, verkey: str): """Establish routing for a public DID.""" return await self._route_for_key(profile, verkey, skip_if_exists=True) + async def route_public_did(self, profile: Profile, verkey: str): + """Establish routing for a public DID. + + [DEPRECATED] Establish routing for a public DID. Use route_verkey() instead. + """ + return await self._route_for_key(profile, verkey, skip_if_exists=True) + async def route_static( self, profile: Profile, diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_route_manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_route_manager.py index fd8eedc05e..f64ecd0cfe 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_route_manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_route_manager.py @@ -394,6 +394,14 @@ async def test_route_public_did(profile: Profile, route_manager: RouteManager): ) +@pytest.mark.asyncio +async def test_route_verkey(profile: Profile, route_manager: RouteManager): + await route_manager.route_verkey(profile, "test-verkey") + route_manager._route_for_key.assert_called_once_with( + profile, "test-verkey", skip_if_exists=True + ) + + @pytest.mark.asyncio async def test_route_static( profile: Profile, route_manager: RouteManager, conn_record: ConnRecord diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py index 58d46431f6..4cf530a0ec 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py @@ -322,6 +322,10 @@ async def create_invitation( endpoint=my_endpoint, routing_keys=routing_keys, ).serialize() + # Need to make sure the created key is routed by the base wallet + await self._route_manager.route_verkey( + self.profile, connection_key.verkey + ) routing_keys = [ key @@ -543,6 +547,11 @@ async def receive_invitation( endpoint=self.profile.settings.get("default_endpoint"), routing_keys=[], ).serialize() + + # Need to make sure the created key is routed by the base wallet + await self._route_manager.route_verkey( + self.profile, connection_key.verkey + ) await oob_record.save(session) await self._process_request_attach(oob_record) diff --git a/aries_cloudagent/wallet/routes.py b/aries_cloudagent/wallet/routes.py index 462192d03e..d1f2a4efed 100644 --- a/aries_cloudagent/wallet/routes.py +++ b/aries_cloudagent/wallet/routes.py @@ -632,7 +632,7 @@ async def promote_wallet_public_did( # Route the public DID route_manager = profile.inject(RouteManager) - await route_manager.route_public_did(profile, info.verkey) + await route_manager.route_verkey(profile, info.verkey) return info, attrib_def From e8fff93c9b75b2ba61fd68b1eb6f3a820cce5d58 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sat, 27 May 2023 22:52:33 +0200 Subject: [PATCH 814/872] test: fix tests Signed-off-by: Timo Glastra --- aries_cloudagent/multitenant/tests/test_base.py | 8 ++++---- .../protocols/connections/v1_0/tests/test_manager.py | 4 ++-- aries_cloudagent/wallet/tests/test_routes.py | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/aries_cloudagent/multitenant/tests/test_base.py b/aries_cloudagent/multitenant/tests/test_base.py index c302b44813..dd1c5fba31 100644 --- a/aries_cloudagent/multitenant/tests/test_base.py +++ b/aries_cloudagent/multitenant/tests/test_base.py @@ -210,7 +210,7 @@ async def test_create_wallet_fails_if_wallet_name_exists(self): async def test_create_wallet_saves_wallet_record_creates_profile(self): mock_route_manager = async_mock.MagicMock() - mock_route_manager.route_public_did = async_mock.CoroutineMock() + mock_route_manager.route_verkey = async_mock.CoroutineMock() self.context.injector.bind_instance(RouteManager, mock_route_manager) with async_mock.patch.object( @@ -232,7 +232,7 @@ async def test_create_wallet_saves_wallet_record_creates_profile(self): {"wallet.key": "test_key"}, provision=True, ) - mock_route_manager.route_public_did.assert_not_called() + mock_route_manager.route_verkey.assert_not_called() assert isinstance(wallet_record, WalletRecord) assert wallet_record.wallet_name == "test_wallet" assert wallet_record.key_management_mode == WalletRecord.MODE_MANAGED @@ -248,7 +248,7 @@ async def test_create_wallet_adds_wallet_route(self): ) mock_route_manager = async_mock.MagicMock() - mock_route_manager.route_public_did = async_mock.CoroutineMock() + mock_route_manager.route_verkey = async_mock.CoroutineMock() with async_mock.patch.object( WalletRecord, "save" @@ -267,7 +267,7 @@ async def test_create_wallet_adds_wallet_route(self): WalletRecord.MODE_MANAGED, ) - mock_route_manager.route_public_did.assert_called_once_with( + mock_route_manager.route_verkey.assert_called_once_with( get_wallet_profile.return_value, did_info.verkey ) diff --git a/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py index fb4105def5..c54c75203e 100644 --- a/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py @@ -146,7 +146,7 @@ async def test_create_invitation_non_multi_use_invitation_fails_on_reuse(self): async def test_create_invitation_public(self): self.context.update_settings({"public_invites": True}) - self.route_manager.route_public_did = async_mock.CoroutineMock() + self.route_manager.route_verkey = async_mock.CoroutineMock() with async_mock.patch.object( InMemoryWallet, "get_public_did", autospec=True ) as mock_wallet_get_public_did: @@ -163,7 +163,7 @@ async def test_create_invitation_public(self): assert connect_record assert connect_invite.did.endswith(self.test_did) - self.route_manager.route_public_did.assert_called_once_with( + self.route_manager.route_verkey.assert_called_once_with( self.profile, self.test_verkey ) diff --git a/aries_cloudagent/wallet/tests/test_routes.py b/aries_cloudagent/wallet/tests/test_routes.py index 051380c7f8..a6dc9e6f88 100644 --- a/aries_cloudagent/wallet/tests/test_routes.py +++ b/aries_cloudagent/wallet/tests/test_routes.py @@ -427,7 +427,7 @@ async def test_set_public_did(self): self.profile.context.injector.bind_instance(BaseLedger, ledger) mock_route_manager = async_mock.MagicMock() - mock_route_manager.route_public_did = async_mock.AsyncMock() + mock_route_manager.route_verkey = async_mock.AsyncMock() mock_route_manager.mediation_record_if_id = async_mock.AsyncMock() mock_route_manager.__aenter__ = async_mock.AsyncMock( return_value=mock_route_manager @@ -587,7 +587,7 @@ async def test_set_public_did_update_endpoint(self): self.profile.context.injector.bind_instance(BaseLedger, ledger) mock_route_manager = async_mock.MagicMock() - mock_route_manager.route_public_did = async_mock.AsyncMock() + mock_route_manager.route_verkey = async_mock.AsyncMock() mock_route_manager.mediation_record_if_id = async_mock.AsyncMock() mock_route_manager.__aenter__ = async_mock.AsyncMock( return_value=mock_route_manager @@ -633,7 +633,7 @@ async def test_set_public_did_update_endpoint_use_default_update_in_wallet(self) self.profile.context.injector.bind_instance(BaseLedger, ledger) mock_route_manager = async_mock.MagicMock() - mock_route_manager.route_public_did = async_mock.AsyncMock() + mock_route_manager.route_verkey = async_mock.AsyncMock() mock_route_manager.mediation_record_if_id = async_mock.AsyncMock( return_value=None ) From 013fb1671bab8896dc1a093157e8d7ee0bb6f271 Mon Sep 17 00:00:00 2001 From: Jason Sherman Date: Fri, 26 May 2023 10:20:35 -0700 Subject: [PATCH 815/872] ./run_demo performance -c 1 --mediation --timing --trace-log Bug with prompt_toolkit and trace logging. Could only bump up the prompt_toolkit library so much since demos run on Python 3.6. However, I did attempt on 3.9 with the latest version of prompt_toolkit and setting an env var (see https://github.com/prompt-toolkit/python-prompt-toolkit/pull/898). This still did not correct the issue, so reverted to existing code and just handled the exception(s). The output of the trace log is being written to the console, my limited understanding of this would be that the trace log works, but also triggers the prompt_toolkit to do the same work and gets into conflict. Signed-off-by: Jason Sherman --- demo/runners/support/agent.py | 13 ++++++++++++- demo/runners/support/utils.py | 7 ++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/demo/runners/support/agent.py b/demo/runners/support/agent.py index 22995ef6b8..06d1765eb0 100644 --- a/demo/runners/support/agent.py +++ b/demo/runners/support/agent.py @@ -663,7 +663,18 @@ def handle_output(self, *output, source: str = None, **kwargs): color = self.color or "fg:ansiblue" else: color = None - log_msg(*output, color=color, prefix=self.prefix_str, end=end, **kwargs) + try: + log_msg(*output, color=color, prefix=self.prefix_str, end=end, **kwargs) + except AssertionError as e: + if self.trace_enabled and self.trace_target == "log": + # when tracing to a log file, + # we hit an issue with the underlying prompt_toolkit. + # it attempts to output what is written by the log and can't find the + # correct terminal and throws an error. The trace log record does show + # in the terminal, so let's just ignore this error. + pass + else: + raise e def log(self, *msg, **kwargs): self.handle_output(*msg, **kwargs) diff --git a/demo/runners/support/utils.py b/demo/runners/support/utils.py index 63982d4437..0056686fbf 100644 --- a/demo/runners/support/utils.py +++ b/demo/runners/support/utils.py @@ -113,7 +113,12 @@ def output_reader(handle, callback, *args, **kwargs): for line in iter(handle.readline, b""): if not line: break - run_in_terminal(functools.partial(callback, line, *args)) + try: + run_in_terminal(functools.partial(callback, line, *args)) + except AssertionError as e: + # see comment in DemoAgent.handle_output + # trace log and prompt_toolkit do not get along... + pass def log_msg(*msg, color="fg:ansimagenta", **kwargs): From 5dbc80174d59b71dcf49b27c9029c94392045750 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Tue, 16 May 2023 07:18:21 -0700 Subject: [PATCH 816/872] utility function + template impl Signed-off-by: Shaanjot Gill --- aries_cloudagent/config/logging.py | 35 ++++++++++ aries_cloudagent/config/tests/test_logging.py | 19 ++++++ aries_cloudagent/connections/base_manager.py | 5 +- aries_cloudagent/core/dispatcher.py | 16 +++-- aries_cloudagent/multitenant/admin/routes.py | 67 +++++++++++++++++++ 5 files changed, 135 insertions(+), 7 deletions(-) diff --git a/aries_cloudagent/config/logging.py b/aries_cloudagent/config/logging.py index 4bb03248b8..f2436fecdc 100644 --- a/aries_cloudagent/config/logging.py +++ b/aries_cloudagent/config/logging.py @@ -1,5 +1,6 @@ """Utilities related to logging.""" +import sys import logging from io import TextIOWrapper from logging.config import fileConfig @@ -9,9 +10,43 @@ from ..version import __version__ from .banner import Banner +from .base import BaseSettings DEFAULT_LOGGING_CONFIG_PATH = "aries_cloudagent.config:default_logging_config.ini" +LOG_FORMAT_FILE_ALIAS = logging.Formatter( + "%(asctime)s [%(logger_alias)s] %(name)s %(levelname)s %(message)s" +) +LOG_FORMAT_FILE_NO_ALIAS = logging.Formatter( + "%(asctime)s %(name)s %(levelname)s %(message)s" +) +LOG_FORMAT_STREAM = logging.Formatter("%(asctime)s %(name)s %(levelname)s %(message)s") + + +def get_logger_with_handlers( + settings: BaseSettings, logger: logging.Logger +) -> logging.Logger: + if settings.get("log.file"): + file_path = settings.get("log.file") + logger_alias = settings.get("log.alias") + file_handler = logging.FileHandler(file_path) + if logger_alias: + file_handler.setFormatter(LOG_FORMAT_FILE_ALIAS) + else: + file_handler.setFormatter(LOG_FORMAT_FILE_NO_ALIAS) + logger.addHandler(file_handler) + std_out_handler = logging.StreamHandler(sys.stdout) + std_out_handler.setFormatter(LOG_FORMAT_STREAM) + logger.addHandler(std_out_handler) + if logger_alias: + logger = logging.LoggerAdapter(logger, {"logger_alias": logger_alias}) + # logger_level = ( + # (settings.get("log.level")).upper() + # if settings.get("log.level") + # else logging.INFO + # ) + # logger.setLevel(logger_level) + return logger def load_resource(path: str, encoding: str = None) -> TextIO: diff --git a/aries_cloudagent/config/tests/test_logging.py b/aries_cloudagent/config/tests/test_logging.py index 2a15cdfd21..f8c7b239d1 100644 --- a/aries_cloudagent/config/tests/test_logging.py +++ b/aries_cloudagent/config/tests/test_logging.py @@ -1,4 +1,5 @@ import contextlib +import logging from io import StringIO @@ -7,6 +8,8 @@ from .. import logging as test_module +from ...core.in_memory import InMemoryProfile + class TestLoggingConfigurator: agent_label_arg_value = "Aries Cloud Agent" @@ -92,3 +95,19 @@ def test_load_resource(self): test_module.pkg_resources, "resource_stream", async_mock.MagicMock() ) as mock_res_stream: test_module.load_resource("abc:def", encoding=None) + + def test_get_logger_with_handlers(self): + profile = InMemoryProfile.test_profile() + profile.settings["log.file"] = "test_file.log" + logger = logging.getLogger(__name__) + logger = test_module.get_logger_with_handlers( + settings=profile.settings, + logger=logger, + ) + assert logger + profile.settings["log.alias"] = "tenant_id_123" + logger = test_module.get_logger_with_handlers( + settings=profile.settings, + logger=logger, + ) + assert logger diff --git a/aries_cloudagent/connections/base_manager.py b/aries_cloudagent/connections/base_manager.py index 481c471218..56f261b1ae 100644 --- a/aries_cloudagent/connections/base_manager.py +++ b/aries_cloudagent/connections/base_manager.py @@ -15,6 +15,7 @@ import pydid from pydid.verification_method import Ed25519VerificationKey2018, JsonWebKey2020 +from ..config.logging import get_logger_with_handlers from ..core.error import BaseError from ..core.profile import Profile from ..did.did_key import DIDKey @@ -57,9 +58,11 @@ def __init__(self, profile: Profile): Args: session: The profile session for this presentation """ - self._logger = logging.getLogger(__name__) self._profile = profile self._route_manager = profile.inject(RouteManager) + self._logger = get_logger_with_handlers( + settings=self._profile.settings, logger=logging.getLogger(__name__) + ) async def create_did_document( self, diff --git a/aries_cloudagent/core/dispatcher.py b/aries_cloudagent/core/dispatcher.py index 062a33f3a1..96134f1eec 100644 --- a/aries_cloudagent/core/dispatcher.py +++ b/aries_cloudagent/core/dispatcher.py @@ -16,6 +16,7 @@ from aiohttp.web import HTTPException from ..connections.models.conn_record import ConnRecord +from ..config.logging import get_logger_with_handlers from ..core.profile import Profile from ..messaging.agent_message import AgentMessage from ..messaging.base_message import BaseMessage @@ -43,8 +44,6 @@ # WARNING_VERSION_NOT_SUPPORTED, ) -LOGGER = logging.getLogger(__name__) - class ProblemReportParseError(MessageParseError): """Error to raise on failure to parse problem-report message.""" @@ -63,6 +62,9 @@ def __init__(self, profile: Profile): self.collector: Collector = None self.profile = profile self.task_queue: TaskQueue = None + self._logger = get_logger_with_handlers( + settings=self.profile.settings, logger=logging.getLogger(__name__) + ) async def setup(self): """Perform async instance setup.""" @@ -88,7 +90,7 @@ def log_task(self, task: CompletedTask): """Log a completed task using the stats collector.""" if task.exc_info and not issubclass(task.exc_info[0], HTTPException): # skip errors intentionally returned to HTTP clients - LOGGER.exception( + self._logger.exception( "Handler error: %s", task.ident or "", exc_info=task.exc_info ) if self.collector: @@ -158,7 +160,9 @@ async def handle_message( except ProblemReportParseError: pass # avoid problem report recursion except MessageParseError as e: - LOGGER.error(f"Message parsing failed: {str(e)}, sending problem report") + self._logger.error( + f"Message parsing failed: {str(e)}, sending problem report" + ) error_result = ProblemReport( description={ "en": str(e), @@ -170,7 +174,7 @@ async def handle_message( # if warning: # warning_message_type = inbound_message.payload.get("@type") # if warning == WARNING_DEGRADED_FEATURES: - # LOGGER.error( + # self._logger.error( # f"Sending {WARNING_DEGRADED_FEATURES} problem report, " # "message type received with a minor version at or higher" # " than protocol minimum supported and current minor version " @@ -187,7 +191,7 @@ async def handle_message( # } # ) # elif warning == WARNING_VERSION_MISMATCH: - # LOGGER.error( + # self._logger.error( # f"Sending {WARNING_VERSION_MISMATCH} problem report, message " # "type received with a minor version higher than current minor " # f"version for message_type {warning_message_type}" diff --git a/aries_cloudagent/multitenant/admin/routes.py b/aries_cloudagent/multitenant/admin/routes.py index e95bac1f59..10dd7b30ba 100644 --- a/aries_cloudagent/multitenant/admin/routes.py +++ b/aries_cloudagent/multitenant/admin/routes.py @@ -23,6 +23,33 @@ from ..error import WalletKeyMissingError +ACAPY_LIFECYCLE_CONFIG_FLAG_MAP = { + "ACAPY_LOG_LEVEL": "log.level", + "ACAPY_LOG_ALIAS": "log.alias", + "ACAPY_INVITE_PUBLIC": "debug.invite_public", + "ACAPY_PUBLIC_INVITES": "public_invites", + "ACAPY_AUTO_ACCEPT_INVITES": "debug.auto_accept_invites", + "ACAPY_AUTO_ACCEPT_REQUESTS": "debug.auto_accept_requests", + "ACAPY_AUTO_PING_CONNECTION": "auto_ping_connection", + "ACAPY_MONITOR_PING": "debug.monitor_ping", + "ACAPY_AUTO_RESPOND_MESSAGES": "debug.auto_respond_messages", + "ACAPY_AUTO_RESPOND_CREDENTIAL_OFFER": "debug.auto_resopnd_credential_offer", + "ACAPY_AUTO_RESPOND_CREDENTIAL_REQUEST": "debug.auto_respond_credential_request", + "ACAPY_AUTO_VERIFY_PRESENTATION": "debug.auto_verify_presentation", + "ACAPY_NOTIFY_REVOCATION": "revocation.notify", + "ACAPY_AUTO_REQUEST_ENDORSEMENT": "endorser.auto_request", + "ACAPY_AUTO_WRITE_TRANSACTIONS": "endorser.auto_write", + "ACAPY_CREATE_REVOCATION_TRANSACTIONS": "endorser.auto_create_rev_reg", + "ACAPY_ENDORSER_ROLE": "endorser.protocol_role", +} + +ACAPY_ENDORSER_FLGAS_DEPENDENT_ON_AUTHOR_ROLE = [ + "ACAPY_AUTO_REQUEST_ENDORSEMENT", + "ACAPY_AUTO_WRITE_TRANSACTIONS", + "ACAPY_CREATE_REVOCATION_TRANSACTIONS", +] + + def format_wallet_record(wallet_record: WalletRecord): """Serialize a WalletRecord object.""" @@ -35,6 +62,29 @@ def format_wallet_record(wallet_record: WalletRecord): return wallet_info +def get_extra_settings_dict_per_tenant(tenant_settings: dict) -> dict: + """Get per tenant settings to be applied when creating wallet.""" + + endorser_role_flag = tenant_settings.get("ACAPY_ENDORSER_ROLE") + extra_settings = {} + if endorser_role_flag == "author": + extra_settings["endorser.author"] = True + elif endorser_role_flag == "endorser": + extra_settings["endorser.endorser"] = True + for flag in tenant_settings.keys(): + if ( + flag in ACAPY_ENDORSER_FLGAS_DEPENDENT_ON_AUTHOR_ROLE + and endorser_role_flag != "author" + ): + # These flags require endorser role as author, if not set as author then + # this setting will be ignored. + continue + if flag != "ACAPY_ENDORSER_ROLE": + map_flag = ACAPY_LIFECYCLE_CONFIG_FLAG_MAP[flag] + extra_settings[map_flag] = tenant_settings[flag] + return extra_settings + + class MultitenantModuleResponseSchema(OpenAPISchema): """Response schema for multitenant module.""" @@ -56,6 +106,12 @@ class CreateWalletRequestSchema(OpenAPISchema): description="Master key used for key derivation.", example="MySecretKey123" ) + extra_settings = fields.Dict( + keys=fields.Str(description="Agent Config Flag"), + values=fields.Str(description="Parameter"), + allow_none=True, + ) + wallet_key_derivation = fields.Str( description="Key derivation", required=False, @@ -142,6 +198,11 @@ class UpdateWalletRequestSchema(OpenAPISchema): default="default", validate=validate.OneOf(["default", "both", "base"]), ) + extra_settings = fields.Dict( + keys=fields.Str(description="Agent Config Flag"), + values=fields.Str(description="Parameter"), + allow_none=True, + ) wallet_webhook_urls = fields.List( fields.Str( description="Optional webhook URL to receive webhook messages", @@ -294,6 +355,7 @@ async def wallet_create(request: web.BaseRequest): wallet_key = body.get("wallet_key") wallet_webhook_urls = body.get("wallet_webhook_urls") or [] wallet_dispatch_type = body.get("wallet_dispatch_type") or "default" + extra_settings = body.get("extra_settings") or {} # If no webhooks specified, then dispatch only to base webhook targets if wallet_webhook_urls == []: wallet_dispatch_type = "base" @@ -305,6 +367,8 @@ async def wallet_create(request: web.BaseRequest): "wallet.webhook_urls": wallet_webhook_urls, "wallet.dispatch_type": wallet_dispatch_type, } + extra_subwallet_setting = get_extra_settings_dict_per_tenant(extra_settings) + settings.update(extra_subwallet_setting) label = body.get("label") image_url = body.get("image_url") @@ -354,6 +418,7 @@ async def wallet_update(request: web.BaseRequest): wallet_dispatch_type = body.get("wallet_dispatch_type") label = body.get("label") image_url = body.get("image_url") + extra_settings = body.get("extra_settings") or {} if all( v is None for v in (wallet_webhook_urls, wallet_dispatch_type, label, image_url) @@ -376,6 +441,8 @@ async def wallet_update(request: web.BaseRequest): settings["default_label"] = label if image_url is not None: settings["image_url"] = image_url + extra_subwallet_setting = get_extra_settings_dict_per_tenant(extra_settings) + settings.update(extra_subwallet_setting) try: multitenant_mgr = context.profile.inject(BaseMultitenantManager) From 64647040ce36b3dd5005e19c72e8085896aee79a Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Wed, 17 May 2023 09:27:57 -0700 Subject: [PATCH 817/872] logging utility updates Signed-off-by: Shaanjot Gill --- aries_cloudagent/config/logging.py | 53 +++++++++++++++---- aries_cloudagent/config/tests/test_logging.py | 15 ++++++ aries_cloudagent/connections/base_manager.py | 9 ++-- aries_cloudagent/core/dispatcher.py | 9 ++-- aries_cloudagent/core/oob_processor.py | 46 ++++++++++------ 5 files changed, 101 insertions(+), 31 deletions(-) diff --git a/aries_cloudagent/config/logging.py b/aries_cloudagent/config/logging.py index f2436fecdc..dc9a46177d 100644 --- a/aries_cloudagent/config/logging.py +++ b/aries_cloudagent/config/logging.py @@ -8,25 +8,58 @@ import pkg_resources +from ..core.profile import Profile from ..version import __version__ + from .banner import Banner from .base import BaseSettings DEFAULT_LOGGING_CONFIG_PATH = "aries_cloudagent.config:default_logging_config.ini" LOG_FORMAT_FILE_ALIAS = logging.Formatter( - "%(asctime)s [%(logger_alias)s] %(name)s %(levelname)s %(message)s" + "%(asctime)s [%(logger_alias)s] %(levelname)s %(filename)s %(lineno)d %(message)s" ) LOG_FORMAT_FILE_NO_ALIAS = logging.Formatter( - "%(asctime)s %(name)s %(levelname)s %(message)s" + "%(asctime)s %(levelname)s %(filename)s %(lineno)d %(message)s" +) +LOG_FORMAT_STREAM = logging.Formatter( + "%(asctime)s %(levelname)s %(filename)s %(lineno)d %(message)s" ) -LOG_FORMAT_STREAM = logging.Formatter("%(asctime)s %(name)s %(levelname)s %(message)s") + + +def clear_prev_handlers(logger: logging.Logger) -> logging.Logger: + """Remove all handler classes associated with logger instance.""" + iter_count = 0 + num_handlers = len(logger.handlers) + while iter_count < num_handlers: + logger.removeHandler(logger.handlers[0]) + iter_count = iter_count + 1 + return logger + + +def get_logger_inst(profile: Profile, logger_name) -> logging.Logger: + """Return a logger instance with provided name and handlers.""" + logger = None + if profile.settings.get("log.alias"): + logger = get_logger_with_handlers( + settings=profile.settings, + logger=logging.getLogger(f"{logger_name}_{profile.settings['log.alias']}"), + ) + else: + logger = get_logger_with_handlers( + settings=profile.settings, logger=logging.getLogger(logger_name) + ) + return logger def get_logger_with_handlers( settings: BaseSettings, logger: logging.Logger ) -> logging.Logger: + """Return logger instance with necessary handlers if required.""" if settings.get("log.file"): + # Clear handlers set previously for this logger instance + logger = clear_prev_handlers(logger) + # log file handler file_path = settings.get("log.file") logger_alias = settings.get("log.alias") file_handler = logging.FileHandler(file_path) @@ -35,17 +68,19 @@ def get_logger_with_handlers( else: file_handler.setFormatter(LOG_FORMAT_FILE_NO_ALIAS) logger.addHandler(file_handler) + # stream console handler std_out_handler = logging.StreamHandler(sys.stdout) std_out_handler.setFormatter(LOG_FORMAT_STREAM) logger.addHandler(std_out_handler) if logger_alias: logger = logging.LoggerAdapter(logger, {"logger_alias": logger_alias}) - # logger_level = ( - # (settings.get("log.level")).upper() - # if settings.get("log.level") - # else logging.INFO - # ) - # logger.setLevel(logger_level) + # set log level + logger_level = ( + (settings.get("log.level")).upper() + if settings.get("log.level") + else logging.INFO + ) + logger.setLevel(logger_level) return logger diff --git a/aries_cloudagent/config/tests/test_logging.py b/aries_cloudagent/config/tests/test_logging.py index f8c7b239d1..e88aff0a21 100644 --- a/aries_cloudagent/config/tests/test_logging.py +++ b/aries_cloudagent/config/tests/test_logging.py @@ -111,3 +111,18 @@ def test_get_logger_with_handlers(self): logger=logger, ) assert logger + + def test_get_logger_inst(self): + profile = InMemoryProfile.test_profile() + profile.settings["log.file"] = "test_file.log" + logger = test_module.get_logger_inst( + profile=profile, + logger_name=__name__, + ) + assert logger + profile.settings["log.alias"] = "tenant_id_123" + logger = test_module.get_logger_inst( + profile=profile, + logger_name=__name__, + ) + assert logger diff --git a/aries_cloudagent/connections/base_manager.py b/aries_cloudagent/connections/base_manager.py index 56f261b1ae..016f4ce0dd 100644 --- a/aries_cloudagent/connections/base_manager.py +++ b/aries_cloudagent/connections/base_manager.py @@ -15,7 +15,7 @@ import pydid from pydid.verification_method import Ed25519VerificationKey2018, JsonWebKey2020 -from ..config.logging import get_logger_with_handlers +from ..config.logging import get_logger_inst from ..core.error import BaseError from ..core.profile import Profile from ..did.did_key import DIDKey @@ -40,6 +40,8 @@ from .models.diddoc import DIDDoc, PublicKey, PublicKeyType, Service from ..wallet.util import bytes_to_b58, b64_to_bytes +LOGGER_NAME = __name__ + class BaseConnectionManagerError(BaseError): """BaseConnectionManager error.""" @@ -60,8 +62,9 @@ def __init__(self, profile: Profile): """ self._profile = profile self._route_manager = profile.inject(RouteManager) - self._logger = get_logger_with_handlers( - settings=self._profile.settings, logger=logging.getLogger(__name__) + self._logger = get_logger_inst( + profile=self._profile, + logger_name=LOGGER_NAME, ) async def create_did_document( diff --git a/aries_cloudagent/core/dispatcher.py b/aries_cloudagent/core/dispatcher.py index 96134f1eec..2eee381820 100644 --- a/aries_cloudagent/core/dispatcher.py +++ b/aries_cloudagent/core/dispatcher.py @@ -16,7 +16,7 @@ from aiohttp.web import HTTPException from ..connections.models.conn_record import ConnRecord -from ..config.logging import get_logger_with_handlers +from ..config.logging import get_logger_inst from ..core.profile import Profile from ..messaging.agent_message import AgentMessage from ..messaging.base_message import BaseMessage @@ -44,6 +44,8 @@ # WARNING_VERSION_NOT_SUPPORTED, ) +LOGGER_NAME = __name__ + class ProblemReportParseError(MessageParseError): """Error to raise on failure to parse problem-report message.""" @@ -62,8 +64,9 @@ def __init__(self, profile: Profile): self.collector: Collector = None self.profile = profile self.task_queue: TaskQueue = None - self._logger = get_logger_with_handlers( - settings=self.profile.settings, logger=logging.getLogger(__name__) + self._logger = get_logger_inst( + profile=self.profile, + logger_name=LOGGER_NAME, ) async def setup(self): diff --git a/aries_cloudagent/core/oob_processor.py b/aries_cloudagent/core/oob_processor.py index 0bb8c440ee..ff577e6725 100644 --- a/aries_cloudagent/core/oob_processor.py +++ b/aries_cloudagent/core/oob_processor.py @@ -4,6 +4,7 @@ import logging from typing import Any, Callable, Dict, List, Optional, cast +from ..config.logging import get_logger_inst from ..messaging.agent_message import AgentMessage from ..connections.models.conn_record import ConnRecord from ..connections.models.connection_target import ConnectionTarget @@ -22,7 +23,7 @@ from .error import BaseError from .profile import Profile -LOGGER = logging.getLogger(__name__) +LOGGER_NAME = __name__ class OobMessageProcessorError(BaseError): @@ -71,6 +72,10 @@ async def find_oob_target_for_outbound_message( self, profile: Profile, outbound_message: OutboundMessage ) -> Optional[ConnectionTarget]: """Find connection target for the outbound message.""" + _logger = get_logger_inst( + profile=profile, + logger_name=LOGGER_NAME, + ) try: async with profile.session() as session: # Try to find the oob record for the outbound message: @@ -78,7 +83,7 @@ async def find_oob_target_for_outbound_message( session, {"attach_thread_id": outbound_message.reply_thread_id} ) - LOGGER.debug( + _logger.debug( "extracting their service from oob record %s", oob_record.their_service, ) @@ -88,7 +93,7 @@ async def find_oob_target_for_outbound_message( # Attach ~service decorator so other message can respond message = json.loads(outbound_message.payload) if not message.get("~service"): - LOGGER.debug( + _logger.debug( "Setting our service on the message ~service %s", oob_record.our_service, ) @@ -101,7 +106,9 @@ async def find_oob_target_for_outbound_message( outbound_message.payload = json.dumps(message) - LOGGER.debug("Sending oob message payload %s", outbound_message.payload) + _logger.debug( + "Sending oob message payload %s", outbound_message.payload + ) return ConnectionTarget( endpoint=their_service.endpoint, @@ -118,12 +125,16 @@ async def find_oob_record_for_inbound_message( """Find oob record for inbound message.""" message_type = context.message._type oob_record = None + _logger = get_logger_inst( + profile=context.profile, + logger_name=LOGGER_NAME, + ) async with context.profile.session() as session: # First try to find the oob record based on the associated pthid if context.message_receipt.parent_thread_id: try: - LOGGER.debug( + _logger.debug( "Retrieving OOB record using pthid " f"{context.message_receipt.parent_thread_id} " f"for message type {message_type}" @@ -147,7 +158,7 @@ async def find_oob_record_for_inbound_message( and context.message_receipt.recipient_verkey ): try: - LOGGER.debug( + _logger.debug( "Retrieving OOB record using thid " f"{context.message_receipt.thread_id} and recipient verkey" f" {context.message_receipt.recipient_verkey} for " @@ -168,7 +179,7 @@ async def find_oob_record_for_inbound_message( if not oob_record: return None - LOGGER.debug( + _logger.debug( f"Found out of band record for inbound message with type {message_type}" f": {oob_record.oob_id}" ) @@ -184,14 +195,14 @@ async def find_oob_record_for_inbound_message( and context.connection_record and context.connection_record.connection_id != oob_record.connection_id ): - LOGGER.debug( + _logger.debug( f"Oob record connection id {oob_record.connection_id} is different from" f" inbound message connection {context.connection_record.connection_id}", ) # Mismatch in connection id's in only allowed in state await response # (connection id can change bc of reuse) if oob_record.state != OobRecord.STATE_AWAIT_RESPONSE: - LOGGER.debug( + _logger.debug( "Inbound message has incorrect connection_id " f"{context.connection_record.connection_id}. Oob record " f"{oob_record.oob_id} associated with connection id " @@ -206,7 +217,7 @@ async def find_oob_record_for_inbound_message( oob_record.invitation.requests_attach and oob_record.state == OobRecord.STATE_AWAIT_RESPONSE ): - LOGGER.debug( + _logger.debug( f"Removing stale connection {oob_record.connection_id} due " "to connection reuse" ) @@ -231,7 +242,7 @@ async def find_oob_record_for_inbound_message( ] if context.message_receipt.thread_id not in allowed_thread_ids: - LOGGER.debug( + _logger.debug( "Inbound message is for not allowed thread " f"{context.message_receipt.thread_id}. Allowed " f"threads are {allowed_thread_ids}" @@ -243,7 +254,7 @@ async def find_oob_record_for_inbound_message( oob_record.attach_thread_id and context.message_receipt.thread_id != oob_record.attach_thread_id ): - LOGGER.debug( + _logger.debug( f"Inbound message thread id {context.message_receipt.thread_id} does not" f" match oob record thread id {oob_record.attach_thread_id}" ) @@ -270,7 +281,7 @@ async def find_oob_record_for_inbound_message( ) ) ): - LOGGER.debug( + _logger.debug( "Inbound message sender verkey does not match stored service on oob" " record" ) @@ -279,7 +290,7 @@ async def find_oob_record_for_inbound_message( # If the message has a ~service decorator we save it in the oob record so we # can reply to this message if context._message._service: - LOGGER.debug( + _logger.debug( "Storing service decorator in oob record %s", context.message._service.serialize(), ) @@ -307,7 +318,10 @@ async def handle_message( their_service: Optional[ServiceDecorator] = None, ): """Message handler for inbound messages.""" - + _logger = get_logger_inst( + profile=profile, + logger_name=LOGGER_NAME, + ) supported_types = [ CREDENTIAL_OFFER, CRED_20_OFFER, @@ -347,7 +361,7 @@ async def handle_message( if not oob_record.connection_id: oob_record.attach_thread_id = self.get_thread_id(message) if their_service: - LOGGER.debug( + _logger.debug( "Storing their service in oob record %s", their_service ) oob_record.their_service = their_service.serialize() From bf6b33fb3aca827850326d0c0a0750f5c89bdaf6 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Wed, 17 May 2023 12:30:10 -0700 Subject: [PATCH 818/872] flake8 fix Signed-off-by: Shaanjot Gill --- aries_cloudagent/connections/base_manager.py | 2 +- aries_cloudagent/core/dispatcher.py | 2 +- aries_cloudagent/core/oob_processor.py | 6 +++--- aries_cloudagent/multitenant/admin/routes.py | 13 ++++++++++++- 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/aries_cloudagent/connections/base_manager.py b/aries_cloudagent/connections/base_manager.py index 016f4ce0dd..6ad977c032 100644 --- a/aries_cloudagent/connections/base_manager.py +++ b/aries_cloudagent/connections/base_manager.py @@ -62,7 +62,7 @@ def __init__(self, profile: Profile): """ self._profile = profile self._route_manager = profile.inject(RouteManager) - self._logger = get_logger_inst( + self._logger: logging.Logger = get_logger_inst( profile=self._profile, logger_name=LOGGER_NAME, ) diff --git a/aries_cloudagent/core/dispatcher.py b/aries_cloudagent/core/dispatcher.py index 2eee381820..6a7766cdf9 100644 --- a/aries_cloudagent/core/dispatcher.py +++ b/aries_cloudagent/core/dispatcher.py @@ -64,7 +64,7 @@ def __init__(self, profile: Profile): self.collector: Collector = None self.profile = profile self.task_queue: TaskQueue = None - self._logger = get_logger_inst( + self._logger: logging.Logger = get_logger_inst( profile=self.profile, logger_name=LOGGER_NAME, ) diff --git a/aries_cloudagent/core/oob_processor.py b/aries_cloudagent/core/oob_processor.py index ff577e6725..3ba7199d16 100644 --- a/aries_cloudagent/core/oob_processor.py +++ b/aries_cloudagent/core/oob_processor.py @@ -72,7 +72,7 @@ async def find_oob_target_for_outbound_message( self, profile: Profile, outbound_message: OutboundMessage ) -> Optional[ConnectionTarget]: """Find connection target for the outbound message.""" - _logger = get_logger_inst( + _logger: logging.Logger = get_logger_inst( profile=profile, logger_name=LOGGER_NAME, ) @@ -125,7 +125,7 @@ async def find_oob_record_for_inbound_message( """Find oob record for inbound message.""" message_type = context.message._type oob_record = None - _logger = get_logger_inst( + _logger: logging.Logger = get_logger_inst( profile=context.profile, logger_name=LOGGER_NAME, ) @@ -318,7 +318,7 @@ async def handle_message( their_service: Optional[ServiceDecorator] = None, ): """Message handler for inbound messages.""" - _logger = get_logger_inst( + _logger: logging.Logger = get_logger_inst( profile=profile, logger_name=LOGGER_NAME, ) diff --git a/aries_cloudagent/multitenant/admin/routes.py b/aries_cloudagent/multitenant/admin/routes.py index 10dd7b30ba..14549a034c 100644 --- a/aries_cloudagent/multitenant/admin/routes.py +++ b/aries_cloudagent/multitenant/admin/routes.py @@ -9,6 +9,7 @@ response_schema, ) from marshmallow import ValidationError, fields, validate, validates_schema +from typing import Union from ...admin.request_context import AdminRequestContext from ...core.error import BaseError @@ -62,6 +63,16 @@ def format_wallet_record(wallet_record: WalletRecord): return wallet_info +def process_bool_label(label: str) -> Union[bool, str]: + """Return processed extra settings dict value.""" + if label == "True" or label == "true": + return True + elif label == "False" or label == "false": + return False + else: + return label + + def get_extra_settings_dict_per_tenant(tenant_settings: dict) -> dict: """Get per tenant settings to be applied when creating wallet.""" @@ -81,7 +92,7 @@ def get_extra_settings_dict_per_tenant(tenant_settings: dict) -> dict: continue if flag != "ACAPY_ENDORSER_ROLE": map_flag = ACAPY_LIFECYCLE_CONFIG_FLAG_MAP[flag] - extra_settings[map_flag] = tenant_settings[flag] + extra_settings[map_flag] = process_bool_label(tenant_settings[flag]) return extra_settings From 60283b30a9933bd08990f006d631228362c96b5e Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Wed, 31 May 2023 12:51:11 -0700 Subject: [PATCH 819/872] custom timed file handler + json formatter Signed-off-by: Shaanjot Gill --- aries_cloudagent/config/argparse.py | 34 + aries_cloudagent/config/logging.py | 721 +++++++++++++++++-- aries_cloudagent/connections/base_manager.py | 4 +- aries_cloudagent/core/dispatcher.py | 4 +- aries_cloudagent/core/oob_processor.py | 8 +- aries_cloudagent/multitenant/admin/routes.py | 1 - requirements.txt | 1 + 7 files changed, 687 insertions(+), 86 deletions(-) diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index 84ee7a99a6..a9349c91b8 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -1001,6 +1001,23 @@ def add_arguments(self, parser: ArgumentParser): "('debug', 'info', 'warning', 'error', 'critical')" ), ) + parser.add_argument( + "--log-handler-config", + dest="log_handler_config", + type=str, + metavar="", + default=None, + env_var="ACAPY_LOG_HANDLER_CONFIG", + help=( + "Specifies when, interval, backupCount for the " + "TimedRotatingFileHandler. These attributes are " + "passed as a ; seperated string. For example, " + "when of D (days), interval of 7 and backupCount " + "of 1 will be passed as 'D;7;1'. Note: " + "backupCount of 0 will mean all backup log files " + "will be retained and not deleted at all." + ), + ) def get_settings(self, args: Namespace) -> dict: """Extract logging settings.""" @@ -1011,6 +1028,23 @@ def get_settings(self, args: Namespace) -> dict: settings["log.file"] = args.log_file if args.log_level: settings["log.level"] = args.log_level + if args.log_handler_config: + try: + handler_config_attribs = (args.log_handler_config).split(";") + settings["log.handler_when"] = handler_config_attribs[0] + settings["log.handler_interval"] = int(handler_config_attribs[1]) + settings["log.handler_bakcount"] = int(handler_config_attribs[2]) + except IndexError: + raise ArgsParseError( + "With --log-handler-config, the provided argument must be " + "in 'when;interval;backupCount' format. Each of the 3 " + "attributes for TimedRotatingFileHandler must be specified." + ) + except ValueError: + raise ArgsParseError( + "With --log-handler-config, 'interval' and 'backupCount' " + "should be a number [int]" + ) return settings diff --git a/aries_cloudagent/config/logging.py b/aries_cloudagent/config/logging.py index dc9a46177d..06222dc1d6 100644 --- a/aries_cloudagent/config/logging.py +++ b/aries_cloudagent/config/logging.py @@ -1,89 +1,33 @@ """Utilities related to logging.""" - -import sys +import asyncio +import importlib +import json import logging +import os +import pkg_resources +import sys +from random import randint +import re +import time as mod_time +import traceback + +from collections import OrderedDict +from datetime import date, datetime, time, timezone, timedelta +from inspect import istraceback from io import TextIOWrapper +from logging.handlers import BaseRotatingHandler from logging.config import fileConfig -from typing import TextIO - -import pkg_resources +from portalocker import lock, unlock, LOCK_EX +from typing import Any, Callable, Dict, List, Optional, Tuple, Union, TextIO from ..core.profile import Profile from ..version import __version__ +from ..wallet.base import BaseWallet, DIDInfo from .banner import Banner from .base import BaseSettings -DEFAULT_LOGGING_CONFIG_PATH = "aries_cloudagent.config:default_logging_config.ini" -LOG_FORMAT_FILE_ALIAS = logging.Formatter( - "%(asctime)s [%(logger_alias)s] %(levelname)s %(filename)s %(lineno)d %(message)s" -) -LOG_FORMAT_FILE_NO_ALIAS = logging.Formatter( - "%(asctime)s %(levelname)s %(filename)s %(lineno)d %(message)s" -) -LOG_FORMAT_STREAM = logging.Formatter( - "%(asctime)s %(levelname)s %(filename)s %(lineno)d %(message)s" -) - - -def clear_prev_handlers(logger: logging.Logger) -> logging.Logger: - """Remove all handler classes associated with logger instance.""" - iter_count = 0 - num_handlers = len(logger.handlers) - while iter_count < num_handlers: - logger.removeHandler(logger.handlers[0]) - iter_count = iter_count + 1 - return logger - - -def get_logger_inst(profile: Profile, logger_name) -> logging.Logger: - """Return a logger instance with provided name and handlers.""" - logger = None - if profile.settings.get("log.alias"): - logger = get_logger_with_handlers( - settings=profile.settings, - logger=logging.getLogger(f"{logger_name}_{profile.settings['log.alias']}"), - ) - else: - logger = get_logger_with_handlers( - settings=profile.settings, logger=logging.getLogger(logger_name) - ) - return logger - - -def get_logger_with_handlers( - settings: BaseSettings, logger: logging.Logger -) -> logging.Logger: - """Return logger instance with necessary handlers if required.""" - if settings.get("log.file"): - # Clear handlers set previously for this logger instance - logger = clear_prev_handlers(logger) - # log file handler - file_path = settings.get("log.file") - logger_alias = settings.get("log.alias") - file_handler = logging.FileHandler(file_path) - if logger_alias: - file_handler.setFormatter(LOG_FORMAT_FILE_ALIAS) - else: - file_handler.setFormatter(LOG_FORMAT_FILE_NO_ALIAS) - logger.addHandler(file_handler) - # stream console handler - std_out_handler = logging.StreamHandler(sys.stdout) - std_out_handler.setFormatter(LOG_FORMAT_STREAM) - logger.addHandler(std_out_handler) - if logger_alias: - logger = logging.LoggerAdapter(logger, {"logger_alias": logger_alias}) - # set log level - logger_level = ( - (settings.get("log.level")).upper() - if settings.get("log.level") - else logging.INFO - ) - logger.setLevel(logger_level) - return logger - - def load_resource(path: str, encoding: str = None) -> TextIO: """ Open a resource file located in a python package or the local filesystem. @@ -255,3 +199,632 @@ def print_banner( print() print("Listening...") print() + + +###################################################################### +# Derived from +# https://github.com/python/cpython/blob/main/Lib/logging/handlers.py +# and https://github.com/yorks/mpfhandler/blob/master/src/mpfhandler.py +###################################################################### +class TimedRotatingFileMultiProcessHandler(BaseRotatingHandler): + """ + Handler for logging to a file, rotating the log file at certain timed + with file lock unlock mechanism to support multi-process writing + to log file. + """ + + def __init__( + self, + filename, + when="h", + interval=1, + backupCount=1, + encoding=None, + delay=False, + utc=False, + atTime=None, + errors=None, + ): + BaseRotatingHandler.__init__( + self, + filename, + "a", + encoding=encoding, + delay=delay, + errors=errors, + ) + self.when = when.upper() + self.backupCount = backupCount + self.utc = utc + self.atTime = atTime + self.mylogfile = "%s.%08d" % ("/tmp/trfmphanldler", randint(0, 99999999)) + self.interval = interval + + if self.when == "S": + self.interval = 1 + self.suffix = "%Y-%m-%d_%H-%M-%S" + self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}(\.\w+)?$" + elif self.when == "M": + self.interval = 60 + self.suffix = "%Y-%m-%d_%H-%M" + self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}(\.\w+)?$" + elif self.when == "H": + self.interval = 60 * 60 + self.suffix = "%Y-%m-%d_%H" + self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}(\.\w+)?$" + elif self.when == "D" or self.when == "MIDNIGHT": + self.interval = 60 * 60 * 24 + self.suffix = "%Y-%m-%d" + self.extMatch = r"^\d{4}-\d{2}-\d{2}(\.\w+)?$" + elif self.when.startswith("W"): + self.interval = 60 * 60 * 24 * 7 + if len(self.when) != 2: + raise ValueError( + "You must specify a day for weekly rollover from 0 " + "to 6 (0 is Monday): %s" % self.when + ) + if self.when[1] < "0" or self.when[1] > "6": + raise ValueError( + "Invalid day specified for weekly rollover: %s" % self.when + ) + self.dayOfWeek = int(self.when[1]) + self.suffix = "%Y-%m-%d" + self.extMatch = r"^\d{4}-\d{2}-\d{2}(\.\w+)?$" + else: + raise ValueError("Invalid rollover interval specified: %s" % self.when) + + self.extMatch = re.compile(self.extMatch, re.ASCII) + self.interval = self.interval * interval + self.stream_lock = None + self.lock_file = self._getLockFile() + self.next_rollover_time = self.get_next_rollover_time() + if not self.next_rollover_time: + self.next_rollover_time = self.compute_next_rollover_time() + self.save_next_rollover_time() + + def _log2mylog(self, msg): + """ + Write to external log file. + """ + time_str = mod_time.strftime( + "%Y-%m-%d %H:%M:%S", mod_time.localtime(mod_time.time()) + ) + msg = str(msg) + content = "%s [%s]\n" % (time_str, msg) + fa = open(self.mylogfile, "a") + fa.write(content) + fa.close() + + def _getLockFile(self): + """ + Return log lock file. + """ + if self.baseFilename.endswith(".log"): + lock_file = self.baseFilename[:-4] + else: + lock_file = self.baseFilename + lock_file += ".lock" + return lock_file + + def _openLockFile(self): + """ + Open log lock file. + """ + lock_file = self._getLockFile() + self.stream_lock = open(lock_file, "w") + + def compute_next_rollover_time(self): + """ + Return next rollover time. + """ + next_time = None + current_datetime = datetime.now() + if self.when == "D": + next_datetime = current_datetime + timedelta(days=self.interval) + next_date = next_datetime.date() + next_time = int(mod_time.mktime(next_date.timetuple())) + elif self.when.startswith("W"): + days = 0 + current_weekday = current_datetime.weekday() + if current_weekday == self.dayOfWeek: + days = self.interval + 7 + elif current_weekday < self.dayOfWeek: + days = self.dayOfWeek - current_weekday + else: + days = 6 - current_weekday + self.dayOfWeek + 1 + next_datetime = current_datetime + timedelta(days=days) + next_date = next_datetime.date() + next_time = int(mod_time.mktime(next_date.timetuple())) + else: + tmp_next_datetime = current_datetime + timedelta(seconds=self.interval) + next_datetime = tmp_next_datetime.replace(microsecond=0) + if self.when == "H": + next_datetime = tmp_next_datetime.replace( + minute=0, second=0, microsecond=0 + ) + elif self.when == "M": + next_datetime = tmp_next_datetime.replace(second=0, microsecond=0) + next_time = int(mod_time.mktime(next_datetime.timetuple())) + return next_time + + def get_next_rollover_time(self): + """ + Get next rollover time stamp from lock file. + """ + try: + fp = open(self.lock_file, "r") + c = fp.read() + fp.close() + return int(c) + except Exception: + return False + + def save_next_rollover_time(self): + """ + save the nextRolloverTimestamp to lock file. + """ + if not self.next_rollover_time: + return 0 + content = "%d" % self.next_rollover_time + if not self.stream_lock: + self._openLockFile() + lock(self.stream_lock, LOCK_EX) + try: + self.stream_lock.seek(0) + self.stream_lock.write(content) + self.stream_lock.flush() + except Exception: + pass + finally: + unlock(self.stream_lock) + + def acquire(self): + """ + Acquire thread and file locks. + """ + BaseRotatingHandler.acquire(self) + if self.stream_lock: + if self.stream_lock.closed: + try: + self._openLockFile() + except Exception: + self.stream_lock = None + return + lock(self.stream_lock, LOCK_EX) + + def release(self): + """ + Release file and thread locks. + """ + try: + if self.stream_lock and not self.stream_lock.closed: + unlock(self.stream_lock) + except Exception: + pass + finally: + BaseRotatingHandler.release(self) + + def _close_stream(self): + """Close the log file stream""" + if self.stream: + try: + if not self.stream.closed: + self.stream.flush() + self.stream.close() + finally: + self.stream = None + + def _close_stream_lock(self): + """Close the lock file stream""" + if self.stream_lock: + try: + if not self.stream_lock.closed: + self.stream_lock.flush() + self.stream_lock.close() + finally: + self.stream_lock = None + + def close(self): + """ + Close log stream and stream_lock. + """ + try: + self._close_stream() + self._close_stream_lock() + finally: + self.stream = None + self.stream_lock = None + + def get_log_files_to_delete(self): + dir_name, base_name = os.path.split(self.baseFilename) + file_names = os.listdir(dir_name) + result = [] + n, e = os.path.splitext(base_name) + prefix = n + "." + plen = len(prefix) + for file_name in file_names: + if self.namer is None: + if not file_name.startswith(base_name): + continue + else: + if ( + not file_name.startswith(base_name) + and file_name.endswith(e) + and len(file_name) > (plen + 1) + and not file_name[plen + 1].isdigit() + ): + continue + if file_name[:plen] == prefix: + suffix = file_name[plen:] + parts = suffix.split(".") + for part in parts: + if self.extMatch.match(part): + result.append(os.path.join(dir_name, file_name)) + break + if len(result) < self.backupCount: + result = [] + else: + result.sort() + result = result[: len(result) - self.backupCount] + return result + + def shouldRollover(self, record): + """ + Determine if rollover should occur. + """ + t = int(mod_time.time()) + if t >= self.next_rollover_time: + return 1 + return 0 + + def doRollover(self): + """ + Perform rollover. + """ + self._close_stream() + self.acquire() + try: + file_next_rollover_time = self.get_next_rollover_time() + if not file_next_rollover_time: + self.release() + return 0 + if self.next_rollover_time < file_next_rollover_time: + self.next_rollover_time = file_next_rollover_time + self.release() + return 0 + except Exception: + pass + time_tuple = mod_time.localtime(self.next_rollover_time - 1) + dfn = self.baseFilename + "." + mod_time.strftime(self.suffix, time_tuple) + if os.path.exists(dfn): + bakname = dfn + ".bak" + while os.path.exists(bakname): + bakname = "%s.%08d" % (bakname, randint(0, 99999999)) + try: + os.rename(dfn, bakname) + except Exception: + pass + if os.path.exists(self.baseFilename): + try: + os.rename(self.baseFilename, dfn) + except Exception: + pass + self.next_rollover_time = self.compute_next_rollover_time() + self.save_next_rollover_time() + if self.backupCount > 0: + for s in self.get_log_files_to_delete(): + os.remove(s) + if not self.delay: + self.stream = self._open() + self.release() + + +###################################################################### +# Derived from +# https://github.com/madzak/python-json-logger/blob/master/src/ +# pythonjsonlogger/jsonlogger.py +###################################################################### +RESERVED_ATTRS: Tuple[str, ...] = ( + "args", + "asctime", + "created", + "exc_info", + "exc_text", + "filename", + "funcName", + "levelname", + "levelno", + "lineno", + "module", + "msecs", + "message", + "msg", + "name", + "pathname", + "process", + "processName", + "relativeCreated", + "stack_info", + "thread", + "threadName", +) + + +def merge_record_extra( + record: logging.LogRecord, + target: Dict, + reserved: Union[Dict, List], + rename_fields: Optional[Dict[str, str]] = None, +) -> Dict: + """ + Merges extra attrib from LogRecord into target dictionary. + + :param record: logging.LogRecord + :param target: dict to update + :param reserved: dict or list with reserved keys to skip + :param rename_fields: an optional dict, used to rename + field names in the output. + """ + if rename_fields is None: + rename_fields = {} + for key, value in record.__dict__.items(): + # this allows to have numeric keys + if key not in reserved and not ( + hasattr(key, "startswith") and key.startswith("_") + ): + target[rename_fields.get(key, key)] = value + return target + + +class JsonEncoder(json.JSONEncoder): + """ + Custom JSONEncoder. + """ + + def default(self, obj): + if isinstance(obj, (date, datetime, time)): + return self.format_datetime_obj(obj) + + elif istraceback(obj): + return "".join(traceback.format_tb(obj)).strip() + + elif type(obj) == Exception or isinstance(obj, Exception) or type(obj) == type: + return str(obj) + + try: + return super(JsonEncoder, self).default(obj) + + except TypeError: + try: + return str(obj) + + except Exception: + return None + + def format_datetime_obj(self, obj): + return obj.isoformat() + + +class CustomJsonFormatter(logging.Formatter): + """ + Custom logging JSONFormatter. + """ + + def __init__( + self, + *args: Any, + json_default: Optional[Union[Callable, str]] = None, + json_encoder: Optional[Union[Callable, str]] = None, + json_serialiser: Union[Callable, str] = json.dumps, + json_indent: Optional[Union[int, str]] = None, + json_ensure_ascii: bool = True, + prefix: str = "", + rename_fields: Optional[dict] = None, + static_fields: Optional[dict] = None, + reserved_attrs: Tuple[str, ...] = RESERVED_ATTRS, + timestamp: Union[bool, str] = False, + **kwargs: Any, + ): + self.json_default = self._str_to_fn(json_default) + self.json_encoder = self._str_to_fn(json_encoder) + self.json_serializer = self._str_to_fn(json_serialiser) + self.json_indent = json_indent + self.json_ensure_ascii = json_ensure_ascii + self.prefix = prefix + self.rename_fields = rename_fields or {} + self.static_fields = static_fields or {} + self.reserved_attrs = dict(zip(reserved_attrs, reserved_attrs)) + self.timestamp = timestamp + + logging.Formatter.__init__(self, *args, **kwargs) + if not self.json_encoder and not self.json_default: + self.json_encoder = JsonEncoder + + self._required_fields = self.parse() + self._skip_fields = dict(zip(self._required_fields, self._required_fields)) + self._skip_fields.update(self.reserved_attrs) + + def _str_to_fn(self, fn_as_str): + """ + Parse string as package.module.function, imports module and + returns function. + """ + if not isinstance(fn_as_str, str): + return fn_as_str + + path, _, function = fn_as_str.rpartition(".") + module = importlib.import_module(path) + return getattr(module, function) + + def parse(self) -> List[str]: + """ + Parse format string looking for substitutions. + """ + if isinstance(self._style, logging.StringTemplateStyle): + formatter_style_pattern = re.compile(r"\$\{(.+?)\}", re.IGNORECASE) + elif isinstance(self._style, logging.StrFormatStyle): + formatter_style_pattern = re.compile(r"\{(.+?)\}", re.IGNORECASE) + elif isinstance(self._style, logging.PercentStyle): + formatter_style_pattern = re.compile(r"%\((.+?)\)", re.IGNORECASE) + else: + raise ValueError("Invalid format: %s" % self._fmt) + + if self._fmt: + return formatter_style_pattern.findall(self._fmt) + else: + return [] + + def add_fields( + self, + log_record: Dict[str, Any], + record: logging.LogRecord, + message_dict: Dict[str, Any], + ) -> None: + """Add fields, overwriting logic provided in logging.Formatter.""" + for field in self._required_fields: + log_record[field] = record.__dict__.get(field) + + log_record.update(self.static_fields) + log_record.update(message_dict) + merge_record_extra( + record, + log_record, + reserved=self._skip_fields, + rename_fields=self.rename_fields, + ) + + if self.timestamp: + key = self.timestamp if type(self.timestamp) == str else "timestamp" + log_record[key] = datetime.fromtimestamp(record.created, tz=timezone.utc) + + self._perform_rename_log_fields(log_record) + + def _perform_rename_log_fields(self, log_record): + for old_field_name, new_field_name in self.rename_fields.items(): + log_record[new_field_name] = log_record[old_field_name] + del log_record[old_field_name] + + def format(self, record: logging.LogRecord) -> str: + """Formats a log record and serializes to json.""" + message_dict: Dict[str, Any] = {} + if isinstance(record.msg, dict): + message_dict = record.msg + record.message = "" + else: + record.message = record.getMessage() + record.asctime = self.formatTime(record, self.datefmt) + if record.exc_info and not message_dict.get("exc_info"): + message_dict["exc_info"] = self.formatException(record.exc_info) + if not message_dict.get("exc_info") and record.exc_text: + message_dict["exc_info"] = record.exc_text + if record.stack_info and not message_dict.get("stack_info"): + message_dict["stack_info"] = self.formatStack(record.stack_info) + + log_record = OrderedDict() + self.add_fields(log_record, record, message_dict) + + return self.json_serializer( + log_record, + default=self.json_default, + cls=self.json_encoder, + indent=self.json_indent, + ensure_ascii=self.json_ensure_ascii, + ) + + +DEFAULT_LOGGING_CONFIG_PATH = "aries_cloudagent.config:default_logging_config.ini" +LOG_FORMAT_FILE_ALIAS = CustomJsonFormatter( + "%(asctime)s [%(public_did)s] %(levelname)s %(filename)s %(lineno)d %(message)s" +) +LOG_FORMAT_FILE_NO_ALIAS = CustomJsonFormatter( + "%(asctime)s %(levelname)s %(filename)s %(lineno)d %(message)s" +) +LOG_FORMAT_STREAM = logging.Formatter( + "%(asctime)s %(levelname)s %(filename)s %(lineno)d %(message)s" +) + + +def clear_prev_handlers(logger: logging.Logger) -> logging.Logger: + """Remove all handler classes associated with logger instance.""" + iter_count = 0 + num_handlers = len(logger.handlers) + while iter_count < num_handlers: + logger.removeHandler(logger.handlers[0]) + iter_count = iter_count + 1 + return logger + + +def get_logger_inst(profile: Profile, logger_name) -> logging.Logger: + """Return a logger instance with provided name and handlers.""" + logger = None + loop = asyncio.get_event_loop() + public_did_ident = loop.run_until_complete(get_public_did_ident(profile)) + if public_did_ident: + logger = get_logger_with_handlers( + settings=profile.settings, + logger=logging.getLogger(f"{logger_name}_{public_did_ident}"), + public_did_ident=public_did_ident, + interval=profile.settings.get("log.handler_interval") or 7, + backup_count=profile.settings.get("log.handler_bakcount") or 1, + at_when=profile.settings.get("log.handler_when") or "d", + ) + else: + logger = get_logger_with_handlers( + settings=profile.settings, + logger=logging.getLogger(logger_name), + interval=profile.settings.get("log.handler_interval") or 7, + backup_count=profile.settings.get("log.handler_bakcount") or 1, + at_when=profile.settings.get("log.handler_when") or "d", + ) + return logger + + +async def get_public_did_ident(profile: Profile) -> Optional[str]: + if profile.settings.get("log.file"): + async with profile.session() as session: + wallet = session.inject(BaseWallet) + public_did_info: DIDInfo = await wallet.get_public_did() + return public_did_info.did + else: + return None + + +def get_logger_with_handlers( + settings: BaseSettings, + logger: logging.Logger, + at_when: str = None, + interval: int = None, + backup_count: int = None, + public_did_ident: str = None, +) -> logging.Logger: + """Return logger instance with necessary handlers if required.""" + if settings.get("log.file"): + # Clear handlers set previously for this logger instance + logger = clear_prev_handlers(logger) + # log file handler + file_path = settings.get("log.file") + file_handler = TimedRotatingFileMultiProcessHandler( + filename=file_path, + interval=interval, + when=at_when, + backupCount=backup_count, + ) + if public_did_ident: + file_handler.setFormatter(LOG_FORMAT_FILE_ALIAS) + else: + file_handler.setFormatter(LOG_FORMAT_FILE_NO_ALIAS) + logger.addHandler(file_handler) + # stream console handler + std_out_handler = logging.StreamHandler(sys.stdout) + std_out_handler.setFormatter(LOG_FORMAT_STREAM) + logger.addHandler(std_out_handler) + if public_did_ident: + logger = logging.LoggerAdapter(logger, {"public_did": public_did_ident}) + # set log level + logger_level = ( + (settings.get("log.level")).upper() + if settings.get("log.level") + else logging.INFO + ) + logger.setLevel(logger_level) + return logger diff --git a/aries_cloudagent/connections/base_manager.py b/aries_cloudagent/connections/base_manager.py index 6ad977c032..400a494ca9 100644 --- a/aries_cloudagent/connections/base_manager.py +++ b/aries_cloudagent/connections/base_manager.py @@ -40,8 +40,6 @@ from .models.diddoc import DIDDoc, PublicKey, PublicKeyType, Service from ..wallet.util import bytes_to_b58, b64_to_bytes -LOGGER_NAME = __name__ - class BaseConnectionManagerError(BaseError): """BaseConnectionManager error.""" @@ -64,7 +62,7 @@ def __init__(self, profile: Profile): self._route_manager = profile.inject(RouteManager) self._logger: logging.Logger = get_logger_inst( profile=self._profile, - logger_name=LOGGER_NAME, + logger_name=__name__, ) async def create_did_document( diff --git a/aries_cloudagent/core/dispatcher.py b/aries_cloudagent/core/dispatcher.py index 6a7766cdf9..264ded40e1 100644 --- a/aries_cloudagent/core/dispatcher.py +++ b/aries_cloudagent/core/dispatcher.py @@ -44,8 +44,6 @@ # WARNING_VERSION_NOT_SUPPORTED, ) -LOGGER_NAME = __name__ - class ProblemReportParseError(MessageParseError): """Error to raise on failure to parse problem-report message.""" @@ -66,7 +64,7 @@ def __init__(self, profile: Profile): self.task_queue: TaskQueue = None self._logger: logging.Logger = get_logger_inst( profile=self.profile, - logger_name=LOGGER_NAME, + logger_name=__name__, ) async def setup(self): diff --git a/aries_cloudagent/core/oob_processor.py b/aries_cloudagent/core/oob_processor.py index 3ba7199d16..ab40b639cf 100644 --- a/aries_cloudagent/core/oob_processor.py +++ b/aries_cloudagent/core/oob_processor.py @@ -23,8 +23,6 @@ from .error import BaseError from .profile import Profile -LOGGER_NAME = __name__ - class OobMessageProcessorError(BaseError): """Base error for OobMessageProcessor.""" @@ -74,7 +72,7 @@ async def find_oob_target_for_outbound_message( """Find connection target for the outbound message.""" _logger: logging.Logger = get_logger_inst( profile=profile, - logger_name=LOGGER_NAME, + logger_name=__name__, ) try: async with profile.session() as session: @@ -127,7 +125,7 @@ async def find_oob_record_for_inbound_message( oob_record = None _logger: logging.Logger = get_logger_inst( profile=context.profile, - logger_name=LOGGER_NAME, + logger_name=__name__, ) async with context.profile.session() as session: @@ -320,7 +318,7 @@ async def handle_message( """Message handler for inbound messages.""" _logger: logging.Logger = get_logger_inst( profile=profile, - logger_name=LOGGER_NAME, + logger_name=__name__, ) supported_types = [ CREDENTIAL_OFFER, diff --git a/aries_cloudagent/multitenant/admin/routes.py b/aries_cloudagent/multitenant/admin/routes.py index 14549a034c..a113bcfcab 100644 --- a/aries_cloudagent/multitenant/admin/routes.py +++ b/aries_cloudagent/multitenant/admin/routes.py @@ -26,7 +26,6 @@ ACAPY_LIFECYCLE_CONFIG_FLAG_MAP = { "ACAPY_LOG_LEVEL": "log.level", - "ACAPY_LOG_ALIAS": "log.alias", "ACAPY_INVITE_PUBLIC": "debug.invite_public", "ACAPY_PUBLIC_INVITES": "public_invites", "ACAPY_AUTO_ACCEPT_INVITES": "debug.auto_accept_invites", diff --git a/requirements.txt b/requirements.txt index 20b8cf9652..3ad09c2ebd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -21,6 +21,7 @@ pyyaml~=5.4.0 ConfigArgParse~=1.5.3 pyjwt~=2.4.0 pydid~=0.3.6 +portalocker~=2.7.0 jsonpath_ng==1.5.2 pytz~=2021.1 python-dateutil~=2.8.1 From a1e296c2a4f566077e67ab32aa9a045afb0ebbc2 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Thu, 1 Jun 2023 09:17:28 -0700 Subject: [PATCH 820/872] doc update, flake8 fix and test update Signed-off-by: Shaanjot Gill --- Logging.md | 6 +- aries_cloudagent/config/logging.py | 98 ++++++++----------- .../config/tests/test_argparse.py | 26 +++++ aries_cloudagent/config/tests/test_logging.py | 30 ++++-- 4 files changed, 95 insertions(+), 65 deletions(-) diff --git a/Logging.md b/Logging.md index 9c3162ac84..8d26478aa4 100644 --- a/Logging.md +++ b/Logging.md @@ -12,11 +12,12 @@ Other log levels fall back to `WARNING`. * `--log-level` - The log level to log on std out. * `--log-file` - Path to a file to log to. +* `--log-handler-config` - Specifies `when`, `interval`, `backupCount` for the `TimedRotatingFileMultiProcessHandler`. These 3 attributes are passed as a `;` seperated string. For example, `when` of d (days), `interval` of 7 and `backupCount` of 1 will be passed as `D;7;1`. Note: `backupCount` of 0 will mean all backup log files will be retained and not deleted at all. More details about these attributes can be found [here](https://docs.python.org/3/library/logging.handlers.html#timedrotatingfilehandler). `TimedRotatingFileMultiProcessHandler` supports the ability to cleanup logs by time and mantain backup logs and a custom JSON formatter for logs. Example: ```sh -./bin/aca-py start --log-level debug --log-file acapy.log +./bin/aca-py start --log-level debug --log-file acapy.log --log-handler-config d;7;1 ``` ## Environment Variables @@ -24,11 +25,12 @@ Example: The log level can be configured using the environment variable `ACAPY_LOG_LEVEL`. The log file can be set by `ACAPY_LOG_FILE`. The log config can be set by `ACAPY_LOG_CONFIG`. +The log rotating file handler config can be set by `ACAPY_LOG_HANDLER_CONFIG`. Example: ```sh -ACAPY_LOG_LEVEL=info ACAPY_LOG_FILE=./acapy.log ACAPY_LOG_CONFIG=./acapy_log.ini ./bin/aca-py start +ACAPY_LOG_LEVEL=info ACAPY_LOG_FILE=./acapy.log ACAPY_LOG_CONFIG=./acapy_log.ini ACAPY_LOG_HANDLER_CONFIG=d;7;1 ./bin/aca-py start ``` ## Acapy Config File diff --git a/aries_cloudagent/config/logging.py b/aries_cloudagent/config/logging.py index 06222dc1d6..92044e2ea8 100644 --- a/aries_cloudagent/config/logging.py +++ b/aries_cloudagent/config/logging.py @@ -28,6 +28,9 @@ from .base import BaseSettings +DEFAULT_LOGGING_CONFIG_PATH = "aries_cloudagent.config:default_logging_config.ini" + + def load_resource(path: str, encoding: str = None) -> TextIO: """ Open a resource file located in a python package or the local filesystem. @@ -208,9 +211,10 @@ def print_banner( ###################################################################### class TimedRotatingFileMultiProcessHandler(BaseRotatingHandler): """ - Handler for logging to a file, rotating the log file at certain timed - with file lock unlock mechanism to support multi-process writing - to log file. + Handler for logging to a file. + + Rotating the log file at certain timed with file lock unlock + mechanism to support multi-process writing to log file. """ def __init__( @@ -223,15 +227,24 @@ def __init__( delay=False, utc=False, atTime=None, - errors=None, ): + """ + Initialize an instance of `TimedRotatingFileMultiProcessHandler`. + + Args: + filename: log file name with path + when: specify when to rotate log file + interval: interval when to rotate + backupCount: count of backup file, backupCount of 0 will mean + no limit on count of backup file [no backup will be deleted] + + """ BaseRotatingHandler.__init__( self, filename, "a", encoding=encoding, delay=delay, - errors=errors, ) self.when = when.upper() self.backupCount = backupCount @@ -283,9 +296,7 @@ def __init__( self.save_next_rollover_time() def _log2mylog(self, msg): - """ - Write to external log file. - """ + """Write to external log file.""" time_str = mod_time.strftime( "%Y-%m-%d %H:%M:%S", mod_time.localtime(mod_time.time()) ) @@ -296,9 +307,7 @@ def _log2mylog(self, msg): fa.close() def _getLockFile(self): - """ - Return log lock file. - """ + """Return log lock file.""" if self.baseFilename.endswith(".log"): lock_file = self.baseFilename[:-4] else: @@ -307,16 +316,12 @@ def _getLockFile(self): return lock_file def _openLockFile(self): - """ - Open log lock file. - """ + """Open log lock file.""" lock_file = self._getLockFile() self.stream_lock = open(lock_file, "w") def compute_next_rollover_time(self): - """ - Return next rollover time. - """ + """Return next rollover time.""" next_time = None current_datetime = datetime.now() if self.when == "D": @@ -348,9 +353,7 @@ def compute_next_rollover_time(self): return next_time def get_next_rollover_time(self): - """ - Get next rollover time stamp from lock file. - """ + """Get next rollover time stamp from lock file.""" try: fp = open(self.lock_file, "r") c = fp.read() @@ -360,9 +363,7 @@ def get_next_rollover_time(self): return False def save_next_rollover_time(self): - """ - save the nextRolloverTimestamp to lock file. - """ + """Save the nextRolloverTimestamp to lock file.""" if not self.next_rollover_time: return 0 content = "%d" % self.next_rollover_time @@ -379,9 +380,7 @@ def save_next_rollover_time(self): unlock(self.stream_lock) def acquire(self): - """ - Acquire thread and file locks. - """ + """Acquire thread and file locks.""" BaseRotatingHandler.acquire(self) if self.stream_lock: if self.stream_lock.closed: @@ -393,9 +392,7 @@ def acquire(self): lock(self.stream_lock, LOCK_EX) def release(self): - """ - Release file and thread locks. - """ + """Release file and thread locks.""" try: if self.stream_lock and not self.stream_lock.closed: unlock(self.stream_lock) @@ -405,7 +402,7 @@ def release(self): BaseRotatingHandler.release(self) def _close_stream(self): - """Close the log file stream""" + """Close the log file stream.""" if self.stream: try: if not self.stream.closed: @@ -415,7 +412,7 @@ def _close_stream(self): self.stream = None def _close_stream_lock(self): - """Close the lock file stream""" + """Close the lock file stream.""" if self.stream_lock: try: if not self.stream_lock.closed: @@ -425,9 +422,7 @@ def _close_stream_lock(self): self.stream_lock = None def close(self): - """ - Close log stream and stream_lock. - """ + """Close log stream and stream_lock.""" try: self._close_stream() self._close_stream_lock() @@ -436,6 +431,7 @@ def close(self): self.stream_lock = None def get_log_files_to_delete(self): + """Delete backup files on rotation based on backupCount.""" dir_name, base_name = os.path.split(self.baseFilename) file_names = os.listdir(dir_name) result = [] @@ -469,18 +465,14 @@ def get_log_files_to_delete(self): return result def shouldRollover(self, record): - """ - Determine if rollover should occur. - """ + """Determine if rollover should occur.""" t = int(mod_time.time()) if t >= self.next_rollover_time: return 1 return 0 def doRollover(self): - """ - Perform rollover. - """ + """Perform rollover.""" self._close_stream() self.acquire() try: @@ -557,7 +549,7 @@ def merge_record_extra( rename_fields: Optional[Dict[str, str]] = None, ) -> Dict: """ - Merges extra attrib from LogRecord into target dictionary. + Merge extra attrib from LogRecord into target dictionary. :param record: logging.LogRecord :param target: dict to update @@ -577,11 +569,10 @@ def merge_record_extra( class JsonEncoder(json.JSONEncoder): - """ - Custom JSONEncoder. - """ + """Custom JSONEncoder.""" def default(self, obj): + """Return a serializable object or calls the base implementation.""" if isinstance(obj, (date, datetime, time)): return self.format_datetime_obj(obj) @@ -602,13 +593,12 @@ def default(self, obj): return None def format_datetime_obj(self, obj): + """Return formatted datetime object.""" return obj.isoformat() class CustomJsonFormatter(logging.Formatter): - """ - Custom logging JSONFormatter. - """ + """Custom logging JSONFormatter.""" def __init__( self, @@ -625,6 +615,7 @@ def __init__( timestamp: Union[bool, str] = False, **kwargs: Any, ): + """Initialize an instance of `CustomJsonFormatter`.""" self.json_default = self._str_to_fn(json_default) self.json_encoder = self._str_to_fn(json_encoder) self.json_serializer = self._str_to_fn(json_serialiser) @@ -645,10 +636,7 @@ def __init__( self._skip_fields.update(self.reserved_attrs) def _str_to_fn(self, fn_as_str): - """ - Parse string as package.module.function, imports module and - returns function. - """ + """Parse string as package.module.function and return function.""" if not isinstance(fn_as_str, str): return fn_as_str @@ -657,9 +645,7 @@ def _str_to_fn(self, fn_as_str): return getattr(module, function) def parse(self) -> List[str]: - """ - Parse format string looking for substitutions. - """ + """Parse format string looking for substitutions.""" if isinstance(self._style, logging.StringTemplateStyle): formatter_style_pattern = re.compile(r"\$\{(.+?)\}", re.IGNORECASE) elif isinstance(self._style, logging.StrFormatStyle): @@ -705,7 +691,7 @@ def _perform_rename_log_fields(self, log_record): del log_record[old_field_name] def format(self, record: logging.LogRecord) -> str: - """Formats a log record and serializes to json.""" + """Format a log record and serializes to json.""" message_dict: Dict[str, Any] = {} if isinstance(record.msg, dict): message_dict = record.msg @@ -732,7 +718,6 @@ def format(self, record: logging.LogRecord) -> str: ) -DEFAULT_LOGGING_CONFIG_PATH = "aries_cloudagent.config:default_logging_config.ini" LOG_FORMAT_FILE_ALIAS = CustomJsonFormatter( "%(asctime)s [%(public_did)s] %(levelname)s %(filename)s %(lineno)d %(message)s" ) @@ -780,6 +765,7 @@ def get_logger_inst(profile: Profile, logger_name) -> logging.Logger: async def get_public_did_ident(profile: Profile) -> Optional[str]: + """Get public did identifier for logging, if applicable.""" if profile.settings.get("log.file"): async with profile.session() as session: wallet = session.inject(BaseWallet) diff --git a/aries_cloudagent/config/tests/test_argparse.py b/aries_cloudagent/config/tests/test_argparse.py index 044bb217ad..0c43b8128f 100644 --- a/aries_cloudagent/config/tests/test_argparse.py +++ b/aries_cloudagent/config/tests/test_argparse.py @@ -292,6 +292,32 @@ async def test_endorser_settings(self): assert settings.get("endorser.endorser_public_did") == "did:sov:12345" assert settings.get("endorser.auto_endorse") == False + async def test_logging(self): + """Test logging.""" + + parser = argparse.create_argument_parser() + group = argparse.LoggingGroup() + group.add_arguments(parser) + + result = parser.parse_args( + [ + "--log-file", + "test_file.log", + "--log-level", + "INFO", + "--log-handler-config", + "d;7;1", + ] + ) + + settings = group.get_settings(result) + + assert settings.get("log.file") == "test_file.log" + assert settings.get("log.level") == "INFO" + assert settings.get("log.handler_when") == "d" + assert settings.get("log.handler_interval") == 7 + assert settings.get("log.handler_bakcount") == 1 + async def test_error_raised_when_multitenancy_used_and_no_jwt_provided(self): """Test that error is raised if no jwt_secret is provided with multitenancy.""" diff --git a/aries_cloudagent/config/tests/test_logging.py b/aries_cloudagent/config/tests/test_logging.py index e88aff0a21..26e1cd45b4 100644 --- a/aries_cloudagent/config/tests/test_logging.py +++ b/aries_cloudagent/config/tests/test_logging.py @@ -3,15 +3,17 @@ from io import StringIO -from asynctest import mock as async_mock +from asynctest import mock as async_mock, TestCase as AsyncTestCase from tempfile import NamedTemporaryFile from .. import logging as test_module from ...core.in_memory import InMemoryProfile +from ...wallet.base import BaseWallet +from ...wallet.did_method import SOV, DIDMethods +from ...wallet.key_type import ED25519 - -class TestLoggingConfigurator: +class TestLoggingConfigurator(AsyncTestCase): agent_label_arg_value = "Aries Cloud Agent" transport_arg_value = "transport" host_arg_value = "host" @@ -103,24 +105,38 @@ def test_get_logger_with_handlers(self): logger = test_module.get_logger_with_handlers( settings=profile.settings, logger=logger, + at_when="m", + interval=1, + backup_count=1, ) assert logger - profile.settings["log.alias"] = "tenant_id_123" logger = test_module.get_logger_with_handlers( settings=profile.settings, logger=logger, + public_did_ident="tenant_did_123", + at_when="m", + interval=1, + backup_count=1, ) assert logger - def test_get_logger_inst(self): + async def test_get_logger_inst(self): profile = InMemoryProfile.test_profile() - profile.settings["log.file"] = "test_file.log" logger = test_module.get_logger_inst( profile=profile, logger_name=__name__, ) assert logger - profile.settings["log.alias"] = "tenant_id_123" + profile.settings["log.file"] = "test_file.log" + profile.context.injector.bind_instance(DIDMethods, DIDMethods()) + async with profile.session() as session: + wallet: BaseWallet = session.inject_or(BaseWallet) + await wallet.create_local_did( + SOV, + ED25519, + did="DJGEjaMunDtFtBVrn1qJMT", + ) + await wallet.set_public_did("DJGEjaMunDtFtBVrn1qJMT") logger = test_module.get_logger_inst( profile=profile, logger_name=__name__, From 5d3ef86ebc197012274d4863bf5ae0286b3a5026 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Thu, 1 Jun 2023 09:18:17 -0700 Subject: [PATCH 821/872] black fmt change Signed-off-by: Shaanjot Gill --- aries_cloudagent/config/tests/test_logging.py | 1 + 1 file changed, 1 insertion(+) diff --git a/aries_cloudagent/config/tests/test_logging.py b/aries_cloudagent/config/tests/test_logging.py index 26e1cd45b4..d1b9cf0a34 100644 --- a/aries_cloudagent/config/tests/test_logging.py +++ b/aries_cloudagent/config/tests/test_logging.py @@ -13,6 +13,7 @@ from ...wallet.did_method import SOV, DIDMethods from ...wallet.key_type import ED25519 + class TestLoggingConfigurator(AsyncTestCase): agent_label_arg_value = "Aries Cloud Agent" transport_arg_value = "transport" From 67f4889ba17ec0ecd0416a247c4b326dd1c88d56 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Thu, 1 Jun 2023 10:30:50 -0700 Subject: [PATCH 822/872] not public did logic Signed-off-by: Shaanjot Gill --- aries_cloudagent/config/logging.py | 6 ++++-- aries_cloudagent/config/tests/test_logging.py | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/config/logging.py b/aries_cloudagent/config/logging.py index 92044e2ea8..0f675c90a8 100644 --- a/aries_cloudagent/config/logging.py +++ b/aries_cloudagent/config/logging.py @@ -769,8 +769,10 @@ async def get_public_did_ident(profile: Profile) -> Optional[str]: if profile.settings.get("log.file"): async with profile.session() as session: wallet = session.inject(BaseWallet) - public_did_info: DIDInfo = await wallet.get_public_did() - return public_did_info.did + req_did_info: DIDInfo = await wallet.get_public_did() + if not req_did_info: + req_did_info: DIDInfo = (await wallet.get_local_dids())[0] + return req_did_info.did else: return None diff --git a/aries_cloudagent/config/tests/test_logging.py b/aries_cloudagent/config/tests/test_logging.py index d1b9cf0a34..7904bba8dc 100644 --- a/aries_cloudagent/config/tests/test_logging.py +++ b/aries_cloudagent/config/tests/test_logging.py @@ -128,6 +128,7 @@ async def test_get_logger_inst(self): logger_name=__name__, ) assert logger + # public did profile.settings["log.file"] = "test_file.log" profile.context.injector.bind_instance(DIDMethods, DIDMethods()) async with profile.session() as session: @@ -143,3 +144,19 @@ async def test_get_logger_inst(self): logger_name=__name__, ) assert logger + # not public did + profile = InMemoryProfile.test_profile() + profile.settings["log.file"] = "test_file.log" + profile.context.injector.bind_instance(DIDMethods, DIDMethods()) + async with profile.session() as session: + wallet: BaseWallet = session.inject_or(BaseWallet) + await wallet.create_local_did( + SOV, + ED25519, + did="DJGEjaMunDtFtBVrn1qJMT", + ) + logger = test_module.get_logger_inst( + profile=profile, + logger_name=__name__, + ) + assert logger From b63783062658d03ae65195f715d84714416ddc83 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Thu, 1 Jun 2023 10:48:54 -0700 Subject: [PATCH 823/872] cleanup Signed-off-by: Shaanjot Gill --- aries_cloudagent/config/logging.py | 27 ++++++++++--------- aries_cloudagent/config/tests/test_logging.py | 2 +- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/aries_cloudagent/config/logging.py b/aries_cloudagent/config/logging.py index 0f675c90a8..dc4a9366cc 100644 --- a/aries_cloudagent/config/logging.py +++ b/aries_cloudagent/config/logging.py @@ -719,7 +719,7 @@ def format(self, record: logging.LogRecord) -> str: LOG_FORMAT_FILE_ALIAS = CustomJsonFormatter( - "%(asctime)s [%(public_did)s] %(levelname)s %(filename)s %(lineno)d %(message)s" + "%(asctime)s [%(did)s] %(levelname)s %(filename)s %(lineno)d %(message)s" ) LOG_FORMAT_FILE_NO_ALIAS = CustomJsonFormatter( "%(asctime)s %(levelname)s %(filename)s %(lineno)d %(message)s" @@ -743,12 +743,12 @@ def get_logger_inst(profile: Profile, logger_name) -> logging.Logger: """Return a logger instance with provided name and handlers.""" logger = None loop = asyncio.get_event_loop() - public_did_ident = loop.run_until_complete(get_public_did_ident(profile)) - if public_did_ident: + did_ident = loop.run_until_complete(get_did_ident(profile)) + if did_ident: logger = get_logger_with_handlers( settings=profile.settings, - logger=logging.getLogger(f"{logger_name}_{public_did_ident}"), - public_did_ident=public_did_ident, + logger=logging.getLogger(f"{logger_name}_{did_ident}"), + did_ident=did_ident, interval=profile.settings.get("log.handler_interval") or 7, backup_count=profile.settings.get("log.handler_bakcount") or 1, at_when=profile.settings.get("log.handler_when") or "d", @@ -764,17 +764,20 @@ def get_logger_inst(profile: Profile, logger_name) -> logging.Logger: return logger -async def get_public_did_ident(profile: Profile) -> Optional[str]: +async def get_did_ident(profile: Profile) -> Optional[str]: """Get public did identifier for logging, if applicable.""" + did_ident = None if profile.settings.get("log.file"): async with profile.session() as session: wallet = session.inject(BaseWallet) req_did_info: DIDInfo = await wallet.get_public_did() if not req_did_info: req_did_info: DIDInfo = (await wallet.get_local_dids())[0] - return req_did_info.did + if req_did_info: + did_ident = req_did_info.did + return did_ident else: - return None + return did_ident def get_logger_with_handlers( @@ -783,7 +786,7 @@ def get_logger_with_handlers( at_when: str = None, interval: int = None, backup_count: int = None, - public_did_ident: str = None, + did_ident: str = None, ) -> logging.Logger: """Return logger instance with necessary handlers if required.""" if settings.get("log.file"): @@ -797,7 +800,7 @@ def get_logger_with_handlers( when=at_when, backupCount=backup_count, ) - if public_did_ident: + if did_ident: file_handler.setFormatter(LOG_FORMAT_FILE_ALIAS) else: file_handler.setFormatter(LOG_FORMAT_FILE_NO_ALIAS) @@ -806,8 +809,8 @@ def get_logger_with_handlers( std_out_handler = logging.StreamHandler(sys.stdout) std_out_handler.setFormatter(LOG_FORMAT_STREAM) logger.addHandler(std_out_handler) - if public_did_ident: - logger = logging.LoggerAdapter(logger, {"public_did": public_did_ident}) + if did_ident: + logger = logging.LoggerAdapter(logger, {"did": did_ident}) # set log level logger_level = ( (settings.get("log.level")).upper() diff --git a/aries_cloudagent/config/tests/test_logging.py b/aries_cloudagent/config/tests/test_logging.py index 7904bba8dc..b2d4cecb2b 100644 --- a/aries_cloudagent/config/tests/test_logging.py +++ b/aries_cloudagent/config/tests/test_logging.py @@ -114,7 +114,7 @@ def test_get_logger_with_handlers(self): logger = test_module.get_logger_with_handlers( settings=profile.settings, logger=logger, - public_did_ident="tenant_did_123", + did_ident="tenant_did_123", at_when="m", interval=1, backup_count=1, From 4f9fbeb6f261eea2116bdb8dfd25abb978a8b719 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Fri, 2 Jun 2023 11:12:33 -0700 Subject: [PATCH 824/872] updates based on feedback Signed-off-by: Shaanjot Gill --- Logging.md | 8 +- aries_cloudagent/config/argparse.py | 27 ++ aries_cloudagent/config/logging.py | 263 +++--------------- .../config/tests/test_argparse.py | 28 ++ aries_cloudagent/config/tests/test_logging.py | 20 ++ aries_cloudagent/connections/base_manager.py | 6 +- aries_cloudagent/core/dispatcher.py | 17 +- aries_cloudagent/core/oob_processor.py | 46 ++- aries_cloudagent/multitenant/admin/routes.py | 77 ----- requirements.txt | 1 + 10 files changed, 149 insertions(+), 344 deletions(-) diff --git a/Logging.md b/Logging.md index 8d26478aa4..c8f778a801 100644 --- a/Logging.md +++ b/Logging.md @@ -13,11 +13,13 @@ Other log levels fall back to `WARNING`. * `--log-level` - The log level to log on std out. * `--log-file` - Path to a file to log to. * `--log-handler-config` - Specifies `when`, `interval`, `backupCount` for the `TimedRotatingFileMultiProcessHandler`. These 3 attributes are passed as a `;` seperated string. For example, `when` of d (days), `interval` of 7 and `backupCount` of 1 will be passed as `D;7;1`. Note: `backupCount` of 0 will mean all backup log files will be retained and not deleted at all. More details about these attributes can be found [here](https://docs.python.org/3/library/logging.handlers.html#timedrotatingfilehandler). `TimedRotatingFileMultiProcessHandler` supports the ability to cleanup logs by time and mantain backup logs and a custom JSON formatter for logs. +* `--log-fmt-pattern` - Specifies logging.Formatter pattern to override default patterns. +* `--log-json-fmt` - Specifies whether to use JSON logging formatter or text logging formatter. Defaults to `False`. Example: ```sh -./bin/aca-py start --log-level debug --log-file acapy.log --log-handler-config d;7;1 +./bin/aca-py start --log-level debug --log-file acapy.log --log-handler-config "d;7;1" --log-fmt-pattern "%(asctime)s [%(did)s] %(filename)s %(lineno)d %(message)s" --log-json-fmt ``` ## Environment Variables @@ -26,11 +28,13 @@ The log level can be configured using the environment variable `ACAPY_LOG_LEVEL` The log file can be set by `ACAPY_LOG_FILE`. The log config can be set by `ACAPY_LOG_CONFIG`. The log rotating file handler config can be set by `ACAPY_LOG_HANDLER_CONFIG`. +The log formatter pattern can be set by `ACAPY_LOG_FMT_PATTERN`. +The log json formatter flag can be set by `ACAPY_LOG_JSON_FMT`. Example: ```sh -ACAPY_LOG_LEVEL=info ACAPY_LOG_FILE=./acapy.log ACAPY_LOG_CONFIG=./acapy_log.ini ACAPY_LOG_HANDLER_CONFIG=d;7;1 ./bin/aca-py start +ACAPY_LOG_LEVEL=info ACAPY_LOG_FILE=./acapy.log ACAPY_LOG_CONFIG=./acapy_log.ini ACAPY_LOG_HANDLER_CONFIG="d;7;1" ./bin/aca-py start ``` ## Acapy Config File diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index a9349c91b8..1a014a53e9 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -1018,6 +1018,27 @@ def add_arguments(self, parser: ArgumentParser): "will be retained and not deleted at all." ), ) + parser.add_argument( + "--log-fmt-pattern", + dest="log_fmt_pattern", + type=str, + metavar="", + default=None, + env_var="ACAPY_LOG_FMT_PATTERN", + help=( + "Specifies logging formatter pattern as string. For example: " + "%(asctime)s [%(did)s] %(filename)s %(lineno)d %(message)s." + ), + ) + parser.add_argument( + "--log-json-fmt", + action="store_true", + env_var="ACAPY_LOG_JSON_FMT", + help=( + "Specifies whether to use JSON logging formatter or " + "text logging formatter." + ), + ) def get_settings(self, args: Namespace) -> dict: """Extract logging settings.""" @@ -1045,6 +1066,12 @@ def get_settings(self, args: Namespace) -> dict: "With --log-handler-config, 'interval' and 'backupCount' " "should be a number [int]" ) + if args.log_fmt_pattern: + settings["log.fmt_pattern"] = args.log_fmt_pattern + if args.log_json_fmt: + settings["log.json_fmt"] = True + else: + settings["log.json_fmt"] = False return settings diff --git a/aries_cloudagent/config/logging.py b/aries_cloudagent/config/logging.py index dc4a9366cc..18bbdd1dc0 100644 --- a/aries_cloudagent/config/logging.py +++ b/aries_cloudagent/config/logging.py @@ -1,7 +1,5 @@ """Utilities related to logging.""" import asyncio -import importlib -import json import logging import os import pkg_resources @@ -9,16 +7,14 @@ from random import randint import re import time as mod_time -import traceback -from collections import OrderedDict -from datetime import date, datetime, time, timezone, timedelta -from inspect import istraceback +from datetime import datetime, timedelta from io import TextIOWrapper from logging.handlers import BaseRotatingHandler from logging.config import fileConfig from portalocker import lock, unlock, LOCK_EX -from typing import Any, Callable, Dict, List, Optional, Tuple, Union, TextIO +from pythonjsonlogger import jsonlogger +from typing import Optional, TextIO from ..core.profile import Profile from ..version import __version__ @@ -208,6 +204,10 @@ def print_banner( # Derived from # https://github.com/python/cpython/blob/main/Lib/logging/handlers.py # and https://github.com/yorks/mpfhandler/blob/master/src/mpfhandler.py +# +# interval and backupCount are not working as intended in mpfhandler +# library. Also the old backup files were not being deleted on rotation. +# This required the following custom implementation. ###################################################################### class TimedRotatingFileMultiProcessHandler(BaseRotatingHandler): """ @@ -511,220 +511,15 @@ def doRollover(self): self.release() -###################################################################### -# Derived from -# https://github.com/madzak/python-json-logger/blob/master/src/ -# pythonjsonlogger/jsonlogger.py -###################################################################### -RESERVED_ATTRS: Tuple[str, ...] = ( - "args", - "asctime", - "created", - "exc_info", - "exc_text", - "filename", - "funcName", - "levelname", - "levelno", - "lineno", - "module", - "msecs", - "message", - "msg", - "name", - "pathname", - "process", - "processName", - "relativeCreated", - "stack_info", - "thread", - "threadName", -) - - -def merge_record_extra( - record: logging.LogRecord, - target: Dict, - reserved: Union[Dict, List], - rename_fields: Optional[Dict[str, str]] = None, -) -> Dict: - """ - Merge extra attrib from LogRecord into target dictionary. - - :param record: logging.LogRecord - :param target: dict to update - :param reserved: dict or list with reserved keys to skip - :param rename_fields: an optional dict, used to rename - field names in the output. - """ - if rename_fields is None: - rename_fields = {} - for key, value in record.__dict__.items(): - # this allows to have numeric keys - if key not in reserved and not ( - hasattr(key, "startswith") and key.startswith("_") - ): - target[rename_fields.get(key, key)] = value - return target - - -class JsonEncoder(json.JSONEncoder): - """Custom JSONEncoder.""" - - def default(self, obj): - """Return a serializable object or calls the base implementation.""" - if isinstance(obj, (date, datetime, time)): - return self.format_datetime_obj(obj) - - elif istraceback(obj): - return "".join(traceback.format_tb(obj)).strip() - - elif type(obj) == Exception or isinstance(obj, Exception) or type(obj) == type: - return str(obj) - - try: - return super(JsonEncoder, self).default(obj) - - except TypeError: - try: - return str(obj) - - except Exception: - return None - - def format_datetime_obj(self, obj): - """Return formatted datetime object.""" - return obj.isoformat() - - -class CustomJsonFormatter(logging.Formatter): - """Custom logging JSONFormatter.""" - - def __init__( - self, - *args: Any, - json_default: Optional[Union[Callable, str]] = None, - json_encoder: Optional[Union[Callable, str]] = None, - json_serialiser: Union[Callable, str] = json.dumps, - json_indent: Optional[Union[int, str]] = None, - json_ensure_ascii: bool = True, - prefix: str = "", - rename_fields: Optional[dict] = None, - static_fields: Optional[dict] = None, - reserved_attrs: Tuple[str, ...] = RESERVED_ATTRS, - timestamp: Union[bool, str] = False, - **kwargs: Any, - ): - """Initialize an instance of `CustomJsonFormatter`.""" - self.json_default = self._str_to_fn(json_default) - self.json_encoder = self._str_to_fn(json_encoder) - self.json_serializer = self._str_to_fn(json_serialiser) - self.json_indent = json_indent - self.json_ensure_ascii = json_ensure_ascii - self.prefix = prefix - self.rename_fields = rename_fields or {} - self.static_fields = static_fields or {} - self.reserved_attrs = dict(zip(reserved_attrs, reserved_attrs)) - self.timestamp = timestamp - - logging.Formatter.__init__(self, *args, **kwargs) - if not self.json_encoder and not self.json_default: - self.json_encoder = JsonEncoder - - self._required_fields = self.parse() - self._skip_fields = dict(zip(self._required_fields, self._required_fields)) - self._skip_fields.update(self.reserved_attrs) - - def _str_to_fn(self, fn_as_str): - """Parse string as package.module.function and return function.""" - if not isinstance(fn_as_str, str): - return fn_as_str - - path, _, function = fn_as_str.rpartition(".") - module = importlib.import_module(path) - return getattr(module, function) - - def parse(self) -> List[str]: - """Parse format string looking for substitutions.""" - if isinstance(self._style, logging.StringTemplateStyle): - formatter_style_pattern = re.compile(r"\$\{(.+?)\}", re.IGNORECASE) - elif isinstance(self._style, logging.StrFormatStyle): - formatter_style_pattern = re.compile(r"\{(.+?)\}", re.IGNORECASE) - elif isinstance(self._style, logging.PercentStyle): - formatter_style_pattern = re.compile(r"%\((.+?)\)", re.IGNORECASE) - else: - raise ValueError("Invalid format: %s" % self._fmt) - - if self._fmt: - return formatter_style_pattern.findall(self._fmt) - else: - return [] - - def add_fields( - self, - log_record: Dict[str, Any], - record: logging.LogRecord, - message_dict: Dict[str, Any], - ) -> None: - """Add fields, overwriting logic provided in logging.Formatter.""" - for field in self._required_fields: - log_record[field] = record.__dict__.get(field) - - log_record.update(self.static_fields) - log_record.update(message_dict) - merge_record_extra( - record, - log_record, - reserved=self._skip_fields, - rename_fields=self.rename_fields, - ) - - if self.timestamp: - key = self.timestamp if type(self.timestamp) == str else "timestamp" - log_record[key] = datetime.fromtimestamp(record.created, tz=timezone.utc) - - self._perform_rename_log_fields(log_record) - - def _perform_rename_log_fields(self, log_record): - for old_field_name, new_field_name in self.rename_fields.items(): - log_record[new_field_name] = log_record[old_field_name] - del log_record[old_field_name] - - def format(self, record: logging.LogRecord) -> str: - """Format a log record and serializes to json.""" - message_dict: Dict[str, Any] = {} - if isinstance(record.msg, dict): - message_dict = record.msg - record.message = "" - else: - record.message = record.getMessage() - record.asctime = self.formatTime(record, self.datefmt) - if record.exc_info and not message_dict.get("exc_info"): - message_dict["exc_info"] = self.formatException(record.exc_info) - if not message_dict.get("exc_info") and record.exc_text: - message_dict["exc_info"] = record.exc_text - if record.stack_info and not message_dict.get("stack_info"): - message_dict["stack_info"] = self.formatStack(record.stack_info) - - log_record = OrderedDict() - self.add_fields(log_record, record, message_dict) - - return self.json_serializer( - log_record, - default=self.json_default, - cls=self.json_encoder, - indent=self.json_indent, - ensure_ascii=self.json_ensure_ascii, - ) - - -LOG_FORMAT_FILE_ALIAS = CustomJsonFormatter( +LOG_FORMAT_FILE_ALIAS_PATTERN = ( "%(asctime)s [%(did)s] %(levelname)s %(filename)s %(lineno)d %(message)s" ) -LOG_FORMAT_FILE_NO_ALIAS = CustomJsonFormatter( + +LOG_FORMAT_FILE_NO_ALIAS_PATTERN = ( "%(asctime)s %(levelname)s %(filename)s %(lineno)d %(message)s" ) -LOG_FORMAT_STREAM = logging.Formatter( + +LOG_FORMAT_STREAM_PATTERN = ( "%(asctime)s %(levelname)s %(filename)s %(lineno)d %(message)s" ) @@ -801,13 +596,41 @@ def get_logger_with_handlers( backupCount=backup_count, ) if did_ident: - file_handler.setFormatter(LOG_FORMAT_FILE_ALIAS) + if settings.get("log.json_fmt"): + file_handler.setFormatter( + jsonlogger.JsonFormatter( + settings.get("log.fmt_pattern") or LOG_FORMAT_FILE_ALIAS_PATTERN + ) + ) + else: + file_handler.setFormatter( + logging.Formatter( + settings.get("log.fmt_pattern") or LOG_FORMAT_FILE_ALIAS_PATTERN + ) + ) else: - file_handler.setFormatter(LOG_FORMAT_FILE_NO_ALIAS) + if settings.get("log.json_fmt"): + file_handler.setFormatter( + jsonlogger.JsonFormatter( + settings.get("log.fmt_pattern") + or LOG_FORMAT_FILE_NO_ALIAS_PATTERN + ) + ) + else: + file_handler.setFormatter( + logging.Formatter( + settings.get("log.fmt_pattern") + or LOG_FORMAT_FILE_NO_ALIAS_PATTERN + ) + ) logger.addHandler(file_handler) # stream console handler std_out_handler = logging.StreamHandler(sys.stdout) - std_out_handler.setFormatter(LOG_FORMAT_STREAM) + std_out_handler.setFormatter( + logging.Formatter( + settings.get("log.fmt_pattern") or LOG_FORMAT_STREAM_PATTERN + ) + ) logger.addHandler(std_out_handler) if did_ident: logger = logging.LoggerAdapter(logger, {"did": did_ident}) diff --git a/aries_cloudagent/config/tests/test_argparse.py b/aries_cloudagent/config/tests/test_argparse.py index 0c43b8128f..a439fd0220 100644 --- a/aries_cloudagent/config/tests/test_argparse.py +++ b/aries_cloudagent/config/tests/test_argparse.py @@ -307,6 +307,8 @@ async def test_logging(self): "INFO", "--log-handler-config", "d;7;1", + "--log-fmt-pattern", + "%(asctime)s %(levelname)s %(filename)s %(lineno)d %(message)s", ] ) @@ -317,6 +319,32 @@ async def test_logging(self): assert settings.get("log.handler_when") == "d" assert settings.get("log.handler_interval") == 7 assert settings.get("log.handler_bakcount") == 1 + assert ( + settings.get("log.fmt_pattern") + == "%(asctime)s %(levelname)s %(filename)s %(lineno)d %(message)s" + ) + assert not settings.get("log.json_fmt") + + result = parser.parse_args( + [ + "--log-file", + "test_file.log", + "--log-level", + "INFO", + "--log-handler-config", + "d;7;1", + "--log-json-fmt", + ] + ) + + settings = group.get_settings(result) + + assert settings.get("log.file") == "test_file.log" + assert settings.get("log.level") == "INFO" + assert settings.get("log.handler_when") == "d" + assert settings.get("log.handler_interval") == 7 + assert settings.get("log.handler_bakcount") == 1 + assert settings.get("log.json_fmt") async def test_error_raised_when_multitenancy_used_and_no_jwt_provided(self): """Test that error is raised if no jwt_secret is provided with multitenancy.""" diff --git a/aries_cloudagent/config/tests/test_logging.py b/aries_cloudagent/config/tests/test_logging.py index b2d4cecb2b..853b723692 100644 --- a/aries_cloudagent/config/tests/test_logging.py +++ b/aries_cloudagent/config/tests/test_logging.py @@ -143,10 +143,21 @@ async def test_get_logger_inst(self): profile=profile, logger_name=__name__, ) + # public did, json_fmt, pattern + profile.settings["log.file"] = "test_file.log" + profile.settings["log.json_fmt"] = True + profile.settings[ + "log.fmt_pattern" + ] = "%(asctime)s [%(did)s] %(lineno)d %(message)s" + logger = test_module.get_logger_inst( + profile=profile, + logger_name=__name__, + ) assert logger # not public did profile = InMemoryProfile.test_profile() profile.settings["log.file"] = "test_file.log" + profile.settings["log.json_fmt"] = False profile.context.injector.bind_instance(DIDMethods, DIDMethods()) async with profile.session() as session: wallet: BaseWallet = session.inject_or(BaseWallet) @@ -160,3 +171,12 @@ async def test_get_logger_inst(self): logger_name=__name__, ) assert logger + # not public did, json_fmt, pattern + profile.settings["log.file"] = "test_file.log" + profile.settings["log.json_fmt"] = True + profile.settings["log.fmt_pattern"] = "%(asctime)s %(lineno)d %(message)s" + logger = test_module.get_logger_inst( + profile=profile, + logger_name=__name__, + ) + assert logger diff --git a/aries_cloudagent/connections/base_manager.py b/aries_cloudagent/connections/base_manager.py index 400a494ca9..481c471218 100644 --- a/aries_cloudagent/connections/base_manager.py +++ b/aries_cloudagent/connections/base_manager.py @@ -15,7 +15,6 @@ import pydid from pydid.verification_method import Ed25519VerificationKey2018, JsonWebKey2020 -from ..config.logging import get_logger_inst from ..core.error import BaseError from ..core.profile import Profile from ..did.did_key import DIDKey @@ -58,12 +57,9 @@ def __init__(self, profile: Profile): Args: session: The profile session for this presentation """ + self._logger = logging.getLogger(__name__) self._profile = profile self._route_manager = profile.inject(RouteManager) - self._logger: logging.Logger = get_logger_inst( - profile=self._profile, - logger_name=__name__, - ) async def create_did_document( self, diff --git a/aries_cloudagent/core/dispatcher.py b/aries_cloudagent/core/dispatcher.py index 264ded40e1..062a33f3a1 100644 --- a/aries_cloudagent/core/dispatcher.py +++ b/aries_cloudagent/core/dispatcher.py @@ -16,7 +16,6 @@ from aiohttp.web import HTTPException from ..connections.models.conn_record import ConnRecord -from ..config.logging import get_logger_inst from ..core.profile import Profile from ..messaging.agent_message import AgentMessage from ..messaging.base_message import BaseMessage @@ -44,6 +43,8 @@ # WARNING_VERSION_NOT_SUPPORTED, ) +LOGGER = logging.getLogger(__name__) + class ProblemReportParseError(MessageParseError): """Error to raise on failure to parse problem-report message.""" @@ -62,10 +63,6 @@ def __init__(self, profile: Profile): self.collector: Collector = None self.profile = profile self.task_queue: TaskQueue = None - self._logger: logging.Logger = get_logger_inst( - profile=self.profile, - logger_name=__name__, - ) async def setup(self): """Perform async instance setup.""" @@ -91,7 +88,7 @@ def log_task(self, task: CompletedTask): """Log a completed task using the stats collector.""" if task.exc_info and not issubclass(task.exc_info[0], HTTPException): # skip errors intentionally returned to HTTP clients - self._logger.exception( + LOGGER.exception( "Handler error: %s", task.ident or "", exc_info=task.exc_info ) if self.collector: @@ -161,9 +158,7 @@ async def handle_message( except ProblemReportParseError: pass # avoid problem report recursion except MessageParseError as e: - self._logger.error( - f"Message parsing failed: {str(e)}, sending problem report" - ) + LOGGER.error(f"Message parsing failed: {str(e)}, sending problem report") error_result = ProblemReport( description={ "en": str(e), @@ -175,7 +170,7 @@ async def handle_message( # if warning: # warning_message_type = inbound_message.payload.get("@type") # if warning == WARNING_DEGRADED_FEATURES: - # self._logger.error( + # LOGGER.error( # f"Sending {WARNING_DEGRADED_FEATURES} problem report, " # "message type received with a minor version at or higher" # " than protocol minimum supported and current minor version " @@ -192,7 +187,7 @@ async def handle_message( # } # ) # elif warning == WARNING_VERSION_MISMATCH: - # self._logger.error( + # LOGGER.error( # f"Sending {WARNING_VERSION_MISMATCH} problem report, message " # "type received with a minor version higher than current minor " # f"version for message_type {warning_message_type}" diff --git a/aries_cloudagent/core/oob_processor.py b/aries_cloudagent/core/oob_processor.py index ab40b639cf..0bb8c440ee 100644 --- a/aries_cloudagent/core/oob_processor.py +++ b/aries_cloudagent/core/oob_processor.py @@ -4,7 +4,6 @@ import logging from typing import Any, Callable, Dict, List, Optional, cast -from ..config.logging import get_logger_inst from ..messaging.agent_message import AgentMessage from ..connections.models.conn_record import ConnRecord from ..connections.models.connection_target import ConnectionTarget @@ -23,6 +22,8 @@ from .error import BaseError from .profile import Profile +LOGGER = logging.getLogger(__name__) + class OobMessageProcessorError(BaseError): """Base error for OobMessageProcessor.""" @@ -70,10 +71,6 @@ async def find_oob_target_for_outbound_message( self, profile: Profile, outbound_message: OutboundMessage ) -> Optional[ConnectionTarget]: """Find connection target for the outbound message.""" - _logger: logging.Logger = get_logger_inst( - profile=profile, - logger_name=__name__, - ) try: async with profile.session() as session: # Try to find the oob record for the outbound message: @@ -81,7 +78,7 @@ async def find_oob_target_for_outbound_message( session, {"attach_thread_id": outbound_message.reply_thread_id} ) - _logger.debug( + LOGGER.debug( "extracting their service from oob record %s", oob_record.their_service, ) @@ -91,7 +88,7 @@ async def find_oob_target_for_outbound_message( # Attach ~service decorator so other message can respond message = json.loads(outbound_message.payload) if not message.get("~service"): - _logger.debug( + LOGGER.debug( "Setting our service on the message ~service %s", oob_record.our_service, ) @@ -104,9 +101,7 @@ async def find_oob_target_for_outbound_message( outbound_message.payload = json.dumps(message) - _logger.debug( - "Sending oob message payload %s", outbound_message.payload - ) + LOGGER.debug("Sending oob message payload %s", outbound_message.payload) return ConnectionTarget( endpoint=their_service.endpoint, @@ -123,16 +118,12 @@ async def find_oob_record_for_inbound_message( """Find oob record for inbound message.""" message_type = context.message._type oob_record = None - _logger: logging.Logger = get_logger_inst( - profile=context.profile, - logger_name=__name__, - ) async with context.profile.session() as session: # First try to find the oob record based on the associated pthid if context.message_receipt.parent_thread_id: try: - _logger.debug( + LOGGER.debug( "Retrieving OOB record using pthid " f"{context.message_receipt.parent_thread_id} " f"for message type {message_type}" @@ -156,7 +147,7 @@ async def find_oob_record_for_inbound_message( and context.message_receipt.recipient_verkey ): try: - _logger.debug( + LOGGER.debug( "Retrieving OOB record using thid " f"{context.message_receipt.thread_id} and recipient verkey" f" {context.message_receipt.recipient_verkey} for " @@ -177,7 +168,7 @@ async def find_oob_record_for_inbound_message( if not oob_record: return None - _logger.debug( + LOGGER.debug( f"Found out of band record for inbound message with type {message_type}" f": {oob_record.oob_id}" ) @@ -193,14 +184,14 @@ async def find_oob_record_for_inbound_message( and context.connection_record and context.connection_record.connection_id != oob_record.connection_id ): - _logger.debug( + LOGGER.debug( f"Oob record connection id {oob_record.connection_id} is different from" f" inbound message connection {context.connection_record.connection_id}", ) # Mismatch in connection id's in only allowed in state await response # (connection id can change bc of reuse) if oob_record.state != OobRecord.STATE_AWAIT_RESPONSE: - _logger.debug( + LOGGER.debug( "Inbound message has incorrect connection_id " f"{context.connection_record.connection_id}. Oob record " f"{oob_record.oob_id} associated with connection id " @@ -215,7 +206,7 @@ async def find_oob_record_for_inbound_message( oob_record.invitation.requests_attach and oob_record.state == OobRecord.STATE_AWAIT_RESPONSE ): - _logger.debug( + LOGGER.debug( f"Removing stale connection {oob_record.connection_id} due " "to connection reuse" ) @@ -240,7 +231,7 @@ async def find_oob_record_for_inbound_message( ] if context.message_receipt.thread_id not in allowed_thread_ids: - _logger.debug( + LOGGER.debug( "Inbound message is for not allowed thread " f"{context.message_receipt.thread_id}. Allowed " f"threads are {allowed_thread_ids}" @@ -252,7 +243,7 @@ async def find_oob_record_for_inbound_message( oob_record.attach_thread_id and context.message_receipt.thread_id != oob_record.attach_thread_id ): - _logger.debug( + LOGGER.debug( f"Inbound message thread id {context.message_receipt.thread_id} does not" f" match oob record thread id {oob_record.attach_thread_id}" ) @@ -279,7 +270,7 @@ async def find_oob_record_for_inbound_message( ) ) ): - _logger.debug( + LOGGER.debug( "Inbound message sender verkey does not match stored service on oob" " record" ) @@ -288,7 +279,7 @@ async def find_oob_record_for_inbound_message( # If the message has a ~service decorator we save it in the oob record so we # can reply to this message if context._message._service: - _logger.debug( + LOGGER.debug( "Storing service decorator in oob record %s", context.message._service.serialize(), ) @@ -316,10 +307,7 @@ async def handle_message( their_service: Optional[ServiceDecorator] = None, ): """Message handler for inbound messages.""" - _logger: logging.Logger = get_logger_inst( - profile=profile, - logger_name=__name__, - ) + supported_types = [ CREDENTIAL_OFFER, CRED_20_OFFER, @@ -359,7 +347,7 @@ async def handle_message( if not oob_record.connection_id: oob_record.attach_thread_id = self.get_thread_id(message) if their_service: - _logger.debug( + LOGGER.debug( "Storing their service in oob record %s", their_service ) oob_record.their_service = their_service.serialize() diff --git a/aries_cloudagent/multitenant/admin/routes.py b/aries_cloudagent/multitenant/admin/routes.py index a113bcfcab..e95bac1f59 100644 --- a/aries_cloudagent/multitenant/admin/routes.py +++ b/aries_cloudagent/multitenant/admin/routes.py @@ -9,7 +9,6 @@ response_schema, ) from marshmallow import ValidationError, fields, validate, validates_schema -from typing import Union from ...admin.request_context import AdminRequestContext from ...core.error import BaseError @@ -24,32 +23,6 @@ from ..error import WalletKeyMissingError -ACAPY_LIFECYCLE_CONFIG_FLAG_MAP = { - "ACAPY_LOG_LEVEL": "log.level", - "ACAPY_INVITE_PUBLIC": "debug.invite_public", - "ACAPY_PUBLIC_INVITES": "public_invites", - "ACAPY_AUTO_ACCEPT_INVITES": "debug.auto_accept_invites", - "ACAPY_AUTO_ACCEPT_REQUESTS": "debug.auto_accept_requests", - "ACAPY_AUTO_PING_CONNECTION": "auto_ping_connection", - "ACAPY_MONITOR_PING": "debug.monitor_ping", - "ACAPY_AUTO_RESPOND_MESSAGES": "debug.auto_respond_messages", - "ACAPY_AUTO_RESPOND_CREDENTIAL_OFFER": "debug.auto_resopnd_credential_offer", - "ACAPY_AUTO_RESPOND_CREDENTIAL_REQUEST": "debug.auto_respond_credential_request", - "ACAPY_AUTO_VERIFY_PRESENTATION": "debug.auto_verify_presentation", - "ACAPY_NOTIFY_REVOCATION": "revocation.notify", - "ACAPY_AUTO_REQUEST_ENDORSEMENT": "endorser.auto_request", - "ACAPY_AUTO_WRITE_TRANSACTIONS": "endorser.auto_write", - "ACAPY_CREATE_REVOCATION_TRANSACTIONS": "endorser.auto_create_rev_reg", - "ACAPY_ENDORSER_ROLE": "endorser.protocol_role", -} - -ACAPY_ENDORSER_FLGAS_DEPENDENT_ON_AUTHOR_ROLE = [ - "ACAPY_AUTO_REQUEST_ENDORSEMENT", - "ACAPY_AUTO_WRITE_TRANSACTIONS", - "ACAPY_CREATE_REVOCATION_TRANSACTIONS", -] - - def format_wallet_record(wallet_record: WalletRecord): """Serialize a WalletRecord object.""" @@ -62,39 +35,6 @@ def format_wallet_record(wallet_record: WalletRecord): return wallet_info -def process_bool_label(label: str) -> Union[bool, str]: - """Return processed extra settings dict value.""" - if label == "True" or label == "true": - return True - elif label == "False" or label == "false": - return False - else: - return label - - -def get_extra_settings_dict_per_tenant(tenant_settings: dict) -> dict: - """Get per tenant settings to be applied when creating wallet.""" - - endorser_role_flag = tenant_settings.get("ACAPY_ENDORSER_ROLE") - extra_settings = {} - if endorser_role_flag == "author": - extra_settings["endorser.author"] = True - elif endorser_role_flag == "endorser": - extra_settings["endorser.endorser"] = True - for flag in tenant_settings.keys(): - if ( - flag in ACAPY_ENDORSER_FLGAS_DEPENDENT_ON_AUTHOR_ROLE - and endorser_role_flag != "author" - ): - # These flags require endorser role as author, if not set as author then - # this setting will be ignored. - continue - if flag != "ACAPY_ENDORSER_ROLE": - map_flag = ACAPY_LIFECYCLE_CONFIG_FLAG_MAP[flag] - extra_settings[map_flag] = process_bool_label(tenant_settings[flag]) - return extra_settings - - class MultitenantModuleResponseSchema(OpenAPISchema): """Response schema for multitenant module.""" @@ -116,12 +56,6 @@ class CreateWalletRequestSchema(OpenAPISchema): description="Master key used for key derivation.", example="MySecretKey123" ) - extra_settings = fields.Dict( - keys=fields.Str(description="Agent Config Flag"), - values=fields.Str(description="Parameter"), - allow_none=True, - ) - wallet_key_derivation = fields.Str( description="Key derivation", required=False, @@ -208,11 +142,6 @@ class UpdateWalletRequestSchema(OpenAPISchema): default="default", validate=validate.OneOf(["default", "both", "base"]), ) - extra_settings = fields.Dict( - keys=fields.Str(description="Agent Config Flag"), - values=fields.Str(description="Parameter"), - allow_none=True, - ) wallet_webhook_urls = fields.List( fields.Str( description="Optional webhook URL to receive webhook messages", @@ -365,7 +294,6 @@ async def wallet_create(request: web.BaseRequest): wallet_key = body.get("wallet_key") wallet_webhook_urls = body.get("wallet_webhook_urls") or [] wallet_dispatch_type = body.get("wallet_dispatch_type") or "default" - extra_settings = body.get("extra_settings") or {} # If no webhooks specified, then dispatch only to base webhook targets if wallet_webhook_urls == []: wallet_dispatch_type = "base" @@ -377,8 +305,6 @@ async def wallet_create(request: web.BaseRequest): "wallet.webhook_urls": wallet_webhook_urls, "wallet.dispatch_type": wallet_dispatch_type, } - extra_subwallet_setting = get_extra_settings_dict_per_tenant(extra_settings) - settings.update(extra_subwallet_setting) label = body.get("label") image_url = body.get("image_url") @@ -428,7 +354,6 @@ async def wallet_update(request: web.BaseRequest): wallet_dispatch_type = body.get("wallet_dispatch_type") label = body.get("label") image_url = body.get("image_url") - extra_settings = body.get("extra_settings") or {} if all( v is None for v in (wallet_webhook_urls, wallet_dispatch_type, label, image_url) @@ -451,8 +376,6 @@ async def wallet_update(request: web.BaseRequest): settings["default_label"] = label if image_url is not None: settings["image_url"] = image_url - extra_subwallet_setting = get_extra_settings_dict_per_tenant(extra_settings) - settings.update(extra_subwallet_setting) try: multitenant_mgr = context.profile.inject(BaseMultitenantManager) diff --git a/requirements.txt b/requirements.txt index 3ad09c2ebd..c19b79fb4e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -25,6 +25,7 @@ portalocker~=2.7.0 jsonpath_ng==1.5.2 pytz~=2021.1 python-dateutil~=2.8.1 +python-json-logger~=2.0.7 rlp==1.2.0 unflatten~=0.1 qrcode[pil]~=6.1 From 7f5d5e97905b0274074535c742c370da11e5feaa Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Fri, 2 Jun 2023 14:32:31 -0700 Subject: [PATCH 825/872] argparse argument help keywork fix Signed-off-by: Shaanjot Gill --- aries_cloudagent/config/argparse.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index 1a014a53e9..af00b4220d 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -1026,8 +1026,10 @@ def add_arguments(self, parser: ArgumentParser): default=None, env_var="ACAPY_LOG_FMT_PATTERN", help=( - "Specifies logging formatter pattern as string. For example: " - "%(asctime)s [%(did)s] %(filename)s %(lineno)d %(message)s." + "Specifies logging formatter pattern as string. Examples are included " + "in 'Logging.md'. For information regarding different attributes " + "supported in the pattern, please look at " + "https://docs.python.org/3/library/logging.html#logrecord-attributes." ), ) parser.add_argument( From 00f90bcea30b9ea3ac9ea05e293bc7d0269380b3 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Tue, 6 Jun 2023 10:51:59 -0700 Subject: [PATCH 826/872] add templates using new function Signed-off-by: Shaanjot Gill --- aries_cloudagent/connections/base_manager.py | 6 ++- aries_cloudagent/core/dispatcher.py | 17 +++++--- aries_cloudagent/core/oob_processor.py | 44 ++++++++++++-------- 3 files changed, 43 insertions(+), 24 deletions(-) diff --git a/aries_cloudagent/connections/base_manager.py b/aries_cloudagent/connections/base_manager.py index 481c471218..400a494ca9 100644 --- a/aries_cloudagent/connections/base_manager.py +++ b/aries_cloudagent/connections/base_manager.py @@ -15,6 +15,7 @@ import pydid from pydid.verification_method import Ed25519VerificationKey2018, JsonWebKey2020 +from ..config.logging import get_logger_inst from ..core.error import BaseError from ..core.profile import Profile from ..did.did_key import DIDKey @@ -57,9 +58,12 @@ def __init__(self, profile: Profile): Args: session: The profile session for this presentation """ - self._logger = logging.getLogger(__name__) self._profile = profile self._route_manager = profile.inject(RouteManager) + self._logger: logging.Logger = get_logger_inst( + profile=self._profile, + logger_name=__name__, + ) async def create_did_document( self, diff --git a/aries_cloudagent/core/dispatcher.py b/aries_cloudagent/core/dispatcher.py index 062a33f3a1..c1a176dd4f 100644 --- a/aries_cloudagent/core/dispatcher.py +++ b/aries_cloudagent/core/dispatcher.py @@ -15,6 +15,7 @@ from aiohttp.web import HTTPException +from ..config.logging import get_logger_inst from ..connections.models.conn_record import ConnRecord from ..core.profile import Profile from ..messaging.agent_message import AgentMessage @@ -43,8 +44,6 @@ # WARNING_VERSION_NOT_SUPPORTED, ) -LOGGER = logging.getLogger(__name__) - class ProblemReportParseError(MessageParseError): """Error to raise on failure to parse problem-report message.""" @@ -63,6 +62,10 @@ def __init__(self, profile: Profile): self.collector: Collector = None self.profile = profile self.task_queue: TaskQueue = None + self.logger: logging.Logger = get_logger_inst( + profile=self.profile, + logger_name=__name__, + ) async def setup(self): """Perform async instance setup.""" @@ -88,7 +91,7 @@ def log_task(self, task: CompletedTask): """Log a completed task using the stats collector.""" if task.exc_info and not issubclass(task.exc_info[0], HTTPException): # skip errors intentionally returned to HTTP clients - LOGGER.exception( + self.logger.exception( "Handler error: %s", task.ident or "", exc_info=task.exc_info ) if self.collector: @@ -158,7 +161,9 @@ async def handle_message( except ProblemReportParseError: pass # avoid problem report recursion except MessageParseError as e: - LOGGER.error(f"Message parsing failed: {str(e)}, sending problem report") + self.logger.error( + f"Message parsing failed: {str(e)}, sending problem report" + ) error_result = ProblemReport( description={ "en": str(e), @@ -170,7 +175,7 @@ async def handle_message( # if warning: # warning_message_type = inbound_message.payload.get("@type") # if warning == WARNING_DEGRADED_FEATURES: - # LOGGER.error( + # self.logger.error( # f"Sending {WARNING_DEGRADED_FEATURES} problem report, " # "message type received with a minor version at or higher" # " than protocol minimum supported and current minor version " @@ -187,7 +192,7 @@ async def handle_message( # } # ) # elif warning == WARNING_VERSION_MISMATCH: - # LOGGER.error( + # self.logger.error( # f"Sending {WARNING_VERSION_MISMATCH} problem report, message " # "type received with a minor version higher than current minor " # f"version for message_type {warning_message_type}" diff --git a/aries_cloudagent/core/oob_processor.py b/aries_cloudagent/core/oob_processor.py index 0bb8c440ee..ccecc1a606 100644 --- a/aries_cloudagent/core/oob_processor.py +++ b/aries_cloudagent/core/oob_processor.py @@ -5,6 +5,7 @@ from typing import Any, Callable, Dict, List, Optional, cast from ..messaging.agent_message import AgentMessage +from ..config.logging import get_logger_inst from ..connections.models.conn_record import ConnRecord from ..connections.models.connection_target import ConnectionTarget from ..messaging.decorators.service_decorator import ServiceDecorator @@ -22,8 +23,6 @@ from .error import BaseError from .profile import Profile -LOGGER = logging.getLogger(__name__) - class OobMessageProcessorError(BaseError): """Base error for OobMessageProcessor.""" @@ -71,6 +70,10 @@ async def find_oob_target_for_outbound_message( self, profile: Profile, outbound_message: OutboundMessage ) -> Optional[ConnectionTarget]: """Find connection target for the outbound message.""" + logger: logging.Logger = get_logger_inst( + profile=profile, + logger_name=__name__, + ) try: async with profile.session() as session: # Try to find the oob record for the outbound message: @@ -78,7 +81,7 @@ async def find_oob_target_for_outbound_message( session, {"attach_thread_id": outbound_message.reply_thread_id} ) - LOGGER.debug( + logger.debug( "extracting their service from oob record %s", oob_record.their_service, ) @@ -88,7 +91,7 @@ async def find_oob_target_for_outbound_message( # Attach ~service decorator so other message can respond message = json.loads(outbound_message.payload) if not message.get("~service"): - LOGGER.debug( + logger.debug( "Setting our service on the message ~service %s", oob_record.our_service, ) @@ -101,7 +104,7 @@ async def find_oob_target_for_outbound_message( outbound_message.payload = json.dumps(message) - LOGGER.debug("Sending oob message payload %s", outbound_message.payload) + logger.debug("Sending oob message payload %s", outbound_message.payload) return ConnectionTarget( endpoint=their_service.endpoint, @@ -118,12 +121,15 @@ async def find_oob_record_for_inbound_message( """Find oob record for inbound message.""" message_type = context.message._type oob_record = None - + logger: logging.Logger = get_logger_inst( + profile=context.profile, + logger_name=__name__, + ) async with context.profile.session() as session: # First try to find the oob record based on the associated pthid if context.message_receipt.parent_thread_id: try: - LOGGER.debug( + logger.debug( "Retrieving OOB record using pthid " f"{context.message_receipt.parent_thread_id} " f"for message type {message_type}" @@ -147,7 +153,7 @@ async def find_oob_record_for_inbound_message( and context.message_receipt.recipient_verkey ): try: - LOGGER.debug( + logger.debug( "Retrieving OOB record using thid " f"{context.message_receipt.thread_id} and recipient verkey" f" {context.message_receipt.recipient_verkey} for " @@ -168,7 +174,7 @@ async def find_oob_record_for_inbound_message( if not oob_record: return None - LOGGER.debug( + logger.debug( f"Found out of band record for inbound message with type {message_type}" f": {oob_record.oob_id}" ) @@ -184,14 +190,14 @@ async def find_oob_record_for_inbound_message( and context.connection_record and context.connection_record.connection_id != oob_record.connection_id ): - LOGGER.debug( + logger.debug( f"Oob record connection id {oob_record.connection_id} is different from" f" inbound message connection {context.connection_record.connection_id}", ) # Mismatch in connection id's in only allowed in state await response # (connection id can change bc of reuse) if oob_record.state != OobRecord.STATE_AWAIT_RESPONSE: - LOGGER.debug( + logger.debug( "Inbound message has incorrect connection_id " f"{context.connection_record.connection_id}. Oob record " f"{oob_record.oob_id} associated with connection id " @@ -206,7 +212,7 @@ async def find_oob_record_for_inbound_message( oob_record.invitation.requests_attach and oob_record.state == OobRecord.STATE_AWAIT_RESPONSE ): - LOGGER.debug( + logger.debug( f"Removing stale connection {oob_record.connection_id} due " "to connection reuse" ) @@ -231,7 +237,7 @@ async def find_oob_record_for_inbound_message( ] if context.message_receipt.thread_id not in allowed_thread_ids: - LOGGER.debug( + logger.debug( "Inbound message is for not allowed thread " f"{context.message_receipt.thread_id}. Allowed " f"threads are {allowed_thread_ids}" @@ -243,7 +249,7 @@ async def find_oob_record_for_inbound_message( oob_record.attach_thread_id and context.message_receipt.thread_id != oob_record.attach_thread_id ): - LOGGER.debug( + logger.debug( f"Inbound message thread id {context.message_receipt.thread_id} does not" f" match oob record thread id {oob_record.attach_thread_id}" ) @@ -270,7 +276,7 @@ async def find_oob_record_for_inbound_message( ) ) ): - LOGGER.debug( + logger.debug( "Inbound message sender verkey does not match stored service on oob" " record" ) @@ -279,7 +285,7 @@ async def find_oob_record_for_inbound_message( # If the message has a ~service decorator we save it in the oob record so we # can reply to this message if context._message._service: - LOGGER.debug( + logger.debug( "Storing service decorator in oob record %s", context.message._service.serialize(), ) @@ -307,6 +313,10 @@ async def handle_message( their_service: Optional[ServiceDecorator] = None, ): """Message handler for inbound messages.""" + logger: logging.Logger = get_logger_inst( + profile=profile, + logger_name=__name__, + ) supported_types = [ CREDENTIAL_OFFER, @@ -347,7 +357,7 @@ async def handle_message( if not oob_record.connection_id: oob_record.attach_thread_id = self.get_thread_id(message) if their_service: - LOGGER.debug( + logger.debug( "Storing their service in oob record %s", their_service ) oob_record.their_service = their_service.serialize() From 74fbfd9300c22b40f5843d24ff4c96de925e945f Mon Sep 17 00:00:00 2001 From: Jason Sherman Date: Mon, 5 Jun 2023 15:51:11 -0700 Subject: [PATCH 827/872] Resolve definitions.py fix Include searching by the module base package when resolving plugin definitions location. Signed-off-by: Jason Sherman --- aries_cloudagent/core/util.py | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/aries_cloudagent/core/util.py b/aries_cloudagent/core/util.py index 58b7713e8f..bef086d171 100644 --- a/aries_cloudagent/core/util.py +++ b/aries_cloudagent/core/util.py @@ -111,13 +111,32 @@ def get_proto_default_version(def_path: str, major_version: int = 1) -> str: return _get_default_version_from_version_def(version_definition) +def _resolve_definition(search_path: str, msg_class: type) -> str: + try: + path = os.path.normpath(inspect.getfile(msg_class)) + path = search_path + path.rsplit(search_path, 1)[1] + version = (re.search(r"v(\d+\_)?(\*|\d+)", path)).group() + path = path.split(version, 1)[0] + definition_path = (path.replace("/", ".")) + "definition" + if ClassLoader.load_module(definition_path): + return definition_path + except Exception: + # we expect some exceptions resolving paths + pass + + def _get_path_from_msg_class(msg_class: type) -> str: - path = os.path.normpath(inspect.getfile(msg_class)) - split_str = os.getenv("ACAPY_HOME") or "aries_cloudagent" - path = split_str + path.rsplit(split_str, 1)[1] - version = (re.search(r"v(\d+\_)?(\*|\d+)", path)).group() - path = path.split(version, 1)[0] - return (path.replace("/", ".")) + "definition" + search_paths = ["aries_cloudagent", msg_class.__module__.split(".", 1)[0]] + if os.getenv("ACAPY_HOME"): + search_paths.insert(os.getenv("ACAPY_HOME"), 0) + + definition_path = None + searches = 0 + while not definition_path and searches < len(search_paths): + definition_path = _resolve_definition(search_paths[searches], msg_class) + searches = searches + 1 + # we could throw an exception here, + return definition_path def _get_version_def_from_path(definition_path: str, major_version: int = 1): @@ -140,6 +159,7 @@ async def get_version_def_from_msg_class( profile: Profile, msg_class: type, major_version: int = 1 ): """Return version_definition of a protocol from msg_class.""" + print(f"get_version_def_from_msg_class({profile}, {msg_class}, {major_version})") cache = profile.inject_or(BaseCache) version_definition = None if cache: @@ -149,7 +169,9 @@ async def get_version_def_from_msg_class( if version_definition: return version_definition definition_path = _get_path_from_msg_class(msg_class) + print(f"definition_path = {definition_path}") version_definition = _get_version_def_from_path(definition_path, major_version) + print(f"version_definition = {version_definition}") if not version_definition: raise ProtocolDefinitionValidationError( f"Unable to load protocol version_definition for {str(msg_class)}" From f657c40c8dfc15d1d0dec323263651181b4be55d Mon Sep 17 00:00:00 2001 From: Jason Sherman Date: Tue, 6 Jun 2023 08:17:27 -0700 Subject: [PATCH 828/872] remove print statements... Signed-off-by: Jason Sherman --- aries_cloudagent/core/util.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/aries_cloudagent/core/util.py b/aries_cloudagent/core/util.py index bef086d171..dd28347e4e 100644 --- a/aries_cloudagent/core/util.py +++ b/aries_cloudagent/core/util.py @@ -159,7 +159,6 @@ async def get_version_def_from_msg_class( profile: Profile, msg_class: type, major_version: int = 1 ): """Return version_definition of a protocol from msg_class.""" - print(f"get_version_def_from_msg_class({profile}, {msg_class}, {major_version})") cache = profile.inject_or(BaseCache) version_definition = None if cache: @@ -169,9 +168,7 @@ async def get_version_def_from_msg_class( if version_definition: return version_definition definition_path = _get_path_from_msg_class(msg_class) - print(f"definition_path = {definition_path}") version_definition = _get_version_def_from_path(definition_path, major_version) - print(f"version_definition = {version_definition}") if not version_definition: raise ProtocolDefinitionValidationError( f"Unable to load protocol version_definition for {str(msg_class)}" From 489f17392038542cfa35a7d94c2e7cd4c3affd9b Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Thu, 8 Jun 2023 18:23:40 +0000 Subject: [PATCH 829/872] Release 0.8.2-rc0 Signed-off-by: Stephen Curran --- CHANGELOG.md | 41 +++++++++++++++++++++++++++++++++++++ PUBLISHING.md | 12 +++++++++-- aries_cloudagent/version.py | 2 +- docs/conf.py | 6 ++++-- open-api/openapi.json | 2 +- open-api/swagger.json | 2 +- 6 files changed, 58 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 822288547b..537200b50f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,44 @@ +# 0.8.2-rc0 + +## June 9, 2023 + +Release 0.8.2 contains a number of minor fixes and updates to ACA-Py, including +the correction of a regression in Release 0.8.0 related to the use of plugins +(see [\#2255]). Highlights include making it easier to use tracing in a +development environment to collect detailed performance information about what +is going in within ACA-Py. + +There are no breaking changes in this release. + +### Categorized List of Pull Requests + +- Connections Fixes/Updates + - Resolve definitions.py fix [\#2255](https://github.com/hyperledger/aries-cloudagent-python/pull/2255) [usingtechnology](https://github.com/usingtechnology) + - Add support for JsonWebKey2020 for the connection invitations [\#2173](https://github.com/hyperledger/aries-cloudagent-python/pull/2173) [dkulic](https://github.com/dkulic) + - fix: only cache completed connection targets [\#2240](https://github.com/hyperledger/aries-cloudagent-python/pull/2240) [dbluhm](https://github.com/dbluhm) + - Connection target should not be limited only to indy dids [\#2229](https://github.com/hyperledger/aries-cloudagent-python/pull/2229) [dkulic](https://github.com/dkulic) + - Disable webhook trigger on initial response to multi-use connection invitation [\#2223](https://github.com/hyperledger/aries-cloudagent-python/pull/2223) [esune](https://github.com/esune) +- Credential Exchange (Issue, Present) Updates + - Pass document loader to jsonld.expand [\#2175](https://github.com/hyperledger/aries-cloudagent-python/pull/2175) [andrewwhitehead](https://github.com/andrewwhitehead) +- Multi-tenancy fixes/updates + - stand up multiple agents (single and multi) for local development and testing [\#2230](https://github.com/hyperledger/aries-cloudagent-python/pull/2230) [usingtechnology](https://github.com/usingtechnology) + - Multi-tenant self-managed mediation verkey lookup [\#2232](https://github.com/hyperledger/aries-cloudagent-python/pull/2232) [usingtechnology](https://github.com/usingtechnology) + - fix: route multitenant connectionless oob invitation [\#2243](https://github.com/hyperledger/aries-cloudagent-python/pull/2243) [TimoGlastra](https://github.com/TimoGlastra) + - Fix multitenant/mediation in demo [\#2075](https://github.com/hyperledger/aries-cloudagent-python/pull/2075) [ianco](https://github.com/ianco) +- Other Bug and Documentation Fixes + - ./run_demo performance -c 1 --mediation --timing --trace-log [#2245](https://github.com/hyperledger/aries-cloudagent-python/pull/2245) [usingtechnology](https://github.com/usingtechnology) + - Fix formatting and grammatical errors in different readme's [\#2222](https://github.com/hyperledger/aries-cloudagent-python/pull/2222) [ff137](https://github.com/ff137) + - Fix broken link in README [\#2221](https://github.com/hyperledger/aries-cloudagent-python/pull/2221) [ff137](https://github.com/ff137) + - fix: run only on main, forks ok [\#2166](https://github.com/hyperledger/aries-cloudagent-python/pull/2166) [anwalker293](https://github.com/anwalker293) + - Update Alice Wants a JSON-LD Credential to fix invocation [\#2219](https://github.com/hyperledger/aries-cloudagent-python/pull/2219) [swcurran](https://github.com/swcurran) +- Dependencies and Internal Updates + - Bump requests from 2.30.0 to 2.31.0 in /demo/playground/scripts dependenciesPull requests that update a dependency file [\#2238](https://github.com/hyperledger/aries-cloudagent-python/pull/2238) [dependabot bot](https://github.com/dependabot) + - Upgrade codegen tools in scripts/generate-open-api-spec and publish Swagger 2.0 and OpenAPI 3.0 specs [\#2246](https://github.com/hyperledger/aries-cloudagent-python/pull/2246) [ff137](https://github.com/ff137) +- Message Tracing/Timing Updates + - Add updated ELK stack for demos. [\#2236](https://github.com/hyperledger/aries-cloudagent-python/pull/2236) [usingtechnology](https://github.com/usingtechnology) +- Release management pull requests + - 0.8.2-rc0 [\#2259](https://github.com/hyperledger/aries-cloudagent-python/pull/2259) [swcurran](https://github.com/swcurran) + # 0.8.1 ## April 5, 2023 diff --git a/PUBLISHING.md b/PUBLISHING.md index 13f7921055..828811df2d 100644 --- a/PUBLISHING.md +++ b/PUBLISHING.md @@ -83,7 +83,8 @@ Once you have the list of PRs: 5. Update the version number listed in [aries_cloudagent/version.py](aries_cloudagent/version.py) and, prefixed with - a "v" in [open-api/openapi.json](open-api/openapi.json) (e.g. "0.7.2" in the + a "v" in [open-api/openapi.json](open-api/openapi.json) and + [open-api/swagger.json](open-api/swagger.json) (e.g. "0.7.2" in the version.py file and "v0.7.2" in the openapi.json file). The incremented version number should adhere to the [Semantic Versioning Specification](https://semver.org/#semantic-versioning-specification-semver) @@ -101,7 +102,7 @@ Once you have the list of PRs: If there are still further changes to be merged, mark the PR as "Draft", repeat **ALL** of the steps again, and then mark this PR as ready and then wait until it is merged. It's embarrassing when you have to do a whole new - release just becaused you missed something silly...I know! + release just because you missed something silly...I know! 8. Immediately after it is merged, create a new GitHub tag representing the version. The tag name and title of the release should be the same as the @@ -128,3 +129,10 @@ Once you have the list of PRs: 10. Update the ACA-Py Read The Docs site by building the new "latest" (main branch) and activating and building the new release. Appropriate permissions are required to publish the new documentation version. + +11. Update the [https://aca-py.org] website with the latest documentation by + creating a PR and tag of the latest documentation from this site. Details + are provided in the [aries-acapy-docs] repository. + +[https://aca-py.org]: https://aca-py.org +[aries-acapy-docs]: https://github.com/hyperledger/aries-acapy-docs diff --git a/aries_cloudagent/version.py b/aries_cloudagent/version.py index b745dcd5ec..653bdd623d 100644 --- a/aries_cloudagent/version.py +++ b/aries_cloudagent/version.py @@ -1,4 +1,4 @@ """Library version information.""" -__version__ = "0.8.1" +__version__ = "0.8.2-rc0" RECORD_TYPE_ACAPY_VERSION = "acapy_version" diff --git a/docs/conf.py b/docs/conf.py index 55f83f6ec5..34143cef8b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -50,6 +50,8 @@ "marshmallow", "typing_extensions", "async_timeout", + "portalocker", + "pythonjsonlogger", ] # "aries_cloudagent.tests.test_conductor", @@ -62,7 +64,7 @@ # -- Project information ----------------------------------------------------- project = "Aries Cloud Agent - Python" -copyright = "2021, Province of British Columbia" +copyright = "2023, Province of British Columbia" author = "Province of British Columbia" # The short X.Y version @@ -105,7 +107,7 @@ # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. -language = None +language = "en" # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. diff --git a/open-api/openapi.json b/open-api/openapi.json index a77d9ac129..3708ccf5bd 100644 --- a/open-api/openapi.json +++ b/open-api/openapi.json @@ -2,7 +2,7 @@ "openapi" : "3.0.1", "info" : { "title" : "Aries Cloud Agent", - "version" : "v0.8.1" + "version" : "v0.8.2-rc0" }, "servers" : [ { "url" : "/" diff --git a/open-api/swagger.json b/open-api/swagger.json index bebb794b6c..c2ab7e5cfc 100644 --- a/open-api/swagger.json +++ b/open-api/swagger.json @@ -1,7 +1,7 @@ { "swagger" : "2.0", "info" : { - "version" : "v0.8.1", + "version" : "v0.8.2-rc0", "title" : "Aries Cloud Agent" }, "tags" : [ { From 20825b5865307785a196cda5eb6a5d918a56438c Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Thu, 8 Jun 2023 18:25:05 +0000 Subject: [PATCH 830/872] Update the number of this PR in the CHANGELOG Signed-off-by: Stephen Curran --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 537200b50f..544b90755c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,7 +37,7 @@ There are no breaking changes in this release. - Message Tracing/Timing Updates - Add updated ELK stack for demos. [\#2236](https://github.com/hyperledger/aries-cloudagent-python/pull/2236) [usingtechnology](https://github.com/usingtechnology) - Release management pull requests - - 0.8.2-rc0 [\#2259](https://github.com/hyperledger/aries-cloudagent-python/pull/2259) [swcurran](https://github.com/swcurran) + - 0.8.2-rc0 [\#2260](https://github.com/hyperledger/aries-cloudagent-python/pull/2260) [swcurran](https://github.com/swcurran) # 0.8.1 From e0d44c1d723f05cb7c9aca739ba4d6206c14fb64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Humbert?= Date: Fri, 9 Jun 2023 12:35:25 +0200 Subject: [PATCH 831/872] feat(did creation route): reject unregistered did methods MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Clément Humbert --- aries_cloudagent/wallet/routes.py | 8 +++++++- aries_cloudagent/wallet/tests/test_routes.py | 11 +++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/wallet/routes.py b/aries_cloudagent/wallet/routes.py index d1f2a4efed..7ec93334c5 100644 --- a/aries_cloudagent/wallet/routes.py +++ b/aries_cloudagent/wallet/routes.py @@ -366,7 +366,13 @@ async def wallet_create_did(request: web.BaseRequest): info = None async with context.session() as session: did_methods = session.inject(DIDMethods) - method = did_methods.from_method(body.get("method", "")) or SOV + + method = did_methods.from_method(body.get("method", "sov")) + if not method: + raise web.HTTPForbidden( + reason=(f"method {body.get('method')} is not supported by the agent.") + ) + key_types = session.inject(KeyTypes) # set default method and key type for backwards compat key_type = ( diff --git a/aries_cloudagent/wallet/tests/test_routes.py b/aries_cloudagent/wallet/tests/test_routes.py index a6dc9e6f88..c4ac8839c1 100644 --- a/aries_cloudagent/wallet/tests/test_routes.py +++ b/aries_cloudagent/wallet/tests/test_routes.py @@ -129,6 +129,17 @@ async def test_create_did(self): ) assert result is json_response.return_value + async def test_create_did_unsupported_method(self): + self.request.json = async_mock.AsyncMock( + return_value={ + "method": "madeupmethod", + "options": {"key_type": "bls12381g2"}, + } + ) + + with self.assertRaises(test_module.web.HTTPForbidden): + await test_module.wallet_create_did(self.request) + async def test_create_did_unsupported_key_type(self): self.request.json = async_mock.AsyncMock( return_value={"method": "sov", "options": {"key_type": "bls12381g2"}} From f209afbe3aa8c508a780bb3da9e6cc00d40f495c Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Thu, 8 Jun 2023 17:14:23 +0000 Subject: [PATCH 832/872] Updating Maintainers list to be accurate and using the TOC format Signed-off-by: Stephen Curran --- MAINTAINERS.md | 132 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 91 insertions(+), 41 deletions(-) diff --git a/MAINTAINERS.md b/MAINTAINERS.md index 3306ec0012..2e6d1d4207 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -1,54 +1,94 @@ # Maintainers +## Maintainer Scopes, GitHub Roles and GitHub Teams + +Maintainers are assigned the following scopes in this repository: + +| Scope | Definition | GitHub Role | GitHub Team | +| ---------- | ------------------------ | ----------- | ------------------------------------ | +| Admin | | Admin | [aries-admins] | +| Maintainer | The GitHub Maintain role | Maintain | [aries-cloudagent-python committers] | +| Triage | The GitHub Triage role | Triage | [aries triage] | +| Read | The GitHub Read role | Read | [Aries Contributors] | +| Read | The GitHub Read role | Read | [TOC] | +| Read | The GitHub Read role | Read | [aries-framework-go-ext committers] | + +[aries-admins]: https://github.com/orgs/hyperledger/teams/aries-admins +[aries-cloudagent-python committers]: https://github.com/orgs/hyperledger/teams/aries-cloudagent-python-committers +[aries triage]: https://github.com/orgs/hyperledger/teams/aries-triage +[Aries Contributors]: https://github.com/orgs/hyperledger/teams/aries-contributors +[TOC]: https://github.com/orgs/hyperledger/teams/toc +[aries-framework-go-ext committers]: https://github.com/orgs/hyperledger/teams/aries-framework-go-ext-committers + ## Active Maintainers -| Name | Github | LFID | -| ---------------- | ---------------- | ---------------- | -| Sam Curren | TelegramSam | TelegramSam | -| Stephen Curran | swcurran | swcurran | -| Andrew Whitehead | andrewwhitehead | cywolf | +| GitHub ID | Name | Scope | LFID | Discord ID | Email | Company Affiliation | +| --------------- | ---------------- | ---------- | ---- | ---------- | ----- | ------------------- | +| andrewwhitehead | Andrew Whitehead | Admin | | | | BC Gov | +| dbluhm | Daniel Bluhm | Admin | | | | Indicio PBC | +| dh1128 | Daniel Hardman | Admin | | | | Provident | +| shaangill025 | Shaanjot Gill | Maintainer | | | | BC Gov | +| swcurran | Stephen Curran | Admin | | | | BC Gov | +| TelegramSam | Sam Curren | Maintainer | | | | Indicio PBC | +| TimoGlastra | Timo Glastra | Admin | | | | Animo Solutions | +| WadeBarnes | Wade Barnes | Admin | | | | BC Gov | ## Emeritus Maintainers -| Name | Github | LFID | -|--------------|---------|---------| -| | | | +| Name | GitHub ID | Scope | LFID | Discord ID | Email | Company Affiliation | +|----- | --------- | ----- | ---- | ---------- | ----- | ------------------- | +| | | | | | | | + +## The Duties of a Maintainer + +Maintainers are expected to perform the following duties for this repository. The duties are listed in more or less priority order: + +- Review, respond, and act on any security vulnerabilities reported against the repository. +- Review, provide feedback on, and merge or reject GitHub Pull Requests from + Contributors. +- Review, triage, comment on, and close GitHub Issues + submitted by Contributors. +- When appropriate, lead/facilitate architectural discussions in the community. +- When appropriate, lead/facilitate the creation of a product roadmap. +- Create, clarify, and label issues to be worked on by Contributors. +- Ensure that there is a well defined (and ideally automated) product test and + release pipeline, including the publication of release artifacts. +- When appropriate, execute the product release process. +- Maintain the repository CONTRIBUTING.md file and getting started documents to + give guidance and encouragement to those wanting to contribute to the product, and those wanting to become maintainers. +- Contribute to the product via GitHub Pull Requests. +- Monitor requests from the Hyperledger Technical Oversight Committee about the +contents and management of Hyperledger repositories, such as branch handling, +required files in repositories and so on. +- Contribute to the Hyperledger Project's Quarterly Report. ## Becoming a Maintainer -The Aries Cloud Agent community welcomes contributions. Contributors may progress to become a -maintainer. To become a maintainer the following steps occur, roughly in order. - -- 5 significant changes have been authored by the proposed maintainer and - accepted. -- The proposed maintainer has the sponsorship of at least one other maintainer. - - This sponsoring maintainer will create a PR modifying the list of - maintainers. - - The proposed maintainer accepts the nomination and expresses a willingness - to be a long-term (more than 6 month) committer. - - This would be a comment in the above PR. - - This PR will be communicated in all appropriate communication channels. It - should be mentioned in any maintainer/community call. It should also be - posted to the appropriate mailing list or chat channels if they exist. -- Approval by at least 3 current maintainers within two weeks of the proposal or - an absolute majority of current maintainers. - - These votes will be recorded in the PR modifying the list of maintainers. -- No veto by another maintainer within two weeks of proposal are recorded. - - All vetoes must be accompanied by a public explanation as a comment in the - PR for adding this maintainer - - The explanation of the veto must be reasonable. - - A veto can be retracted, in that case the approval/veto timeframe is reset. - - It is bad form to veto, retract, and veto again. -- The proposed maintainer becomes a maintainer - - Either two weeks have passed since the third approval, - - Or an absolute majority of maintainers approve. - - In either case, no maintainer presents a veto. +This community welcomes contributions. Interested contributors are encouraged to +progress to become maintainers. To become a maintainer the following steps +occur, roughly in order. + +- The proposed maintainer establishes their reputation in the community, + including authoring five (5) significant merged pull requests, and expresses + an interest in becoming a maintainer for the repository. +- A PR is created to update this file to add the proposed maintainer to the list of active maintainers. +- The PR is authored by an existing maintainer or has a comment on the PR from an existing maintainer supporting the proposal. +- The PR is authored by the proposed maintainer or has a comment on the PR from the proposed maintainer confirming their interest in being a maintainer. + - The PR or comment from the proposed maintainer must include their + willingness to be a long-term (more than 6 month) maintainer. +- Once the PR and necessary comments have been received, an approval timeframe begins. +- The PR **MUST** be communicated on all appropriate communication channels, including relevant community calls, chat channels and mailing lists. Comments of support from the community are welcome. +- The PR is merged and the proposed maintainer becomes a maintainer if either: + - Two weeks have passed since at least three (3) Maintainer PR approvals have been recorded, OR + - An absolute majority of maintainers have approved the PR. +- If the PR does not get the requisite PR approvals, it may be closed. +- Once the add maintainer PR has been merged, any necessary updates to the GitHub Teams are made. ## Removing Maintainers -Being a maintainer is not a status symbol or a title to be maintained +Being a maintainer is not a status symbol or a title to be carried indefinitely. It will occasionally be necessary and appropriate to move a maintainer to emeritus status. This can occur in the following situations: @@ -56,15 +96,25 @@ maintainer to emeritus status. This can occur in the following situations: - Violation of the Code of Conduct warranting removal. - Inactivity. - A general measure of inactivity will be no commits or code review comments - for one reporting quarter, although this will not be strictly enforced if + for one reporting quarter. This will not be strictly enforced if the maintainer expresses a reasonable intent to continue contributing. - Reasonable exceptions to inactivity will be granted for known long term leave such as parental leave and medical leave. -- Other unspecified circumstances. +- Other circumstances at the discretion of the other Maintainers. + +The process to move a maintainer from active to emeritus status is comparable to the process for adding a maintainer, outlined above. In the case of voluntary +resignation, the Pull Request can be merged following a maintainer PR approval. If the removal is for any other reason, the following steps **SHOULD** be followed: -Like adding a maintainer the record and governance process for moving a -maintainer to emeritus status is recorded in the github PR making that change. +- A PR is created to update this file to move the maintainer to the list of emeritus maintainers. +- The PR is authored by, or has a comment supporting the proposal from, an existing maintainer or Hyperledger GitHub organization administrator. +- Once the PR and necessary comments have been received, the approval timeframe begins. +- The PR **MAY** be communicated on appropriate communication channels, including relevant community calls, chat channels and mailing lists. +- The PR is merged and the maintainer transitions to maintainer emeritus if: + - The PR is approved by the maintainer to be transitioned, OR + - Two weeks have passed since at least three (3) Maintainer PR approvals have been recorded, OR + - An absolute majority of maintainers have approved the PR. +- If the PR does not get the requisite PR approvals, it may be closed. Returning to active status from emeritus status uses the same steps as adding a new maintainer. Note that the emeritus maintainer already has the 5 required -significant changes as there is no contribution time horizon for those. \ No newline at end of file +significant changes as there is no contribution time horizon for those. From 5aac7abf377c9007b0655ffde881d6767e416b2a Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Fri, 9 Jun 2023 18:23:05 +0000 Subject: [PATCH 833/872] Add email addresses for the maintainers Signed-off-by: Stephen Curran --- MAINTAINERS.md | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/MAINTAINERS.md b/MAINTAINERS.md index 2e6d1d4207..3563bdb595 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -24,16 +24,18 @@ Maintainers are assigned the following scopes in this repository: -| GitHub ID | Name | Scope | LFID | Discord ID | Email | Company Affiliation | -| --------------- | ---------------- | ---------- | ---- | ---------- | ----- | ------------------- | -| andrewwhitehead | Andrew Whitehead | Admin | | | | BC Gov | -| dbluhm | Daniel Bluhm | Admin | | | | Indicio PBC | -| dh1128 | Daniel Hardman | Admin | | | | Provident | -| shaangill025 | Shaanjot Gill | Maintainer | | | | BC Gov | -| swcurran | Stephen Curran | Admin | | | | BC Gov | -| TelegramSam | Sam Curren | Maintainer | | | | Indicio PBC | -| TimoGlastra | Timo Glastra | Admin | | | | Animo Solutions | -| WadeBarnes | Wade Barnes | Admin | | | | BC Gov | +| GitHub ID | Name | Scope | LFID | Discord ID | Email | Company Affiliation | +| --------------- | ---------------- | ---------- | ---- | ---------- | ------------------------ | ------------------- | +| andrewwhitehead | Andrew Whitehead | Admin | | | cywolf@gmail.com | BC Gov | +| dbluhm | Daniel Bluhm | Admin | | | daniel@indicio.tech | Indicio PBC | +| dh1128 | Daniel Hardman | Admin | | | daniel.hardman@gmail.com | Provident | +| shaangill025 | Shaanjot Gill | Maintainer | | | gill.shaanjots@gmail.com | BC Gov | +| swcurran | Stephen Curran | Admin | | | swcurran@cloudcompass.ca | BC Gov | +| TelegramSam | Sam Curren | Maintainer | | | telegramsam@gmail.com | Indicio PBC | +| TimoGlastra | Timo Glastra | Admin | | | timo@animo.id | Animo Solutions | +| WadeBarnes | Wade Barnes | Admin | | | wade@neoterictech.ca | BC Gov | + +Daniel Bluhm <>, Daniel Hardman , Andrew Whitehead , Timo Glastra , Shaanjot Gill , Sam Curren , Wade Barnes ## Emeritus Maintainers From 47281246a0eb849a4c65e26636e65900e1749792 Mon Sep 17 00:00:00 2001 From: Jason Sherman Date: Thu, 8 Jun 2023 17:03:06 -0700 Subject: [PATCH 834/872] Assign ~thread.thid with thread_id value The previous assignment was using a value from a field that had not been assigned yet. Signed-off-by: Jason Sherman --- aries_cloudagent/protocols/issue_credential/v1_0/manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/manager.py b/aries_cloudagent/protocols/issue_credential/v1_0/manager.py index 1bca37dea8..b2fcc41d2a 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/manager.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/manager.py @@ -309,7 +309,7 @@ async def _create(cred_def_id): offers_attach=[CredentialOffer.wrap_indy_offer(credential_offer)], ) - credential_offer_message._thread = {"thid": cred_ex_record.thread_id} + credential_offer_message._thread = {"thid": credential_offer_message._thread_id} credential_offer_message.assign_trace_decorator( self._profile.settings, cred_ex_record.trace ) From d2356e0d91e48446b319a0e1349bf0ad84bc8d2f Mon Sep 17 00:00:00 2001 From: Jason Sherman Date: Thu, 8 Jun 2023 18:05:32 -0700 Subject: [PATCH 835/872] add random-word to dev, useful for local dev scripts Signed-off-by: Jason Sherman --- requirements.dev.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements.dev.txt b/requirements.dev.txt index 4f7b1de964..5483cee9b0 100644 --- a/requirements.dev.txt +++ b/requirements.dev.txt @@ -19,4 +19,5 @@ sphinx-rtd-theme>=0.4.3 ptvsd==4.3.2 pydevd==1.5.1 -pydevd-pycharm~=193.6015.39 \ No newline at end of file +pydevd-pycharm~=193.6015.39 +random-word==1.0.11; python_version >= "3" From 68e976f6723cb210977c9bd075b3beed6eda55fc Mon Sep 17 00:00:00 2001 From: Jason Sherman Date: Fri, 9 Jun 2023 09:38:03 -0700 Subject: [PATCH 836/872] remove dev dependency as only really added for kick starting GH actions. Signed-off-by: Jason Sherman --- requirements.dev.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements.dev.txt b/requirements.dev.txt index 5483cee9b0..0636eba1fa 100644 --- a/requirements.dev.txt +++ b/requirements.dev.txt @@ -20,4 +20,3 @@ ptvsd==4.3.2 pydevd==1.5.1 pydevd-pycharm~=193.6015.39 -random-word==1.0.11; python_version >= "3" From a3b0143d11ed18dbe08fe15c8d6ae20aa549d355 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Fri, 9 Jun 2023 18:42:22 +0000 Subject: [PATCH 837/872] Remove extra line Signed-off-by: Stephen Curran --- MAINTAINERS.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/MAINTAINERS.md b/MAINTAINERS.md index 3563bdb595..954b66262f 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -35,8 +35,6 @@ Maintainers are assigned the following scopes in this repository: | TimoGlastra | Timo Glastra | Admin | | | timo@animo.id | Animo Solutions | | WadeBarnes | Wade Barnes | Admin | | | wade@neoterictech.ca | BC Gov | -Daniel Bluhm <>, Daniel Hardman , Andrew Whitehead , Timo Glastra , Shaanjot Gill , Sam Curren , Wade Barnes - ## Emeritus Maintainers | Name | GitHub ID | Scope | LFID | Discord ID | Email | Company Affiliation | From fecd68e00c0a04538b83ee632a50696c93d6a1fd Mon Sep 17 00:00:00 2001 From: Sacha Kozma Date: Tue, 13 Jun 2023 09:29:17 +0200 Subject: [PATCH 838/872] chore: rename injection token and classes Signed-off-by: Sacha Kozma --- aries_cloudagent/config/default_context.py | 7 +++++-- .../issue_credential/v2_0/formats/ld_proof/handler.py | 4 ++-- .../v2_0/formats/ld_proof/tests/test_handler.py | 3 ++- .../protocols/present_proof/dif/pres_exch_handler.py | 6 ++++-- .../present_proof/dif/tests/test_pres_exch_handler.py | 7 +++++-- .../wallet/default_verification_key_strategy.py | 4 ++-- 6 files changed, 20 insertions(+), 11 deletions(-) diff --git a/aries_cloudagent/config/default_context.py b/aries_cloudagent/config/default_context.py index 2194c1e077..203aaac65b 100644 --- a/aries_cloudagent/config/default_context.py +++ b/aries_cloudagent/config/default_context.py @@ -17,7 +17,10 @@ from ..transport.wire_format import BaseWireFormat from ..utils.dependencies import is_indy_sdk_module_installed from ..utils.stats import Collector -from ..wallet.default_verification_key_strategy import DefaultVerificationKeyStrategy +from ..wallet.default_verification_key_strategy import ( + DefaultVerificationKeyStrategy, + BaseVerificationKeyStrategy, +) from ..wallet.did_method import DIDMethods from ..wallet.key_type import KeyTypes from .base_context import ContextBuilder @@ -55,7 +58,7 @@ async def build_context(self) -> InjectionContext: context.injector.bind_instance(DIDMethods, DIDMethods()) context.injector.bind_instance(KeyTypes, KeyTypes()) context.injector.bind_instance( - DefaultVerificationKeyStrategy, DefaultVerificationKeyStrategy() + BaseVerificationKeyStrategy, DefaultVerificationKeyStrategy() ) await self.bind_providers(context) diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py index 95cf55e017..908ee0e804 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py @@ -35,7 +35,7 @@ from ......vc.ld_proofs.constants import SECURITY_CONTEXT_BBS_URL from ......wallet.base import BaseWallet, DIDInfo from ......wallet.default_verification_key_strategy import ( - DefaultVerificationKeyStrategy, + BaseVerificationKeyStrategy, ) from ......wallet.error import WalletNotFoundError from ......wallet.key_type import BLS12381G2, ED25519 @@ -272,7 +272,7 @@ async def _get_suite_for_detail( ) did_info = await self._did_info_for_did(issuer_id) - verkey_id_strategy = self.profile.context.inject(DefaultVerificationKeyStrategy) + verkey_id_strategy = self.profile.context.inject(BaseVerificationKeyStrategy) verification_method = ( verification_method or verkey_id_strategy.get_verification_method_id_for_did(issuer_id) diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/tests/test_handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/tests/test_handler.py index 5384d0d132..4c059e52cd 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/tests/test_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/tests/test_handler.py @@ -28,6 +28,7 @@ from .......vc.tests.document_loader import custom_document_loader from .......wallet.default_verification_key_strategy import ( DefaultVerificationKeyStrategy, + BaseVerificationKeyStrategy, ) from .......wallet.key_type import BLS12381G2, ED25519 from .......wallet.error import WalletNotFoundError @@ -129,7 +130,7 @@ async def setUp(self): # Set default verkey ID strategy self.context.injector.bind_instance( - DefaultVerificationKeyStrategy, DefaultVerificationKeyStrategy() + BaseVerificationKeyStrategy, DefaultVerificationKeyStrategy() ) self.handler = LDProofCredFormatHandler(self.profile) diff --git a/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py b/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py index 0d2ac468de..033b29c7b8 100644 --- a/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py +++ b/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py @@ -38,7 +38,9 @@ ) from ....vc.vc_ld.prove import sign_presentation, create_presentation, derive_credential from ....wallet.base import BaseWallet, DIDInfo -from ....wallet.default_verification_key_strategy import DefaultVerificationKeyStrategy +from ....wallet.default_verification_key_strategy import ( + BaseVerificationKeyStrategy, +) from ....wallet.error import WalletError, WalletNotFoundError from ....wallet.key_type import BLS12381G2, ED25519 @@ -117,7 +119,7 @@ async def _get_issue_suite( ): """Get signature suite for signing presentation.""" did_info = await self._did_info_for_did(issuer_id) - verkey_id_strategy = self.profile.context.inject(DefaultVerificationKeyStrategy) + verkey_id_strategy = self.profile.context.inject(BaseVerificationKeyStrategy) verification_method = verkey_id_strategy.get_verification_method_id_for_did( issuer_id ) diff --git a/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py b/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py index 51211e824a..e487d218cf 100644 --- a/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py +++ b/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py @@ -15,7 +15,10 @@ from .....storage.vc_holder.vc_record import VCRecord from .....wallet.base import BaseWallet, DIDInfo from .....wallet.crypto import KeyType -from .....wallet.default_verification_key_strategy import DefaultVerificationKeyStrategy +from .....wallet.default_verification_key_strategy import ( + DefaultVerificationKeyStrategy, + BaseVerificationKeyStrategy, +) from .....wallet.did_method import SOV, KEY, DIDMethods from .....wallet.error import WalletNotFoundError from .....vc.ld_proofs import ( @@ -75,7 +78,7 @@ def profile(): context.injector.bind_instance(DIDResolver, DIDResolver([])) context.injector.bind_instance(DocumentLoader, custom_document_loader) context.injector.bind_instance( - DefaultVerificationKeyStrategy, DefaultVerificationKeyStrategy() + BaseVerificationKeyStrategy, DefaultVerificationKeyStrategy() ) context.settings["debug.auto_respond_presentation_request"] = True return profile diff --git a/aries_cloudagent/wallet/default_verification_key_strategy.py b/aries_cloudagent/wallet/default_verification_key_strategy.py index cd3e65c59a..40ff763e9c 100644 --- a/aries_cloudagent/wallet/default_verification_key_strategy.py +++ b/aries_cloudagent/wallet/default_verification_key_strategy.py @@ -5,7 +5,7 @@ from aries_cloudagent.did.did_key import DIDKey -class DefaultVerificationKeyStrategyBase(ABC): +class BaseVerificationKeyStrategy(ABC): """Base class for defining which verification method is in use.""" @abstractmethod @@ -20,7 +20,7 @@ def get_verification_method_id_for_did(self, did) -> Optional[str]: pass -class DefaultVerificationKeyStrategy(DefaultVerificationKeyStrategyBase): +class DefaultVerificationKeyStrategy(BaseVerificationKeyStrategy): """A basic implementation for verkey strategy. Supports did:key: and did:sov only. From 379509fa7b0b200fb507b061aa419a1fcfcdb573 Mon Sep 17 00:00:00 2001 From: Sacha Kozma Date: Tue, 13 Jun 2023 13:06:03 +0200 Subject: [PATCH 839/872] feat: add proof_purpose and allowed_method_types Signed-off-by: Sacha Kozma --- .../v2_0/formats/ld_proof/handler.py | 4 +++- .../present_proof/dif/pres_exch_handler.py | 2 +- .../default_verification_key_strategy.py | 24 +++++++++++++++---- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py index 908ee0e804..c9e6eb8ee1 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py @@ -275,7 +275,9 @@ async def _get_suite_for_detail( verkey_id_strategy = self.profile.context.inject(BaseVerificationKeyStrategy) verification_method = ( verification_method - or verkey_id_strategy.get_verification_method_id_for_did(issuer_id) + or verkey_id_strategy.get_verification_method_id_for_did( + issuer_id, proof_purpose="assertionMethod" + ) ) if verification_method is None: diff --git a/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py b/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py index 033b29c7b8..5fe28602a1 100644 --- a/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py +++ b/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py @@ -121,7 +121,7 @@ async def _get_issue_suite( did_info = await self._did_info_for_did(issuer_id) verkey_id_strategy = self.profile.context.inject(BaseVerificationKeyStrategy) verification_method = verkey_id_strategy.get_verification_method_id_for_did( - issuer_id + issuer_id, proof_purpose="assertionMethod" ) if verification_method is None: diff --git a/aries_cloudagent/wallet/default_verification_key_strategy.py b/aries_cloudagent/wallet/default_verification_key_strategy.py index 40ff763e9c..4a5ffd89b1 100644 --- a/aries_cloudagent/wallet/default_verification_key_strategy.py +++ b/aries_cloudagent/wallet/default_verification_key_strategy.py @@ -1,6 +1,8 @@ """Utilities for specifying which verification method is in use for a given DID.""" from abc import ABC, abstractmethod -from typing import Optional +from typing import Optional, List + +from aries_cloudagent.wallet.key_type import KeyType from aries_cloudagent.did.did_key import DIDKey @@ -9,12 +11,19 @@ class BaseVerificationKeyStrategy(ABC): """Base class for defining which verification method is in use.""" @abstractmethod - def get_verification_method_id_for_did(self, did) -> Optional[str]: + def get_verification_method_id_for_did( + self, + did: str, + allowed_verification_method_types: Optional[List[KeyType]] = None, + proof_purpose: Optional[str] = None, + ) -> Optional[str]: """Given a DID, returns the verification key ID in use. Returns None if no strategy is specified for this DID. - :params str did: the did + :params did: the did + :params allowed_verification_method_types: list of accepted key types + :params proof_purpose: the verkey relationship (assertionMethod, keyAgreement, ..) :returns Optional[str]: the current verkey ID """ pass @@ -26,12 +35,19 @@ class DefaultVerificationKeyStrategy(BaseVerificationKeyStrategy): Supports did:key: and did:sov only. """ - def get_verification_method_id_for_did(self, did) -> Optional[str]: + def get_verification_method_id_for_did( + self, + did: str, + allowed_verification_method_types: Optional[List[KeyType]] = None, + proof_purpose: Optional[str] = None, + ) -> Optional[str]: """Given a did:key or did:sov, returns the verification key ID in use. Returns None if no strategy is specified for this DID. :params str did: the did + :params allowed_verification_method_types: list of accepted key types + :params proof_purpose: the verkey relationship (assertionMethod, keyAgreement, ..) :returns Optional[str]: the current verkey ID """ if did.startswith("did:key:"): From 6ec12c8907fa3a6a35b0498523874f3d0cd2b864 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Fri, 9 Jun 2023 22:37:29 +0000 Subject: [PATCH 840/872] Propose adding Jason Sherman usingtechnology as a Maintainer Signed-off-by: Stephen Curran --- MAINTAINERS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS.md b/MAINTAINERS.md index 954b66262f..69abb1d17e 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -34,6 +34,7 @@ Maintainers are assigned the following scopes in this repository: | TelegramSam | Sam Curren | Maintainer | | | telegramsam@gmail.com | Indicio PBC | | TimoGlastra | Timo Glastra | Admin | | | timo@animo.id | Animo Solutions | | WadeBarnes | Wade Barnes | Admin | | | wade@neoterictech.ca | BC Gov | +| usingtechnology | Jason Sherman | Maintainer | | | tools@usingtechnolo.gy | BC Gov | ## Emeritus Maintainers From 388d6aed95bd0a72b24d044fcefe69d0849d14ca Mon Sep 17 00:00:00 2001 From: Sacha Kozma Date: Wed, 14 Jun 2023 16:03:43 +0200 Subject: [PATCH 841/872] fix: allow awaiting in verkey strategy Signed-off-by: Sacha Kozma --- .../v2_0/formats/ld_proof/handler.py | 2 +- .../present_proof/dif/pres_exch_handler.py | 6 ++++-- .../wallet/default_verification_key_strategy.py | 4 ++-- .../test_default_verification_key_strategy.py | 14 ++++++++------ 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py index c9e6eb8ee1..0ba95b5765 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py @@ -275,7 +275,7 @@ async def _get_suite_for_detail( verkey_id_strategy = self.profile.context.inject(BaseVerificationKeyStrategy) verification_method = ( verification_method - or verkey_id_strategy.get_verification_method_id_for_did( + or await verkey_id_strategy.get_verification_method_id_for_did( issuer_id, proof_purpose="assertionMethod" ) ) diff --git a/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py b/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py index 5fe28602a1..bdfd488d7f 100644 --- a/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py +++ b/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py @@ -120,8 +120,10 @@ async def _get_issue_suite( """Get signature suite for signing presentation.""" did_info = await self._did_info_for_did(issuer_id) verkey_id_strategy = self.profile.context.inject(BaseVerificationKeyStrategy) - verification_method = verkey_id_strategy.get_verification_method_id_for_did( - issuer_id, proof_purpose="assertionMethod" + verification_method = ( + await verkey_id_strategy.get_verification_method_id_for_did( + issuer_id, proof_purpose="assertionMethod" + ) ) if verification_method is None: diff --git a/aries_cloudagent/wallet/default_verification_key_strategy.py b/aries_cloudagent/wallet/default_verification_key_strategy.py index 4a5ffd89b1..be9f356aa4 100644 --- a/aries_cloudagent/wallet/default_verification_key_strategy.py +++ b/aries_cloudagent/wallet/default_verification_key_strategy.py @@ -11,7 +11,7 @@ class BaseVerificationKeyStrategy(ABC): """Base class for defining which verification method is in use.""" @abstractmethod - def get_verification_method_id_for_did( + async def get_verification_method_id_for_did( self, did: str, allowed_verification_method_types: Optional[List[KeyType]] = None, @@ -35,7 +35,7 @@ class DefaultVerificationKeyStrategy(BaseVerificationKeyStrategy): Supports did:key: and did:sov only. """ - def get_verification_method_id_for_did( + async def get_verification_method_id_for_did( self, did: str, allowed_verification_method_types: Optional[List[KeyType]] = None, diff --git a/aries_cloudagent/wallet/tests/test_default_verification_key_strategy.py b/aries_cloudagent/wallet/tests/test_default_verification_key_strategy.py index c53207836e..28ed0490dd 100644 --- a/aries_cloudagent/wallet/tests/test_default_verification_key_strategy.py +++ b/aries_cloudagent/wallet/tests/test_default_verification_key_strategy.py @@ -11,20 +11,22 @@ class TestDefaultVerificationKeyStrategy(TestCase): - def test_with_did_sov(self): + async def test_with_did_sov(self): strategy = DefaultVerificationKeyStrategy() assert ( - strategy.get_verification_method_id_for_did(TEST_DID_SOV) + await strategy.get_verification_method_id_for_did(TEST_DID_SOV) == TEST_DID_SOV + "#key-1" ) - def test_with_did_key(self): + async def test_with_did_key(self): strategy = DefaultVerificationKeyStrategy() assert ( - strategy.get_verification_method_id_for_did(TEST_DID_KEY) + await strategy.get_verification_method_id_for_did(TEST_DID_KEY) == DIDKey.from_did(TEST_DID_KEY).key_id ) - def test_unsupported_did_method(self): + async def test_unsupported_did_method(self): strategy = DefaultVerificationKeyStrategy() - assert strategy.get_verification_method_id_for_did("did:test:test") is None + assert ( + await strategy.get_verification_method_id_for_did("did:test:test") is None + ) From bb664f117989b9b1f580b878d7d1ea3c242e0342 Mon Sep 17 00:00:00 2001 From: Sacha Kozma Date: Thu, 15 Jun 2023 14:25:23 +0200 Subject: [PATCH 842/872] feat: add optionnal profile in method sig Signed-off-by: Sacha Kozma --- .../issue_credential/v2_0/formats/ld_proof/handler.py | 2 +- .../protocols/present_proof/dif/pres_exch_handler.py | 2 +- aries_cloudagent/wallet/default_verification_key_strategy.py | 4 ++++ .../wallet/tests/test_default_verification_key_strategy.py | 3 +++ 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py index 0ba95b5765..747c112235 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py @@ -276,7 +276,7 @@ async def _get_suite_for_detail( verification_method = ( verification_method or await verkey_id_strategy.get_verification_method_id_for_did( - issuer_id, proof_purpose="assertionMethod" + issuer_id, self.profile, proof_purpose="assertionMethod" ) ) diff --git a/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py b/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py index bdfd488d7f..46962448f0 100644 --- a/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py +++ b/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py @@ -122,7 +122,7 @@ async def _get_issue_suite( verkey_id_strategy = self.profile.context.inject(BaseVerificationKeyStrategy) verification_method = ( await verkey_id_strategy.get_verification_method_id_for_did( - issuer_id, proof_purpose="assertionMethod" + issuer_id, self.profile, proof_purpose="assertionMethod" ) ) diff --git a/aries_cloudagent/wallet/default_verification_key_strategy.py b/aries_cloudagent/wallet/default_verification_key_strategy.py index be9f356aa4..49a54a9a33 100644 --- a/aries_cloudagent/wallet/default_verification_key_strategy.py +++ b/aries_cloudagent/wallet/default_verification_key_strategy.py @@ -2,6 +2,8 @@ from abc import ABC, abstractmethod from typing import Optional, List +from aries_cloudagent.core.profile import Profile + from aries_cloudagent.wallet.key_type import KeyType from aries_cloudagent.did.did_key import DIDKey @@ -14,6 +16,7 @@ class BaseVerificationKeyStrategy(ABC): async def get_verification_method_id_for_did( self, did: str, + profile: Optional[Profile] = None, allowed_verification_method_types: Optional[List[KeyType]] = None, proof_purpose: Optional[str] = None, ) -> Optional[str]: @@ -38,6 +41,7 @@ class DefaultVerificationKeyStrategy(BaseVerificationKeyStrategy): async def get_verification_method_id_for_did( self, did: str, + profile: Optional[Profile] = None, allowed_verification_method_types: Optional[List[KeyType]] = None, proof_purpose: Optional[str] = None, ) -> Optional[str]: diff --git a/aries_cloudagent/wallet/tests/test_default_verification_key_strategy.py b/aries_cloudagent/wallet/tests/test_default_verification_key_strategy.py index 28ed0490dd..f892f086b3 100644 --- a/aries_cloudagent/wallet/tests/test_default_verification_key_strategy.py +++ b/aries_cloudagent/wallet/tests/test_default_verification_key_strategy.py @@ -1,10 +1,13 @@ from unittest import TestCase +from aries_cloudagent.core.profile import Profile + from aries_cloudagent.did.did_key import DIDKey from aries_cloudagent.wallet.default_verification_key_strategy import ( DefaultVerificationKeyStrategy, ) +from build.lib.aries_cloudagent.config.injection_context import InjectionContext TEST_DID_SOV = "did:sov:LjgpST2rjsoxYegQDRm7EL" TEST_DID_KEY = "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL" From f6b75dcec56bd5155078ea52de2f5ea4dc4d163a Mon Sep 17 00:00:00 2001 From: Sacha Kozma Date: Thu, 15 Jun 2023 14:33:15 +0200 Subject: [PATCH 843/872] chore: remove unused imports Signed-off-by: Sacha Kozma --- .../wallet/tests/test_default_verification_key_strategy.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/aries_cloudagent/wallet/tests/test_default_verification_key_strategy.py b/aries_cloudagent/wallet/tests/test_default_verification_key_strategy.py index f892f086b3..28ed0490dd 100644 --- a/aries_cloudagent/wallet/tests/test_default_verification_key_strategy.py +++ b/aries_cloudagent/wallet/tests/test_default_verification_key_strategy.py @@ -1,13 +1,10 @@ from unittest import TestCase -from aries_cloudagent.core.profile import Profile - from aries_cloudagent.did.did_key import DIDKey from aries_cloudagent.wallet.default_verification_key_strategy import ( DefaultVerificationKeyStrategy, ) -from build.lib.aries_cloudagent.config.injection_context import InjectionContext TEST_DID_SOV = "did:sov:LjgpST2rjsoxYegQDRm7EL" TEST_DID_KEY = "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL" From 956bd18f5e0db0f8ac025ae8b3569e1c9677ff43 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Wed, 14 Jun 2023 16:36:31 -0700 Subject: [PATCH 844/872] Create .readthedocs.yaml file Add readthedocs YAML file as required by RTD. Signed-off-by: Stephen Curran --- .readthedocs.yaml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .readthedocs.yaml diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000000..529a112354 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,9 @@ +version: 2 + +build: + os: "ubuntu-20.04" + tools: + python: "3.8" + +sphinx: + configuration: docs/conf.py From b557e9a9d8fd6cb07947cc27532bb35b49c5aab8 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Wed, 14 Jun 2023 16:45:41 -0700 Subject: [PATCH 845/872] Tweaks to .readthedocs.yaml Adding sphinx option Signed-off-by: Stephen Curran --- .readthedocs.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 529a112354..c68ede4684 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -3,7 +3,8 @@ version: 2 build: os: "ubuntu-20.04" tools: - python: "3.8" + python: "3.9" sphinx: + builder: dirhtml configuration: docs/conf.py From 22f76b70a5dba6c2d32c87fbdbb263544fcb18cc Mon Sep 17 00:00:00 2001 From: Matus Kempa Date: Thu, 15 Jun 2023 15:41:23 +0200 Subject: [PATCH 846/872] fix: do not replace public verkey on mediator Signed-off-by: Matus Kempa --- .../v1_0/route_manager.py | 12 +++++- .../v1_0/tests/test_route_manager.py | 40 +++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py index 645ca45adf..990252cd5a 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/route_manager.py @@ -144,11 +144,21 @@ async def route_connection_as_inviter( """Set up routing for a new connection when we are the inviter.""" LOGGER.debug("Routing connection as inviter") my_info = await self.get_or_create_my_did(profile, conn_record) + + replace_key = conn_record.invitation_key + async with profile.session() as session: + wallet = session.inject(BaseWallet) + public_did = await wallet.get_public_did() + + # Do not replace key, if it is public + if public_did and public_did.verkey == conn_record.invitation_key: + replace_key = None + return await self._route_for_key( profile, my_info.verkey, mediation_record, - replace_key=conn_record.invitation_key, + replace_key=replace_key, skip_if_exists=True, ) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_route_manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_route_manager.py index f64ecd0cfe..4d9efceb72 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_route_manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_route_manager.py @@ -8,7 +8,9 @@ from .....messaging.responder import BaseResponder, MockResponder from .....storage.error import StorageNotFoundError from .....wallet.did_info import DIDInfo +from .....wallet.did_method import SOV from .....wallet.in_memory import InMemoryWallet +from .....wallet.key_type import ED25519 from ....routing.v1_0.models.route_record import RouteRecord from ..manager import MediationManager from ..messages.keylist_update import KeylistUpdate @@ -19,6 +21,7 @@ RouteManagerError, ) +TEST_RECORD_DID = "55GkHamhTU1ZbTbV2ab9DE" TEST_RECORD_VERKEY = "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx" TEST_VERKEY = "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL" TEST_ROUTE_RECORD_VERKEY = "9WCgWKUaAJj3VWxxtzvvMQN3AoFxoBtBDo9ntwJnVVCC" @@ -313,6 +316,43 @@ async def test_route_connection_as_inviter( ) +@pytest.mark.asyncio +async def test_route_connection_state_inviter_replace_key_none( + profile: Profile, route_manager: RouteManager, conn_record: ConnRecord +): + mediation_record = MediationRecord(mediation_id="test-mediation-id") + mock_did_info = mock.MagicMock(DIDInfo) + conn_record.invitation_key = TEST_RECORD_VERKEY + + with mock.patch.object( + route_manager, + "get_or_create_my_did", + mock.CoroutineMock(return_value=mock_did_info), + ), mock.patch.object( + InMemoryWallet, + "get_public_did", + mock.CoroutineMock( + return_value=DIDInfo( + TEST_RECORD_DID, + TEST_RECORD_VERKEY, + None, + method=SOV, + key_type=ED25519, + ) + ), + ): + await route_manager.route_connection_as_inviter( + profile, conn_record, mediation_record + ) + route_manager._route_for_key.assert_called_once_with( + profile, + mock_did_info.verkey, + mediation_record, + replace_key=None, + skip_if_exists=True, + ) + + @pytest.mark.asyncio async def test_route_connection_state_invitee( profile: Profile, route_manager: RouteManager, conn_record: ConnRecord From 86cf47c4ce2dcb209f6f99a927c16aa4f33ec273 Mon Sep 17 00:00:00 2001 From: Sacha Kozma Date: Tue, 20 Jun 2023 09:22:57 +0200 Subject: [PATCH 847/872] fix: make profile mandatory and update docs Signed-off-by: Sacha Kozma --- .../wallet/default_verification_key_strategy.py | 8 +++++--- .../tests/test_default_verification_key_strategy.py | 11 ++++++++--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/aries_cloudagent/wallet/default_verification_key_strategy.py b/aries_cloudagent/wallet/default_verification_key_strategy.py index 49a54a9a33..4f580618b3 100644 --- a/aries_cloudagent/wallet/default_verification_key_strategy.py +++ b/aries_cloudagent/wallet/default_verification_key_strategy.py @@ -16,7 +16,7 @@ class BaseVerificationKeyStrategy(ABC): async def get_verification_method_id_for_did( self, did: str, - profile: Optional[Profile] = None, + profile: Optional[Profile], allowed_verification_method_types: Optional[List[KeyType]] = None, proof_purpose: Optional[str] = None, ) -> Optional[str]: @@ -25,6 +25,7 @@ async def get_verification_method_id_for_did( Returns None if no strategy is specified for this DID. :params did: the did + :params profile: context of the call :params allowed_verification_method_types: list of accepted key types :params proof_purpose: the verkey relationship (assertionMethod, keyAgreement, ..) :returns Optional[str]: the current verkey ID @@ -41,7 +42,7 @@ class DefaultVerificationKeyStrategy(BaseVerificationKeyStrategy): async def get_verification_method_id_for_did( self, did: str, - profile: Optional[Profile] = None, + profile: Optional[Profile], allowed_verification_method_types: Optional[List[KeyType]] = None, proof_purpose: Optional[str] = None, ) -> Optional[str]: @@ -49,7 +50,8 @@ async def get_verification_method_id_for_did( Returns None if no strategy is specified for this DID. - :params str did: the did + :params did: the did + :params profile: context of the call :params allowed_verification_method_types: list of accepted key types :params proof_purpose: the verkey relationship (assertionMethod, keyAgreement, ..) :returns Optional[str]: the current verkey ID diff --git a/aries_cloudagent/wallet/tests/test_default_verification_key_strategy.py b/aries_cloudagent/wallet/tests/test_default_verification_key_strategy.py index 28ed0490dd..1d610f53fe 100644 --- a/aries_cloudagent/wallet/tests/test_default_verification_key_strategy.py +++ b/aries_cloudagent/wallet/tests/test_default_verification_key_strategy.py @@ -1,5 +1,7 @@ from unittest import TestCase +from aries_cloudagent.core.profile import Profile + from aries_cloudagent.did.did_key import DIDKey from aries_cloudagent.wallet.default_verification_key_strategy import ( @@ -14,19 +16,22 @@ class TestDefaultVerificationKeyStrategy(TestCase): async def test_with_did_sov(self): strategy = DefaultVerificationKeyStrategy() assert ( - await strategy.get_verification_method_id_for_did(TEST_DID_SOV) + await strategy.get_verification_method_id_for_did(TEST_DID_SOV, Profile()) == TEST_DID_SOV + "#key-1" ) async def test_with_did_key(self): strategy = DefaultVerificationKeyStrategy() assert ( - await strategy.get_verification_method_id_for_did(TEST_DID_KEY) + await strategy.get_verification_method_id_for_did(TEST_DID_KEY, Profile()) == DIDKey.from_did(TEST_DID_KEY).key_id ) async def test_unsupported_did_method(self): strategy = DefaultVerificationKeyStrategy() assert ( - await strategy.get_verification_method_id_for_did("did:test:test") is None + await strategy.get_verification_method_id_for_did( + "did:test:test", Profile() + ) + is None ) From c79a50aabb18eb66faf2815f86b882cd06a13b36 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Mon, 26 Jun 2023 19:32:21 +0000 Subject: [PATCH 848/872] 0.8.2-rc1 Signed-off-by: Stephen Curran --- CHANGELOG.md | 9 +++++++++ aries_cloudagent/version.py | 2 +- docs/generated/aries_cloudagent.wallet.rst | 8 ++++++++ open-api/openapi.json | 2 +- open-api/swagger.json | 2 +- 5 files changed, 20 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 544b90755c..8e8fb9608b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,12 @@ There are no breaking changes in this release. - fix: route multitenant connectionless oob invitation [\#2243](https://github.com/hyperledger/aries-cloudagent-python/pull/2243) [TimoGlastra](https://github.com/TimoGlastra) - Fix multitenant/mediation in demo [\#2075](https://github.com/hyperledger/aries-cloudagent-python/pull/2075) [ianco](https://github.com/ianco) - Other Bug and Documentation Fixes + - Assign ~thread.thid with thread_id value [\#2261](https://github.com/hyperledger/aries-cloudagent-python/pull/2261) [usingtechnology](https://github.com/usingtechnology) + - Fix: Do not replace public verkey on mediator [\#2269](https://github.com/hyperledger/aries-cloudagent-python/pull/2269) [mkempa](https://github.com/mkempa) + - Provide an optional Profile to the verification key strategy [\#2265](https://github.com/hyperledger/aries-cloudagent-python/pull/2265) [yvgny](https://github.com/yvgny) + - refactor: Extract verification method ID generation to a separate class [\#2235](https://github.com/hyperledger/aries-cloudagent-python/pull/2235) [yvgny](https://github.com/yvgny) + - Create .readthedocs.yaml file [\#2268](https://github.com/hyperledger/aries-cloudagent-python/pull/2268) [swcurran](https://github.com/swcurran) + - feat(did creation route): reject unregistered did methods [\#2262](https://github.com/hyperledger/aries-cloudagent-python/pull/2262) [chumbert](https://github.com/chumbert) - ./run_demo performance -c 1 --mediation --timing --trace-log [#2245](https://github.com/hyperledger/aries-cloudagent-python/pull/2245) [usingtechnology](https://github.com/usingtechnology) - Fix formatting and grammatical errors in different readme's [\#2222](https://github.com/hyperledger/aries-cloudagent-python/pull/2222) [ff137](https://github.com/ff137) - Fix broken link in README [\#2221](https://github.com/hyperledger/aries-cloudagent-python/pull/2221) [ff137](https://github.com/ff137) @@ -34,6 +40,9 @@ There are no breaking changes in this release. - Dependencies and Internal Updates - Bump requests from 2.30.0 to 2.31.0 in /demo/playground/scripts dependenciesPull requests that update a dependency file [\#2238](https://github.com/hyperledger/aries-cloudagent-python/pull/2238) [dependabot bot](https://github.com/dependabot) - Upgrade codegen tools in scripts/generate-open-api-spec and publish Swagger 2.0 and OpenAPI 3.0 specs [\#2246](https://github.com/hyperledger/aries-cloudagent-python/pull/2246) [ff137](https://github.com/ff137) +- ACA-Py Administrative Updates + - Propose adding Jason Sherman [usingtechnology](https://github.com/usingtechnology) as a Maintainer [\#2263](https://github.com/hyperledger/aries-cloudagent-python/pull/2263) [swcurran](https://github.com/swcurran) + - Updating Maintainers list to be accurate and using the TOC format [\#2258](https://github.com/hyperledger/aries-cloudagent-python/pull/2258) [swcurran](https://github.com/swcurran) - Message Tracing/Timing Updates - Add updated ELK stack for demos. [\#2236](https://github.com/hyperledger/aries-cloudagent-python/pull/2236) [usingtechnology](https://github.com/usingtechnology) - Release management pull requests diff --git a/aries_cloudagent/version.py b/aries_cloudagent/version.py index 653bdd623d..02cd553e5e 100644 --- a/aries_cloudagent/version.py +++ b/aries_cloudagent/version.py @@ -1,4 +1,4 @@ """Library version information.""" -__version__ = "0.8.2-rc0" +__version__ = "0.8.2-rc1" RECORD_TYPE_ACAPY_VERSION = "acapy_version" diff --git a/docs/generated/aries_cloudagent.wallet.rst b/docs/generated/aries_cloudagent.wallet.rst index d7ef3b5efa..41de770ff4 100644 --- a/docs/generated/aries_cloudagent.wallet.rst +++ b/docs/generated/aries_cloudagent.wallet.rst @@ -49,6 +49,14 @@ aries\_cloudagent.wallet.crypto module :undoc-members: :show-inheritance: +aries\_cloudagent.wallet.default\_verification\_key\_strategy module +-------------------------------------------------------------------- + +.. automodule:: aries_cloudagent.wallet.default_verification_key_strategy + :members: + :undoc-members: + :show-inheritance: + aries\_cloudagent.wallet.did\_info module ----------------------------------------- diff --git a/open-api/openapi.json b/open-api/openapi.json index 3708ccf5bd..87a283d1e7 100644 --- a/open-api/openapi.json +++ b/open-api/openapi.json @@ -2,7 +2,7 @@ "openapi" : "3.0.1", "info" : { "title" : "Aries Cloud Agent", - "version" : "v0.8.2-rc0" + "version" : "v0.8.2-rc1" }, "servers" : [ { "url" : "/" diff --git a/open-api/swagger.json b/open-api/swagger.json index c2ab7e5cfc..b6b4493c15 100644 --- a/open-api/swagger.json +++ b/open-api/swagger.json @@ -1,7 +1,7 @@ { "swagger" : "2.0", "info" : { - "version" : "v0.8.2-rc0", + "version" : "v0.8.2-rc1", "title" : "Aries Cloud Agent" }, "tags" : [ { From 362f3c23f3d177580291afe2eeff0140437217f5 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Mon, 26 Jun 2023 21:51:04 +0000 Subject: [PATCH 849/872] Add note about PR 2255, and change its title Signed-off-by: Stephen Curran --- CHANGELOG.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e8fb9608b..9747d2d2c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ -# 0.8.2-rc0 +# 0.8.2-rc1 -## June 9, 2023 +## June 26, 2023 Release 0.8.2 contains a number of minor fixes and updates to ACA-Py, including the correction of a regression in Release 0.8.0 related to the use of plugins @@ -8,12 +8,15 @@ the correction of a regression in Release 0.8.0 related to the use of plugins development environment to collect detailed performance information about what is going in within ACA-Py. +For those using plugins, RC1 includes PR [\#2255](https://github.com/hyperledger/aries-cloudagent-python/pull/2255) to fix a backwards compatibility break in +the loading of plugins that was inadvertently introduced in 0.8.1. + There are no breaking changes in this release. ### Categorized List of Pull Requests - Connections Fixes/Updates - - Resolve definitions.py fix [\#2255](https://github.com/hyperledger/aries-cloudagent-python/pull/2255) [usingtechnology](https://github.com/usingtechnology) + - Resolve definitions.py fix to fix backwards compatibility break in plugins [\#2255](https://github.com/hyperledger/aries-cloudagent-python/pull/2255) [usingtechnology](https://github.com/usingtechnology) - Add support for JsonWebKey2020 for the connection invitations [\#2173](https://github.com/hyperledger/aries-cloudagent-python/pull/2173) [dkulic](https://github.com/dkulic) - fix: only cache completed connection targets [\#2240](https://github.com/hyperledger/aries-cloudagent-python/pull/2240) [dbluhm](https://github.com/dbluhm) - Connection target should not be limited only to indy dids [\#2229](https://github.com/hyperledger/aries-cloudagent-python/pull/2229) [dkulic](https://github.com/dkulic) @@ -46,6 +49,7 @@ There are no breaking changes in this release. - Message Tracing/Timing Updates - Add updated ELK stack for demos. [\#2236](https://github.com/hyperledger/aries-cloudagent-python/pull/2236) [usingtechnology](https://github.com/usingtechnology) - Release management pull requests + - 0.8.2-rc1 [\#2282](https://github.com/hyperledger/aries-cloudagent-python/pull/2282) [swcurran](https://github.com/swcurran) - 0.8.2-rc0 [\#2260](https://github.com/hyperledger/aries-cloudagent-python/pull/2260) [swcurran](https://github.com/swcurran) # 0.8.1 From ad12d67db5b7db74eb721fa991c73f0a83aee4eb Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Tue, 16 May 2023 06:55:15 -0700 Subject: [PATCH 850/872] per tenant settings impl Signed-off-by: Shaanjot Gill --- .../ledger/multiple_ledger/base_manager.py | 4 + .../ledger/multiple_ledger/indy_manager.py | 6 + .../multiple_ledger/indy_vdr_manager.py | 7 ++ .../ledger_requests_executor.py | 5 + .../tests/test_indy_ledger_requests.py | 7 ++ .../tests/test_indy_manager.py | 8 ++ .../tests/test_indy_vdr_manager.py | 8 ++ aries_cloudagent/multitenant/admin/routes.py | 66 +++++++++++ .../multitenant/admin/tests/test_routes.py | 104 ++++++++++++++++++ 9 files changed, 215 insertions(+) diff --git a/aries_cloudagent/ledger/multiple_ledger/base_manager.py b/aries_cloudagent/ledger/multiple_ledger/base_manager.py index b421119d22..346f36af77 100644 --- a/aries_cloudagent/ledger/multiple_ledger/base_manager.py +++ b/aries_cloudagent/ledger/multiple_ledger/base_manager.py @@ -37,6 +37,10 @@ async def _get_ledger_by_did( ) -> Optional[Tuple[str, BaseLedger, bool]]: """Build and submit GET_NYM request and process response.""" + @abstractmethod + async def get_ledger_inst_by_id(self, ledger_id: str) -> Optional[BaseLedger]: + """Return ledger instance by identifier.""" + @abstractmethod async def lookup_did_in_configured_ledgers( self, did: str, cache_did: bool diff --git a/aries_cloudagent/ledger/multiple_ledger/indy_manager.py b/aries_cloudagent/ledger/multiple_ledger/indy_manager.py index e8f86e8577..9ba534b19f 100644 --- a/aries_cloudagent/ledger/multiple_ledger/indy_manager.py +++ b/aries_cloudagent/ledger/multiple_ledger/indy_manager.py @@ -60,6 +60,12 @@ async def get_write_ledger(self) -> Optional[Tuple[str, IndySdkLedger]]: else: return None + async def get_ledger_inst_by_id(self, ledger_id: str) -> Optional[BaseLedger]: + """Return BaseLedger instance.""" + return self.production_ledgers.get( + ledger_id + ) or self.non_production_ledgers.get(ledger_id) + async def get_prod_ledgers(self) -> Mapping: """Return production ledgers mapping.""" return self.production_ledgers diff --git a/aries_cloudagent/ledger/multiple_ledger/indy_vdr_manager.py b/aries_cloudagent/ledger/multiple_ledger/indy_vdr_manager.py index 8cb16f6b5a..0c4d09aa26 100644 --- a/aries_cloudagent/ledger/multiple_ledger/indy_vdr_manager.py +++ b/aries_cloudagent/ledger/multiple_ledger/indy_vdr_manager.py @@ -9,6 +9,7 @@ from ...cache.base import BaseCache from ...core.profile import Profile +from ...ledger.base import BaseLedger from ...ledger.error import LedgerError from ...wallet.crypto import did_is_self_certified @@ -63,6 +64,12 @@ async def get_nonprod_ledgers(self) -> Mapping: """Return non_production ledgers mapping.""" return self.non_production_ledgers + async def get_ledger_inst_by_id(self, ledger_id: str) -> Optional[BaseLedger]: + """Return BaseLedger instance.""" + return self.production_ledgers.get( + ledger_id + ) or self.non_production_ledgers.get(ledger_id) + async def _get_ledger_by_did( self, ledger_id: str, diff --git a/aries_cloudagent/ledger/multiple_ledger/ledger_requests_executor.py b/aries_cloudagent/ledger/multiple_ledger/ledger_requests_executor.py index a56d93dfef..0cf0b4dcb8 100644 --- a/aries_cloudagent/ledger/multiple_ledger/ledger_requests_executor.py +++ b/aries_cloudagent/ledger/multiple_ledger/ledger_requests_executor.py @@ -61,3 +61,8 @@ async def get_ledger_for_identifier( except (MultipleLedgerManagerError, InjectionError): pass return (None, self.profile.inject_or(BaseLedger)) + + async def get_ledger_inst(self, ledger_id: str) -> Optional[BaseLedger]: + """Return ledger instance from ledger_id set in config.""" + multiledger_mgr = self.profile.inject(BaseMultipleLedgerManager) + return await multiledger_mgr.get_ledger_inst_by_id(ledger_id=ledger_id) diff --git a/aries_cloudagent/ledger/multiple_ledger/tests/test_indy_ledger_requests.py b/aries_cloudagent/ledger/multiple_ledger/tests/test_indy_ledger_requests.py index 52e6d52f41..f087660fa5 100644 --- a/aries_cloudagent/ledger/multiple_ledger/tests/test_indy_ledger_requests.py +++ b/aries_cloudagent/ledger/multiple_ledger/tests/test_indy_ledger_requests.py @@ -38,6 +38,9 @@ async def setUp(self): lookup_did_in_configured_ledgers=async_mock.CoroutineMock( return_value=("test_prod_1", self.ledger) ), + get_ledger_inst_by_id=async_mock.CoroutineMock( + return_value=self.ledger + ), ), ) self.profile.context.injector.bind_instance(BaseLedger, self.ledger) @@ -53,6 +56,10 @@ async def test_get_ledger_for_identifier(self): assert ledger_id == "test_prod_1" assert ledger_inst.pool.name == "test_prod_1" + async def test_get_ledger_inst(self): + ledger_inst = await self.indy_ledger_requestor.get_ledger_inst("test_prod_1") + assert ledger_inst + async def test_get_ledger_for_identifier_is_digit(self): ledger_id, ledger = await self.indy_ledger_requestor.get_ledger_for_identifier( "123", 0 diff --git a/aries_cloudagent/ledger/multiple_ledger/tests/test_indy_manager.py b/aries_cloudagent/ledger/multiple_ledger/tests/test_indy_manager.py index 572992a313..fc3f972999 100644 --- a/aries_cloudagent/ledger/multiple_ledger/tests/test_indy_manager.py +++ b/aries_cloudagent/ledger/multiple_ledger/tests/test_indy_manager.py @@ -60,6 +60,14 @@ async def test_get_write_ledger(self): assert ledger_id == "test_prod_1" assert ledger_inst.pool.name == "test_prod_1" + async def test_get_ledger_inst_by_id(self): + ledger_inst = await self.manager.get_ledger_inst_by_id("test_prod_2") + assert ledger_inst + ledger_inst = await self.manager.get_ledger_inst_by_id("test_non_prod_2") + assert ledger_inst + ledger_inst = await self.manager.get_ledger_inst_by_id("test_invalid") + assert not ledger_inst + @async_mock.patch("aries_cloudagent.ledger.indy.IndySdkLedgerPool.context_open") @async_mock.patch("aries_cloudagent.ledger.indy.IndySdkLedgerPool.context_close") @async_mock.patch("indy.ledger.build_get_nym_request") diff --git a/aries_cloudagent/ledger/multiple_ledger/tests/test_indy_vdr_manager.py b/aries_cloudagent/ledger/multiple_ledger/tests/test_indy_vdr_manager.py index 86aa27a3d5..4c4798750d 100644 --- a/aries_cloudagent/ledger/multiple_ledger/tests/test_indy_vdr_manager.py +++ b/aries_cloudagent/ledger/multiple_ledger/tests/test_indy_vdr_manager.py @@ -88,6 +88,14 @@ async def test_get_write_ledger(self): assert ledger_id == "test_prod_1" assert ledger_inst.pool.name == "test_prod_1" + async def test_get_ledger_inst_by_id(self): + ledger_inst = await self.manager.get_ledger_inst_by_id("test_prod_2") + assert ledger_inst + ledger_inst = await self.manager.get_ledger_inst_by_id("test_non_prod_2") + assert ledger_inst + ledger_inst = await self.manager.get_ledger_inst_by_id("test_invalid") + assert not ledger_inst + @async_mock.patch("aries_cloudagent.ledger.indy_vdr.IndyVdrLedgerPool.context_open") @async_mock.patch( "aries_cloudagent.ledger.indy_vdr.IndyVdrLedgerPool.context_close" diff --git a/aries_cloudagent/multitenant/admin/routes.py b/aries_cloudagent/multitenant/admin/routes.py index e95bac1f59..3474f89ecb 100644 --- a/aries_cloudagent/multitenant/admin/routes.py +++ b/aries_cloudagent/multitenant/admin/routes.py @@ -23,6 +23,32 @@ from ..error import WalletKeyMissingError +ACAPY_LIFECYCLE_CONFIG_FLAG_MAP = { + "ACAPY_LOG_LEVEL": "log.level", + "ACAPY_INVITE_PUBLIC": "debug.invite_public", + "ACAPY_PUBLIC_INVITES": "public_invites", + "ACAPY_AUTO_ACCEPT_INVITES": "debug.auto_accept_invites", + "ACAPY_AUTO_ACCEPT_REQUESTS": "debug.auto_accept_requests", + "ACAPY_AUTO_PING_CONNECTION": "auto_ping_connection", + "ACAPY_MONITOR_PING": "debug.monitor_ping", + "ACAPY_AUTO_RESPOND_MESSAGES": "debug.auto_respond_messages", + "ACAPY_AUTO_RESPOND_CREDENTIAL_OFFER": "debug.auto_resopnd_credential_offer", + "ACAPY_AUTO_RESPOND_CREDENTIAL_REQUEST": "debug.auto_respond_credential_request", + "ACAPY_AUTO_VERIFY_PRESENTATION": "debug.auto_verify_presentation", + "ACAPY_NOTIFY_REVOCATION": "revocation.notify", + "ACAPY_AUTO_REQUEST_ENDORSEMENT": "endorser.auto_request", + "ACAPY_AUTO_WRITE_TRANSACTIONS": "endorser.auto_write", + "ACAPY_CREATE_REVOCATION_TRANSACTIONS": "endorser.auto_create_rev_reg", + "ACAPY_ENDORSER_ROLE": "endorser.protocol_role", +} + +ACAPY_ENDORSER_FLGAS_DEPENDENT_ON_AUTHOR_ROLE = [ + "ACAPY_AUTO_REQUEST_ENDORSEMENT", + "ACAPY_AUTO_WRITE_TRANSACTIONS", + "ACAPY_CREATE_REVOCATION_TRANSACTIONS", +] + + def format_wallet_record(wallet_record: WalletRecord): """Serialize a WalletRecord object.""" @@ -35,6 +61,29 @@ def format_wallet_record(wallet_record: WalletRecord): return wallet_info +def get_extra_settings_dict_per_tenant(tenant_settings: dict) -> dict: + """Get per tenant settings to be applied when creating wallet.""" + + endorser_role_flag = tenant_settings.get("ACAPY_ENDORSER_ROLE") + extra_settings = {} + if endorser_role_flag == "author": + extra_settings["endorser.author"] = True + elif endorser_role_flag == "endorser": + extra_settings["endorser.endorser"] = True + for flag in tenant_settings.keys(): + if ( + flag in ACAPY_ENDORSER_FLGAS_DEPENDENT_ON_AUTHOR_ROLE + and endorser_role_flag != "author" + ): + # These flags require endorser role as author, if not set as author then + # this setting will be ignored. + continue + if flag != "ACAPY_ENDORSER_ROLE": + map_flag = ACAPY_LIFECYCLE_CONFIG_FLAG_MAP[flag] + extra_settings[map_flag] = tenant_settings[flag] + return extra_settings + + class MultitenantModuleResponseSchema(OpenAPISchema): """Response schema for multitenant module.""" @@ -56,6 +105,12 @@ class CreateWalletRequestSchema(OpenAPISchema): description="Master key used for key derivation.", example="MySecretKey123" ) + extra_settings = fields.Dict( + keys=fields.Str(description="Agent Config Flag"), + values=fields.Str(description="Parameter"), + allow_none=True, + ) + wallet_key_derivation = fields.Str( description="Key derivation", required=False, @@ -142,6 +197,11 @@ class UpdateWalletRequestSchema(OpenAPISchema): default="default", validate=validate.OneOf(["default", "both", "base"]), ) + extra_settings = fields.Dict( + keys=fields.Str(description="Agent Config Flag"), + values=fields.Str(description="Parameter"), + allow_none=True, + ) wallet_webhook_urls = fields.List( fields.Str( description="Optional webhook URL to receive webhook messages", @@ -294,6 +354,7 @@ async def wallet_create(request: web.BaseRequest): wallet_key = body.get("wallet_key") wallet_webhook_urls = body.get("wallet_webhook_urls") or [] wallet_dispatch_type = body.get("wallet_dispatch_type") or "default" + extra_settings = body.get("extra_settings") or {} # If no webhooks specified, then dispatch only to base webhook targets if wallet_webhook_urls == []: wallet_dispatch_type = "base" @@ -305,6 +366,8 @@ async def wallet_create(request: web.BaseRequest): "wallet.webhook_urls": wallet_webhook_urls, "wallet.dispatch_type": wallet_dispatch_type, } + extra_subwallet_setting = get_extra_settings_dict_per_tenant(extra_settings) + settings.update(extra_subwallet_setting) label = body.get("label") image_url = body.get("image_url") @@ -354,6 +417,7 @@ async def wallet_update(request: web.BaseRequest): wallet_dispatch_type = body.get("wallet_dispatch_type") label = body.get("label") image_url = body.get("image_url") + extra_settings = body.get("extra_settings") or {} if all( v is None for v in (wallet_webhook_urls, wallet_dispatch_type, label, image_url) @@ -376,6 +440,8 @@ async def wallet_update(request: web.BaseRequest): settings["default_label"] = label if image_url is not None: settings["image_url"] = image_url + extra_subwallet_setting = get_extra_settings_dict_per_tenant(extra_settings) + settings.update(extra_subwallet_setting) try: multitenant_mgr = context.profile.inject(BaseMultitenantManager) diff --git a/aries_cloudagent/multitenant/admin/tests/test_routes.py b/aries_cloudagent/multitenant/admin/tests/test_routes.py index 98ad47dca2..312997c4b6 100644 --- a/aries_cloudagent/multitenant/admin/tests/test_routes.py +++ b/aries_cloudagent/multitenant/admin/tests/test_routes.py @@ -139,6 +139,63 @@ async def test_wallets_list_query(self): } ) + async def test_wallet_create_tenant_settings(self): + body = { + "wallet_name": "test", + "default_label": "test_label", + "wallet_type": "indy", + "wallet_key": "test", + "key_management_mode": "managed", + "wallet_webhook_urls": [], + "wallet_dispatch_type": "base", + "extra_settings": { + "ACAPY_LOG_LEVEL": "INFO", + "ACAPY_INVITE_PUBLIC": True, + "ACAPY_PUBLIC_INVITES": True, + }, + } + self.request.json = async_mock.CoroutineMock(return_value=body) + + with async_mock.patch.object(test_module.web, "json_response") as mock_response: + wallet_mock = async_mock.MagicMock( + serialize=async_mock.MagicMock( + return_value={ + "wallet_id": "test", + "settings": {}, + "key_management_mode": body["key_management_mode"], + } + ) + ) # wallet_record + self.mock_multitenant_mgr.create_wallet = async_mock.CoroutineMock( + return_value=wallet_mock + ) + + self.mock_multitenant_mgr.create_auth_token = async_mock.CoroutineMock( + return_value="test_token" + ) + print(self.request["context"]) + await test_module.wallet_create(self.request) + + self.mock_multitenant_mgr.create_wallet.assert_called_once_with( + { + "wallet.name": body["wallet_name"], + "wallet.type": body["wallet_type"], + "wallet.key": body["wallet_key"], + "wallet.webhook_urls": body["wallet_webhook_urls"], + "wallet.dispatch_type": body["wallet_dispatch_type"], + "log.level": "INFO", + "debug.invite_public": True, + "public_invites": True, + }, + body["key_management_mode"], + ) + self.mock_multitenant_mgr.create_auth_token.assert_called_once_with( + wallet_mock, body["wallet_key"] + ) + mock_response.assert_called_once_with( + {**test_module.format_wallet_record(wallet_mock), "token": "test_token"} + ) + async def test_wallet_create(self): body = { "wallet_name": "test", @@ -259,6 +316,53 @@ async def test_wallet_create_raw_key_derivation(self): WalletRecord.MODE_MANAGED, ) + async def test_wallet_update_tenant_settings(self): + self.request.match_info = {"wallet_id": "test-wallet-id"} + body = { + "wallet_webhook_urls": ["test-webhook-url"], + "wallet_dispatch_type": "default", + "label": "test-label", + "image_url": "test-image-url", + "extra_settings": { + "ACAPY_LOG_LEVEL": "INFO", + "ACAPY_INVITE_PUBLIC": True, + "ACAPY_PUBLIC_INVITES": True, + }, + } + self.request.json = async_mock.CoroutineMock(return_value=body) + + with async_mock.patch.object(test_module.web, "json_response") as mock_response: + settings = { + "wallet.webhook_urls": body["wallet_webhook_urls"], + "wallet.dispatch_type": body["wallet_dispatch_type"], + "default_label": body["label"], + "image_url": body["image_url"], + "log.level": "INFO", + "debug.invite_public": True, + "public_invites": True, + } + wallet_mock = async_mock.MagicMock( + serialize=async_mock.MagicMock( + return_value={ + "wallet_id": "test-wallet-id", + "settings": settings, + } + ) + ) + self.mock_multitenant_mgr.update_wallet = async_mock.CoroutineMock( + return_value=wallet_mock + ) + + await test_module.wallet_update(self.request) + + self.mock_multitenant_mgr.update_wallet.assert_called_once_with( + "test-wallet-id", + settings, + ) + mock_response.assert_called_once_with( + {"wallet_id": "test-wallet-id", "settings": settings} + ) + async def test_wallet_update(self): self.request.match_info = {"wallet_id": "test-wallet-id"} body = { From f162284088db317f3bd3d96786736cd0fe94c458 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Tue, 16 May 2023 07:46:56 -0700 Subject: [PATCH 851/872] fix processing bool values Signed-off-by: Shaanjot Gill --- aries_cloudagent/multitenant/admin/routes.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/multitenant/admin/routes.py b/aries_cloudagent/multitenant/admin/routes.py index 3474f89ecb..ace16ff201 100644 --- a/aries_cloudagent/multitenant/admin/routes.py +++ b/aries_cloudagent/multitenant/admin/routes.py @@ -61,6 +61,15 @@ def format_wallet_record(wallet_record: WalletRecord): return wallet_info +def process_bool_label(label): + if label == "True" or label == "true": + return True + elif label == "False" or label == "false": + return False + else: + return label + + def get_extra_settings_dict_per_tenant(tenant_settings: dict) -> dict: """Get per tenant settings to be applied when creating wallet.""" @@ -80,7 +89,7 @@ def get_extra_settings_dict_per_tenant(tenant_settings: dict) -> dict: continue if flag != "ACAPY_ENDORSER_ROLE": map_flag = ACAPY_LIFECYCLE_CONFIG_FLAG_MAP[flag] - extra_settings[map_flag] = tenant_settings[flag] + extra_settings[map_flag] = process_bool_label(tenant_settings[flag]) return extra_settings From a77b91967251cda67a563cbab0b2f5c2848edf4a Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Wed, 17 May 2023 05:45:25 -0700 Subject: [PATCH 852/872] flake8 fix Signed-off-by: Shaanjot Gill --- aries_cloudagent/multitenant/admin/routes.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/multitenant/admin/routes.py b/aries_cloudagent/multitenant/admin/routes.py index ace16ff201..a113bcfcab 100644 --- a/aries_cloudagent/multitenant/admin/routes.py +++ b/aries_cloudagent/multitenant/admin/routes.py @@ -9,6 +9,7 @@ response_schema, ) from marshmallow import ValidationError, fields, validate, validates_schema +from typing import Union from ...admin.request_context import AdminRequestContext from ...core.error import BaseError @@ -61,7 +62,8 @@ def format_wallet_record(wallet_record: WalletRecord): return wallet_info -def process_bool_label(label): +def process_bool_label(label: str) -> Union[bool, str]: + """Return processed extra settings dict value.""" if label == "True" or label == "true": return True elif label == "False" or label == "false": From d34d065d523b1c9e2f9884228ea5974fe27fb8aa Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Thu, 1 Jun 2023 09:58:28 -0700 Subject: [PATCH 853/872] doc update Signed-off-by: Shaanjot Gill --- Multitenancy.md | 65 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/Multitenancy.md b/Multitenancy.md index 032c43be2a..75704420b0 100644 --- a/Multitenancy.md +++ b/Multitenancy.md @@ -190,6 +190,71 @@ WebSocket example: } ``` +## Per tenant settings + +To allow configurablity of ACA-Py startup parameters/environment variables at a tenant/subwallet level. [PR#2233](https://github.com/hyperledger/aries-cloudagent-python/pull/2233) will provide the ability to update the following subset of settings when creating or updating the subwallet: + +| Label | Setting | +|---|---| +| ACAPY_LOG_LEVEL | log.level | +| ACAPY_INVITE_PUBLIC | debug.invite_public | +| ACAPY_PUBLIC_INVITES | public_invites | +| ACAPY_AUTO_ACCEPT_INVITES | debug.auto_accept_invites | +| ACAPY_AUTO_ACCEPT_REQUESTS | debug.auto_accept_requests | +| ACAPY_AUTO_PING_CONNECTION | auto_ping_connection | +| ACAPY_MONITOR_PING | debug.monitor_ping | +| ACAPY_AUTO_RESPOND_MESSAGES | debug.auto_respond_messages | +| ACAPY_AUTO_RESPOND_CREDENTIAL_OFFER | debug.auto_resopnd_credential_offer | +| ACAPY_AUTO_RESPOND_CREDENTIAL_REQUEST | debug.auto_respond_credential_request | +| ACAPY_AUTO_VERIFY_PRESENTATION | debug.auto_verify_presentation | +| ACAPY_NOTIFY_REVOCATION | revocation.notify | +| ACAPY_AUTO_REQUEST_ENDORSEMENT | endorser.auto_request | +| ACAPY_AUTO_WRITE_TRANSACTIONS | endorser.auto_write | +| ACAPY_CREATE_REVOCATION_TRANSACTIONS | endorser.auto_create_rev_reg | +| ACAPY_ENDORSER_ROLE | endorser.protocol_role | + +- `POST /multitenancy/wallet` + + Added `extra_settings` dict field to request schema. `extra_settings` can be configured in the request body as below: + + ``` + Example request body + { + "wallet_name": " ... ", + "default_label": " ... ", + "wallet_type": " ... ", + "wallet_key": " ... ", + "key_management_mode": "managed", + "wallet_webhook_urls": [], + "wallet_dispatch_type": "base", + "extra_settings": { + "ACAPY_LOG_LEVEL": "INFO", + "ACAPY_INVITE_PUBLIC": "true", + "ACAPY_PUBLIC_INVITES": "True", + }, + } + ``` + +- `PUT /multitenancy/wallet/{wallet_id}` + + Added `extra_settings` dict field to request schema. + + ``` + { + "wallet_webhook_urls": [ ... ], + "wallet_dispatch_type": "default", + "label": " ... ", + "image_url": " ... ", + "extra_settings": { + "ACAPY_LOG_LEVEL": "INFO", + "ACAPY_INVITE_PUBLIC": "True", + "ACAPY_PUBLIC_INVITES": "false", + }, + } + ``` + +All the currently suppoorted settings in `extra_settings` are either accepting str or boolean. But the bool attributes in the request body should be passed along as a string such as `"true", "True", "false", "False"`. + ## Authentication When multi-tenancy is not enabled you can authenticate with the agent using the `x-api-key` header. As there is only a single wallet, this provides sufficient authentication and authorization. From eaf7eea04f1503a0b196011cfaf0344d2a5b5f8d Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Thu, 1 Jun 2023 10:05:29 -0700 Subject: [PATCH 854/872] doc update Signed-off-by: Shaanjot Gill --- Multitenancy.md | 146 +++++++++++++++++++++++++++--------------------- 1 file changed, 81 insertions(+), 65 deletions(-) diff --git a/Multitenancy.md b/Multitenancy.md index 75704420b0..0284767eb9 100644 --- a/Multitenancy.md +++ b/Multitenancy.md @@ -29,6 +29,7 @@ This allows ACA-Py to be used for a wider range of use cases. One use case could - [Tenant Management](#tenant-management) - [Update a tenant](#update-a-tenant) - [Remove a tenant](#remove-a-tenant) + - [Per tenant settings](#per-tenant-settings) ## General Concept @@ -190,71 +191,6 @@ WebSocket example: } ``` -## Per tenant settings - -To allow configurablity of ACA-Py startup parameters/environment variables at a tenant/subwallet level. [PR#2233](https://github.com/hyperledger/aries-cloudagent-python/pull/2233) will provide the ability to update the following subset of settings when creating or updating the subwallet: - -| Label | Setting | -|---|---| -| ACAPY_LOG_LEVEL | log.level | -| ACAPY_INVITE_PUBLIC | debug.invite_public | -| ACAPY_PUBLIC_INVITES | public_invites | -| ACAPY_AUTO_ACCEPT_INVITES | debug.auto_accept_invites | -| ACAPY_AUTO_ACCEPT_REQUESTS | debug.auto_accept_requests | -| ACAPY_AUTO_PING_CONNECTION | auto_ping_connection | -| ACAPY_MONITOR_PING | debug.monitor_ping | -| ACAPY_AUTO_RESPOND_MESSAGES | debug.auto_respond_messages | -| ACAPY_AUTO_RESPOND_CREDENTIAL_OFFER | debug.auto_resopnd_credential_offer | -| ACAPY_AUTO_RESPOND_CREDENTIAL_REQUEST | debug.auto_respond_credential_request | -| ACAPY_AUTO_VERIFY_PRESENTATION | debug.auto_verify_presentation | -| ACAPY_NOTIFY_REVOCATION | revocation.notify | -| ACAPY_AUTO_REQUEST_ENDORSEMENT | endorser.auto_request | -| ACAPY_AUTO_WRITE_TRANSACTIONS | endorser.auto_write | -| ACAPY_CREATE_REVOCATION_TRANSACTIONS | endorser.auto_create_rev_reg | -| ACAPY_ENDORSER_ROLE | endorser.protocol_role | - -- `POST /multitenancy/wallet` - - Added `extra_settings` dict field to request schema. `extra_settings` can be configured in the request body as below: - - ``` - Example request body - { - "wallet_name": " ... ", - "default_label": " ... ", - "wallet_type": " ... ", - "wallet_key": " ... ", - "key_management_mode": "managed", - "wallet_webhook_urls": [], - "wallet_dispatch_type": "base", - "extra_settings": { - "ACAPY_LOG_LEVEL": "INFO", - "ACAPY_INVITE_PUBLIC": "true", - "ACAPY_PUBLIC_INVITES": "True", - }, - } - ``` - -- `PUT /multitenancy/wallet/{wallet_id}` - - Added `extra_settings` dict field to request schema. - - ``` - { - "wallet_webhook_urls": [ ... ], - "wallet_dispatch_type": "default", - "label": " ... ", - "image_url": " ... ", - "extra_settings": { - "ACAPY_LOG_LEVEL": "INFO", - "ACAPY_INVITE_PUBLIC": "True", - "ACAPY_PUBLIC_INVITES": "false", - }, - } - ``` - -All the currently suppoorted settings in `extra_settings` are either accepting str or boolean. But the bool attributes in the request body should be passed along as a string such as `"true", "True", "false", "False"`. - ## Authentication When multi-tenancy is not enabled you can authenticate with the agent using the `x-api-key` header. As there is only a single wallet, this provides sufficient authentication and authorization. @@ -440,3 +376,83 @@ curl -X POST "${ACAPY_ADMIN_URL}/multitenancy/wallet/{wallet_id}/remove" \ ```jsonc {} ``` + +### Per tenant settings + +To allow configurablity of ACA-Py startup parameters/environment variables at a tenant/subwallet level. [PR#2233](https://github.com/hyperledger/aries-cloudagent-python/pull/2233) will provide the ability to update the following subset of settings when creating or updating the subwallet: + +| Label | Setting | +|---|---| +| ACAPY_LOG_LEVEL | log.level | +| ACAPY_INVITE_PUBLIC | debug.invite_public | +| ACAPY_PUBLIC_INVITES | public_invites | +| ACAPY_AUTO_ACCEPT_INVITES | debug.auto_accept_invites | +| ACAPY_AUTO_ACCEPT_REQUESTS | debug.auto_accept_requests | +| ACAPY_AUTO_PING_CONNECTION | auto_ping_connection | +| ACAPY_MONITOR_PING | debug.monitor_ping | +| ACAPY_AUTO_RESPOND_MESSAGES | debug.auto_respond_messages | +| ACAPY_AUTO_RESPOND_CREDENTIAL_OFFER | debug.auto_resopnd_credential_offer | +| ACAPY_AUTO_RESPOND_CREDENTIAL_REQUEST | debug.auto_respond_credential_request | +| ACAPY_AUTO_VERIFY_PRESENTATION | debug.auto_verify_presentation | +| ACAPY_NOTIFY_REVOCATION | revocation.notify | +| ACAPY_AUTO_REQUEST_ENDORSEMENT | endorser.auto_request | +| ACAPY_AUTO_WRITE_TRANSACTIONS | endorser.auto_write | +| ACAPY_CREATE_REVOCATION_TRANSACTIONS | endorser.auto_create_rev_reg | +| ACAPY_ENDORSER_ROLE | endorser.protocol_role | + +- `POST /multitenancy/wallet` + + Added `extra_settings` dict field to request schema. `extra_settings` can be configured in the request body as below: + + **`Example Request`** + ``` + { + "wallet_name": " ... ", + "default_label": " ... ", + "wallet_type": " ... ", + "wallet_key": " ... ", + "key_management_mode": "managed", + "wallet_webhook_urls": [], + "wallet_dispatch_type": "base", + "extra_settings": { + "ACAPY_LOG_LEVEL": "INFO", + "ACAPY_INVITE_PUBLIC": "true", + "ACAPY_PUBLIC_INVITES": "True", + }, + } + ``` + + ```sh + echo $new_tenant | curl -X POST "${ACAPY_ADMIN_URL}/multitenancy/wallet" \ + -H "Content-Type: application/json" \ + -H "X-Api-Key: $ACAPY_ADMIN_URL_API_KEY" \ + -d @- + ``` + +- `PUT /multitenancy/wallet/{wallet_id}` + + Added `extra_settings` dict field to request schema. + + **`Example Request`** + ``` + { + "wallet_webhook_urls": [ ... ], + "wallet_dispatch_type": "default", + "label": " ... ", + "image_url": " ... ", + "extra_settings": { + "ACAPY_LOG_LEVEL": "INFO", + "ACAPY_INVITE_PUBLIC": "True", + "ACAPY_PUBLIC_INVITES": "false", + }, + } + ``` + + ```sh + echo $update_tenant | curl -X PUT "${ACAPY_ADMIN_URL}/multitenancy/wallet/${WALLET_ID}" \ + -H "Content-Type: application/json" \ + -H "x-api-key: $ACAPY_ADMIN_URL_API_KEY" \ + -d @- + ``` + +All the currently suppoorted settings in `extra_settings` are either accepting str or boolean. But the bool attributes in the request body should be passed along as a string such as `"true", "True", "false", "False"`. From 6be7ea60d1dce0f187f5fa03eff02ab2cb6c03b5 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Fri, 2 Jun 2023 08:54:54 -0700 Subject: [PATCH 855/872] typo fix Signed-off-by: Shaanjot Gill --- aries_cloudagent/multitenant/admin/routes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/multitenant/admin/routes.py b/aries_cloudagent/multitenant/admin/routes.py index a113bcfcab..faa38efee6 100644 --- a/aries_cloudagent/multitenant/admin/routes.py +++ b/aries_cloudagent/multitenant/admin/routes.py @@ -43,7 +43,7 @@ "ACAPY_ENDORSER_ROLE": "endorser.protocol_role", } -ACAPY_ENDORSER_FLGAS_DEPENDENT_ON_AUTHOR_ROLE = [ +ACAPY_ENDORSER_FLAGS_DEPENDENT_ON_AUTHOR_ROLE = [ "ACAPY_AUTO_REQUEST_ENDORSEMENT", "ACAPY_AUTO_WRITE_TRANSACTIONS", "ACAPY_CREATE_REVOCATION_TRANSACTIONS", @@ -83,7 +83,7 @@ def get_extra_settings_dict_per_tenant(tenant_settings: dict) -> dict: extra_settings["endorser.endorser"] = True for flag in tenant_settings.keys(): if ( - flag in ACAPY_ENDORSER_FLGAS_DEPENDENT_ON_AUTHOR_ROLE + flag in ACAPY_ENDORSER_FLAGS_DEPENDENT_ON_AUTHOR_ROLE and endorser_role_flag != "author" ): # These flags require endorser role as author, if not set as author then From 70607190d68b1f3905d8868a2cd8e54bab7f17ad Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Mon, 19 Jun 2023 11:20:45 -0700 Subject: [PATCH 856/872] extra_settings dict field update Signed-off-by: Shaanjot Gill --- Multitenancy.md | 10 ++++----- aries_cloudagent/multitenant/admin/routes.py | 23 +++++--------------- 2 files changed, 9 insertions(+), 24 deletions(-) diff --git a/Multitenancy.md b/Multitenancy.md index 0284767eb9..c6d5fb87c3 100644 --- a/Multitenancy.md +++ b/Multitenancy.md @@ -416,8 +416,8 @@ To allow configurablity of ACA-Py startup parameters/environment variables at a "wallet_dispatch_type": "base", "extra_settings": { "ACAPY_LOG_LEVEL": "INFO", - "ACAPY_INVITE_PUBLIC": "true", - "ACAPY_PUBLIC_INVITES": "True", + "ACAPY_INVITE_PUBLIC": true, + "ACAPY_PUBLIC_INVITES": true }, } ``` @@ -442,8 +442,8 @@ To allow configurablity of ACA-Py startup parameters/environment variables at a "image_url": " ... ", "extra_settings": { "ACAPY_LOG_LEVEL": "INFO", - "ACAPY_INVITE_PUBLIC": "True", - "ACAPY_PUBLIC_INVITES": "false", + "ACAPY_INVITE_PUBLIC": true, + "ACAPY_PUBLIC_INVITES": false }, } ``` @@ -454,5 +454,3 @@ To allow configurablity of ACA-Py startup parameters/environment variables at a -H "x-api-key: $ACAPY_ADMIN_URL_API_KEY" \ -d @- ``` - -All the currently suppoorted settings in `extra_settings` are either accepting str or boolean. But the bool attributes in the request body should be passed along as a string such as `"true", "True", "false", "False"`. diff --git a/aries_cloudagent/multitenant/admin/routes.py b/aries_cloudagent/multitenant/admin/routes.py index faa38efee6..eae4c64e8e 100644 --- a/aries_cloudagent/multitenant/admin/routes.py +++ b/aries_cloudagent/multitenant/admin/routes.py @@ -9,7 +9,6 @@ response_schema, ) from marshmallow import ValidationError, fields, validate, validates_schema -from typing import Union from ...admin.request_context import AdminRequestContext from ...core.error import BaseError @@ -62,16 +61,6 @@ def format_wallet_record(wallet_record: WalletRecord): return wallet_info -def process_bool_label(label: str) -> Union[bool, str]: - """Return processed extra settings dict value.""" - if label == "True" or label == "true": - return True - elif label == "False" or label == "false": - return False - else: - return label - - def get_extra_settings_dict_per_tenant(tenant_settings: dict) -> dict: """Get per tenant settings to be applied when creating wallet.""" @@ -91,7 +80,7 @@ def get_extra_settings_dict_per_tenant(tenant_settings: dict) -> dict: continue if flag != "ACAPY_ENDORSER_ROLE": map_flag = ACAPY_LIFECYCLE_CONFIG_FLAG_MAP[flag] - extra_settings[map_flag] = process_bool_label(tenant_settings[flag]) + extra_settings[map_flag] = tenant_settings[flag] return extra_settings @@ -117,9 +106,8 @@ class CreateWalletRequestSchema(OpenAPISchema): ) extra_settings = fields.Dict( - keys=fields.Str(description="Agent Config Flag"), - values=fields.Str(description="Parameter"), - allow_none=True, + description="Agent config key-value pairs", + required=False, ) wallet_key_derivation = fields.Str( @@ -209,9 +197,8 @@ class UpdateWalletRequestSchema(OpenAPISchema): validate=validate.OneOf(["default", "both", "base"]), ) extra_settings = fields.Dict( - keys=fields.Str(description="Agent Config Flag"), - values=fields.Str(description="Parameter"), - allow_none=True, + description="Agent config key-value pairs", + required=False, ) wallet_webhook_urls = fields.List( fields.Str( From 5dfc85da002b32933ce578ad4b7b545dc23e121d Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Wed, 21 Jun 2023 16:58:05 -0700 Subject: [PATCH 857/872] settings endpoint + other changes Signed-off-by: Shaanjot Gill --- Multitenancy.md | 38 +++--- aries_cloudagent/config/base.py | 4 + aries_cloudagent/config/default_context.py | 1 + aries_cloudagent/config/logging.py | 14 +-- aries_cloudagent/config/plugin_settings.py | 7 ++ aries_cloudagent/config/settings.py | 7 ++ aries_cloudagent/multitenant/admin/routes.py | 38 +++++- aries_cloudagent/settings/__init__.py | 0 aries_cloudagent/settings/routes.py | 113 ++++++++++++++++++ aries_cloudagent/settings/tests/__init__.py | 0 .../settings/tests/test_routes.py | 96 +++++++++++++++ open-api/openapi.json | 45 +++++++ open-api/swagger.json | 45 +++++++ 13 files changed, 377 insertions(+), 31 deletions(-) create mode 100644 aries_cloudagent/settings/__init__.py create mode 100644 aries_cloudagent/settings/routes.py create mode 100644 aries_cloudagent/settings/tests/__init__.py create mode 100644 aries_cloudagent/settings/tests/test_routes.py diff --git a/Multitenancy.md b/Multitenancy.md index c6d5fb87c3..3aaab3314f 100644 --- a/Multitenancy.md +++ b/Multitenancy.md @@ -381,24 +381,24 @@ curl -X POST "${ACAPY_ADMIN_URL}/multitenancy/wallet/{wallet_id}/remove" \ To allow configurablity of ACA-Py startup parameters/environment variables at a tenant/subwallet level. [PR#2233](https://github.com/hyperledger/aries-cloudagent-python/pull/2233) will provide the ability to update the following subset of settings when creating or updating the subwallet: -| Label | Setting | -|---|---| -| ACAPY_LOG_LEVEL | log.level | -| ACAPY_INVITE_PUBLIC | debug.invite_public | -| ACAPY_PUBLIC_INVITES | public_invites | -| ACAPY_AUTO_ACCEPT_INVITES | debug.auto_accept_invites | -| ACAPY_AUTO_ACCEPT_REQUESTS | debug.auto_accept_requests | -| ACAPY_AUTO_PING_CONNECTION | auto_ping_connection | -| ACAPY_MONITOR_PING | debug.monitor_ping | -| ACAPY_AUTO_RESPOND_MESSAGES | debug.auto_respond_messages | -| ACAPY_AUTO_RESPOND_CREDENTIAL_OFFER | debug.auto_resopnd_credential_offer | -| ACAPY_AUTO_RESPOND_CREDENTIAL_REQUEST | debug.auto_respond_credential_request | -| ACAPY_AUTO_VERIFY_PRESENTATION | debug.auto_verify_presentation | -| ACAPY_NOTIFY_REVOCATION | revocation.notify | -| ACAPY_AUTO_REQUEST_ENDORSEMENT | endorser.auto_request | -| ACAPY_AUTO_WRITE_TRANSACTIONS | endorser.auto_write | -| ACAPY_CREATE_REVOCATION_TRANSACTIONS | endorser.auto_create_rev_reg | -| ACAPY_ENDORSER_ROLE | endorser.protocol_role | +| Labels | | Setting | +|---|---|---| +| ACAPY_LOG_LEVEL | log_level | log.level | +| ACAPY_INVITE_PUBLIC | invite_public | debug.invite_public | +| ACAPY_PUBLIC_INVITES | public_invites | public_invites | +| ACAPY_AUTO_ACCEPT_INVITES | auto_accept_invites | debug.auto_accept_invites | +| ACAPY_AUTO_ACCEPT_REQUESTS | auto_accept_requests | debug.auto_accept_requests | +| ACAPY_AUTO_PING_CONNECTION | auto_ping_connection | auto_ping_connection | +| ACAPY_MONITOR_PING | monitor_ping | debug.monitor_ping | +| ACAPY_AUTO_RESPOND_MESSAGES | auto_respond_messages | debug.auto_respond_messages | +| ACAPY_AUTO_RESPOND_CREDENTIAL_OFFER | auto_respond_credential_offer | debug.auto_resopnd_credential_offer | +| ACAPY_AUTO_RESPOND_CREDENTIAL_REQUEST | auto_respond_credential_request | debug.auto_respond_credential_request | +| ACAPY_AUTO_VERIFY_PRESENTATION | auto_verify_presentation | debug.auto_verify_presentation | +| ACAPY_NOTIFY_REVOCATION | notify_revocation | revocation.notify | +| ACAPY_AUTO_REQUEST_ENDORSEMENT | auto_request_endorsement | endorser.auto_request | +| ACAPY_AUTO_WRITE_TRANSACTIONS | auto_write_transactions | endorser.auto_write | +| ACAPY_CREATE_REVOCATION_TRANSACTIONS | auto_create_revocation_transactions | endorser.auto_create_rev_reg | +| ACAPY_ENDORSER_ROLE | endorser_protocol_role | endorser.protocol_role | - `POST /multitenancy/wallet` @@ -417,7 +417,7 @@ To allow configurablity of ACA-Py startup parameters/environment variables at a "extra_settings": { "ACAPY_LOG_LEVEL": "INFO", "ACAPY_INVITE_PUBLIC": true, - "ACAPY_PUBLIC_INVITES": true + "public_invites": true }, } ``` diff --git a/aries_cloudagent/config/base.py b/aries_cloudagent/config/base.py index cdc30ba507..00d0c4a31c 100644 --- a/aries_cloudagent/config/base.py +++ b/aries_cloudagent/config/base.py @@ -97,6 +97,10 @@ def copy(self) -> "BaseSettings": def extend(self, other: Mapping[str, Any]) -> "BaseSettings": """Merge another mapping to produce a new settings instance.""" + @abstractmethod + def to_dict(self) -> dict: + """Return a dict of the settings instance.""" + def __repr__(self) -> str: """Provide a human readable representation of this object.""" items = ("{}={}".format(k, self[k]) for k in self) diff --git a/aries_cloudagent/config/default_context.py b/aries_cloudagent/config/default_context.py index 203aaac65b..a99c3b44a9 100644 --- a/aries_cloudagent/config/default_context.py +++ b/aries_cloudagent/config/default_context.py @@ -133,6 +133,7 @@ async def load_plugins(self, context: InjectionContext): plugin_registry.register_plugin("aries_cloudagent.messaging.jsonld") plugin_registry.register_plugin("aries_cloudagent.revocation") plugin_registry.register_plugin("aries_cloudagent.resolver") + plugin_registry.register_plugin("aries_cloudagent.settings") plugin_registry.register_plugin("aries_cloudagent.wallet") if context.settings.get("multitenant.admin_enabled"): diff --git a/aries_cloudagent/config/logging.py b/aries_cloudagent/config/logging.py index 18bbdd1dc0..bbd91ef9d2 100644 --- a/aries_cloudagent/config/logging.py +++ b/aries_cloudagent/config/logging.py @@ -634,11 +634,11 @@ def get_logger_with_handlers( logger.addHandler(std_out_handler) if did_ident: logger = logging.LoggerAdapter(logger, {"did": did_ident}) - # set log level - logger_level = ( - (settings.get("log.level")).upper() - if settings.get("log.level") - else logging.INFO - ) - logger.setLevel(logger_level) + # set log level + logger_level = ( + (settings.get("log.level")).upper() + if settings.get("log.level") + else logging.INFO + ) + logger.setLevel(logger_level) return logger diff --git a/aries_cloudagent/config/plugin_settings.py b/aries_cloudagent/config/plugin_settings.py index d1e78cdb74..1da1392ef0 100644 --- a/aries_cloudagent/config/plugin_settings.py +++ b/aries_cloudagent/config/plugin_settings.py @@ -55,6 +55,13 @@ def extend(self, other: Mapping[str, Any]) -> BaseSettings: vals.update(other) return PluginSettings(vals) + def to_dict(self) -> dict: + """Return a dict of the settings instance.""" + setting_dict = {} + for k in self: + setting_dict[k] = self[k] + return setting_dict + def get_value(self, *var_names: str, default: Any = None): """Fetch a setting. diff --git a/aries_cloudagent/config/settings.py b/aries_cloudagent/config/settings.py index d4a7677d13..386e434a49 100644 --- a/aries_cloudagent/config/settings.py +++ b/aries_cloudagent/config/settings.py @@ -97,6 +97,13 @@ def extend(self, other: Mapping[str, Any]) -> BaseSettings: vals.update(other) return Settings(vals) + def to_dict(self) -> dict: + """Return a dict of the settings instance.""" + setting_dict = {} + for k in self: + setting_dict[k] = self[k] + return setting_dict + def update(self, other: Mapping[str, Any]): """Update the settings in place.""" self._values.update(other) diff --git a/aries_cloudagent/multitenant/admin/routes.py b/aries_cloudagent/multitenant/admin/routes.py index eae4c64e8e..42b7348b79 100644 --- a/aries_cloudagent/multitenant/admin/routes.py +++ b/aries_cloudagent/multitenant/admin/routes.py @@ -32,7 +32,7 @@ "ACAPY_AUTO_PING_CONNECTION": "auto_ping_connection", "ACAPY_MONITOR_PING": "debug.monitor_ping", "ACAPY_AUTO_RESPOND_MESSAGES": "debug.auto_respond_messages", - "ACAPY_AUTO_RESPOND_CREDENTIAL_OFFER": "debug.auto_resopnd_credential_offer", + "ACAPY_AUTO_RESPOND_CREDENTIAL_OFFER": "debug.auto_respond_credential_offer", "ACAPY_AUTO_RESPOND_CREDENTIAL_REQUEST": "debug.auto_respond_credential_request", "ACAPY_AUTO_VERIFY_PRESENTATION": "debug.auto_verify_presentation", "ACAPY_NOTIFY_REVOCATION": "revocation.notify", @@ -42,10 +42,32 @@ "ACAPY_ENDORSER_ROLE": "endorser.protocol_role", } +ACAPY_LIFECYCLE_CONFIG_FLAG_ARGS_MAP = { + "log_level": "log.level", + "invite_public": "debug.invite_public", + "public_invites": "public_invites", + "auto_accept_invites": "debug.auto_accept_invites", + "auto_accept_requests": "debug.auto_accept_requests", + "auto_ping_connection": "auto_ping_connection", + "monitor_ping": "debug.monitor_ping", + "auto_respond_messages": "debug.auto_respond_messages", + "auto_respond_credential_offer": "debug.auto_respond_credential_offer", + "auto_respond_credential_request": "debug.auto_respond_credential_request", + "auto_verify_presentation": "debug.auto_verify_presentation", + "notify_revocation": "revocation.notify", + "auto_request_endorsement": "endorser.auto_request", + "auto_write_transactions": "endorser.auto_write", + "auto_create_revocation_transactions": "endorser.auto_create_rev_reg", + "endorser_protocol_role": "endorser.protocol_role", +} + ACAPY_ENDORSER_FLAGS_DEPENDENT_ON_AUTHOR_ROLE = [ "ACAPY_AUTO_REQUEST_ENDORSEMENT", "ACAPY_AUTO_WRITE_TRANSACTIONS", "ACAPY_CREATE_REVOCATION_TRANSACTIONS", + "auto_request_endorsement", + "auto_write_transactions", + "auto_create_revocation_transactions", ] @@ -64,11 +86,13 @@ def format_wallet_record(wallet_record: WalletRecord): def get_extra_settings_dict_per_tenant(tenant_settings: dict) -> dict: """Get per tenant settings to be applied when creating wallet.""" - endorser_role_flag = tenant_settings.get("ACAPY_ENDORSER_ROLE") + endorser_role_flag = tenant_settings.get( + "ACAPY_ENDORSER_ROLE" + ) or tenant_settings.get("endorser_protocol_role") extra_settings = {} - if endorser_role_flag == "author": + if endorser_role_flag and endorser_role_flag == "author": extra_settings["endorser.author"] = True - elif endorser_role_flag == "endorser": + elif endorser_role_flag and endorser_role_flag == "endorser": extra_settings["endorser.endorser"] = True for flag in tenant_settings.keys(): if ( @@ -79,7 +103,11 @@ def get_extra_settings_dict_per_tenant(tenant_settings: dict) -> dict: # this setting will be ignored. continue if flag != "ACAPY_ENDORSER_ROLE": - map_flag = ACAPY_LIFECYCLE_CONFIG_FLAG_MAP[flag] + map_flag = ACAPY_LIFECYCLE_CONFIG_FLAG_MAP.get( + flag + ) or ACAPY_LIFECYCLE_CONFIG_FLAG_ARGS_MAP.get(flag) + if not map_flag: + continue extra_settings[map_flag] = tenant_settings[flag] return extra_settings diff --git a/aries_cloudagent/settings/__init__.py b/aries_cloudagent/settings/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/aries_cloudagent/settings/routes.py b/aries_cloudagent/settings/routes.py new file mode 100644 index 0000000000..8b1231d503 --- /dev/null +++ b/aries_cloudagent/settings/routes.py @@ -0,0 +1,113 @@ +"""Settings routes.""" + +import logging + +from aiohttp import web +from aiohttp_apispec import docs, request_schema, response_schema +from marshmallow import fields + +from ..admin.request_context import AdminRequestContext +from ..core.error import BaseError +from ..messaging.models.openapi import OpenAPISchema +from ..multitenant.admin.routes import get_extra_settings_dict_per_tenant + +LOGGER = logging.getLogger(__name__) + + +class UpdateProfileSettingsSchema(OpenAPISchema): + """Schema to update profile settings.""" + + extra_settings = fields.Dict( + description="Agent config key-value pairs", + required=False, + example={ + "log_level": "INFO", + "ACAPY_INVITE_PUBLIC": True, + "public_invites": False, + }, + ) + + +class ProfileSettingsSchema(OpenAPISchema): + """Profile settings response schema.""" + + settings = fields.Dict( + description="Profile settings dict", + example={ + "log.level": "INFO", + "debug.invite_public": True, + "public_invites": False, + }, + ) + + +@docs( + tags=["settings"], + summary="Update settings or config associated with the profile.", +) +@request_schema(UpdateProfileSettingsSchema()) +@response_schema(ProfileSettingsSchema(), 200, description="") +async def update_profile_settings(request: web.BaseRequest): + """ + Request handler for updating setting associated with profile. + + Args: + request: aiohttp request object + """ + context: AdminRequestContext = request["context"] + try: + body = await request.json() + extra_setting = get_extra_settings_dict_per_tenant( + body.get("extra_settings") or {} + ) + context.profile.settings.update(extra_setting) + result = context.profile.settings + except BaseError as err: + raise web.HTTPBadRequest(reason=err.roll_up) from err + return web.json_response(result.to_dict()) + + +@docs( + tags=["settings"], + summary="Get the settings or config associated with the profile.", +) +@response_schema(ProfileSettingsSchema(), 200, description="") +async def get_profile_settings(request: web.BaseRequest): + """ + Request handler for getting setting associated with profile. + + Args: + request: aiohttp request object + """ + context: AdminRequestContext = request["context"] + + try: + result = context.profile.settings + except BaseError as err: + raise web.HTTPBadRequest(reason=err.roll_up) from err + return web.json_response(result.to_dict()) + + +async def register(app: web.Application): + """Register routes.""" + + app.add_routes( + [ + web.put("/settings", update_profile_settings), + web.get("/settings", get_profile_settings, allow_head=False), + ] + ) + + +def post_process_routes(app: web.Application): + """Amend swagger API.""" + + # Add top-level tags description + if "tags" not in app._state["swagger_dict"]: + app._state["swagger_dict"]["tags"] = [] + app._state["swagger_dict"]["tags"].append( + { + "name": "settings", + "description": "Agent settings interface.", + } + ) diff --git a/aries_cloudagent/settings/tests/__init__.py b/aries_cloudagent/settings/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/aries_cloudagent/settings/tests/test_routes.py b/aries_cloudagent/settings/tests/test_routes.py new file mode 100644 index 0000000000..83aba881c2 --- /dev/null +++ b/aries_cloudagent/settings/tests/test_routes.py @@ -0,0 +1,96 @@ +"""Test settings routes.""" + +# pylint: disable=redefined-outer-name + +import pytest +from asynctest import mock as async_mock +from ...core.in_memory import InMemoryProfile + +from .. import routes as test_module + + +@pytest.fixture +def mock_response(): + json_response = async_mock.MagicMock() + temp_value = test_module.web.json_response + test_module.web.json_response = json_response + yield json_response + test_module.web.json_response = temp_value + + +@pytest.mark.asyncio +async def test_get_profile_settings(mock_response): + profile = InMemoryProfile.test_profile() + profile.settings.update( + { + "admin.admin_client_max_request_size": 1, + "debug.auto_respond_credential_offer": True, + "debug.auto_respond_credential_request": True, + "debug.auto_respond_presentation_proposal": True, + "debug.auto_verify_presentation": True, + "debug.auto_accept_invites": True, + "debug.auto_accept_requests": True, + } + ) + context = profile.context + setattr(context, "profile", profile) + request_dict = { + "context": context, + } + request = async_mock.MagicMock( + query={}, + json=async_mock.CoroutineMock(return_value={}), + __getitem__=lambda _, k: request_dict[k], + ) + await test_module.get_profile_settings(request) + assert mock_response.call_args[0][0] == { + "admin.admin_client_max_request_size": 1, + "debug.auto_respond_credential_offer": True, + "debug.auto_respond_credential_request": True, + "debug.auto_respond_presentation_proposal": True, + "debug.auto_verify_presentation": True, + "debug.auto_accept_invites": True, + "debug.auto_accept_requests": True, + } + + +@pytest.mark.asyncio +async def test_update_profile_settings(mock_response): + profile = InMemoryProfile.test_profile() + profile.settings.update( + { + "public_invites": True, + "debug.invite_public": True, + "debug.auto_accept_invites": True, + "debug.auto_accept_requests": True, + "auto_ping_connection": True, + } + ) + context = profile.context + setattr(context, "profile", profile) + request_dict = { + "context": context, + } + request = async_mock.MagicMock( + query={}, + json=async_mock.CoroutineMock( + return_value={ + "extra_settings": { + "ACAPY_INVITE_PUBLIC": False, + "ACAPY_PUBLIC_INVITES": False, + "ACAPY_AUTO_ACCEPT_INVITES": False, + "ACAPY_AUTO_ACCEPT_REQUESTS": False, + "ACAPY_AUTO_PING_CONNECTION": False, + } + } + ), + __getitem__=lambda _, k: request_dict[k], + ) + await test_module.update_profile_settings(request) + assert mock_response.call_args[0][0] == { + "public_invites": False, + "debug.invite_public": False, + "debug.auto_accept_invites": False, + "debug.auto_accept_requests": False, + "auto_ping_connection": False, + } diff --git a/open-api/openapi.json b/open-api/openapi.json index 87a283d1e7..b27af39ad6 100644 --- a/open-api/openapi.json +++ b/open-api/openapi.json @@ -5300,6 +5300,41 @@ "tags" : [ "schema" ] } }, + "/settings" : { + "get" : { + "tags" : [ "settings" ], + "summary" : "Get profile settings or config", + "produces" : [ "application/json" ], + "parameters" : [ ], + "responses" : { + "200" : { + "type" : "object", + "description" : "Settings for this wallet.", + "properties" : { } + } + } + }, + "put" : { + "tags" : [ "settings" ], + "summary" : "Update profile settings or config", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/UpdateProfileSettingsRequest" + } + } ], + "responses" : { + "200" : { + "type" : "object", + "description" : "Settings for this wallet.", + "properties" : { } + } + } + } + }, "/shutdown" : { "get" : { "responses" : { @@ -12988,6 +13023,16 @@ }, "type" : "object" }, + "UpdateProfileSettingsRequest" : { + "type" : "object", + "properties" : { + "extra_settings" : { + "type" : "object", + "description" : "Settings or config to update.", + "properties" : { } + } + } + }, "ActionMenuFetchResult_result" : { "allOf" : [ { "$ref" : "#/components/schemas/Menu" diff --git a/open-api/swagger.json b/open-api/swagger.json index b6b4493c15..a0f84c7ed3 100644 --- a/open-api/swagger.json +++ b/open-api/swagger.json @@ -4334,6 +4334,41 @@ } } }, + "/settings" : { + "get" : { + "tags" : [ "settings" ], + "summary" : "Get profile settings or config", + "produces" : [ "application/json" ], + "parameters" : [ ], + "responses" : { + "200" : { + "type" : "object", + "description" : "Settings for this wallet.", + "properties" : { } + } + } + }, + "put" : { + "tags" : [ "settings" ], + "summary" : "Update profile settings or config", + "produces" : [ "application/json" ], + "parameters" : [ { + "in" : "body", + "name" : "body", + "required" : false, + "schema" : { + "$ref" : "#/definitions/UpdateProfileSettingsRequest" + } + } ], + "responses" : { + "200" : { + "type" : "object", + "description" : "Settings for this wallet.", + "properties" : { } + } + } + } + }, "/shutdown" : { "get" : { "tags" : [ "server" ], @@ -11914,6 +11949,16 @@ } } }, + "UpdateProfileSettingsRequest" : { + "type" : "object", + "properties" : { + "extra_settings" : { + "type" : "object", + "description" : "Settings or config to update.", + "properties" : { } + } + } + }, "ActionMenuFetchResult_result" : { "type" : "object", "description" : "Action menu" From 0e557fee7186b05f01e70f7afeb3a72b107e0272 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Tue, 27 Jun 2023 09:53:03 -0700 Subject: [PATCH 858/872] wallet record access on settings endpoint Signed-off-by: Shaanjot Gill --- aries_cloudagent/admin/request_context.py | 16 ++- aries_cloudagent/admin/server.py | 23 +++- .../admin/tests/test_request_context.py | 14 +++ aries_cloudagent/multitenant/admin/routes.py | 38 +++--- aries_cloudagent/multitenant/base.py | 24 +++- .../multitenant/tests/test_base.py | 58 +++++++++ aries_cloudagent/settings/routes.py | 60 +++++++-- .../settings/tests/test_routes.py | 118 ++++++++++++++++-- 8 files changed, 311 insertions(+), 40 deletions(-) diff --git a/aries_cloudagent/admin/request_context.py b/aries_cloudagent/admin/request_context.py index 1fe7f79076..c7a64a11b0 100644 --- a/aries_cloudagent/admin/request_context.py +++ b/aries_cloudagent/admin/request_context.py @@ -23,11 +23,15 @@ def __init__( profile: Profile, *, context: InjectionContext = None, - settings: Mapping[str, object] = None + settings: Mapping[str, object] = None, + root_profile: Profile = None, + metadata: dict = None ): """Initialize an instance of AdminRequestContext.""" self._context = (context or profile.context).start_scope("admin", settings) self._profile = profile + self._root_profile = root_profile + self._metadata = metadata @property def injector(self) -> Injector: @@ -39,6 +43,16 @@ def profile(self) -> Profile: """Accessor for the associated `Profile` instance.""" return self._profile + @property + def root_profile(self) -> Optional[Profile]: + """Accessor for the associated root_profile instance.""" + return self._root_profile + + @property + def metadata(self) -> dict: + """Accessor for the associated metadata.""" + return self._metadata + @property def settings(self) -> Settings: """Accessor for the context settings.""" diff --git a/aries_cloudagent/admin/server.py b/aries_cloudagent/admin/server.py index 544bc8f585..91dcaf94a7 100644 --- a/aries_cloudagent/admin/server.py +++ b/aries_cloudagent/admin/server.py @@ -384,7 +384,7 @@ async def check_multitenant_authorization(request: web.Request, handler): async def setup_context(request: web.Request, handler): authorization_header = request.headers.get("Authorization") profile = self.root_profile - + meta_data = {} # Multitenancy context setup if self.multitenant_manager and authorization_header: try: @@ -397,6 +397,16 @@ async def setup_context(request: web.Request, handler): profile = await self.multitenant_manager.get_profile_for_token( self.context, token ) + ( + walletid, + walletkey, + ) = self.multitenant_manager.get_wallet_details_from_token( + token=token + ) + meta_data = { + "wallet_id": walletid, + "wallet_key": walletkey, + } except MultitenantManagerError as err: raise web.HTTPUnauthorized(reason=err.roll_up) except (jwt.InvalidTokenError, StorageNotFoundError): @@ -411,7 +421,16 @@ async def setup_context(request: web.Request, handler): # TODO may dynamically adjust the profile used here according to # headers or other parameters - admin_context = AdminRequestContext(profile) + if self.multitenant_manager and authorization_header: + admin_context = AdminRequestContext( + profile=profile, + root_profile=self.root_profile, + metadata=meta_data, + ) + else: + admin_context = AdminRequestContext( + profile=profile, + ) request["context"] = admin_context request["outbound_message_router"] = responder.send diff --git a/aries_cloudagent/admin/tests/test_request_context.py b/aries_cloudagent/admin/tests/test_request_context.py index 7775262638..e74763004e 100644 --- a/aries_cloudagent/admin/tests/test_request_context.py +++ b/aries_cloudagent/admin/tests/test_request_context.py @@ -12,12 +12,26 @@ def setUp(self): self.ctx = test_module.AdminRequestContext(InMemoryProfile.test_profile()) assert self.ctx.__class__.__name__ in str(self.ctx) + self.ctx_with_added_attrs = test_module.AdminRequestContext( + profile=InMemoryProfile.test_profile(), + root_profile=InMemoryProfile.test_profile(), + metadata={"test_attrib_key": "test_attrib_value"}, + ) + assert self.ctx_with_added_attrs.__class__.__name__ in str( + self.ctx_with_added_attrs + ) + def test_session_transaction(self): sesn = self.ctx.session() assert isinstance(sesn, ProfileSession) txn = self.ctx.transaction() assert isinstance(txn, ProfileSession) + sesn = self.ctx_with_added_attrs.session() + assert isinstance(sesn, ProfileSession) + txn = self.ctx_with_added_attrs.transaction() + assert isinstance(txn, ProfileSession) + async def test_session_inject_x(self): test_ctx = test_module.AdminRequestContext.test_context({Collector: None}) async with test_ctx.session() as test_sesn: diff --git a/aries_cloudagent/multitenant/admin/routes.py b/aries_cloudagent/multitenant/admin/routes.py index 42b7348b79..b9c78f9e20 100644 --- a/aries_cloudagent/multitenant/admin/routes.py +++ b/aries_cloudagent/multitenant/admin/routes.py @@ -43,31 +43,31 @@ } ACAPY_LIFECYCLE_CONFIG_FLAG_ARGS_MAP = { - "log_level": "log.level", - "invite_public": "debug.invite_public", - "public_invites": "public_invites", - "auto_accept_invites": "debug.auto_accept_invites", - "auto_accept_requests": "debug.auto_accept_requests", - "auto_ping_connection": "auto_ping_connection", - "monitor_ping": "debug.monitor_ping", - "auto_respond_messages": "debug.auto_respond_messages", - "auto_respond_credential_offer": "debug.auto_respond_credential_offer", - "auto_respond_credential_request": "debug.auto_respond_credential_request", - "auto_verify_presentation": "debug.auto_verify_presentation", - "notify_revocation": "revocation.notify", - "auto_request_endorsement": "endorser.auto_request", - "auto_write_transactions": "endorser.auto_write", - "auto_create_revocation_transactions": "endorser.auto_create_rev_reg", - "endorser_protocol_role": "endorser.protocol_role", + "log-level": "log.level", + "invite-public": "debug.invite_public", + "public-invites": "public_invites", + "auto-accept-invites": "debug.auto_accept_invites", + "auto-accept-requests": "debug.auto_accept_requests", + "auto-ping-connection": "auto_ping_connection", + "monitor-ping": "debug.monitor_ping", + "auto-respond-messages": "debug.auto_respond_messages", + "auto-respond-credential-offer": "debug.auto_respond_credential_offer", + "auto-respond-credential-request": "debug.auto_respond_credential_request", + "auto-verify-presentation": "debug.auto_verify_presentation", + "notify-revocation": "revocation.notify", + "auto-request-endorsement": "endorser.auto_request", + "auto-write-transactions": "endorser.auto_write", + "auto-create-revocation-transactions": "endorser.auto_create_rev_reg", + "endorser-protocol-role": "endorser.protocol_role", } ACAPY_ENDORSER_FLAGS_DEPENDENT_ON_AUTHOR_ROLE = [ "ACAPY_AUTO_REQUEST_ENDORSEMENT", "ACAPY_AUTO_WRITE_TRANSACTIONS", "ACAPY_CREATE_REVOCATION_TRANSACTIONS", - "auto_request_endorsement", - "auto_write_transactions", - "auto_create_revocation_transactions", + "auto-request-endorsement", + "auto-write-transactions", + "auto-create-revocation-transactions", ] diff --git a/aries_cloudagent/multitenant/base.py b/aries_cloudagent/multitenant/base.py index 2afec03b00..03fbb7a515 100644 --- a/aries_cloudagent/multitenant/base.py +++ b/aries_cloudagent/multitenant/base.py @@ -3,7 +3,7 @@ from abc import ABC, abstractmethod from datetime import datetime import logging -from typing import Iterable, List, Optional, cast +from typing import Iterable, List, Optional, cast, Tuple import jwt @@ -318,6 +318,28 @@ async def create_auth_token( return token + def get_wallet_details_from_token(self, token: str) -> Tuple[str, str]: + """Get the wallet_id and wallet_key from provided token.""" + jwt_secret = self._profile.context.settings.get("multitenant.jwt_secret") + token_body = jwt.decode(token, jwt_secret, algorithms=["HS256"]) + wallet_id = token_body.get("wallet_id") + wallet_key = token_body.get("wallet_key") + return wallet_id, wallet_key + + async def get_wallet_and_profile( + self, context: InjectionContext, wallet_id: str, wallet_key: str + ) -> Tuple[WalletRecord, Profile]: + """Get the wallet_record and profile associated with wallet id and key.""" + extra_settings = {} + async with self._profile.session() as session: + wallet = await WalletRecord.retrieve_by_id(session, wallet_id) + if wallet.requires_external_key: + if not wallet_key: + raise WalletKeyMissingError() + extra_settings["wallet.key"] = wallet_key + profile = await self.get_wallet_profile(context, wallet, extra_settings) + return (wallet, profile) + async def get_profile_for_token( self, context: InjectionContext, token: str ) -> Profile: diff --git a/aries_cloudagent/multitenant/tests/test_base.py b/aries_cloudagent/multitenant/tests/test_base.py index dd1c5fba31..1e28b90b18 100644 --- a/aries_cloudagent/multitenant/tests/test_base.py +++ b/aries_cloudagent/multitenant/tests/test_base.py @@ -418,6 +418,64 @@ async def test_create_auth_token_unmanaged(self): assert wallet_record.jwt_iat == iat assert expected_token == token + async def test_get_wallet_details_from_token(self): + self.profile.settings["multitenant.jwt_secret"] = "very_secret_jwt" + wallet_record = WalletRecord( + key_management_mode=WalletRecord.MODE_MANAGED, + settings={"wallet.type": "indy", "wallet.key": "wallet_key"}, + jwt_iat=100, + ) + session = await self.profile.session() + await wallet_record.save(session) + token = jwt.encode( + {"wallet_id": wallet_record.wallet_id, "iat": 100}, + "very_secret_jwt", + algorithm="HS256", + ) + ret_wallet_id, ret_wallet_key = self.manager.get_wallet_details_from_token( + token + ) + assert ret_wallet_id == wallet_record.wallet_id + assert not ret_wallet_key + + token = jwt.encode( + { + "wallet_id": wallet_record.wallet_id, + "iat": 100, + "wallet_key": "wallet_key", + }, + "very_secret_jwt", + algorithm="HS256", + ) + ret_wallet_id, ret_wallet_key = self.manager.get_wallet_details_from_token( + token + ) + assert ret_wallet_id == wallet_record.wallet_id + assert ret_wallet_key == "wallet_key" + + async def test_get_wallet_and_profile(self): + self.profile.settings["multitenant.jwt_secret"] = "very_secret_jwt" + wallet_record = WalletRecord( + key_management_mode=WalletRecord.MODE_MANAGED, + settings={"wallet.type": "indy", "wallet.key": "wallet_key"}, + jwt_iat=100, + ) + + session = await self.profile.session() + await wallet_record.save(session) + + with async_mock.patch.object( + self.manager, "get_wallet_profile" + ) as get_wallet_profile: + mock_profile = InMemoryProfile.test_profile() + get_wallet_profile.return_value = mock_profile + + wallet, profile = await self.manager.get_wallet_and_profile( + self.profile.context, wallet_record.wallet_id, "wallet_key" + ) + assert wallet == wallet_record + assert profile == mock_profile + async def test_get_profile_for_token_invalid_token_raises(self): self.profile.settings["multitenant.jwt_secret"] = "very_secret_jwt" diff --git a/aries_cloudagent/settings/routes.py b/aries_cloudagent/settings/routes.py index 8b1231d503..625fa59b58 100644 --- a/aries_cloudagent/settings/routes.py +++ b/aries_cloudagent/settings/routes.py @@ -7,9 +7,13 @@ from marshmallow import fields from ..admin.request_context import AdminRequestContext +from ..multitenant.base import BaseMultitenantManager from ..core.error import BaseError from ..messaging.models.openapi import OpenAPISchema -from ..multitenant.admin.routes import get_extra_settings_dict_per_tenant +from ..multitenant.admin.routes import ( + get_extra_settings_dict_per_tenant, + ACAPY_LIFECYCLE_CONFIG_FLAG_ARGS_MAP, +) LOGGER = logging.getLogger(__name__) @@ -41,6 +45,16 @@ class ProfileSettingsSchema(OpenAPISchema): ) +def _get_filtered_settings_dict(wallet_settings: dict): + """Get filtered settings dict to display.""" + filter_param_list = list(ACAPY_LIFECYCLE_CONFIG_FLAG_ARGS_MAP.values()) + settings_dict = {} + for param in filter_param_list: + if param in wallet_settings: + settings_dict[param] = wallet_settings.get(param) + return settings_dict + + @docs( tags=["settings"], summary="Update settings or config associated with the profile.", @@ -55,21 +69,34 @@ async def update_profile_settings(request: web.BaseRequest): request: aiohttp request object """ context: AdminRequestContext = request["context"] + root_profile = context.root_profile or context.profile try: body = await request.json() - extra_setting = get_extra_settings_dict_per_tenant( + extra_settings = get_extra_settings_dict_per_tenant( body.get("extra_settings") or {} ) - context.profile.settings.update(extra_setting) - result = context.profile.settings + async with root_profile.session() as session: + multitenant_mgr = session.inject_or(BaseMultitenantManager) + if multitenant_mgr: + wallet_id = context.metadata.get("wallet_id") + wallet_record = await multitenant_mgr.update_wallet( + wallet_id, extra_settings + ) + wallet_settings = wallet_record.settings + settings_dict = _get_filtered_settings_dict(wallet_settings) + else: + root_profile.context.update_settings(extra_settings) + settings_dict = _get_filtered_settings_dict( + (context.profile.settings).to_dict() + ) except BaseError as err: raise web.HTTPBadRequest(reason=err.roll_up) from err - return web.json_response(result.to_dict()) + return web.json_response(settings_dict) @docs( tags=["settings"], - summary="Get the settings or config associated with the profile.", + summary="Get the configurable settings associated with the profile.", ) @response_schema(ProfileSettingsSchema(), 200, description="") async def get_profile_settings(request: web.BaseRequest): @@ -80,12 +107,27 @@ async def get_profile_settings(request: web.BaseRequest): request: aiohttp request object """ context: AdminRequestContext = request["context"] - + root_profile = context.root_profile or context.profile try: - result = context.profile.settings + async with root_profile.session() as session: + multitenant_mgr = session.inject_or(BaseMultitenantManager) + if multitenant_mgr: + wallet_id = context.metadata.get("wallet_id") + wallet_key = context.metadata.get("wallet_key") + wallet_record, profile = await multitenant_mgr.get_wallet_and_profile( + root_profile.context, wallet_id, wallet_key + ) + profile_settings = profile.settings.to_dict() + wallet_settings = wallet_record.settings + all_settings = {**profile_settings, **wallet_settings} + settings_dict = _get_filtered_settings_dict(all_settings) + else: + settings_dict = _get_filtered_settings_dict( + (root_profile.settings).to_dict() + ) except BaseError as err: raise web.HTTPBadRequest(reason=err.roll_up) from err - return web.json_response(result.to_dict()) + return web.json_response(settings_dict) async def register(app: web.Application): diff --git a/aries_cloudagent/settings/tests/test_routes.py b/aries_cloudagent/settings/tests/test_routes.py index 83aba881c2..474b792cc4 100644 --- a/aries_cloudagent/settings/tests/test_routes.py +++ b/aries_cloudagent/settings/tests/test_routes.py @@ -4,7 +4,11 @@ import pytest from asynctest import mock as async_mock + +from ...admin.request_context import AdminRequestContext from ...core.in_memory import InMemoryProfile +from ...multitenant.base import BaseMultitenantManager +from ...multitenant.manager import MultitenantManager from .. import routes as test_module @@ -32,10 +36,10 @@ async def test_get_profile_settings(mock_response): "debug.auto_accept_requests": True, } ) - context = profile.context - setattr(context, "profile", profile) request_dict = { - "context": context, + "context": AdminRequestContext( + profile=profile, + ), } request = async_mock.MagicMock( query={}, @@ -44,10 +48,55 @@ async def test_get_profile_settings(mock_response): ) await test_module.get_profile_settings(request) assert mock_response.call_args[0][0] == { - "admin.admin_client_max_request_size": 1, "debug.auto_respond_credential_offer": True, "debug.auto_respond_credential_request": True, - "debug.auto_respond_presentation_proposal": True, + "debug.auto_verify_presentation": True, + "debug.auto_accept_invites": True, + "debug.auto_accept_requests": True, + } + # Multitenant + profile = InMemoryProfile.test_profile() + multi_tenant_manager = MultitenantManager(profile) + profile.context.injector.bind_instance( + BaseMultitenantManager, + multi_tenant_manager, + ) + request_dict = { + "context": AdminRequestContext( + profile=profile, + root_profile=profile, + metadata={ + "wallet_id": "walletid", + "wallet_key": "walletkey", + }, + ), + } + request = async_mock.MagicMock( + query={}, + json=async_mock.CoroutineMock(return_value={}), + __getitem__=lambda _, k: request_dict[k], + ) + with async_mock.patch.object( + multi_tenant_manager, "get_wallet_and_profile" + ) as get_wallet_and_profile: + get_wallet_and_profile.return_value = ( + async_mock.MagicMock( + settings={ + "admin.admin_client_max_request_size": 1, + "debug.auto_respond_credential_offer": True, + "debug.auto_respond_credential_request": True, + "debug.auto_respond_presentation_proposal": True, + "debug.auto_verify_presentation": True, + "debug.auto_accept_invites": True, + "debug.auto_accept_requests": True, + } + ), + profile, + ) + await test_module.get_profile_settings(request) + assert mock_response.call_args[0][0] == { + "debug.auto_respond_credential_offer": True, + "debug.auto_respond_credential_request": True, "debug.auto_verify_presentation": True, "debug.auto_accept_invites": True, "debug.auto_accept_requests": True, @@ -66,10 +115,10 @@ async def test_update_profile_settings(mock_response): "auto_ping_connection": True, } ) - context = profile.context - setattr(context, "profile", profile) request_dict = { - "context": context, + "context": AdminRequestContext( + profile=profile, + ), } request = async_mock.MagicMock( query={}, @@ -94,3 +143,56 @@ async def test_update_profile_settings(mock_response): "debug.auto_accept_requests": False, "auto_ping_connection": False, } + # Multitenant + profile = InMemoryProfile.test_profile() + multi_tenant_manager = MultitenantManager(profile) + profile.context.injector.bind_instance( + BaseMultitenantManager, + multi_tenant_manager, + ) + + request_dict = { + "context": AdminRequestContext( + profile=profile, + root_profile=profile, + metadata={ + "wallet_id": "walletid", + "wallet_key": "walletkey", + }, + ), + } + request = async_mock.MagicMock( + query={}, + json=async_mock.CoroutineMock( + return_value={ + "extra_settings": { + "ACAPY_INVITE_PUBLIC": False, + "ACAPY_PUBLIC_INVITES": False, + "ACAPY_AUTO_ACCEPT_INVITES": False, + "ACAPY_AUTO_ACCEPT_REQUESTS": False, + "ACAPY_AUTO_PING_CONNECTION": False, + } + } + ), + __getitem__=lambda _, k: request_dict[k], + ) + with async_mock.patch.object( + multi_tenant_manager, "update_wallet" + ) as update_wallet: + update_wallet.return_value = async_mock.MagicMock( + settings={ + "public_invites": False, + "debug.invite_public": False, + "debug.auto_accept_invites": False, + "debug.auto_accept_requests": False, + "auto_ping_connection": False, + } + ) + await test_module.update_profile_settings(request) + assert mock_response.call_args[0][0] == { + "public_invites": False, + "debug.invite_public": False, + "debug.auto_accept_invites": False, + "debug.auto_accept_requests": False, + "auto_ping_connection": False, + } From 044b377fb35a9e668ab106d89621912a98bebda0 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Tue, 27 Jun 2023 11:15:27 -0700 Subject: [PATCH 859/872] final updates Signed-off-by: Shaanjot Gill --- Multitenancy.md | 32 +++++++++---------- aries_cloudagent/settings/routes.py | 10 ++++-- .../settings/tests/test_routes.py | 24 +++++++++++++- 3 files changed, 47 insertions(+), 19 deletions(-) diff --git a/Multitenancy.md b/Multitenancy.md index 3aaab3314f..ebc6340644 100644 --- a/Multitenancy.md +++ b/Multitenancy.md @@ -383,22 +383,22 @@ To allow configurablity of ACA-Py startup parameters/environment variables at a | Labels | | Setting | |---|---|---| -| ACAPY_LOG_LEVEL | log_level | log.level | -| ACAPY_INVITE_PUBLIC | invite_public | debug.invite_public | -| ACAPY_PUBLIC_INVITES | public_invites | public_invites | -| ACAPY_AUTO_ACCEPT_INVITES | auto_accept_invites | debug.auto_accept_invites | -| ACAPY_AUTO_ACCEPT_REQUESTS | auto_accept_requests | debug.auto_accept_requests | -| ACAPY_AUTO_PING_CONNECTION | auto_ping_connection | auto_ping_connection | -| ACAPY_MONITOR_PING | monitor_ping | debug.monitor_ping | -| ACAPY_AUTO_RESPOND_MESSAGES | auto_respond_messages | debug.auto_respond_messages | -| ACAPY_AUTO_RESPOND_CREDENTIAL_OFFER | auto_respond_credential_offer | debug.auto_resopnd_credential_offer | -| ACAPY_AUTO_RESPOND_CREDENTIAL_REQUEST | auto_respond_credential_request | debug.auto_respond_credential_request | -| ACAPY_AUTO_VERIFY_PRESENTATION | auto_verify_presentation | debug.auto_verify_presentation | -| ACAPY_NOTIFY_REVOCATION | notify_revocation | revocation.notify | -| ACAPY_AUTO_REQUEST_ENDORSEMENT | auto_request_endorsement | endorser.auto_request | -| ACAPY_AUTO_WRITE_TRANSACTIONS | auto_write_transactions | endorser.auto_write | -| ACAPY_CREATE_REVOCATION_TRANSACTIONS | auto_create_revocation_transactions | endorser.auto_create_rev_reg | -| ACAPY_ENDORSER_ROLE | endorser_protocol_role | endorser.protocol_role | +| ACAPY_LOG_LEVEL | log-level | log.level | +| ACAPY_INVITE_PUBLIC | invite-public | debug.invite_public | +| ACAPY_PUBLIC_INVITES | public-invites | public_invites | +| ACAPY_AUTO_ACCEPT_INVITES | auto-accept-invites | debug.auto_accept_invites | +| ACAPY_AUTO_ACCEPT_REQUESTS | auto-accept-requests | debug.auto_accept_requests | +| ACAPY_AUTO_PING_CONNECTION | auto-ping-connection | auto_ping_connection | +| ACAPY_MONITOR_PING | monitor-ping | debug.monitor_ping | +| ACAPY_AUTO_RESPOND_MESSAGES | auto-respond-messages | debug.auto_respond_messages | +| ACAPY_AUTO_RESPOND_CREDENTIAL_OFFER | auto-respond-credential-offer | debug.auto_resopnd_credential_offer | +| ACAPY_AUTO_RESPOND_CREDENTIAL_REQUEST | auto-respond-credential-request | debug.auto_respond_credential_request | +| ACAPY_AUTO_VERIFY_PRESENTATION | auto-verify-presentation | debug.auto_verify_presentation | +| ACAPY_NOTIFY_REVOCATION | notify-revocation | revocation.notify | +| ACAPY_AUTO_REQUEST_ENDORSEMENT | auto-request-endorsement | endorser.auto_request | +| ACAPY_AUTO_WRITE_TRANSACTIONS | auto-write-transactions | endorser.auto_write | +| ACAPY_CREATE_REVOCATION_TRANSACTIONS | auto-create-revocation-transactions | endorser.auto_create_rev_reg | +| ACAPY_ENDORSER_ROLE | endorser-protocol-role | endorser.protocol_role | - `POST /multitenancy/wallet` diff --git a/aries_cloudagent/settings/routes.py b/aries_cloudagent/settings/routes.py index 625fa59b58..18a524bcab 100644 --- a/aries_cloudagent/settings/routes.py +++ b/aries_cloudagent/settings/routes.py @@ -57,7 +57,7 @@ def _get_filtered_settings_dict(wallet_settings: dict): @docs( tags=["settings"], - summary="Update settings or config associated with the profile.", + summary="Update configurable settings associated with the profile.", ) @request_schema(UpdateProfileSettingsSchema()) @response_schema(ProfileSettingsSchema(), 200, description="") @@ -79,11 +79,17 @@ async def update_profile_settings(request: web.BaseRequest): multitenant_mgr = session.inject_or(BaseMultitenantManager) if multitenant_mgr: wallet_id = context.metadata.get("wallet_id") + wallet_key = context.metadata.get("wallet_key") wallet_record = await multitenant_mgr.update_wallet( wallet_id, extra_settings ) + wallet_record, profile = await multitenant_mgr.get_wallet_and_profile( + root_profile.context, wallet_id, wallet_key + ) + profile_settings = profile.settings.to_dict() wallet_settings = wallet_record.settings - settings_dict = _get_filtered_settings_dict(wallet_settings) + all_settings = {**profile_settings, **wallet_settings} + settings_dict = _get_filtered_settings_dict(all_settings) else: root_profile.context.update_settings(extra_settings) settings_dict = _get_filtered_settings_dict( diff --git a/aries_cloudagent/settings/tests/test_routes.py b/aries_cloudagent/settings/tests/test_routes.py index 474b792cc4..b17774c050 100644 --- a/aries_cloudagent/settings/tests/test_routes.py +++ b/aries_cloudagent/settings/tests/test_routes.py @@ -178,7 +178,26 @@ async def test_update_profile_settings(mock_response): ) with async_mock.patch.object( multi_tenant_manager, "update_wallet" - ) as update_wallet: + ) as update_wallet, async_mock.patch.object( + multi_tenant_manager, "get_wallet_and_profile" + ) as get_wallet_and_profile: + get_wallet_and_profile.return_value = ( + async_mock.MagicMock( + settings={ + "admin.admin_client_max_request_size": 1, + "debug.auto_respond_credential_offer": True, + "debug.auto_respond_credential_request": True, + "debug.auto_respond_presentation_proposal": True, + "debug.auto_verify_presentation": True, + "public_invites": False, + "debug.invite_public": False, + "debug.auto_accept_invites": False, + "debug.auto_accept_requests": False, + "auto_ping_connection": False, + } + ), + profile, + ) update_wallet.return_value = async_mock.MagicMock( settings={ "public_invites": False, @@ -195,4 +214,7 @@ async def test_update_profile_settings(mock_response): "debug.auto_accept_invites": False, "debug.auto_accept_requests": False, "auto_ping_connection": False, + "debug.auto_respond_credential_offer": True, + "debug.auto_respond_credential_request": True, + "debug.auto_verify_presentation": True, } From 50f5295a640fa19e3264cb34b0381498b2c615c6 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Tue, 27 Jun 2023 15:25:56 -0700 Subject: [PATCH 860/872] cleanup Signed-off-by: Shaanjot Gill --- Multitenancy.md | 2 +- aries_cloudagent/settings/routes.py | 43 +++++++++++++++++++---------- 2 files changed, 30 insertions(+), 15 deletions(-) diff --git a/Multitenancy.md b/Multitenancy.md index ebc6340644..f99566b29e 100644 --- a/Multitenancy.md +++ b/Multitenancy.md @@ -417,7 +417,7 @@ To allow configurablity of ACA-Py startup parameters/environment variables at a "extra_settings": { "ACAPY_LOG_LEVEL": "INFO", "ACAPY_INVITE_PUBLIC": true, - "public_invites": true + "public-invites": true }, } ``` diff --git a/aries_cloudagent/settings/routes.py b/aries_cloudagent/settings/routes.py index 18a524bcab..2124b6b62e 100644 --- a/aries_cloudagent/settings/routes.py +++ b/aries_cloudagent/settings/routes.py @@ -9,6 +9,7 @@ from ..admin.request_context import AdminRequestContext from ..multitenant.base import BaseMultitenantManager from ..core.error import BaseError +from ..core.profile import Profile from ..messaging.models.openapi import OpenAPISchema from ..multitenant.admin.routes import ( get_extra_settings_dict_per_tenant, @@ -55,6 +56,24 @@ def _get_filtered_settings_dict(wallet_settings: dict): return settings_dict +def _get_multitenant_settings_dict( + profile_settings: dict, + wallet_settings: dict, +): + """Get filtered settings dict when multitenant manager is present.""" + all_settings = {**profile_settings, **wallet_settings} + settings_dict = _get_filtered_settings_dict(all_settings) + return settings_dict + + +def _get_settings_dict( + profile: Profile, +): + """Get filtered settings dict when multitenant manager is not present.""" + settings_dict = _get_filtered_settings_dict((profile.settings).to_dict()) + return settings_dict + + @docs( tags=["settings"], summary="Update configurable settings associated with the profile.", @@ -86,15 +105,13 @@ async def update_profile_settings(request: web.BaseRequest): wallet_record, profile = await multitenant_mgr.get_wallet_and_profile( root_profile.context, wallet_id, wallet_key ) - profile_settings = profile.settings.to_dict() - wallet_settings = wallet_record.settings - all_settings = {**profile_settings, **wallet_settings} - settings_dict = _get_filtered_settings_dict(all_settings) + settings_dict = _get_multitenant_settings_dict( + profile_settings=profile.settings.to_dict(), + wallet_settings=wallet_record.settings, + ) else: root_profile.context.update_settings(extra_settings) - settings_dict = _get_filtered_settings_dict( - (context.profile.settings).to_dict() - ) + settings_dict = _get_settings_dict(profile=root_profile) except BaseError as err: raise web.HTTPBadRequest(reason=err.roll_up) from err return web.json_response(settings_dict) @@ -123,14 +140,12 @@ async def get_profile_settings(request: web.BaseRequest): wallet_record, profile = await multitenant_mgr.get_wallet_and_profile( root_profile.context, wallet_id, wallet_key ) - profile_settings = profile.settings.to_dict() - wallet_settings = wallet_record.settings - all_settings = {**profile_settings, **wallet_settings} - settings_dict = _get_filtered_settings_dict(all_settings) - else: - settings_dict = _get_filtered_settings_dict( - (root_profile.settings).to_dict() + settings_dict = _get_multitenant_settings_dict( + profile_settings=profile.settings.to_dict(), + wallet_settings=wallet_record.settings, ) + else: + settings_dict = _get_settings_dict(profile=root_profile) except BaseError as err: raise web.HTTPBadRequest(reason=err.roll_up) from err return web.json_response(settings_dict) From 488c233f4935bca521d55db66659c0012ec4a96c Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Tue, 27 Jun 2023 15:35:57 -0700 Subject: [PATCH 861/872] minor cleanup Signed-off-by: Shaanjot Gill --- aries_cloudagent/settings/routes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/settings/routes.py b/aries_cloudagent/settings/routes.py index 2124b6b62e..9a576da73d 100644 --- a/aries_cloudagent/settings/routes.py +++ b/aries_cloudagent/settings/routes.py @@ -26,9 +26,9 @@ class UpdateProfileSettingsSchema(OpenAPISchema): description="Agent config key-value pairs", required=False, example={ - "log_level": "INFO", + "log-level": "INFO", "ACAPY_INVITE_PUBLIC": True, - "public_invites": False, + "public-invites": False, }, ) From 78433f3ebad9dbd0f6512113de6eef0111622b75 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Wed, 28 Jun 2023 17:25:57 +0000 Subject: [PATCH 862/872] 0.8.2-rc2 Signed-off-by: Stephen Curran --- CHANGELOG.md | 14 ++++++++++---- aries_cloudagent/version.py | 2 +- docs/generated/aries_cloudagent.rst | 1 + docs/generated/aries_cloudagent.settings.rst | 18 ++++++++++++++++++ open-api/openapi.json | 2 +- open-api/swagger.json | 2 +- 6 files changed, 32 insertions(+), 7 deletions(-) create mode 100644 docs/generated/aries_cloudagent.settings.rst diff --git a/CHANGELOG.md b/CHANGELOG.md index 9747d2d2c8..670825bb6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ -# 0.8.2-rc1 +# 0.8.2-rc2 -## June 26, 2023 +## June 28, 2023 Release 0.8.2 contains a number of minor fixes and updates to ACA-Py, including the correction of a regression in Release 0.8.0 related to the use of plugins @@ -8,11 +8,15 @@ the correction of a regression in Release 0.8.0 related to the use of plugins development environment to collect detailed performance information about what is going in within ACA-Py. -For those using plugins, RC1 includes PR [\#2255](https://github.com/hyperledger/aries-cloudagent-python/pull/2255) to fix a backwards compatibility break in -the loading of plugins that was inadvertently introduced in 0.8.1. +An important new feature in this release is the ability to set some instance +configuration settings at the tenant level of a multi-tenant deployment. See PR +[\#2233]. There are no breaking changes in this release. +[\#2255]: https://github.com/hyperledger/aries-cloudagent-python/pull/2255 +[\#2233]: https://github.com/hyperledger/aries-cloudagent-python/pull/2233 + ### Categorized List of Pull Requests - Connections Fixes/Updates @@ -24,6 +28,7 @@ There are no breaking changes in this release. - Credential Exchange (Issue, Present) Updates - Pass document loader to jsonld.expand [\#2175](https://github.com/hyperledger/aries-cloudagent-python/pull/2175) [andrewwhitehead](https://github.com/andrewwhitehead) - Multi-tenancy fixes/updates + - Allow Configuration Settings on a per-tenant basis [\#2233](https://github.com/hyperledger/aries-cloudagent-python/pull/2233) [shaangill025](https://github.com/shaangill025) - stand up multiple agents (single and multi) for local development and testing [\#2230](https://github.com/hyperledger/aries-cloudagent-python/pull/2230) [usingtechnology](https://github.com/usingtechnology) - Multi-tenant self-managed mediation verkey lookup [\#2232](https://github.com/hyperledger/aries-cloudagent-python/pull/2232) [usingtechnology](https://github.com/usingtechnology) - fix: route multitenant connectionless oob invitation [\#2243](https://github.com/hyperledger/aries-cloudagent-python/pull/2243) [TimoGlastra](https://github.com/TimoGlastra) @@ -49,6 +54,7 @@ There are no breaking changes in this release. - Message Tracing/Timing Updates - Add updated ELK stack for demos. [\#2236](https://github.com/hyperledger/aries-cloudagent-python/pull/2236) [usingtechnology](https://github.com/usingtechnology) - Release management pull requests + - 0.8.2-rc2 [\#2283](https://github.com/hyperledger/aries-cloudagent-python/pull/2283) [swcurran](https://github.com/swcurran) - 0.8.2-rc1 [\#2282](https://github.com/hyperledger/aries-cloudagent-python/pull/2282) [swcurran](https://github.com/swcurran) - 0.8.2-rc0 [\#2260](https://github.com/hyperledger/aries-cloudagent-python/pull/2260) [swcurran](https://github.com/swcurran) diff --git a/aries_cloudagent/version.py b/aries_cloudagent/version.py index 02cd553e5e..9de01f1cbf 100644 --- a/aries_cloudagent/version.py +++ b/aries_cloudagent/version.py @@ -1,4 +1,4 @@ """Library version information.""" -__version__ = "0.8.2-rc1" +__version__ = "0.8.2-rc2" RECORD_TYPE_ACAPY_VERSION = "acapy_version" diff --git a/docs/generated/aries_cloudagent.rst b/docs/generated/aries_cloudagent.rst index ef01b45059..4e431df88e 100644 --- a/docs/generated/aries_cloudagent.rst +++ b/docs/generated/aries_cloudagent.rst @@ -28,6 +28,7 @@ Subpackages aries_cloudagent.protocols aries_cloudagent.resolver aries_cloudagent.revocation + aries_cloudagent.settings aries_cloudagent.storage aries_cloudagent.tails aries_cloudagent.transport diff --git a/docs/generated/aries_cloudagent.settings.rst b/docs/generated/aries_cloudagent.settings.rst new file mode 100644 index 0000000000..fff50e5c48 --- /dev/null +++ b/docs/generated/aries_cloudagent.settings.rst @@ -0,0 +1,18 @@ +aries\_cloudagent.settings package +================================== + +.. automodule:: aries_cloudagent.settings + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +aries\_cloudagent.settings.routes module +---------------------------------------- + +.. automodule:: aries_cloudagent.settings.routes + :members: + :undoc-members: + :show-inheritance: diff --git a/open-api/openapi.json b/open-api/openapi.json index b27af39ad6..0b1734b955 100644 --- a/open-api/openapi.json +++ b/open-api/openapi.json @@ -2,7 +2,7 @@ "openapi" : "3.0.1", "info" : { "title" : "Aries Cloud Agent", - "version" : "v0.8.2-rc1" + "version" : "v0.8.2-rc2" }, "servers" : [ { "url" : "/" diff --git a/open-api/swagger.json b/open-api/swagger.json index a0f84c7ed3..2e2e230f07 100644 --- a/open-api/swagger.json +++ b/open-api/swagger.json @@ -1,7 +1,7 @@ { "swagger" : "2.0", "info" : { - "version" : "v0.8.2-rc1", + "version" : "v0.8.2-rc2", "title" : "Aries Cloud Agent" }, "tags" : [ { From 2fc81794c6d72527414482eb37a99051ab21d770 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Wed, 28 Jun 2023 17:26:58 +0000 Subject: [PATCH 863/872] Change number of this PR Signed-off-by: Stephen Curran --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 670825bb6e..5f6b56916f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,7 +54,8 @@ There are no breaking changes in this release. - Message Tracing/Timing Updates - Add updated ELK stack for demos. [\#2236](https://github.com/hyperledger/aries-cloudagent-python/pull/2236) [usingtechnology](https://github.com/usingtechnology) - Release management pull requests - - 0.8.2-rc2 [\#2283](https://github.com/hyperledger/aries-cloudagent-python/pull/2283) [swcurran](https://github.com/swcurran) + - 0.8.2-rc2 [\#2284](https://github.com/hyperledger/aries-cloudagent-python/pull/2284 + ) [swcurran](https://github.com/swcurran) - 0.8.2-rc1 [\#2282](https://github.com/hyperledger/aries-cloudagent-python/pull/2282) [swcurran](https://github.com/swcurran) - 0.8.2-rc0 [\#2260](https://github.com/hyperledger/aries-cloudagent-python/pull/2260) [swcurran](https://github.com/swcurran) From 91c76817716663d3da4504e3ec243f939ff5f9b8 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Wed, 28 Jun 2023 22:48:13 +0000 Subject: [PATCH 864/872] 0.8.2 Signed-off-by: Stephen Curran --- CHANGELOG.md | 15 +++++++++++---- aries_cloudagent/version.py | 2 +- open-api/openapi.json | 2 +- open-api/swagger.json | 2 +- requirements.askar.txt | 2 +- 5 files changed, 15 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f6b56916f..b10863afaf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ -# 0.8.2-rc2 +# 0.8.2 -## June 28, 2023 +## June 29, 2023 Release 0.8.2 contains a number of minor fixes and updates to ACA-Py, including the correction of a regression in Release 0.8.0 related to the use of plugins @@ -8,6 +8,9 @@ the correction of a regression in Release 0.8.0 related to the use of plugins development environment to collect detailed performance information about what is going in within ACA-Py. +This release pulls in [indy-shared-rs] Release 3.3 which fixes a serious issue in AnonCreds verification, as described in issue [\#2036], where the verification of a presentation with multiple revocable credentials fails when using [Aries Askar] and the +other shared components. This issue occurs only when using [Aries Askar] and [indy-credx Release 3.3]. + An important new feature in this release is the ability to set some instance configuration settings at the tenant level of a multi-tenant deployment. See PR [\#2233]. @@ -16,6 +19,10 @@ There are no breaking changes in this release. [\#2255]: https://github.com/hyperledger/aries-cloudagent-python/pull/2255 [\#2233]: https://github.com/hyperledger/aries-cloudagent-python/pull/2233 +[\#2036]: https://github.com/hyperledger/aries-cloudagent-python/issues/2036 +[indy-shared-rs]: https://github.com/hyperledger/indy-shared-rs +[Aries Askar]: https://github.com/hyperledger/aries-askar +[indy-credx Release 3.3]: https://github.com/hyperledger/indy-shared-rs/releases/tag/v0.3.3 ### Categorized List of Pull Requests @@ -54,8 +61,8 @@ There are no breaking changes in this release. - Message Tracing/Timing Updates - Add updated ELK stack for demos. [\#2236](https://github.com/hyperledger/aries-cloudagent-python/pull/2236) [usingtechnology](https://github.com/usingtechnology) - Release management pull requests - - 0.8.2-rc2 [\#2284](https://github.com/hyperledger/aries-cloudagent-python/pull/2284 - ) [swcurran](https://github.com/swcurran) + - 0.8.2 [\#2285](https://github.com/hyperledger/aries-cloudagent-python/pull/2285) [swcurran](https://github.com/swcurran) + - 0.8.2-rc2 [\#2284](https://github.com/hyperledger/aries-cloudagent-python/pull/2283) [swcurran](https://github.com/swcurran) - 0.8.2-rc1 [\#2282](https://github.com/hyperledger/aries-cloudagent-python/pull/2282) [swcurran](https://github.com/swcurran) - 0.8.2-rc0 [\#2260](https://github.com/hyperledger/aries-cloudagent-python/pull/2260) [swcurran](https://github.com/swcurran) diff --git a/aries_cloudagent/version.py b/aries_cloudagent/version.py index 9de01f1cbf..2ec2b6d7de 100644 --- a/aries_cloudagent/version.py +++ b/aries_cloudagent/version.py @@ -1,4 +1,4 @@ """Library version information.""" -__version__ = "0.8.2-rc2" +__version__ = "0.8.2" RECORD_TYPE_ACAPY_VERSION = "acapy_version" diff --git a/open-api/openapi.json b/open-api/openapi.json index 0b1734b955..4214f7064f 100644 --- a/open-api/openapi.json +++ b/open-api/openapi.json @@ -2,7 +2,7 @@ "openapi" : "3.0.1", "info" : { "title" : "Aries Cloud Agent", - "version" : "v0.8.2-rc2" + "version" : "v0.8.2" }, "servers" : [ { "url" : "/" diff --git a/open-api/swagger.json b/open-api/swagger.json index 2e2e230f07..353041c04e 100644 --- a/open-api/swagger.json +++ b/open-api/swagger.json @@ -1,7 +1,7 @@ { "swagger" : "2.0", "info" : { - "version" : "v0.8.2-rc2", + "version" : "v0.8.2", "title" : "Aries Cloud Agent" }, "tags" : [ { diff --git a/requirements.askar.txt b/requirements.askar.txt index 6a44c42e39..9b34f060dc 100644 --- a/requirements.askar.txt +++ b/requirements.askar.txt @@ -1,3 +1,3 @@ aries-askar~=0.2.5 -indy-credx~=0.3 +main indy-vdr~=0.3.3 From 7f391f10381a8b93304f9ba0435b5e32f3739c45 Mon Sep 17 00:00:00 2001 From: Stephen Curran Date: Wed, 28 Jun 2023 23:22:19 +0000 Subject: [PATCH 865/872] Fix typo in requirements file Signed-off-by: Stephen Curran --- requirements.askar.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.askar.txt b/requirements.askar.txt index 9b34f060dc..6a44c42e39 100644 --- a/requirements.askar.txt +++ b/requirements.askar.txt @@ -1,3 +1,3 @@ aries-askar~=0.2.5 -main +indy-credx~=0.3 indy-vdr~=0.3.3 From 25d916dcce4442d9dd546bdf2d53a8a32da01cdc Mon Sep 17 00:00:00 2001 From: Pritam Singh Date: Fri, 4 Aug 2023 11:55:38 +0530 Subject: [PATCH 866/872] fix: add thread_id to /issue-credential-2.0/send-offer Signed-off-by: Pritam Singh --- aries_cloudagent/protocols/issue_credential/v2_0/routes.py | 4 ++++ open-api/openapi.json | 5 +++++ open-api/swagger.json | 5 +++++ 3 files changed, 14 insertions(+) diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/routes.py b/aries_cloudagent/protocols/issue_credential/v2_0/routes.py index 129b98250f..760c602815 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/routes.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/routes.py @@ -812,6 +812,7 @@ async def _create_free_offer( preview_spec: dict = None, comment: str = None, trace_msg: bool = None, + thread_id: str = None, ): """Create a credential offer and related exchange record.""" @@ -834,6 +835,7 @@ async def _create_free_offer( auto_issue=auto_issue, auto_remove=auto_remove, trace=trace_msg, + thread_id=thread_id ) cred_manager = V20CredManager(profile) @@ -953,6 +955,7 @@ async def credential_exchange_send_free_offer(request: web.BaseRequest): comment = body.get("comment") preview_spec = body.get("credential_preview") trace_msg = body.get("trace") + thread_id = body.get("thread_id") cred_ex_record = None conn_record = None @@ -971,6 +974,7 @@ async def credential_exchange_send_free_offer(request: web.BaseRequest): preview_spec=preview_spec, comment=comment, trace_msg=trace_msg, + thread_id=thread_id ) result = cred_ex_record.serialize() diff --git a/open-api/openapi.json b/open-api/openapi.json index 4214f7064f..17178dd66b 100644 --- a/open-api/openapi.json +++ b/open-api/openapi.json @@ -12148,6 +12148,11 @@ "trace" : { "description" : "Record trace information, based on agent configuration", "type" : "boolean" + }, + "thread_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "thread identifier" } }, "required" : [ "connection_id", "filter" ], diff --git a/open-api/swagger.json b/open-api/swagger.json index 353041c04e..71860db595 100644 --- a/open-api/swagger.json +++ b/open-api/swagger.json @@ -11072,6 +11072,11 @@ "trace" : { "type" : "boolean", "description" : "Record trace information, based on agent configuration" + }, + "thread_id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "thread identifier" } } }, From 897cdbd21f808313e9796b2eb4ed1f2229d69696 Mon Sep 17 00:00:00 2001 From: Pritam Singh Date: Mon, 28 Aug 2023 12:32:02 +0530 Subject: [PATCH 867/872] fix: add thread_id to /present-proof/create-request Signed-off-by: Pritam Singh --- aries_cloudagent/protocols/present_proof/v1_0/routes.py | 1 + aries_cloudagent/protocols/present_proof/v2_0/routes.py | 1 + 2 files changed, 2 insertions(+) diff --git a/aries_cloudagent/protocols/present_proof/v1_0/routes.py b/aries_cloudagent/protocols/present_proof/v1_0/routes.py index f13d757cfd..f2f79ced2a 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/routes.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/routes.py @@ -494,6 +494,7 @@ async def presentation_exchange_create_request(request: web.BaseRequest): ) ], ) + presentation_request_message.assign_thread_id(body.get("thread_id")) auto_verify = body.get( "auto_verify", context.settings.get("debug.auto_verify_presentation") ) diff --git a/aries_cloudagent/protocols/present_proof/v2_0/routes.py b/aries_cloudagent/protocols/present_proof/v2_0/routes.py index 68a55087ef..db9cf5fc1e 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/routes.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/routes.py @@ -825,6 +825,7 @@ async def present_proof_create_request(request: web.BaseRequest): will_confirm=True, **_formats_attach(pres_request_spec, PRES_20_REQUEST, "request_presentations"), ) + pres_request_message.assign_thread_id(body.get("thread_id")) auto_verify = body.get( "auto_verify", context.settings.get("debug.auto_verify_presentation") ) From cc0c3f93a1ee4ea48ba66746557b7d29619131b4 Mon Sep 17 00:00:00 2001 From: Pritam Singh Date: Wed, 30 Aug 2023 14:45:56 +0530 Subject: [PATCH 868/872] fix: add thread_id to /present-proof/send-request Signed-off-by: Pritam Singh --- aries_cloudagent/protocols/present_proof/v1_0/routes.py | 1 + aries_cloudagent/protocols/present_proof/v2_0/routes.py | 1 + 2 files changed, 2 insertions(+) diff --git a/aries_cloudagent/protocols/present_proof/v1_0/routes.py b/aries_cloudagent/protocols/present_proof/v1_0/routes.py index f2f79ced2a..997534f678 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/routes.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/routes.py @@ -579,6 +579,7 @@ async def presentation_exchange_send_free_request(request: web.BaseRequest): ) ], ) + presentation_request_message.assign_thread_id(body.get("thread_id")) trace_msg = body.get("trace") presentation_request_message.assign_trace_decorator( context.settings, diff --git a/aries_cloudagent/protocols/present_proof/v2_0/routes.py b/aries_cloudagent/protocols/present_proof/v2_0/routes.py index db9cf5fc1e..e9d886b9ac 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/routes.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/routes.py @@ -907,6 +907,7 @@ async def present_proof_send_free_request(request: web.BaseRequest): will_confirm=True, **_formats_attach(pres_request_spec, PRES_20_REQUEST, "request_presentations"), ) + pres_request_message.assign_thread_id(body.get("thread_id")) auto_verify = body.get( "auto_verify", context.settings.get("debug.auto_verify_presentation") ) From fa1008265ffc3bc3df476132640713add8c0255c Mon Sep 17 00:00:00 2001 From: Pritam Singh Date: Wed, 27 Dec 2023 14:53:36 +0530 Subject: [PATCH 869/872] fix: add client_max_body_size to nginx Signed-off-by: Pritam Singh --- deployment/nginx.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/deployment/nginx.conf b/deployment/nginx.conf index 7398a85f49..8efb796f02 100644 --- a/deployment/nginx.conf +++ b/deployment/nginx.conf @@ -15,6 +15,7 @@ http{ location /agent { proxy_pass http://127.0.0.1:11020/; + client_max_body_size 0; } } } From e024d88bd83bdbe8f0df7fa8c35952b5ec70d283 Mon Sep 17 00:00:00 2001 From: Shashwat Vijay Date: Thu, 19 Sep 2024 13:11:09 +0530 Subject: [PATCH 870/872] update: add aws deploy action --- .github/workflows/deploy-development.yml | 54 ++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 .github/workflows/deploy-development.yml diff --git a/.github/workflows/deploy-development.yml b/.github/workflows/deploy-development.yml new file mode 100644 index 0000000000..a7ff48db80 --- /dev/null +++ b/.github/workflows/deploy-development.yml @@ -0,0 +1,54 @@ +name: Deploy Development to App Runner +on: + push: + branches: [development] # Trigger workflow on git push to main branch + workflow_dispatch: # Allow manual invocation of the workflow + inputs: + branch: + description: deploy given branch + required: true + type: string + default: development + +jobs: + deploy: + runs-on: ubuntu-latest + # These permissions are needed to interact with GitHub's OIDC Token endpoint. + permissions: + id-token: write + contents: read + environment: development + + steps: + - name: Checkout Code + uses: actions/checkout@v3 + with: + persist-credentials: false + ref: ${{ github.event.inputs.branch }} + + - name: Configure AWS credentials + id: aws-credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + # Use GitHub OIDC provider + role-to-assume: ${{ secrets.AWS_ASSUME_ROLE_ARN }} + aws-region: ${{ secrets.AWS_REGION }} + + - name: Login to Amazon ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v2 + + - name: Set image tage as current date & time + id: date + run: echo "IMAGE_TAG=$(date +'%Y-%m-%dT%H-%M-%S')" >> $GITHUB_ENV + + - name: Build, tag, and push image to Amazon ECR + id: build-image + env: + ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} + ECR_REPOSITORY: dev/acapy + PKG_TOKEN: ${{ secrets.PKG_TOKEN }} + run: | + docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG -t $ECR_REGISTRY/$ECR_REPOSITORY:latest . --build-arg PKG_TOKEN=$PKG_TOKEN + docker push $ECR_REGISTRY/$ECR_REPOSITORY --all-tags + echo "Pushed $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG to AWS ECR" \ No newline at end of file From 1e22aa87768dccef5ce4037d0c8893d404487157 Mon Sep 17 00:00:00 2001 From: Shashwat Vijay Date: Thu, 19 Sep 2024 13:16:26 +0530 Subject: [PATCH 871/872] fix: aws deploy action --- .github/workflows/deploy-development.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-development.yml b/.github/workflows/deploy-development.yml index a7ff48db80..95eaa1629a 100644 --- a/.github/workflows/deploy-development.yml +++ b/.github/workflows/deploy-development.yml @@ -49,6 +49,6 @@ jobs: ECR_REPOSITORY: dev/acapy PKG_TOKEN: ${{ secrets.PKG_TOKEN }} run: | - docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG -t $ECR_REGISTRY/$ECR_REPOSITORY:latest . --build-arg PKG_TOKEN=$PKG_TOKEN + docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG -t $ECR_REGISTRY/$ECR_REPOSITORY:latest . docker push $ECR_REGISTRY/$ECR_REPOSITORY --all-tags echo "Pushed $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG to AWS ECR" \ No newline at end of file From c42a0347ef7947c3e538d863e2aee2bbd33bdd03 Mon Sep 17 00:00:00 2001 From: Shashwat Vijay Date: Wed, 25 Sep 2024 11:34:53 +0530 Subject: [PATCH 872/872] update: aws prod deploy action --- .github/workflows/deploy-production.yml | 54 +++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 .github/workflows/deploy-production.yml diff --git a/.github/workflows/deploy-production.yml b/.github/workflows/deploy-production.yml new file mode 100644 index 0000000000..015fbf65f9 --- /dev/null +++ b/.github/workflows/deploy-production.yml @@ -0,0 +1,54 @@ +name: Deploy Production to App Runner +on: + push: + branches: [main] # Trigger workflow on git push to main branch + workflow_dispatch: # Allow manual invocation of the workflow + inputs: + branch: + description: deploy given branch + required: true + type: string + default: main + +jobs: + deploy: + runs-on: ubuntu-latest + # These permissions are needed to interact with GitHub's OIDC Token endpoint. + permissions: + id-token: write + contents: read + environment: production + + steps: + - name: Checkout Code + uses: actions/checkout@v3 + with: + persist-credentials: false + ref: ${{ github.event.inputs.branch }} + + - name: Configure AWS credentials + id: aws-credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + # Use GitHub OIDC provider + role-to-assume: ${{ secrets.AWS_ASSUME_ROLE_ARN }} + aws-region: ${{ secrets.AWS_REGION }} + + - name: Login to Amazon ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v2 + + - name: Set image tage as current date & time + id: date + run: echo "IMAGE_TAG=$(date +'%Y-%m-%dT%H-%M-%S')" >> $GITHUB_ENV + + - name: Build, tag, and push image to Amazon ECR + id: build-image + env: + ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} + ECR_REPOSITORY: prod/acapy + PKG_TOKEN: ${{ secrets.PKG_TOKEN }} + run: | + docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG -t $ECR_REGISTRY/$ECR_REPOSITORY:latest . + docker push $ECR_REGISTRY/$ECR_REPOSITORY --all-tags + echo "Pushed $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG to AWS ECR" \ No newline at end of file