diff --git a/.github/workflows/integrationtests.yml b/.github/workflows/integrationtests.yml index b1d499a846..db62b14a34 100644 --- a/.github/workflows/integrationtests.yml +++ b/.github/workflows/integrationtests.yml @@ -16,10 +16,10 @@ jobs: uses: actions/checkout@v2 with: path: acapy - - name: run-von-network - uses: ./acapy/actions/run-von-network - - name: run-indy-tails-server - uses: ./acapy/actions/run-indy-tails-server + #- name: run-von-network + # uses: ./acapy/actions/run-von-network + #- name: run-indy-tails-server + # uses: ./acapy/actions/run-indy-tails-server - name: run-integration-tests uses: ./acapy/actions/run-integration-tests # to run with a specific set of tests include the following parameter: diff --git a/CHANGELOG.md b/CHANGELOG.md index dc2563344b..0c6dd7a822 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,32 @@ +# 0.7.1 + +## August 31, 2021 + +A relatively minor maintenance release to address issues found since the 0.7.0 Release. +Includes some cleanups of JSON-LD Verifiable Credentials and Verifiable Presentations + +- W3C Verifiable Credential cleanups + - Timezone inclusion [ISO 8601] for W3C VC and Proofs ([#1373](https://github.com/hyperledger/aries-cloudagent-python/pull/1373)) + - W3C VC handling where attachment is JSON and not Base64 encoded ([#1352](https://github.com/hyperledger/aries-cloudagent-python/pull/1352)) +- Refactor outbound queue interface ([#1348](https://github.com/hyperledger/aries-cloudagent-python/pull/1348)) +- Command line parameter handling for arbitrary plugins ([#1347](https://github.com/hyperledger/aries-cloudagent-python/pull/1347)) +- Add an optional parameter '--ledger-socks-proxy' ([#1342](https://github.com/hyperledger/aries-cloudagent-python/pull/1342)) +- OOB Protocol - CredentialOffer Support ([#1316](https://github.com/hyperledger/aries-cloudagent-python/pull/1316)), ([#1216](https://github.com/hyperledger/aries-cloudagent-python/pull/1216)) +- Updated IndyCredPrecisSchema - pres_referents renamed to presentation_referents ([#1334](https://github.com/hyperledger/aries-cloudagent-python/pull/1334)) +- Handle unpadded protected header in PackWireFormat::get_recipient_keys ([#1324](https://github.com/hyperledger/aries-cloudagent-python/pull/1324)) +- Initial cut of OpenAPI Code Generation guidelines ([#1339](https://github.com/hyperledger/aries-cloudagent-python/pull/1339)) +- Correct revocation API in credential revocation documentation ([#612](https://github.com/hyperledger/aries-cloudagent-python/pull/612)) +- Documentation updates for Read-The-Docs ([#1359](https://github.com/hyperledger/aries-cloudagent-python/pull/1359), +[#1366](https://github.com/hyperledger/aries-cloudagent-python/pull/1366), [#1371](https://github.com/hyperledger/aries-cloudagent-python/pull/1371)) +- Add `inject_or` method to dynamic injection framework to resolve typing ambiguity ([#1376](https://github.com/hyperledger/aries-cloudagent-python/pull/1376)) +- Other fixes: + - Indy Proof processing fix, error not raised in predicate timestamp check ([#1364](https://github.com/hyperledger/aries-cloudagent-python/pull/1364)) + - Problem Report handler for connection specific problems ([#1356](https://github.com/hyperledger/aries-cloudagent-python/pull/1356)) + - fix: error on deserializing conn record with protocol ([#1325](https://github.com/hyperledger/aries-cloudagent-python/pull/1325)) + - fix: failure to verify jsonld on non-conformant doc but vaild vmethod ([#1301](https://github.com/hyperledger/aries-cloudagent-python/pull/1301)) + - fix: allow underscore in endpoints ([#1378](https://github.com/hyperledger/aries-cloudagent-python/pull/1378)) + + # 0.7.0 ## July 14, 2021 diff --git a/actions/run-integration-tests/action.yml b/actions/run-integration-tests/action.yml index bcf49d8382..cd64509d02 100644 --- a/actions/run-integration-tests/action.yml +++ b/actions/run-integration-tests/action.yml @@ -6,13 +6,21 @@ inputs: description: "Set of flags that defines the test scope" required: false default: "-t @GHA" + IN_LEDGER_URL: + description: "URL to the von network ledger browser" + required: false + default: "http://test.bcovrin.vonx.io" + IN_PUBLIC_TAILS_URL: + description: "URL to the tails server" + required: false + default: "https://tails.vonx.io" runs: using: "composite" steps: - 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: ./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 }} shell: bash env: NO_TTY: "1" diff --git a/aries_cloudagent/admin/request_context.py b/aries_cloudagent/admin/request_context.py index 97ed6bf6ef..1fe7f79076 100644 --- a/aries_cloudagent/admin/request_context.py +++ b/aries_cloudagent/admin/request_context.py @@ -61,9 +61,7 @@ def inject( self, base_cls: Type[InjectType], settings: Mapping[str, object] = None, - *, - required: bool = True - ) -> Optional[InjectType]: + ) -> InjectType: """ Get the provided instance of a given class identifier. @@ -75,7 +73,27 @@ def inject( An instance of the base class, or None """ - return self._context.inject(base_cls, settings, required=required) + return self._context.inject(base_cls, settings) + + def inject_or( + self, + base_cls: Type[InjectType], + settings: Mapping[str, object] = None, + default: Optional[InjectType] = None, + ) -> Optional[InjectType]: + """ + Get the provided instance of a given class identifier or default if not found. + + Args: + base_cls: The base class to retrieve an instance of + settings: An optional dict providing configuration to the provider + default: default return value if no instance is found + + Returns: + An instance of the base class, or None + + """ + return self._context.inject_or(base_cls, settings, default) def update_settings(self, settings: Mapping[str, object]): """Update the current scope with additional settings.""" @@ -96,17 +114,26 @@ def test_context( def _test_session(self) -> ProfileSession: session = self.profile.session(self._context) - def _inject(base_cls, required=True): + def _inject(base_cls): if session._active and base_cls in self.session_inject: ret = self.session_inject[base_cls] - if ret is None and required: + if ret is None: raise InjectionError( "No instance provided for class: {}".format(base_cls.__name__) ) return ret - return session._context.injector.inject(base_cls, required=required) + return session._context.injector.inject(base_cls) + + def _inject_or(base_cls, default=None): + if session._active and base_cls in self.session_inject: + ret = self.session_inject[base_cls] + if ret is None: + ret = default + return ret + return session._context.injector.inject_or(base_cls, default) setattr(session, "inject", _inject) + setattr(session, "inject_or", _inject_or) return session def __repr__(self) -> str: diff --git a/aries_cloudagent/admin/server.py b/aries_cloudagent/admin/server.py index cdda68dedd..c476526997 100644 --- a/aries_cloudagent/admin/server.py +++ b/aries_cloudagent/admin/server.py @@ -26,7 +26,7 @@ from ..ledger.error import LedgerConfigError, LedgerTransactionError from ..messaging.models.openapi import OpenAPISchema from ..messaging.responder import BaseResponder -from ..multitenant.manager import MultitenantManager, MultitenantManagerError +from ..multitenant.base import BaseMultitenantManager, MultitenantManagerError from ..storage.error import StorageNotFoundError from ..transport.outbound.message import OutboundMessage from ..transport.outbound.status import OutboundSendStatus @@ -251,7 +251,7 @@ def __init__( self.webhook_router = webhook_router self.websocket_queues = {} self.site = None - self.multitenant_manager = context.inject(MultitenantManager, required=False) + self.multitenant_manager = context.inject_or(BaseMultitenantManager) self.server_paths = [] @@ -293,7 +293,7 @@ async def check_token(request: web.Request, handler): middlewares.append(check_token) - collector = self.context.inject(Collector, required=False) + collector = self.context.inject_or(Collector) if self.multitenant_manager: @@ -393,7 +393,7 @@ async def setup_context(request: web.Request, handler): self.server_paths = [route.path for route in server_routes] app.add_routes(server_routes) - plugin_registry = self.context.inject(PluginRegistry, required=False) + plugin_registry = self.context.inject_or(PluginRegistry) if plugin_registry: await plugin_registry.register_admin_routes(app) @@ -445,11 +445,11 @@ def sort_dict(raw: dict) -> dict: runner = web.AppRunner(self.app) await runner.setup() - plugin_registry = self.context.inject(PluginRegistry, required=False) + plugin_registry = self.context.inject_or(PluginRegistry) if plugin_registry: plugin_registry.post_process_routes(self.app) - event_bus = self.context.inject(EventBus, required=False) + event_bus = self.context.inject_or(EventBus) if event_bus: event_bus.subscribe(EVENT_PATTERN_WEBHOOK, self._on_webhook_event) event_bus.subscribe(EVENT_PATTERN_RECORD, self._on_record_event) @@ -548,7 +548,7 @@ async def plugins_handler(self, request: web.BaseRequest): The module list response """ - registry = self.context.inject(PluginRegistry, required=False) + registry = self.context.inject_or(PluginRegistry) plugins = registry and sorted(registry.plugin_names) or [] return web.json_response({"result": plugins}) @@ -602,7 +602,7 @@ async def status_handler(self, request: web.BaseRequest): """ status = {"version": __version__} status["label"] = self.context.settings.get("default_label") - collector = self.context.inject(Collector, required=False) + collector = self.context.inject_or(Collector) if collector: status["timing"] = collector.results if self.conductor_stats: @@ -622,7 +622,7 @@ async def status_reset_handler(self, request: web.BaseRequest): The web response """ - collector = self.context.inject(Collector, required=False) + collector = self.context.inject_or(Collector) if collector: collector.reset() return web.json_response({}) diff --git a/aries_cloudagent/admin/tests/test_admin_server.py b/aries_cloudagent/admin/tests/test_admin_server.py index ef8034feeb..46cf380bfb 100644 --- a/aries_cloudagent/admin/tests/test_admin_server.py +++ b/aries_cloudagent/admin/tests/test_admin_server.py @@ -197,8 +197,8 @@ async def test_import_routes_multitenant_middleware(self): context.injector.bind_instance(ProtocolRegistry, ProtocolRegistry()) profile = InMemoryProfile.test_profile() context.injector.bind_instance( - test_module.MultitenantManager, - test_module.MultitenantManager(profile), + test_module.BaseMultitenantManager, + test_module.BaseMultitenantManager(profile), ) await DefaultContextBuilder().load_plugins(context) server = self.get_admin_server( diff --git a/aries_cloudagent/admin/tests/test_request_context.py b/aries_cloudagent/admin/tests/test_request_context.py index 25e3601544..7775262638 100644 --- a/aries_cloudagent/admin/tests/test_request_context.py +++ b/aries_cloudagent/admin/tests/test_request_context.py @@ -22,4 +22,4 @@ async def test_session_inject_x(self): test_ctx = test_module.AdminRequestContext.test_context({Collector: None}) async with test_ctx.session() as test_sesn: with self.assertRaises(test_module.InjectionError): - test_sesn.inject(Collector, required=True) + test_sesn.inject(Collector) diff --git a/aries_cloudagent/askar/profile.py b/aries_cloudagent/askar/profile.py index c82f1c0563..e7ecfe20a0 100644 --- a/aries_cloudagent/askar/profile.py +++ b/aries_cloudagent/askar/profile.py @@ -54,6 +54,11 @@ def store(self) -> Store: """Accessor for the opened Store instance.""" return self.opened.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")) + def init_ledger_pool(self): """Initialize the ledger pool.""" if self.settings.get("ledger.disabled"): @@ -67,7 +72,7 @@ def init_ledger_pool(self): if read_only: LOGGER.error("Note: setting ledger to read-only mode") genesis_transactions = self.settings.get("ledger.genesis_transactions") - cache = self.context.injector.inject(BaseCache, required=False) + cache = self.context.injector.inject_or(BaseCache) self.ledger_pool = IndyVdrLedgerPool( pool_name, keepalive=keepalive, @@ -154,10 +159,11 @@ 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() + self._opener = self.profile.store.transaction(profile_id) else: - self._opener = self.profile.store.session() + self._opener = self.profile.store.session(profile_id) self._handle: Session = None self._acquire_start: float = None self._acquire_end: float = None diff --git a/aries_cloudagent/askar/store.py b/aries_cloudagent/askar/store.py index 8d56b315f6..f96112e4a2 100644 --- a/aries_cloudagent/askar/store.py +++ b/aries_cloudagent/askar/store.py @@ -41,6 +41,15 @@ def __init__(self, config: dict = None): self.key_derivation_method = ( config.get("key_derivation_method") or self.DEFAULT_KEY_DERIVATION ) + + if ( + self.key_derivation_method.lower() == self.KEY_DERIVATION_RAW.lower() + and self.key == "" + ): + raise ProfileError( + f"With key derivation method '{self.KEY_DERIVATION_RAW}'," + "key should also be provided" + ) # self.rekey = config.get("rekey") # self.rekey_derivation_method = config.get("rekey_derivation_method") diff --git a/aries_cloudagent/messaging/ack/__init__.py b/aries_cloudagent/askar/tests/__init__.py similarity index 100% rename from aries_cloudagent/messaging/ack/__init__.py rename to aries_cloudagent/askar/tests/__init__.py diff --git a/aries_cloudagent/askar/tests/test_profile.py b/aries_cloudagent/askar/tests/test_profile.py new file mode 100644 index 0000000000..b8b74f0358 --- /dev/null +++ b/aries_cloudagent/askar/tests/test_profile.py @@ -0,0 +1,86 @@ +import asyncio +import pytest + +from asynctest import TestCase as AsyncTestCase, mock + +from ...askar.profile import AskarProfile +from ...config.injection_context import InjectionContext + +from .. import profile as test_module + + +class TestProfile(AsyncTestCase): + @mock.patch("aries_cloudagent.askar.store.AskarOpenStore") + async def test_init_success(self, AskarOpenStore): + askar_profile = AskarProfile( + AskarOpenStore, + ) + + 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, + } + askar_profile = AskarProfile(openStore, context) + 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) + 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) + + 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 + 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_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) + + 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) diff --git a/aries_cloudagent/askar/tests/test_store.py b/aries_cloudagent/askar/tests/test_store.py new file mode 100644 index 0000000000..2af479165f --- /dev/null +++ b/aries_cloudagent/askar/tests/test_store.py @@ -0,0 +1,33 @@ +from asynctest import TestCase as AsyncTestCase + +from ...core.error import ProfileError + +from ..store import AskarStoreConfig + + +class TestStoreConfig(AsyncTestCase): + key_derivation_method = "Raw" + key = "key" + storage_type = "postgres" + + async def test_init_success(self): + config = { + "key_derivation_method": self.key_derivation_method, + "key": self.key, + "storage_type": self.storage_type, + } + + askar_store = AskarStoreConfig(config) + + assert askar_store.key_derivation_method == self.key_derivation_method + assert askar_store.key == self.key + assert askar_store.storage_type == self.storage_type + + async def test_init_should_fail_when_key_missing(self): + config = { + "key_derivation_method": self.key_derivation_method, + "storage_type": self.storage_type, + } + + with self.assertRaises(ProfileError): + askar_store = AskarStoreConfig(config) diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index b003e96ce3..ef27ebe285 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -1,18 +1,22 @@ """Command line option parsing.""" import abc +import json + from functools import reduce from itertools import chain from os import environ +from typing import Type import deepmerge import yaml + from configargparse import ArgumentParser, Namespace, YAMLConfigFileParser -from typing import Type + +from ..utils.tracing import trace_event from .error import ArgsParseError from .util import BoundedInt, ByteSize -from ..utils.tracing import trace_event CAT_PROVISION = "general" CAT_START = "start" @@ -1310,6 +1314,17 @@ def add_arguments(self, parser: ArgumentParser): "(PostgresConfig)." ), ) + parser.add_argument( + "--wallet-key-derivation-method", + type=str, + metavar="", + env_var="ACAPY_WALLET_KEY_DERIVATION_METHOD", + help=( + "Specifies the key derivation method used for wallet encryption." + "If RAW key derivation method is used, also --wallet-key parameter" + " is expected." + ), + ) parser.add_argument( "--wallet-storage-creds", type=str, @@ -1363,6 +1378,8 @@ def get_settings(self, args: Namespace) -> dict: settings["wallet.storage_type"] = args.wallet_storage_type if args.wallet_type: settings["wallet.type"] = args.wallet_type + if args.wallet_key_derivation_method: + settings["wallet.key_derivation_method"] = args.wallet_key_derivation_method if args.wallet_storage_config: settings["wallet.storage_config"] = args.wallet_storage_config if args.wallet_storage_creds: @@ -1423,6 +1440,18 @@ def add_arguments(self, parser: ArgumentParser): env_var="ACAPY_MULTITENANT_ADMIN", help="Specify whether to enable the multitenant admin api.", ) + parser.add_argument( + "--multitenancy-config", + type=str, + metavar="", + 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"}"' + '"wallet_name" is only used when "wallet_type" is "askar-profile"' + ), + ) def get_settings(self, args: Namespace): """Extract multitenant settings.""" @@ -1439,4 +1468,18 @@ def get_settings(self, args: Namespace): if args.multitenant_admin: settings["multitenant.admin_enabled"] = True + + if args.multitenancy_config: + multitenancyConfig = json.loads(args.multitenancy_config) + + if multitenancyConfig.get("wallet_type"): + settings["multitenant.wallet_type"] = multitenancyConfig.get( + "wallet_type" + ) + + if multitenancyConfig.get("wallet_name"): + settings["multitenant.wallet_name"] = multitenancyConfig.get( + "wallet_name" + ) + return settings diff --git a/aries_cloudagent/config/base.py b/aries_cloudagent/config/base.py index f3275fbc02..92c48f4756 100644 --- a/aries_cloudagent/config/base.py +++ b/aries_cloudagent/config/base.py @@ -112,9 +112,7 @@ def inject( self, base_cls: Type[InjectType], settings: Mapping[str, object] = None, - *, - required: bool = True, - ) -> Optional[InjectType]: + ) -> InjectType: """ Get the provided instance of a given class identifier. @@ -127,6 +125,26 @@ def inject( """ + @abstractmethod + def inject_or( + self, + base_cls: Type[InjectType], + settings: Mapping[str, object] = None, + default: Optional[InjectType] = None, + ) -> Optional[InjectType]: + """ + Get the provided instance of a given class identifier or default if not found. + + Args: + base_cls: The base class to retrieve an instance of + settings: An optional dict providing configuration to the provider + default: default return value if no instance is found + + Returns: + An instance of the base class, or None + + """ + @abstractmethod def copy(self) -> "BaseInjector": """Produce a copy of the injector instance.""" diff --git a/aries_cloudagent/config/injection_context.py b/aries_cloudagent/config/injection_context.py index c30dcde4a6..9e5c5e051b 100644 --- a/aries_cloudagent/config/injection_context.py +++ b/aries_cloudagent/config/injection_context.py @@ -109,9 +109,7 @@ def inject( self, base_cls: Type[InjectType], settings: Mapping[str, object] = None, - *, - required: bool = True - ) -> Optional[InjectType]: + ) -> InjectType: """ Get the provided instance of a given class identifier. @@ -123,7 +121,27 @@ def inject( An instance of the base class, or None """ - return self.injector.inject(base_cls, settings, required=required) + return self.injector.inject(base_cls, settings) + + def inject_or( + self, + base_cls: Type[InjectType], + settings: Mapping[str, object] = None, + default: Optional[InjectType] = None, + ) -> Optional[InjectType]: + """ + Get the provided instance of a given class identifier or default if not found. + + Args: + base_cls: The base class to retrieve an instance of + settings: An optional dict providing configuration to the provider + default: default return value if no instance is found + + Returns: + An instance of the base class, or None + + """ + return self.injector.inject_or(base_cls, settings, default) def copy(self) -> "InjectionContext": """Produce a copy of the injector instance.""" diff --git a/aries_cloudagent/config/injector.py b/aries_cloudagent/config/injector.py index 03fbe91953..9362df2f2a 100644 --- a/aries_cloudagent/config/injector.py +++ b/aries_cloudagent/config/injector.py @@ -51,19 +51,19 @@ def get_provider(self, base_cls: Type[InjectType]): """Find the provider associated with a class binding.""" return self._providers.get(base_cls) - def inject( + def inject_or( self, base_cls: Type[InjectType], settings: Mapping[str, object] = None, - *, - required: bool = True, + default: Optional[InjectType] = None, ) -> Optional[InjectType]: """ - Get the provided instance of a given class identifier. + Get the provided instance of a given class identifier or default if not found. Args: - cls: The base class to retrieve an instance of - params: An optional dict providing configuration to the provider + base_cls: The base class to retrieve an instance of + settings: An optional dict providing configuration to the provider + default: default return value if no instance is found Returns: An instance of the base class, or None @@ -80,17 +80,37 @@ def inject( result = provider.provide(ext_settings, self) else: result = None - if result is None: - if required: - raise InjectionError( - "No instance provided for class: {}".format(base_cls.__name__) - ) - elif not isinstance(result, base_cls) and self.enforce_typing: + + if result and not isinstance(result, base_cls) and self.enforce_typing: raise InjectionError( "Provided instance does not implement the base class: {}".format( base_cls.__name__ ) ) + + return result if result is not None else default + + def inject( + self, + base_cls: Type[InjectType], + settings: Mapping[str, object] = None, + ) -> InjectType: + """ + Get the provided instance of a given class identifier. + + Args: + cls: The base class to retrieve an instance of + params: An optional dict providing configuration to the provider + + Returns: + An instance of the base class, or None + + """ + result = self.inject_or(base_cls, settings) + if result is None: + raise InjectionError( + "No instance provided for class: {}".format(base_cls.__name__) + ) return result def copy(self) -> BaseInjector: diff --git a/aries_cloudagent/config/ledger.py b/aries_cloudagent/config/ledger.py index 1453ad9814..6a0ca49fad 100644 --- a/aries_cloudagent/config/ledger.py +++ b/aries_cloudagent/config/ledger.py @@ -63,7 +63,7 @@ async def ledger_config( session = await profile.session() - ledger = session.inject(BaseLedger, required=False) + ledger = session.inject_or(BaseLedger) if not ledger: LOGGER.info("Ledger instance not provided") return False diff --git a/aries_cloudagent/config/provider.py b/aries_cloudagent/config/provider.py index 0d0bd91290..19a2f34ca4 100644 --- a/aries_cloudagent/config/provider.py +++ b/aries_cloudagent/config/provider.py @@ -129,7 +129,7 @@ def provide(self, config: BaseSettings, injector: BaseInjector): """Provide the object instance given a config and injector.""" instance = self._provider.provide(config, injector) if self._methods: - collector: Collector = injector.inject(Collector, required=False) + collector: Collector = injector.inject_or(Collector) if collector: collector.wrap( instance, self._methods, ignore_missing=self._ignore_missing diff --git a/aries_cloudagent/config/tests/test_argparse.py b/aries_cloudagent/config/tests/test_argparse.py index b7a617714e..c4e1edbc85 100644 --- a/aries_cloudagent/config/tests/test_argparse.py +++ b/aries_cloudagent/config/tests/test_argparse.py @@ -167,6 +167,48 @@ async def test_transport_settings_file(self): ) # no asserts, just testing that the parser doesn't fail + async def test_multitenancy_settings(self): + """Test required argument parsing.""" + + parser = argparse.create_argument_parser() + group = argparse.MultitenantGroup() + group.add_arguments(parser) + + 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_error_raised_when_multitenancy_used_and_no_jwt_provided(self): + """Test that error is raised if no jwt_secret is provided with multitenancy.""" + + parser = argparse.create_argument_parser() + group = argparse.MultitenantGroup() + group.add_arguments(parser) + + result = parser.parse_args( + [ + "--multitenant", + "--multitenancy-config", + '{"wallet_type":"askar","wallet_name":"test"}', + ] + ) + + with self.assertRaises(argparse.ArgsParseError): + settings = group.get_settings(result) + def test_bytesize(self): bs = ByteSize() with self.assertRaises(ArgumentTypeError): @@ -260,3 +302,37 @@ def test_plugin_config_value_parsing(self): assert settings["plugin_config"]["x"]["y"]["z"] == "value" assert settings["plugin_config"]["a_dict"] == {"key": "value"} assert settings["plugin_config"]["a_list"] == ["one", "two"] + + async def test_wallet_key_derivation_method_value_parsing(self): + key_derivation_method = "key_derivation_method" + parser = argparse.create_argument_parser() + group = argparse.WalletGroup() + group.add_arguments(parser) + + result = parser.parse_args( + [ + "--wallet-key-derivation-method", + key_derivation_method, + ] + ) + + settings = group.get_settings(result) + + assert settings.get("wallet.key_derivation_method") == key_derivation_method + + async def test_wallet_key_value_parsing(self): + key_value = "some_key_value" + parser = argparse.create_argument_parser() + group = argparse.WalletGroup() + group.add_arguments(parser) + + result = parser.parse_args( + [ + "--wallet-key", + key_value, + ] + ) + + settings = group.get_settings(result) + + assert settings.get("wallet.key") == key_value diff --git a/aries_cloudagent/config/tests/test_injection_context.py b/aries_cloudagent/config/tests/test_injection_context.py index 57376435cb..9f0b0a2ed0 100644 --- a/aries_cloudagent/config/tests/test_injection_context.py +++ b/aries_cloudagent/config/tests/test_injection_context.py @@ -53,7 +53,7 @@ def test_settings_scope(self): async def test_inject_simple(self): """Test a basic injection.""" - assert self.test_instance.inject(str, required=False) is None + assert self.test_instance.inject_or(str) is None with self.assertRaises(InjectionError): self.test_instance.inject(str) self.test_instance.injector.bind_instance(str, self.test_value) @@ -65,10 +65,10 @@ async def test_inject_simple(self): async def test_inject_scope(self): """Test a scoped injection.""" context = self.test_instance.start_scope(self.test_scope) - assert context.inject(str, required=False) is None + assert context.inject_or(str) is None context.injector.bind_instance(str, self.test_value) assert context.inject(str) is self.test_value - assert self.test_instance.inject(str, required=False) is None + assert self.test_instance.inject_or(str) is None root = context.injector_for_scope(context.ROOT_SCOPE) - assert root.inject(str, required=False) is None - assert self.test_instance.inject(str, required=False) is None + assert root.inject_or(str) is None + assert self.test_instance.inject_or(str) is None diff --git a/aries_cloudagent/config/tests/test_injector.py b/aries_cloudagent/config/tests/test_injector.py index 5216b5e0c6..804e328a0e 100644 --- a/aries_cloudagent/config/tests/test_injector.py +++ b/aries_cloudagent/config/tests/test_injector.py @@ -43,7 +43,7 @@ def test_settings_init(self): def test_inject_simple(self): """Test a basic injection.""" - assert self.test_instance.inject(str, required=False) is None + assert self.test_instance.inject_or(str) is None with self.assertRaises(InjectionError): self.test_instance.inject(str) with self.assertRaises(ValueError): @@ -75,7 +75,7 @@ def test_bad_provider(self): self.test_instance.bind_provider(str, MockProvider(None)) with self.assertRaises(InjectionError): self.test_instance.inject(str) - self.test_instance.inject(str, required=False) + self.test_instance.inject_or(str) self.test_instance.bind_provider(str, MockProvider(1)) self.test_instance.clear_binding(str) assert self.test_instance.get_provider(str) is None @@ -139,3 +139,9 @@ def test_inject_cached(self): i1 = self.test_instance.inject(MockInstance) i2 = self.test_instance.inject(MockInstance) assert i1 is i2 + + def test_falsey_still_returns(self): + """Test the injector still returns falsey values.""" + self.test_instance.bind_instance(dict, dict()) + assert self.test_instance.inject_or(dict) is not None + assert self.test_instance.inject(dict) is not None diff --git a/aries_cloudagent/config/tests/test_wallet.py b/aries_cloudagent/config/tests/test_wallet.py index 092cf48bda..59cb8332ad 100644 --- a/aries_cloudagent/config/tests/test_wallet.py +++ b/aries_cloudagent/config/tests/test_wallet.py @@ -37,8 +37,8 @@ async def setUp(self): transaction=async_mock.CoroutineMock(return_value=self.session), ) - def _inject(cls, required=True): - return self.injector.inject(cls, required=required) + def _inject(cls): + return self.injector.inject(cls) self.session.inject = _inject self.context = InjectionContext() @@ -238,3 +238,31 @@ async def test_wallet_config_seed_no_public_did(self): mock_seed_to_did.return_value = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" await test_module.wallet_config(self.context, provision=True) + + async def test_wallet_config_for_key_derivation_method(self): + self.context.update_settings( + { + "wallet.key_derivation_method": "derivation_method", + } + ) + mock_wallet = async_mock.MagicMock( + get_public_did=async_mock.CoroutineMock( + return_value=async_mock.MagicMock(did=TEST_DID, verkey=TEST_VERKEY) + ), + set_public_did=async_mock.CoroutineMock(), + create_local_did=async_mock.CoroutineMock( + return_value=async_mock.MagicMock(did=TEST_DID, verkey=TEST_VERKEY) + ), + ) + self.injector.bind_instance(BaseWallet, mock_wallet) + + with async_mock.patch.object( + MockManager, "provision", async_mock.CoroutineMock() + ) as mock_mgr_provision: + mock_mgr_provision.return_value = self.profile + + await test_module.wallet_config(self.context, provision=True) + + mock_mgr_provision.assert_called_once_with( + self.context, {"key_derivation_method": "derivation_method"} + ) diff --git a/aries_cloudagent/config/wallet.py b/aries_cloudagent/config/wallet.py index 57fa48bb89..3fefdeadfe 100644 --- a/aries_cloudagent/config/wallet.py +++ b/aries_cloudagent/config/wallet.py @@ -16,7 +16,15 @@ LOGGER = logging.getLogger(__name__) -CFG_MAP = {"key", "rekey", "name", "storage_config", "storage_creds", "storage_type"} +CFG_MAP = { + "key", + "key_derivation_method", + "rekey", + "name", + "storage_config", + "storage_creds", + "storage_type", +} async def wallet_config( diff --git a/aries_cloudagent/core/conductor.py b/aries_cloudagent/core/conductor.py index d2b1797ded..951e7d785a 100644 --- a/aries_cloudagent/core/conductor.py +++ b/aries_cloudagent/core/conductor.py @@ -19,11 +19,11 @@ from ..config.ledger import get_genesis_transactions, ledger_config from ..config.logging import LoggingConfigurator from ..config.wallet import wallet_config -from ..connections.models.conn_record import ConnRecord from ..core.profile import Profile from ..ledger.error import LedgerConfigError, LedgerTransactionError from ..messaging.responder import BaseResponder -from ..multitenant.manager import MultitenantManager +from ..multitenant.base import BaseMultitenantManager +from ..multitenant.manager_provider import MultitenantManagerProvider from ..protocols.connections.v1_0.manager import ( ConnectionManager, ConnectionManagerError, @@ -121,14 +121,15 @@ async def setup(self): self.dispatcher = Dispatcher(self.root_profile) await self.dispatcher.setup() - wire_format = context.inject(BaseWireFormat, required=False) + wire_format = context.inject_or(BaseWireFormat) if wire_format and hasattr(wire_format, "task_queue"): wire_format.task_queue = self.dispatcher.task_queue # Bind manager for multitenancy related tasks if context.settings.get("multitenant.enabled"): - multitenant_mgr = MultitenantManager(self.root_profile) - context.injector.bind_instance(MultitenantManager, multitenant_mgr) + context.injector.bind_provider( + BaseMultitenantManager, MultitenantManagerProvider(self.root_profile) + ) # Bind default PyLD document loader context.injector.bind_instance( @@ -159,7 +160,7 @@ async def setup(self): raise # Fetch stats collector, if any - collector = context.inject(Collector, required=False) + collector = context.inject_or(Collector) if collector: # add stats to our own methods collector.wrap( @@ -303,45 +304,38 @@ async def start(self) -> None: LOGGER.exception("Error creating invitation") # Accept mediation invitation if specified - mediation_invitation = context.settings.get("mediation.invite") + mediation_invitation: str = context.settings.get("mediation.invite") if mediation_invitation: try: mediation_connections_invite = context.settings.get( "mediation.connections_invite", False ) - if mediation_connections_invite: - async with self.root_profile.session() as session: - mgr = ConnectionManager(session) - conn_record = await mgr.receive_invitation( - invitation=ConnectionInvitation.from_url( - mediation_invitation - ), - auto_accept=True, - ) - await conn_record.metadata_set( - session, MediationManager.SEND_REQ_AFTER_CONNECTION, True - ) - await conn_record.metadata_set( - session, MediationManager.SET_TO_DEFAULT_ON_GRANTED, True - ) - print("Attempting to connect to mediator...") - del mgr - else: - async with self.root_profile.session() as session: - mgr = OutOfBandManager(session) - conn_record_dict = await mgr.receive_invitation( - invi_msg=InvitationMessage.from_url(mediation_invitation), - auto_accept=True, - ) - conn_record = ConnRecord.deserialize(conn_record_dict) - await conn_record.metadata_set( - session, MediationManager.SEND_REQ_AFTER_CONNECTION, True - ) - await conn_record.metadata_set( - session, MediationManager.SET_TO_DEFAULT_ON_GRANTED, True - ) - print("Attempting to connect to mediator...") - del mgr + invitation_handler = ( + ConnectionInvitation + if mediation_connections_invite + else InvitationMessage + ) + + async with self.root_profile.session() as session: + mgr = ( + ConnectionManager(session) + if mediation_connections_invite + else OutOfBandManager(session) + ) + + conn_record = await mgr.receive_invitation( + invitation=invitation_handler.from_url(mediation_invitation), + auto_accept=True, + ) + + await conn_record.metadata_set( + session, MediationManager.SEND_REQ_AFTER_CONNECTION, True + ) + await conn_record.metadata_set( + session, MediationManager.SET_TO_DEFAULT_ON_GRANTED, True + ) + print("Attempting to connect to mediator...") + del mgr except Exception: LOGGER.exception("Error accepting mediation invitation") @@ -358,7 +352,7 @@ async def stop(self, timeout=1.0): shutdown.run(self.outbound_transport_manager.stop()) # close multitenant profiles - multitenant_mgr = self.context.inject(MultitenantManager, required=False) + multitenant_mgr = self.context.inject_or(BaseMultitenantManager) if multitenant_mgr: for profile in multitenant_mgr._instances.values(): shutdown.run(profile.close()) diff --git a/aries_cloudagent/core/dispatcher.py b/aries_cloudagent/core/dispatcher.py index 598d59f18a..924580d9c2 100644 --- a/aries_cloudagent/core/dispatcher.py +++ b/aries_cloudagent/core/dispatcher.py @@ -57,7 +57,7 @@ def __init__(self, profile: Profile): async def setup(self): """Perform async instance setup.""" - self.collector = self.profile.inject(Collector, required=False) + self.collector = self.profile.inject_or(Collector) max_active = int(os.getenv("DISPATCHER_MAX_ACTIVE", 50)) self.task_queue = TaskQueue( max_active=max_active, timed=bool(self.collector), trace_fn=self.log_task diff --git a/aries_cloudagent/core/profile.py b/aries_cloudagent/core/profile.py index 2b7adeea38..6cbce6423f 100644 --- a/aries_cloudagent/core/profile.py +++ b/aries_cloudagent/core/profile.py @@ -78,9 +78,7 @@ def inject( self, base_cls: Type[InjectType], settings: Mapping[str, object] = None, - *, - required: bool = True, - ) -> Optional[InjectType]: + ) -> InjectType: """ Get the provided instance of a given class identifier. @@ -92,7 +90,27 @@ def inject( An instance of the base class, or None """ - return self._context.inject(base_cls, settings, required=required) + return self._context.inject(base_cls, settings) + + def inject_or( + self, + base_cls: Type[InjectType], + settings: Mapping[str, object] = None, + default: Optional[InjectType] = None, + ) -> Optional[InjectType]: + """ + Get the provided instance of a given class identifier or default if not found. + + Args: + base_cls: The base class to retrieve an instance of + settings: An optional dict providing configuration to the provider + default: default return value if no instance is found + + Returns: + An instance of the base class, or None + + """ + return self._context.inject_or(base_cls, settings, default) async def close(self): """Close the profile instance.""" @@ -102,7 +120,7 @@ async def remove(self): async def notify(self, topic: str, payload: Any): """Signal an event.""" - event_bus = self.inject(EventBus, required=False) + event_bus = self.inject_or(EventBus) if event_bus: await event_bus.notify(self, Event(topic, payload)) @@ -237,9 +255,7 @@ def inject( self, base_cls: Type[InjectType], settings: Mapping[str, object] = None, - *, - required: bool = True, - ) -> Optional[InjectType]: + ) -> InjectType: """ Get the provided instance of a given class identifier. @@ -253,7 +269,29 @@ def inject( """ if not self._active: raise ProfileSessionInactiveError() - return self._context.inject(base_cls, settings, required=required) + return self._context.inject(base_cls, settings) + + def inject_or( + self, + base_cls: Type[InjectType], + settings: Mapping[str, object] = None, + default: Optional[InjectType] = None, + ) -> Optional[InjectType]: + """ + Get the provided instance of a given class identifier or default if not found. + + Args: + base_cls: The base class to retrieve an instance of + settings: An optional dict providing configuration to the provider + default: default return value if no instance is found + + Returns: + An instance of the base class, or None + + """ + if not self._active: + raise ProfileSessionInactiveError() + return self._context.inject_or(base_cls, settings, default) def __repr__(self) -> str: """Get a human readable string.""" diff --git a/aries_cloudagent/core/tests/test_conductor.py b/aries_cloudagent/core/tests/test_conductor.py index 8e97afbb1c..40716ccf84 100644 --- a/aries_cloudagent/core/tests/test_conductor.py +++ b/aries_cloudagent/core/tests/test_conductor.py @@ -21,7 +21,7 @@ MediationRecord, ) from ...resolver.did_resolver import DIDResolver, DIDResolverRegistry -from ...multitenant.manager import MultitenantManager +from ...multitenant.base import BaseMultitenantManager from ...transport.inbound.message import InboundMessage from ...transport.inbound.receipt import MessageReceipt from ...transport.outbound.base import OutboundDeliveryError @@ -775,7 +775,6 @@ async def test_mediator_invitation_0434(self): ) conn_record.accept = ConnRecord.ACCEPT_MANUAL await conn_record.save(await conductor.root_profile.session()) - conn_record_dict = conn_record.serialize() with async_mock.patch.object( test_module.InvitationMessage, "from_url" ) as mock_from_url, async_mock.patch.object( @@ -784,7 +783,7 @@ async def test_mediator_invitation_0434(self): async_mock.MagicMock( return_value=async_mock.MagicMock( receive_invitation=async_mock.CoroutineMock( - return_value=conn_record_dict + return_value=conn_record ) ) ), @@ -935,8 +934,7 @@ async def test_shutdown_multitenant_profiles(self): ) as mock_logger: await conductor.setup() - - multitenant_mgr = conductor.context.inject(MultitenantManager) + multitenant_mgr = conductor.context.inject(BaseMultitenantManager) multitenant_mgr._instances = { "test1": async_mock.MagicMock(close=async_mock.CoroutineMock()), diff --git a/aries_cloudagent/core/tests/test_dispatcher.py b/aries_cloudagent/core/tests/test_dispatcher.py index b66b675329..3b5e245367 100644 --- a/aries_cloudagent/core/tests/test_dispatcher.py +++ b/aries_cloudagent/core/tests/test_dispatcher.py @@ -1,19 +1,17 @@ -import asyncio import json +import pytest + from asynctest import TestCase as AsyncTestCase, mock as async_mock from marshmallow import EXCLUDE from ...config.injection_context import InjectionContext -from ...connections.models.conn_record import ConnRecord from ...core.event_bus import EventBus from ...core.in_memory import InMemoryProfile from ...core.profile import Profile from ...core.protocol_registry import ProtocolRegistry from ...messaging.agent_message import AgentMessage, AgentMessageSchema -from ...messaging.responder import MockResponder from ...messaging.request_context import RequestContext -from ...messaging.util import datetime_now from ...protocols.didcomm_prefix import DIDCommPrefix from ...protocols.issue_credential.v2_0.message_types import CRED_20_PROBLEM_REPORT from ...protocols.issue_credential.v2_0.messages.cred_problem_report import ( @@ -393,7 +391,8 @@ async def test_create_send_webhook(self): context = RequestContext(profile) message = StubAgentMessage() responder = test_module.DispatcherResponder(context, message, None) - await responder.send_webhook("topic", {"pay": "load"}) + with pytest.deprecated_call(): + await responder.send_webhook("topic", {"pay": "load"}) async def test_create_enc_outbound(self): profile = make_profile() diff --git a/aries_cloudagent/core/tests/test_error.py b/aries_cloudagent/core/tests/test_error.py index 02d962b989..9a7dea11d3 100644 --- a/aries_cloudagent/core/tests/test_error.py +++ b/aries_cloudagent/core/tests/test_error.py @@ -1,6 +1,6 @@ import asyncio -from asynctest import TestCase as AsyncTestCase, mock as async_mock +from asynctest import TestCase as AsyncTestCase from ..error import BaseError diff --git a/aries_cloudagent/core/tests/test_profile.py b/aries_cloudagent/core/tests/test_profile.py index 528608f8e9..32edb646de 100644 --- a/aries_cloudagent/core/tests/test_profile.py +++ b/aries_cloudagent/core/tests/test_profile.py @@ -34,14 +34,14 @@ async def test_session_active(self): await session.rollback() with self.assertRaises(ProfileSessionInactiveError): session.inject(dict) - assert profile.inject(dict, required=False) is None + assert profile.inject_or(dict) is None await session.__aenter__() self.assertEqual(session.active, True) session.context.injector.bind_instance(dict, dict()) - assert session.inject(dict, required=False) is not None - assert profile.inject(dict, required=False) is None + assert session.inject_or(dict) is not None + assert profile.inject_or(dict) is None await session.__aexit__(None, None, None) diff --git a/aries_cloudagent/holder/routes.py b/aries_cloudagent/holder/routes.py index c961c1c9a1..07067c41bb 100644 --- a/aries_cloudagent/holder/routes.py +++ b/aries_cloudagent/holder/routes.py @@ -207,7 +207,7 @@ async def credentials_revoked(request: web.BaseRequest): fro = request.query.get("from") to = request.query.get("to") - ledger = session.inject(BaseLedger, required=False) + ledger = session.inject_or(BaseLedger) if not ledger: reason = "No ledger available" if not context.settings.get_value("wallet.type"): diff --git a/aries_cloudagent/indy/tests/test_verifier.py b/aries_cloudagent/indy/tests/test_verifier.py index 364600df8f..82c9f3c777 100644 --- a/aries_cloudagent/indy/tests/test_verifier.py +++ b/aries_cloudagent/indy/tests/test_verifier.py @@ -482,6 +482,19 @@ async def test_check_timestamps(self): ) assert "is superfluous vs. requested predicate" in str(context.exception) + # mismatched predicates and requested_predicates + proof_x = deepcopy(INDY_PROOF_PRED_NAMES) + proof_req_x = deepcopy(INDY_PROOF_REQ_PRED_NAMES) + proof_x["requested_proof"]["predicates"] = {} + with self.assertRaises(ValueError) as context: + await self.verifier.check_timestamps( + self.ledger, + proof_req_x, + proof_x, + REV_REG_DEFS, + ) + assert "predicates mismatch requested predicate" in str(context.exception) + async def test_non_revoc_intervals(self): big_pres_req = { "nonce": "12301197819298309547817", diff --git a/aries_cloudagent/indy/verifier.py b/aries_cloudagent/indy/verifier.py index d5dd3a222f..87bf076cdf 100644 --- a/aries_cloudagent/indy/verifier.py +++ b/aries_cloudagent/indy/verifier.py @@ -182,7 +182,9 @@ async def check_timestamps( 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}") + 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)): diff --git a/aries_cloudagent/ledger/base.py b/aries_cloudagent/ledger/base.py index 3a1868b6c1..c93f9d8437 100644 --- a/aries_cloudagent/ledger/base.py +++ b/aries_cloudagent/ledger/base.py @@ -5,9 +5,13 @@ from abc import ABC, abstractmethod, ABCMeta from enum import Enum from hashlib import sha256 +from time import time from typing import Sequence, Tuple, Union from ..indy.issuer import IndyIssuer +from ..storage.base import StorageRecord +from ..messaging.credential_definitions.util import CRED_DEF_SENT_RECORD_TYPE +from ..messaging.schemas.util import SCHEMA_SENT_RECORD_TYPE from ..utils import sentinel from ..wallet.did_info import DIDInfo @@ -272,6 +276,58 @@ async def get_revoc_reg_entry( ) -> Tuple[dict, int]: """Get revocation registry entry by revocation registry ID and timestamp.""" + async def add_schema_non_secrets_record(self, schema_id: str, issuer_did: str): + """ + Write the wallet non-secrets record for a schema (already written to the ledger). + + Args: + schema_id: The schema id (or stringified sequence number) + issuer_did: The DID of the issuer + + """ + schema_id_parts = schema_id.split(":") + schema_tags = { + "schema_id": schema_id, + "schema_issuer_did": issuer_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) + storage = self.get_indy_storage() + await storage.add_record(record) + + async def add_cred_def_non_secrets_record( + self, schema_id: str, issuer_did: str, credential_definition_id: str + ): + """ + Write the wallet non-secrets record for cred def (already written to the ledger). + + Note that the cred def private key signing informtion must already exist in the + wallet. + + Args: + schema_id: The schema id (or stringified sequence number) + issuer_did: The DID of the issuer + credential_definition_id: The credential definition id + + """ + 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": issuer_did, + "cred_def_id": credential_definition_id, + "epoch": str(int(time())), + } + record = StorageRecord( + CRED_DEF_SENT_RECORD_TYPE, credential_definition_id, cred_def_tags + ) + storage = self.get_indy_storage() + await storage.add_record(record) + class Role(Enum): """Enum for indy roles.""" diff --git a/aries_cloudagent/ledger/indy.py b/aries_cloudagent/ledger/indy.py index 2d4d0ac6df..eb2eed3d8d 100644 --- a/aries_cloudagent/ledger/indy.py +++ b/aries_cloudagent/ledger/indy.py @@ -17,8 +17,6 @@ from ..config.base import BaseInjector, BaseProvider, BaseSettings from ..indy.issuer import DEFAULT_CRED_DEF_TAG, IndyIssuer, IndyIssuerError from ..indy.sdk.error import IndyErrorHandler -from ..messaging.credential_definitions.util import CRED_DEF_SENT_RECORD_TYPE -from ..messaging.schemas.util import SCHEMA_SENT_RECORD_TYPE from ..storage.base import StorageRecord from ..storage.indy import IndySdkStorage from ..utils import sentinel @@ -58,7 +56,7 @@ def provide(self, settings: BaseSettings, injector: BaseInjector): LOGGER.warning("Note: setting ledger to read-only mode") genesis_transactions = settings.get("ledger.genesis_transactions") - cache = injector.inject(BaseCache, required=False) + cache = injector.inject_or(BaseCache) ledger_pool = IndySdkLedgerPool( pool_name, @@ -502,19 +500,8 @@ async def create_and_send_schema( else: raise - # TODO refactor this code (duplicated in endorser transaction manager) # Add non-secrets record - 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) - storage = self.get_indy_storage() - await storage.add_record(record) + await self.add_schema_non_secrets_record(schema_id, public_info.did) return schema_id, schema_def @@ -743,23 +730,10 @@ async def create_and_send_credential_definition( if not write_ledger: return (credential_definition_id, {"signed_txn": resp}, novel) - # TODO refactor this code (duplicated in endorser transaction manager) # Add non-secrets record - storage = self.get_indy_storage() - 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 + await self.add_cred_def_non_secrets_record( + schema_id, public_info.did, credential_definition_id ) - await storage.add_record(record) return (credential_definition_id, json.loads(credential_definition_json), novel) diff --git a/aries_cloudagent/ledger/routes.py b/aries_cloudagent/ledger/routes.py index f77ffbbc5b..90db11960d 100644 --- a/aries_cloudagent/ledger/routes.py +++ b/aries_cloudagent/ledger/routes.py @@ -170,7 +170,7 @@ async def register_ledger_nym(request: web.BaseRequest): """ context: AdminRequestContext = request["context"] session = await context.session() - ledger = session.inject(BaseLedger, required=False) + ledger = session.inject_or(BaseLedger) if not ledger: reason = "No Indy ledger available" if not session.settings.get_value("wallet.type"): @@ -226,7 +226,7 @@ async def get_nym_role(request: web.BaseRequest): """ context: AdminRequestContext = request["context"] session = await context.session() - ledger = session.inject(BaseLedger, required=False) + ledger = session.inject_or(BaseLedger) if not ledger: reason = "No Indy ledger available" if not session.settings.get_value("wallet.type"): @@ -260,7 +260,7 @@ async def rotate_public_did_keypair(request: web.BaseRequest): """ context: AdminRequestContext = request["context"] session = await context.session() - ledger = session.inject(BaseLedger, required=False) + ledger = session.inject_or(BaseLedger) if not ledger: reason = "No Indy ledger available" if not session.settings.get_value("wallet.type"): @@ -290,7 +290,7 @@ async def get_did_verkey(request: web.BaseRequest): """ context: AdminRequestContext = request["context"] session = await context.session() - ledger = session.inject(BaseLedger, required=False) + ledger = session.inject_or(BaseLedger) if not ledger: reason = "No ledger available" if not session.settings.get_value("wallet.type"): @@ -327,7 +327,7 @@ async def get_did_endpoint(request: web.BaseRequest): """ context: AdminRequestContext = request["context"] session = await context.session() - ledger = session.inject(BaseLedger, required=False) + ledger = session.inject_or(BaseLedger) if not ledger: reason = "No Indy ledger available" if not session.settings.get_value("wallet.type"): @@ -366,7 +366,7 @@ async def ledger_get_taa(request: web.BaseRequest): """ context: AdminRequestContext = request["context"] session = await context.session() - ledger = session.inject(BaseLedger, required=False) + ledger = session.inject_or(BaseLedger) if not ledger: reason = "No Indy ledger available" if not session.settings.get_value("wallet.type"): @@ -407,7 +407,7 @@ async def ledger_accept_taa(request: web.BaseRequest): """ context: AdminRequestContext = request["context"] session = await context.session() - ledger = session.inject(BaseLedger, required=False) + ledger = session.inject_or(BaseLedger) if not ledger: reason = "No Indy ledger available" if not session.settings.get_value("wallet.type"): diff --git a/aries_cloudagent/messaging/ack/message.py b/aries_cloudagent/messaging/ack/message.py deleted file mode 100644 index 201ff20953..0000000000 --- a/aries_cloudagent/messaging/ack/message.py +++ /dev/null @@ -1,47 +0,0 @@ -"""Represents an explicit ack message as per Aries RFC 15.""" - -from marshmallow import EXCLUDE, fields - -from ..agent_message import AgentMessage, AgentMessageSchema - - -class Ack(AgentMessage): - """ - Base class representing an explicit ack message. - - Subclass to adopt, specify Meta message type and handler class. - """ - - class Meta: - """Ack metadata.""" - - schema_class = "AckSchema" - - def __init__(self, status: str = None, **kwargs): - """ - Initialize an explicit ack message instance. - - Args: - status: Status (default OK) - - """ - super().__init__(**kwargs) - self.status = status or "OK" - - -class AckSchema(AgentMessageSchema): - """Schema for Ack base class.""" - - class Meta: - """Ack schema metadata.""" - - model_class = Ack - unknown = EXCLUDE - - status = fields.Constant( - constant="OK", - required=True, - description="Status: specify OK", - default="OK", - example="OK", - ) diff --git a/aries_cloudagent/messaging/ack/tests/test_ack.py b/aries_cloudagent/messaging/ack/tests/test_ack.py deleted file mode 100644 index 1a21187054..0000000000 --- a/aries_cloudagent/messaging/ack/tests/test_ack.py +++ /dev/null @@ -1,12 +0,0 @@ -from unittest import mock, TestCase - -from ..message import Ack - - -class TestAck(TestCase): - """Ack tests""" - - def test_no(self): - """Test: do not instantiate ack; subclass per protocol""" - with self.assertRaises(AttributeError): - ack = Ack() diff --git a/aries_cloudagent/messaging/credential_definitions/routes.py b/aries_cloudagent/messaging/credential_definitions/routes.py index 211550d467..a30d1a6287 100644 --- a/aries_cloudagent/messaging/credential_definitions/routes.py +++ b/aries_cloudagent/messaging/credential_definitions/routes.py @@ -188,7 +188,7 @@ async def credential_definitions_send_credential_definition(request: web.BaseReq ) endorser_did = endorser_info["endorser_did"] - ledger = context.inject(BaseLedger, required=False) + ledger = context.inject_or(BaseLedger) if not ledger: reason = "No ledger available" if not context.settings.get_value("wallet.type"): @@ -337,6 +337,43 @@ async def credential_definitions_get_credential_definition(request: web.BaseRequ cred_def_id = request.match_info["cred_def_id"] + ledger = context.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: + cred_def = await ledger.get_credential_definition(cred_def_id) + + return web.json_response({"credential_definition": cred_def}) + + +@docs( + tags=["credential-definition"], + summary="Writes a credential definition non-secret record to the wallet", +) +@match_info_schema(CredDefIdMatchInfoSchema()) +@response_schema(CredentialDefinitionGetResultSchema(), 200, description="") +async def credential_definitions_fix_cred_def_wallet_record(request: web.BaseRequest): + """ + Request handler for fixing a credential definition wallet non-secret record. + + Args: + request: aiohttp request object + + Returns: + The credential definition details. + + """ + context: AdminRequestContext = request["context"] + + session = await context.session() + storage = session.inject(BaseStorage) + + cred_def_id = request.match_info["cred_def_id"] + ledger = context.inject(BaseLedger, required=False) if not ledger: reason = "No ledger available" @@ -346,6 +383,23 @@ async def credential_definitions_get_credential_definition(request: web.BaseRequ async with ledger: cred_def = await ledger.get_credential_definition(cred_def_id) + cred_def_id_parts = cred_def_id.split(":") + schema_seq_no = cred_def_id_parts[3] + schema_response = await ledger.get_schema(schema_seq_no) + schema_id = schema_response["id"] + iss_did = cred_def_id_parts[0] + + # check if the record exists, if not add it + found = await storage.find_all_records( + type_filter=CRED_DEF_SENT_RECORD_TYPE, + tag_query={ + "cred_def_id": cred_def_id, + }, + ) + if 0 == len(found): + await ledger.add_cred_def_non_secrets_record( + schema_id, iss_did, cred_def_id + ) return web.json_response({"credential_definition": cred_def}) @@ -368,6 +422,10 @@ async def register(app: web.Application): credential_definitions_get_credential_definition, allow_head=False, ), + web.post( + "/credential-definitions/{cred_def_id}/write_record", + credential_definitions_fix_cred_def_wallet_record, + ), ] ) diff --git a/aries_cloudagent/messaging/jsonld/create_verify_data.py b/aries_cloudagent/messaging/jsonld/create_verify_data.py index e53f8fe7cc..34f6c20d39 100644 --- a/aries_cloudagent/messaging/jsonld/create_verify_data.py +++ b/aries_cloudagent/messaging/jsonld/create_verify_data.py @@ -2,8 +2,7 @@ Contains the functions needed to produce and verify a json-ld signature. This file was ported from -https://github.com/transmute-industries/Ed25519Signature2018/blob/master/ - src/createVerifyData/index.js +https://github.com/transmute-industries/Ed25519Signature2018/blob/master/src/createVerifyData/index.js """ import datetime diff --git a/aries_cloudagent/messaging/jsonld/credential.py b/aries_cloudagent/messaging/jsonld/credential.py index 6ffaf14e78..23813276b0 100644 --- a/aries_cloudagent/messaging/jsonld/credential.py +++ b/aries_cloudagent/messaging/jsonld/credential.py @@ -45,7 +45,7 @@ async def jws_sign(session, verify_data, verkey): jws_to_sign = create_jws(encoded_header, verify_data) - wallet = session.inject(BaseWallet, required=True) + wallet = session.inject(BaseWallet) signature = await wallet.sign_message(jws_to_sign, verkey) encoded_signature = bytes_to_b64(signature, urlsafe=True, pad=False) @@ -74,7 +74,7 @@ async def jws_verify(session, verify_data, signature, public_key): jws_to_verify = create_jws(encoded_header, verify_data) - wallet = session.inject(BaseWallet, required=True) + wallet = session.inject(BaseWallet) verified = await wallet.verify_message( jws_to_verify, decoded_signature, public_key, KeyType.ED25519 ) @@ -85,7 +85,7 @@ async def jws_verify(session, verify_data, signature, public_key): async def sign_credential(session, credential, signature_options, verkey): """Sign Credential.""" - document_loader = session.profile.inject(DocumentLoader, required=False) + document_loader = session.profile.inject_or(DocumentLoader) framed, verify_data_hex_string = create_verify_data( credential, signature_options, @@ -99,7 +99,7 @@ async def sign_credential(session, credential, signature_options, verkey): async def verify_credential(session, doc, verkey): """Verify credential.""" - document_loader = session.profile.inject(DocumentLoader, required=False) + document_loader = session.profile.inject_or(DocumentLoader) framed, verify_data_hex_string = create_verify_data( doc, doc["proof"], diff --git a/aries_cloudagent/messaging/models/base_record.py b/aries_cloudagent/messaging/models/base_record.py index 4059bf0c4c..07c56b2008 100644 --- a/aries_cloudagent/messaging/models/base_record.py +++ b/aries_cloudagent/messaging/models/base_record.py @@ -6,7 +6,7 @@ import uuid from datetime import datetime -from typing import Any, Mapping, Optional, Sequence, Union +from typing import Any, Mapping, Optional, Sequence, Type, TypeVar, Union from marshmallow import fields @@ -24,6 +24,9 @@ LOGGER = logging.getLogger(__name__) +RecordType = TypeVar("RecordType", bound="BaseRecord") + + def match_post_filter( record: dict, post_filter: dict, @@ -173,7 +176,7 @@ async def get_cached_key(cls, session: ProfileSession, cache_key: str): """ if not cache_key: return - cache = session.inject(BaseCache, required=False) + cache = session.inject_or(BaseCache) if cache: return await cache.get(cache_key) @@ -193,7 +196,7 @@ async def set_cached_key( if not cache_key: return - cache = session.inject(BaseCache, required=False) + cache = session.inject_or(BaseCache) if cache: await cache.set(cache_key, value, ttl or cls.DEFAULT_CACHE_TTL) @@ -209,14 +212,14 @@ async def clear_cached_key(cls, session: ProfileSession, cache_key: str): if not cache_key: return - cache = session.inject(BaseCache, required=False) + cache = session.inject_or(BaseCache) if cache: await cache.clear(cache_key) @classmethod async def retrieve_by_id( - cls, session: ProfileSession, record_id: str - ) -> "BaseRecord": + cls: Type[RecordType], session: ProfileSession, record_id: str + ) -> RecordType: """ Retrieve a stored record by ID. @@ -234,8 +237,11 @@ async def retrieve_by_id( @classmethod async def retrieve_by_tag_filter( - cls, session: ProfileSession, tag_filter: dict, post_filter: dict = None - ) -> "BaseRecord": + cls: Type[RecordType], + session: ProfileSession, + tag_filter: dict, + post_filter: dict = None, + ) -> RecordType: """ Retrieve a record by tag filter. @@ -275,14 +281,14 @@ async def retrieve_by_tag_filter( @classmethod async def query( - cls, + cls: Type[RecordType], session: ProfileSession, tag_filter: dict = None, *, post_filter_positive: dict = None, post_filter_negative: dict = None, alt: bool = False, - ) -> Sequence["BaseRecord"]: + ) -> Sequence[RecordType]: """ Query stored records. diff --git a/aries_cloudagent/messaging/request_context.py b/aries_cloudagent/messaging/request_context.py index 616ee18246..97e0d1f9ef 100644 --- a/aries_cloudagent/messaging/request_context.py +++ b/aries_cloudagent/messaging/request_context.py @@ -192,9 +192,7 @@ def inject( self, base_cls: Type[InjectType], settings: Mapping[str, object] = None, - *, - required: bool = True - ) -> Optional[InjectType]: + ) -> InjectType: """ Get the provided instance of a given class identifier. @@ -206,7 +204,27 @@ def inject( An instance of the base class, or None """ - return self._context.inject(base_cls, settings, required=required) + return self._context.inject(base_cls, settings) + + def inject_or( + self, + base_cls: Type[InjectType], + settings: Mapping[str, object] = None, + default: Optional[InjectType] = None, + ) -> Optional[InjectType]: + """ + Get the provided instance of a given class identifier or default if not found. + + Args: + base_cls: The base class to retrieve an instance of + settings: An optional dict providing configuration to the provider + default: default return value if no instance is found + + Returns: + An instance of the base class, or None + + """ + return self._context.inject_or(base_cls, settings, default) def update_settings(self, settings: Mapping[str, object]): """Update the scope with additional settings.""" diff --git a/aries_cloudagent/messaging/schemas/routes.py b/aries_cloudagent/messaging/schemas/routes.py index 0153add4e7..9039f90d4e 100644 --- a/aries_cloudagent/messaging/schemas/routes.py +++ b/aries_cloudagent/messaging/schemas/routes.py @@ -137,7 +137,7 @@ class SchemaConnIdMatchInfoSchema(OpenAPISchema): @response_schema(TxnOrSchemaSendResultSchema(), 200, description="") async def schemas_send_schema(request: web.BaseRequest): """ - Request handler for sending a credential offer. + Request handler for creating a schema. Args: request: aiohttp request object @@ -186,7 +186,7 @@ async def schemas_send_schema(request: web.BaseRequest): ) endorser_did = endorser_info["endorser_did"] - ledger = context.inject(BaseLedger, required=False) + ledger = context.inject_or(BaseLedger) if not ledger: reason = "No ledger available" if not context.settings.get_value("wallet.type"): @@ -276,6 +276,43 @@ async def schemas_get_schema(request: web.BaseRequest): schema_id = request.match_info["schema_id"] + ledger = context.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: + try: + schema = await ledger.get_schema(schema_id) + except LedgerError as err: + raise web.HTTPBadRequest(reason=err.roll_up) from err + + return web.json_response({"schema": schema}) + + +@docs(tags=["schema"], summary="Writes a schema non-secret record to the wallet") +@match_info_schema(SchemaIdMatchInfoSchema()) +@response_schema(SchemaGetResultSchema(), 200, description="") +async def schemas_fix_schema_wallet_record(request: web.BaseRequest): + """ + Request handler for fixing a schema's wallet non-secrets records. + + Args: + request: aiohttp request object + + Returns: + The schema details. + + """ + context: AdminRequestContext = request["context"] + + session = await context.session() + storage = session.inject(BaseStorage) + + schema_id = request.match_info["schema_id"] + ledger = context.inject(BaseLedger, required=False) if not ledger: reason = "No ledger available" @@ -286,6 +323,18 @@ async def schemas_get_schema(request: web.BaseRequest): async with ledger: try: schema = await ledger.get_schema(schema_id) + schema_id_parts = schema_id.split(":") + issuer_did = schema_id_parts[0] + + # check if the record exists, if not add it + found = await storage.find_all_records( + type_filter=SCHEMA_SENT_RECORD_TYPE, + tag_query={ + "schema_id": schema_id, + }, + ) + if 0 == len(found): + await ledger.add_schema_non_secrets_record(schema_id, issuer_did) except LedgerError as err: raise web.HTTPBadRequest(reason=err.roll_up) from err @@ -299,6 +348,9 @@ async def register(app: web.Application): web.post("/schemas", schemas_send_schema), web.get("/schemas/created", schemas_created, allow_head=False), web.get("/schemas/{schema_id}", schemas_get_schema, allow_head=False), + web.post( + "/schemas/{schema_id}/write_record", schemas_fix_schema_wallet_record + ), ] ) diff --git a/aries_cloudagent/messaging/tests/test_valid.py b/aries_cloudagent/messaging/tests/test_valid.py index 145e8af813..275ab79b3b 100644 --- a/aries_cloudagent/messaging/tests/test_valid.py +++ b/aries_cloudagent/messaging/tests/test_valid.py @@ -463,7 +463,6 @@ def test_endpoint(self): "", "/path/only", "https://1.2.3.4?query=true&url=false", - "http://no_tld/bad", "no-proto:8080/my/path", "smtp:8080/my/path#fragment", ] diff --git a/aries_cloudagent/messaging/valid.py b/aries_cloudagent/messaging/valid.py index b17762056b..617b50b082 100644 --- a/aries_cloudagent/messaging/valid.py +++ b/aries_cloudagent/messaging/valid.py @@ -2,7 +2,6 @@ import json -from datetime import datetime import re from base58 import alphabet @@ -18,6 +17,8 @@ B58 = alphabet if isinstance(alphabet, str) else alphabet.decode("ascii") +EXAMPLE_TIMESTAMP = 1640995199 # 2021-12-31 23:59:59Z + class StrOrDictField(Field): """URI or Dict field for Marshmallow.""" @@ -81,7 +82,7 @@ def _uri_validator(self, value): class IntEpoch(Range): """Validate value against (integer) epoch format.""" - EXAMPLE = int(datetime.now().timestamp()) + EXAMPLE = EXAMPLE_TIMESTAMP def __init__(self): """Initializer.""" @@ -436,7 +437,7 @@ def __init__(self): class IndyISO8601DateTime(Regexp): """Validate value against ISO 8601 datetime format, indy profile.""" - EXAMPLE = epoch_to_str(int(datetime.now().timestamp())) + EXAMPLE = epoch_to_str(EXAMPLE_TIMESTAMP) PATTERN = ( r"^\d{4}-\d\d-\d\d[T ]\d\d:\d\d" r"(?:\:(?:\d\d(?:\.\d{1,6})?))?(?:[+-]\d\d:?\d\d|Z|)$" @@ -638,7 +639,7 @@ class Endpoint(Regexp): # using Regexp brings in nice visual validator cue EXAMPLE = "https://myhost:8021" PATTERN = ( r"^[A-Za-z0-9\.\-\+]+:" # scheme - r"//([A-Za-z0-9][.A-Za-z0-9-]+[A-Za-z0-9])+" # host + r"//([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+" # host r"(:[1-9][0-9]*)?" # port r"(/[^?&#]+)?$" # path ) diff --git a/aries_cloudagent/multitenant/admin/routes.py b/aries_cloudagent/multitenant/admin/routes.py index f921ee2f1c..8b9593c0b6 100644 --- a/aries_cloudagent/multitenant/admin/routes.py +++ b/aries_cloudagent/multitenant/admin/routes.py @@ -1,6 +1,5 @@ """Multitenant admin routes.""" -from marshmallow import fields, validate, validates_schema, ValidationError from aiohttp import web from aiohttp_apispec import ( docs, @@ -9,17 +8,20 @@ response_schema, querystring_schema, ) +from marshmallow import fields, validate, validates_schema, ValidationError from ...admin.request_context import AdminRequestContext from ...messaging.valid import JSONWebToken, UUIDFour from ...messaging.models.base import BaseModelError from ...messaging.models.openapi import OpenAPISchema +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 ..manager import MultitenantManager + from ..error import WalletKeyMissingError @@ -306,7 +308,7 @@ async def wallet_create(request: web.BaseRequest): async with context.session() as session: try: - multitenant_mgr = session.inject(MultitenantManager) + multitenant_mgr = session.inject(BaseMultitenantManager) wallet_record = await multitenant_mgr.create_wallet( settings, key_management_mode @@ -368,7 +370,7 @@ async def wallet_update(request: web.BaseRequest): async with context.session() as session: try: - multitenant_mgr = session.inject(MultitenantManager) + multitenant_mgr = session.inject(BaseMultitenantManager) wallet_record = await multitenant_mgr.update_wallet(wallet_id, settings) result = format_wallet_record(wallet_record) except StorageNotFoundError as err: @@ -400,7 +402,7 @@ async def wallet_create_token(request: web.BaseRequest): async with context.session() as session: try: - multitenant_mgr = session.inject(MultitenantManager) + multitenant_mgr = session.inject(BaseMultitenantManager) wallet_record = await WalletRecord.retrieve_by_id(session, wallet_id) if (not wallet_record.requires_external_key) and wallet_key: @@ -444,7 +446,7 @@ async def wallet_remove(request: web.BaseRequest): async with context.session() as session: try: - multitenant_mgr = session.inject(MultitenantManager) + multitenant_mgr = session.inject(BaseMultitenantManager) wallet_record = await WalletRecord.retrieve_by_id(session, wallet_id) if (not wallet_record.requires_external_key) and wallet_key: diff --git a/aries_cloudagent/multitenant/admin/tests/test_routes.py b/aries_cloudagent/multitenant/admin/tests/test_routes.py index c4820a71d3..a8eb6dbda2 100644 --- a/aries_cloudagent/multitenant/admin/tests/test_routes.py +++ b/aries_cloudagent/multitenant/admin/tests/test_routes.py @@ -2,10 +2,7 @@ from asynctest import mock as async_mock from marshmallow.exceptions import ValidationError -from ...manager import ( - MultitenantManager, - MultitenantManagerError, -) +from ...base import BaseMultitenantManager, MultitenantManagerError from ....admin.request_context import AdminRequestContext from ....wallet.models.wallet_record import WalletRecord from ....messaging.models.base import BaseModelError @@ -23,7 +20,7 @@ async def setUp(self): return_value=mock_multitenant_mgr ) - self.session_inject = {MultitenantManager: mock_multitenant_mgr} + self.session_inject = {BaseMultitenantManager: mock_multitenant_mgr} self.context = AdminRequestContext.test_context(self.session_inject) self.request_dict = { "context": self.context, @@ -150,7 +147,7 @@ async def test_wallet_create(self): } self.request.json = async_mock.CoroutineMock(return_value=body) - mock_multitenant_mgr = self.session_inject[MultitenantManager] + mock_multitenant_mgr = self.session_inject[BaseMultitenantManager] with async_mock.patch.object(test_module.web, "json_response") as mock_response: wallet_mock = async_mock.MagicMock( @@ -192,7 +189,7 @@ async def test_wallet_create(self): async def test_wallet_create_x(self): body = {} self.request.json = async_mock.CoroutineMock(return_value=body) - mock_multitenant_mgr = self.session_inject[MultitenantManager] + mock_multitenant_mgr = self.session_inject[BaseMultitenantManager] mock_multitenant_mgr.create_wallet.side_effect = MultitenantManagerError() with self.assertRaises(test_module.web.HTTPBadRequest): @@ -216,7 +213,7 @@ async def test_wallet_create_optional_default_fields(self): } self.request.json = async_mock.CoroutineMock(return_value=body) - mock_multitenant_mgr = self.session_inject[MultitenantManager] + mock_multitenant_mgr = self.session_inject[BaseMultitenantManager] with async_mock.patch.object(test_module.web, "json_response") as mock_response: mock_multitenant_mgr.create_wallet = async_mock.CoroutineMock() @@ -245,7 +242,7 @@ async def test_wallet_update(self): "image_url": "test-image-url", } self.request.json = async_mock.CoroutineMock(return_value=body) - mock_multitenant_mgr = self.session_inject[MultitenantManager] + mock_multitenant_mgr = self.session_inject[BaseMultitenantManager] with async_mock.patch.object(test_module.web, "json_response") as mock_response: settings = { @@ -283,7 +280,7 @@ async def test_wallet_update_no_wallet_webhook_urls(self): "image_url": "test-image-url", } self.request.json = async_mock.CoroutineMock(return_value=body) - mock_multitenant_mgr = self.session_inject[MultitenantManager] + mock_multitenant_mgr = self.session_inject[BaseMultitenantManager] with async_mock.patch.object(test_module.web, "json_response") as mock_response: settings = { @@ -320,7 +317,7 @@ async def test_wallet_update_empty_wallet_webhook_urls(self): "image_url": "test-image-url", } self.request.json = async_mock.CoroutineMock(return_value=body) - mock_multitenant_mgr = self.session_inject[MultitenantManager] + mock_multitenant_mgr = self.session_inject[BaseMultitenantManager] with async_mock.patch.object(test_module.web, "json_response") as mock_response: settings = { @@ -359,7 +356,7 @@ async def test_wallet_update_wallet_settings_x(self): "image_url": "test-image-url", } self.request.json = async_mock.CoroutineMock(return_value=body) - mock_multitenant_mgr = self.session_inject[MultitenantManager] + mock_multitenant_mgr = self.session_inject[BaseMultitenantManager] with async_mock.patch.object(test_module.web, "json_response") as mock_response: mock_multitenant_mgr.update_wallet = async_mock.CoroutineMock( @@ -382,7 +379,7 @@ async def test_wallet_update_not_found(self): self.request.match_info = {"wallet_id": "test-wallet-id"} body = {"label": "test-label"} self.request.json = async_mock.CoroutineMock(return_value=body) - mock_multitenant_mgr = self.session_inject[MultitenantManager] + mock_multitenant_mgr = self.session_inject[BaseMultitenantManager] mock_multitenant_mgr.update_wallet = async_mock.CoroutineMock( side_effect=StorageNotFoundError() ) @@ -443,7 +440,7 @@ async def test_wallet_create_token_managed(self): return_value={"settings": {}, "wallet_id": "dummy"} ) - mock_multitenant_mgr = self.session_inject[MultitenantManager] + mock_multitenant_mgr = self.session_inject[BaseMultitenantManager] with async_mock.patch.object( test_module.WalletRecord, "retrieve_by_id", async_mock.CoroutineMock() @@ -473,7 +470,7 @@ async def test_wallet_create_token_unmanaged(self): return_value={"settings": {}, "wallet_id": "dummy"} ) - mock_multitenant_mgr = self.session_inject[MultitenantManager] + mock_multitenant_mgr = self.session_inject[BaseMultitenantManager] with async_mock.patch.object( test_module.WalletRecord, "retrieve_by_id", async_mock.CoroutineMock() @@ -537,7 +534,7 @@ async def test_wallet_remove_managed(self): self.request.has_body = False self.request.match_info = {"wallet_id": "dummy"} - mock_multitenant_mgr = self.session_inject[MultitenantManager] + mock_multitenant_mgr = self.session_inject[BaseMultitenantManager] with async_mock.patch.object( test_module.web, "json_response" @@ -557,7 +554,7 @@ async def test_wallet_remove_unmanaged(self): return_value={"wallet_key": "dummy_key"} ) - mock_multitenant_mgr = self.session_inject[MultitenantManager] + mock_multitenant_mgr = self.session_inject[BaseMultitenantManager] with async_mock.patch.object( test_module.web, "json_response" @@ -594,7 +591,7 @@ async def test_wallet_remove_x(self): self.request.has_body = False self.request.match_info = {"wallet_id": "dummy"} - mock_multitenant_mgr = self.session_inject[MultitenantManager] + mock_multitenant_mgr = self.session_inject[BaseMultitenantManager] mock_multitenant_mgr.remove_wallet = async_mock.CoroutineMock() with async_mock.patch.object( diff --git a/aries_cloudagent/multitenant/askar_profile_manager.py b/aries_cloudagent/multitenant/askar_profile_manager.py new file mode 100644 index 0000000000..96fe137780 --- /dev/null +++ b/aries_cloudagent/multitenant/askar_profile_manager.py @@ -0,0 +1,85 @@ +"""Manager for askar profile multitenancy mode.""" + +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 ..askar.profile import AskarProfile +from ..multitenant.base import BaseMultitenantManager + + +class AskarProfileMultitenantManager(BaseMultitenantManager): + """Class for handling askar profile multitenancy.""" + + DEFAULT_MULTIENANT_WALLET_NAME = "multitenant_sub_wallet" + + def __init__(self, profile: Profile): + """Initialize askar profile multitenant Manager. + + Args: + profile: The base profile for this manager + """ + super().__init__(profile) + + async def get_wallet_profile( + self, + base_context: InjectionContext, + wallet_record: WalletRecord, + extra_settings: dict = {}, + *, + provision=False, + ) -> Profile: + """Get Askar profile for a wallet record. + + Args: + base_context: Base context to extend from + wallet_record: Wallet record to get the context for + extra_settings: Any extra context settings + + Returns: + 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: + context = base_context.copy() + sub_wallet_settings = { + "wallet.recreate": False, + "wallet.seed": None, + "wallet.rekey": None, + "wallet.id": None, + "wallet.name": multitenant_wallet_name, + "wallet.type": "askar", + "mediation.open": None, + "mediation.invite": None, + "mediation.default_id": None, + "mediation.clear": None, + "auto_provision": True, + } + context.settings = context.settings.extend(sub_wallet_settings) + + profile, _ = await wallet_config(context, provision=False) + self._instances[multitenant_wallet_name] = profile + + multitenant_wallet = self._instances[multitenant_wallet_name] + profile_context = multitenant_wallet.context.copy() + + if provision: + await multitenant_wallet.store.create_profile(wallet_record.wallet_id) + + extra_settings = { + "admin.webhook_urls": self.get_webhook_urls(base_context, wallet_record), + "wallet.askar_profile": wallet_record.wallet_id, + } + + profile_context.settings = profile_context.settings.extend( + wallet_record.settings + ).extend(extra_settings) + + return AskarProfile(multitenant_wallet.opened, profile_context) diff --git a/aries_cloudagent/multitenant/base.py b/aries_cloudagent/multitenant/base.py new file mode 100644 index 0000000000..7d46f97e11 --- /dev/null +++ b/aries_cloudagent/multitenant/base.py @@ -0,0 +1,442 @@ +"""Manager for multitenancy.""" + +import logging +from abc import abstractmethod + +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 ..protocols.coordinate_mediation.v1_0.manager import ( + MediationManager, + MediationRecord, +) + +from .error import WalletKeyMissingError + +LOGGER = logging.getLogger(__name__) + + +class MultitenantManagerError(BaseError): + """Generic multitenant error.""" + + +class BaseMultitenantManager: + """Base class for handling multitenancy.""" + + def __init__(self, profile: Profile): + """Initialize base multitenant Manager. + + Args: + profile: The profile for this manager + """ + self._profile = profile + if not profile: + raise MultitenantManagerError("Missing profile") + + self._instances: dict[str, Profile] = {} + + async def get_default_mediator(self) -> Optional[MediationRecord]: + """Retrieve the default mediator used for subwallet routing. + + Returns: + Optional[MediationRecord]: retrieved default mediator or None if not set + + """ + return await MediationManager(self._profile).get_default_mediator() + + async def _wallet_name_exists( + self, session: ProfileSession, wallet_name: str + ) -> bool: + """ + Check whether wallet with specified wallet name already exists. + + Besides checking for wallet records, it will also check if the base wallet + + Args: + session: The profile session to use + wallet_name: the wallet name to check for + + Returns: + bool: Whether the wallet name already exists + + """ + # wallet_name is same as base wallet name + if session.settings.get("wallet.name") == wallet_name: + return True + + # subwallet record exists, we assume the wallet actually exists + wallet_records = await WalletRecord.query(session, {"wallet_name": wallet_name}) + if len(wallet_records) > 0: + return True + + return False + + def get_webhook_urls( + self, + base_context: InjectionContext, + wallet_record: WalletRecord, + ) -> list: + """Get the webhook urls according to dispatch_type. + + Args: + base_context: Base context to get base_webhook_urls + wallet_record: Wallet record to get dispatch_type and webhook_urls + Returns: + webhook urls according to dispatch_type + """ + wallet_id = wallet_record.wallet_id + dispatch_type = wallet_record.wallet_dispatch_type + subwallet_webhook_urls = wallet_record.wallet_webhook_urls or [] + base_webhook_urls = base_context.settings.get("admin.webhook_urls", []) + + if dispatch_type == "both": + webhook_urls = list(set(base_webhook_urls) | set(subwallet_webhook_urls)) + if not webhook_urls: + LOGGER.warning( + "No webhook URLs in context configuration " + f"nor wallet record {wallet_id}, but wallet record " + f"configures dispatch type {dispatch_type}" + ) + elif dispatch_type == "default": + webhook_urls = subwallet_webhook_urls + if not webhook_urls: + LOGGER.warning( + f"No webhook URLs in nor wallet record {wallet_id}, but " + f"wallet record configures dispatch type {dispatch_type}" + ) + else: + webhook_urls = base_webhook_urls + + return webhook_urls + + @abstractmethod + async def get_wallet_profile( + self, + base_context: InjectionContext, + wallet_record: WalletRecord, + extra_settings: dict = {}, + *, + provision=False, + ) -> Profile: + """Get profile for a wallet record. + + Args: + base_context: Base context to extend from + wallet_record: Wallet record to get the context for + extra_settings: Any extra context settings + + Returns: + Profile: Profile for the wallet record + + """ + + async def create_wallet( + self, + settings: dict, + key_management_mode: str, + ) -> WalletRecord: + """Create new wallet and wallet record. + + Args: + settings: The context settings for this wallet + key_management_mode: The mode to use for key management. Either "unmanaged" + to not store the wallet key, or "managed" to store the wallet key + + Raises: + MultitenantManagerError: If the wallet name already exists + + Returns: + WalletRecord: The newly created wallet record + + """ + wallet_key = settings.get("wallet.key") + wallet_name = settings.get("wallet.name") + + # base wallet context + async with self._profile.session() as session: + # Check if the wallet name already exists to avoid indy wallet errors + if wallet_name and await self._wallet_name_exists(session, wallet_name): + raise MultitenantManagerError( + f"Wallet with name {wallet_name} already exists" + ) + + # In unmanaged mode we don't want to store the wallet key + if key_management_mode == WalletRecord.MODE_UNMANAGED: + del settings["wallet.key"] + # create and store wallet record + wallet_record = WalletRecord( + settings=settings, key_management_mode=key_management_mode + ) + + await wallet_record.save(session) + + # 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() + + if public_did_info: + await self.add_key( + wallet_record.wallet_id, public_did_info.verkey, skip_if_exists=True + ) + + return wallet_record + + async def update_wallet( + self, + wallet_id: str, + new_settings: dict, + ) -> WalletRecord: + """Update a 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 + + """ + # update wallet_record + async with self._profile.session() as session: + wallet_record = await WalletRecord.retrieve_by_id(session, wallet_id) + 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): + """Remove the wallet with specified wallet id. + + Args: + wallet_id: The wallet id of the wallet record + wallet_key: The wallet key to open the wallet. + Only required for "unmanaged" wallets + + Raises: + WalletKeyMissingError: If the wallet key is missing. + Only thrown for "unmanaged" wallets + + """ + async with self._profile.session() as session: + wallet = cast( + WalletRecord, + await WalletRecord.retrieve_by_id(session, wallet_id), + ) + + wallet_key = wallet_key or wallet.wallet_key + if wallet.requires_external_key and not wallet_key: + raise WalletKeyMissingError("Missing key to open wallet") + + profile = await self.get_wallet_profile( + self._profile.context, + wallet, + {"wallet.key": wallet_key}, + ) + + del self._instances[wallet_id] + await profile.remove() + + # Remove all routing records associated with wallet + storage = session.inject(BaseStorage) + await storage.delete_all_records( + RouteRecord.RECORD_TYPE, {"wallet_id": wallet.wallet_id} + ) + + await wallet.delete_record(session) + + 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 + ) + + def create_auth_token( + self, wallet_record: WalletRecord, wallet_key: str = None + ) -> str: + """Create JWT auth token for specified wallet record. + + Args: + wallet_record: The wallet record to create the token for + wallet_key: The wallet key to include in the token. + Only required for "unmanaged" wallets + + Raises: + WalletKeyMissingError: If the wallet key is missing. + Only thrown for "unmanaged" wallets + + Returns: + str: JWT auth token + + """ + + jwt_payload = {"wallet_id": wallet_record.wallet_id} + jwt_secret = self._profile.settings.get("multitenant.jwt_secret") + + if wallet_record.requires_external_key: + if not wallet_key: + raise WalletKeyMissingError() + + jwt_payload["wallet_key"] = wallet_key + + token = jwt.encode(jwt_payload, jwt_secret, algorithm="HS256").decode() + + return token + + async def get_profile_for_token( + self, context: InjectionContext, token: str + ) -> Profile: + """Get the profile associated with a JWT header token. + + Args: + context: The context to use for profile creation + token: The token + + Raises: + WalletKeyMissingError: If the wallet_key is missing for an unmanaged wallet + InvalidTokenError: If there is an exception while decoding the token + + Returns: + Profile associated with the token + + """ + jwt_secret = self._profile.context.settings.get("multitenant.jwt_secret") + extra_settings = {} + + token_body = jwt.decode(token, jwt_secret, algorithms=["HS256"]) + + wallet_id = token_body.get("wallet_id") + wallet_key = token_body.get("wallet_key") + + 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 profile + + async def _get_wallet_by_key(self, recipient_key: str) -> Optional[WalletRecord]: + """Get the wallet record associated with the recipient key. + + Args: + recipient_key: The recipient key + Returns: + Wallet record associated with the recipient key + """ + routing_mgr = RoutingManager(self._profile) + + try: + routing_record = await routing_mgr.get_recipient(recipient_key) + async with self._profile.session() as session: + wallet = await WalletRecord.retrieve_by_id( + session, routing_record.wallet_id + ) + + return wallet + except (RouteNotFoundError): + pass + + async def get_wallets_by_message( + self, message_body, wire_format: BaseWireFormat = None + ) -> List[WalletRecord]: + """Get the wallet records associated with the message boy. + + Args: + message_body: The body of the message + wire_format: Wire format to use for recipient detection + + Returns: + Wallet records associated with the message body + + """ + wire_format = wire_format or self._profile.inject(BaseWireFormat) + + recipient_keys = wire_format.get_recipient_keys(message_body) + wallets = [] + + for key in recipient_keys: + wallet = await self._get_wallet_by_key(key) + + if wallet: + wallets.append(wallet) + + return wallets diff --git a/aries_cloudagent/multitenant/manager.py b/aries_cloudagent/multitenant/manager.py index 035b163b1d..f52eda7713 100644 --- a/aries_cloudagent/multitenant/manager.py +++ b/aries_cloudagent/multitenant/manager.py @@ -1,126 +1,24 @@ """Manager for multitenancy.""" -import logging -import jwt -from typing import List, Optional, cast - from ..core.profile import ( Profile, - ProfileSession, ) -from ..messaging.responder import BaseResponder from ..config.wallet import wallet_config 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 ..protocols.coordinate_mediation.v1_0.manager import ( - MediationManager, - MediationRecord, -) - -from .error import WalletKeyMissingError - -LOGGER = logging.getLogger(__name__) - +from ..multitenant.base import BaseMultitenantManager -class MultitenantManagerError(BaseError): - """Generic multitenant error.""" - -class MultitenantManager: +class MultitenantManager(BaseMultitenantManager): """Class for handling multitenancy.""" def __init__(self, profile: Profile): - """Initialize multitenant Manager. + """Initialize default multitenant Manager. Args: profile: The profile for this manager """ - self._profile = profile - if not profile: - raise MultitenantManagerError("Missing profile") - - self._instances: dict[str, Profile] = {} - - async def get_default_mediator(self) -> Optional[MediationRecord]: - """Retrieve the default mediator used for subwallet routing. - - Returns: - Optional[MediationRecord]: retrieved default mediator or None if not set - - """ - return await MediationManager(self._profile).get_default_mediator() - - async def _wallet_name_exists( - self, session: ProfileSession, wallet_name: str - ) -> bool: - """ - Check whether wallet with specified wallet name already exists. - - Besides checking for wallet records, it will also check if the base wallet - - Args: - session: The profile session to use - wallet_name: the wallet name to check for - - Returns: - bool: Whether the wallet name already exists - - """ - # wallet_name is same as base wallet name - if session.settings.get("wallet.name") == wallet_name: - return True - - # subwallet record exists, we assume the wallet actually exists - wallet_records = await WalletRecord.query(session, {"wallet_name": wallet_name}) - if len(wallet_records) > 0: - return True - - return False - - def get_webhook_urls( - self, - base_context: InjectionContext, - wallet_record: WalletRecord, - ) -> list: - """Get the webhook urls according to dispatch_type. - - Args: - base_context: Base context to get base_webhook_urls - wallet_record: Wallet record to get dispatch_type and webhook_urls - Returns: - webhook urls according to dispatch_type - """ - wallet_id = wallet_record.wallet_id - dispatch_type = wallet_record.wallet_dispatch_type - subwallet_webhook_urls = wallet_record.wallet_webhook_urls or [] - base_webhook_urls = base_context.settings.get("admin.webhook_urls", []) - - if dispatch_type == "both": - webhook_urls = list(set(base_webhook_urls) | set(subwallet_webhook_urls)) - if not webhook_urls: - LOGGER.warning( - "No webhook URLs in context configuration " - f"nor wallet record {wallet_id}, but wallet record " - f"configures dispatch type {dispatch_type}" - ) - elif dispatch_type == "default": - webhook_urls = subwallet_webhook_urls - if not webhook_urls: - LOGGER.warning( - f"No webhook URLs in nor wallet record {wallet_id}, but " - f"wallet record configures dispatch type {dispatch_type}" - ) - else: - webhook_urls = base_webhook_urls - - return webhook_urls + super().__init__(profile) async def get_wallet_profile( self, @@ -173,300 +71,3 @@ async def get_wallet_profile( self._instances[wallet_id] = profile return self._instances[wallet_id] - - async def create_wallet( - self, - settings: dict, - key_management_mode: str, - ) -> WalletRecord: - """Create new wallet and wallet record. - - Args: - settings: The context settings for this wallet - key_management_mode: The mode to use for key management. Either "unmanaged" - to not store the wallet key, or "managed" to store the wallet key - - Raises: - MultitenantManagerError: If the wallet name already exists - - Returns: - WalletRecord: The newly created wallet record - - """ - wallet_key = settings.get("wallet.key") - wallet_name = settings.get("wallet.name") - - # base wallet context - async with self._profile.session() as session: - # Check if the wallet name already exists to avoid indy wallet errors - if wallet_name and await self._wallet_name_exists(session, wallet_name): - raise MultitenantManagerError( - f"Wallet with name {wallet_name} already exists" - ) - - # In unmanaged mode we don't want to store the wallet key - if key_management_mode == WalletRecord.MODE_UNMANAGED: - del settings["wallet.key"] - # create and store wallet record - wallet_record = WalletRecord( - settings=settings, key_management_mode=key_management_mode - ) - - await wallet_record.save(session) - - # 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() - - if public_did_info: - await self.add_key( - wallet_record.wallet_id, public_did_info.verkey, skip_if_exists=True - ) - - return wallet_record - - async def update_wallet( - self, - wallet_id: str, - new_settings: dict, - ) -> WalletRecord: - """Update a 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 - - """ - # update wallet_record - async with self._profile.session() as session: - wallet_record = await WalletRecord.retrieve_by_id(session, wallet_id) - 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): - """Remove the wallet with specified wallet id. - - Args: - wallet_id: The wallet id of the wallet record - wallet_key: The wallet key to open the wallet. - Only required for "unmanaged" wallets - - Raises: - WalletKeyMissingError: If the wallet key is missing. - Only thrown for "unmanaged" wallets - - """ - async with self._profile.session() as session: - wallet = cast( - WalletRecord, - await WalletRecord.retrieve_by_id(session, wallet_id), - ) - - wallet_key = wallet_key or wallet.wallet_key - if wallet.requires_external_key and not wallet_key: - raise WalletKeyMissingError("Missing key to open wallet") - - profile = await self.get_wallet_profile( - self._profile.context, - wallet, - {"wallet.key": wallet_key}, - ) - - del self._instances[wallet_id] - await profile.remove() - - # Remove all routing records associated with wallet - storage = session.inject(BaseStorage) - await storage.delete_all_records( - RouteRecord.RECORD_TYPE, {"wallet_id": wallet.wallet_id} - ) - - await wallet.delete_record(session) - - 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 - ) - - def create_auth_token( - self, wallet_record: WalletRecord, wallet_key: str = None - ) -> str: - """Create JWT auth token for specified wallet record. - - Args: - wallet_record: The wallet record to create the token for - wallet_key: The wallet key to include in the token. - Only required for "unmanaged" wallets - - Raises: - WalletKeyMissingError: If the wallet key is missing. - Only thrown for "unmanaged" wallets - - Returns: - str: JWT auth token - - """ - - jwt_payload = {"wallet_id": wallet_record.wallet_id} - jwt_secret = self._profile.settings.get("multitenant.jwt_secret") - - if wallet_record.requires_external_key: - if not wallet_key: - raise WalletKeyMissingError() - - jwt_payload["wallet_key"] = wallet_key - - token = jwt.encode(jwt_payload, jwt_secret, algorithm="HS256").decode() - - return token - - async def get_profile_for_token( - self, context: InjectionContext, token: str - ) -> Profile: - """Get the profile associated with a JWT header token. - - Args: - context: The context to use for profile creation - token: The token - - Raises: - WalletKeyMissingError: If the wallet_key is missing for an unmanaged wallet - InvalidTokenError: If there is an exception while decoding the token - - Returns: - Profile associated with the token - - """ - jwt_secret = self._profile.context.settings.get("multitenant.jwt_secret") - extra_settings = {} - - token_body = jwt.decode(token, jwt_secret, algorithms=["HS256"]) - - wallet_id = token_body.get("wallet_id") - wallet_key = token_body.get("wallet_key") - - 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 profile - - async def _get_wallet_by_key(self, recipient_key: str) -> Optional[WalletRecord]: - """Get the wallet record associated with the recipient key. - - Args: - recipient_key: The recipient key - Returns: - Wallet record associated with the recipient key - """ - routing_mgr = RoutingManager(self._profile) - - try: - routing_record = await routing_mgr.get_recipient(recipient_key) - async with self._profile.session() as session: - wallet = await WalletRecord.retrieve_by_id( - session, routing_record.wallet_id - ) - - return wallet - except (RouteNotFoundError): - pass - - async def get_wallets_by_message( - self, message_body, wire_format: BaseWireFormat = None - ) -> List[WalletRecord]: - """Get the wallet records associated with the message boy. - - Args: - message_body: The body of the message - wire_format: Wire format to use for recipient detection - - Returns: - Wallet records associated with the message body - - """ - wire_format = wire_format or self._profile.inject(BaseWireFormat) - - recipient_keys = wire_format.get_recipient_keys(message_body) - wallets = [] - - for key in recipient_keys: - wallet = await self._get_wallet_by_key(key) - - if wallet: - wallets.append(wallet) - - return wallets diff --git a/aries_cloudagent/multitenant/manager_provider.py b/aries_cloudagent/multitenant/manager_provider.py new file mode 100644 index 0000000000..7988753b36 --- /dev/null +++ b/aries_cloudagent/multitenant/manager_provider.py @@ -0,0 +1,56 @@ +"""Profile manager for multitenancy.""" + +import logging + +from ..config.provider import BaseProvider +from ..config.settings import BaseSettings +from ..config.injector import BaseInjector +from ..config.base import InjectionError +from ..utils.classloader import ClassLoader, ClassNotFoundError + +LOGGER = logging.getLogger(__name__) + + +class MultitenantManagerProvider(BaseProvider): + """ + Multitenant manager provider. + + Decides which manager to use based on the settings. + """ + + askar_profile_manager_path = ( + "aries_cloudagent.multitenant." + "askar_profile_manager.AskarProfileMultitenantManager" + ) + MANAGER_TYPES = { + "basic": "aries_cloudagent.multitenant.manager.MultitenantManager", + "askar-profile": askar_profile_manager_path, + } + + def __init__(self, root_profile): + """Initialize the multitenant profile manager provider.""" + self.root_profile = root_profile + self._inst = {} + + def provide(self, settings: BaseSettings, injector: BaseInjector): + """Create the multitenant manager instance.""" + + multitenant_wallet_type = "multitenant.wallet_type" + manager_type = settings.get_value( + multitenant_wallet_type, default="basic" + ).lower() + + manager_class = self.MANAGER_TYPES.get(manager_type, manager_type) + + if manager_class not in self._inst: + LOGGER.info("Create multitenant manager: %s", manager_type) + try: + self._inst[manager_class] = ClassLoader.load_class(manager_class)( + self.root_profile + ) + except ClassNotFoundError as err: + raise InjectionError( + f"Unknown multitenant manager type: {manager_type}" + ) from err + + return self._inst[manager_class] diff --git a/aries_cloudagent/multitenant/tests/test_askar_profile_manager.py b/aries_cloudagent/multitenant/tests/test_askar_profile_manager.py new file mode 100644 index 0000000000..5483ac2c7d --- /dev/null +++ b/aries_cloudagent/multitenant/tests/test_askar_profile_manager.py @@ -0,0 +1,172 @@ +import asyncio + +from asynctest import TestCase as AsyncTestCase +from asynctest import mock as async_mock + +from ...config.injection_context import InjectionContext +from ...core.in_memory import InMemoryProfile +from ...messaging.responder import BaseResponder +from ...wallet.models.wallet_record import WalletRecord +from ..askar_profile_manager import AskarProfileMultitenantManager + + +class TestAskarProfileMultitenantManager(AsyncTestCase): + DEFAULT_MULTIENANT_WALLET_NAME = "multitenant_sub_wallet" + + async def setUp(self): + self.profile = InMemoryProfile.test_profile() + self.context = self.profile.context + + self.responder = async_mock.CoroutineMock(send=async_mock.CoroutineMock()) + self.context.injector.bind_instance(BaseResponder, self.responder) + + self.manager = AskarProfileMultitenantManager(self.profile) + + async def test_get_wallet_profile_should_open_store_and_return_profile_with_wallet_context( + self, + ): + askar_profile_mock_name = "AskarProfile" + wallet_record = WalletRecord( + wallet_id="test", + settings={ + "wallet.recreate": True, + "wallet.seed": "test_seed", + "wallet.name": "test_name", + "wallet.type": "test_type", + "wallet.rekey": "test_rekey", + "mediation.open": True, + "mediation.invite": "http://invite.com", + "mediation.default_id": "24a96ef5", + "mediation.clear": True, + }, + ) + + 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 + ) + + def side_effect(context, provision): + sub_wallet_profile.name = askar_profile_mock_name + return sub_wallet_profile, None + + wallet_config.side_effect = side_effect + + 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 + ) + + async def test_get_wallet_profile_should_create_profile(self): + wallet_record = WalletRecord(wallet_id="test", settings={}) + 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._instances[ + self.DEFAULT_MULTIENANT_WALLET_NAME + ] = sub_wallet_profile + + await self.manager.get_wallet_profile( + self.profile.context, wallet_record, provision=True + ) + + sub_wallet_profile.store.create_profile.assert_called_once_with( + wallet_record.wallet_id + ) + + async def test_get_wallet_profile_should_use_custom_subwallet_name(self): + wallet_record = WalletRecord(wallet_id="test", settings={}) + multitenant_sub_wallet_name = "custom_wallet_name" + self.profile.context.settings = self.profile.settings.extend( + {"multitenant.wallet_name": multitenant_sub_wallet_name} + ) + + 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 = AskarProfile(None, None) + sub_wallet_profile.context.copy.return_value = InjectionContext() + + def side_effect(context, provision): + return sub_wallet_profile, None + + wallet_config.side_effect = side_effect + + await self.manager.get_wallet_profile( + self.profile.context, wallet_record + ) + + wallet_config.assert_called_once() + assert ( + wallet_config.call_args[0][0].settings.get("wallet.name") + == multitenant_sub_wallet_name + ) diff --git a/aries_cloudagent/multitenant/tests/test_base.py b/aries_cloudagent/multitenant/tests/test_base.py new file mode 100644 index 0000000000..d984c0e4d5 --- /dev/null +++ b/aries_cloudagent/multitenant/tests/test_base.py @@ -0,0 +1,573 @@ +from asynctest import TestCase as AsyncTestCase +from asynctest import mock as async_mock + +import jwt + +from ...core.in_memory import InMemoryProfile +from ...config.base import InjectionError +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, +) +from ...wallet.key_type import KeyType +from ...wallet.did_method import DIDMethod +from ..base import BaseMultitenantManager, MultitenantManagerError +from ..error import WalletKeyMissingError + + +class TestBaseMultitenantManager(AsyncTestCase): + async def setUp(self): + self.profile = InMemoryProfile.test_profile() + self.context = self.profile.context + + self.responder = async_mock.CoroutineMock(send=async_mock.CoroutineMock()) + self.context.injector.bind_instance(BaseResponder, self.responder) + + self.manager = BaseMultitenantManager(self.profile) + + async def test_init_throws_no_profile(self): + with self.assertRaises(MultitenantManagerError): + BaseMultitenantManager(None) + + async def test_get_default_mediator(self): + with async_mock.patch.object( + MediationManager, "get_default_mediator" + ) as get_default_mediator: + mediaton_record = MediationRecord() + + # has default mediator + get_default_mediator.return_value = mediaton_record + default_mediator = await self.manager.get_default_mediator() + assert default_mediator is mediaton_record + + # Doesn't have default mediator + get_default_mediator.return_value = None + default_mediator = await self.manager.get_default_mediator() + assert default_mediator is None + + async def test_get_webhook_urls_dispatch_type_base(self): + wallet_record = WalletRecord( + settings={ + "wallet.dispatch_type": "base", + "wallet.webhook_urls": ["subwallet-webhook-url"], + }, + ) + self.context.update_settings({"admin.webhook_urls": ["base-webhook-url"]}) + webhook_urls = self.manager.get_webhook_urls(self.context, wallet_record) + assert webhook_urls == ["base-webhook-url"] + + async def test_get_webhook_urls_dispatch_type_default(self): + wallet_record = WalletRecord( + settings={ + "wallet.dispatch_type": "default", + "wallet.webhook_urls": ["subwallet-webhook-url"], + }, + ) + self.context.update_settings({"admin.webhook_urls": ["base-webhook-url"]}) + webhook_urls = self.manager.get_webhook_urls(self.context, wallet_record) + assert webhook_urls == ["subwallet-webhook-url"] + + async def test_get_webhook_urls_dispatch_type_both(self): + wallet_record = WalletRecord( + settings={ + "wallet.dispatch_type": "both", + "wallet.webhook_urls": ["subwallet-webhook-url"], + }, + ) + self.context.update_settings({"admin.webhook_urls": ["base-webhook-url"]}) + webhook_urls = self.manager.get_webhook_urls(self.context, wallet_record) + assert "base-webhook-url" in webhook_urls + assert "subwallet-webhook-url" in webhook_urls + + async def test_wallet_exists_name_is_root_profile_name(self): + session = InMemoryProfile.test_session({"wallet.name": "test_wallet"}) + + wallet_name_exists = await self.manager._wallet_name_exists( + session, "test_wallet" + ) + assert wallet_name_exists is True + + async def test_wallet_exists_in_wallet_record(self): + session = InMemoryProfile.test_session({"wallet.name": "test_wallet"}) + + # create wallet record with existing wallet_name + wallet_record = WalletRecord( + key_management_mode="managed", + settings={"wallet.name": "another_test_wallet"}, + ) + await wallet_record.save(session) + + wallet_name_exists = await self.manager._wallet_name_exists( + session, "another_test_wallet" + ) + assert wallet_name_exists is True + + async def test_wallet_exists_false(self): + session = InMemoryProfile.test_session({"wallet.name": "test_wallet"}) + + wallet_name_exists = await self.manager._wallet_name_exists( + session, "another_test_wallet" + ) + assert wallet_name_exists is False + + async def test_get_wallet_by_key_routing_record_does_not_exist(self): + 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) + + assert wallet is None + retrieve_by_id.assert_not_called() + + 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" + wallet_id = "test-wallet-id" + + route_record = RouteRecord(wallet_id=wallet_id, recipient_key=recipient_key) + async with self.profile.session() as session: + await route_record.save(session) + + with self.assertRaises(StorageNotFoundError): + await self.manager._get_wallet_by_key(recipient_key) + + async def test_get_wallet_by_key(self): + recipient_key = "test-recipient-key" + + wallet_record = WalletRecord(settings={}) + async with self.profile.session() as session: + await wallet_record.save(session) + + route_record = RouteRecord( + wallet_id=wallet_record.wallet_id, recipient_key=recipient_key + ) + await route_record.save(session) + + wallet = await self.manager._get_wallet_by_key(recipient_key) + + assert isinstance(wallet, WalletRecord) + + async def test_create_wallet_removes_key_only_unmanaged_mode(self): + with async_mock.patch.object( + BaseMultitenantManager, "get_wallet_profile" + ) as get_wallet_profile: + get_wallet_profile.return_value = InMemoryProfile.test_profile() + + unmanaged_wallet_record = await self.manager.create_wallet( + {"wallet.key": "test_key"}, WalletRecord.MODE_UNMANAGED + ) + managed_wallet_record = await self.manager.create_wallet( + {"wallet.key": "test_key"}, WalletRecord.MODE_MANAGED + ) + + assert unmanaged_wallet_record.settings.get("wallet.key") is None + assert managed_wallet_record.settings.get("wallet.key") == "test_key" + + async def test_create_wallet_fails_if_wallet_name_exists(self): + with async_mock.patch.object( + BaseMultitenantManager, "_wallet_name_exists" + ) as _wallet_name_exists: + _wallet_name_exists.return_value = True + + with self.assertRaises( + MultitenantManagerError, + msg="Wallet with name test_wallet already exists", + ): + await self.manager.create_wallet( + {"wallet.name": "test_wallet"}, WalletRecord.MODE_MANAGED + ) + + 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" + ) as get_wallet_profile, async_mock.patch.object( + BaseMultitenantManager, "add_key" + ) as add_key: + get_wallet_profile.return_value = InMemoryProfile.test_profile() + + wallet_record = await self.manager.create_wallet( + {"wallet.name": "test_wallet", "wallet.key": "test_key"}, + WalletRecord.MODE_MANAGED, + ) + + wallet_record_save.assert_called_once() + get_wallet_profile.assert_called_once_with( + self.profile.context, + wallet_record, + {"wallet.key": "test_key"}, + provision=True, + ) + add_key.assert_not_called() + assert isinstance(wallet_record, WalletRecord) + assert wallet_record.wallet_name == "test_wallet" + assert wallet_record.key_management_mode == WalletRecord.MODE_MANAGED + assert wallet_record.wallet_key == "test_key" + + async def test_create_wallet_adds_wallet_route(self): + did_info = DIDInfo( + did="public-did", + verkey="test_verkey", + metadata={"meta": "data"}, + method=DIDMethod.SOV, + key_type=KeyType.ED25519, + ) + + 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( + InMemoryWallet, "get_public_did" + ) as get_public_did: + get_wallet_profile.return_value = InMemoryProfile.test_profile() + get_public_did.return_value = did_info + + wallet_record = await self.manager.create_wallet( + {"wallet.name": "test_wallet", "wallet.key": "test_key"}, + WalletRecord.MODE_MANAGED, + ) + + add_key.assert_called_once_with( + wallet_record.wallet_id, did_info.verkey, skip_if_exists=True + ) + + wallet_record_save.assert_called_once() + get_wallet_profile.assert_called_once_with( + self.profile.context, + wallet_record, + {"wallet.key": "test_key"}, + provision=True, + ) + assert isinstance(wallet_record, WalletRecord) + assert wallet_record.wallet_name == "test_wallet" + 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): + 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={ + "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_fails_no_wallet_key_but_required(self): + with async_mock.patch.object(WalletRecord, "retrieve_by_id") as retrieve_by_id: + retrieve_by_id.return_value = WalletRecord( + wallet_id="test", + key_management_mode=WalletRecord.MODE_UNMANAGED, + settings={"wallet.type": "indy"}, + ) + + with self.assertRaises(WalletKeyMissingError): + await self.manager.remove_wallet("test") + + 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" + ) as get_wallet_profile, async_mock.patch.object( + InMemoryProfile, "remove" + ) as remove_profile, async_mock.patch.object( + WalletRecord, "delete_record" + ) as wallet_delete_record, async_mock.patch.object( + InMemoryStorage, "delete_all_records" + ) as delete_all_records: + wallet_record = WalletRecord( + wallet_id="test", + key_management_mode=WalletRecord.MODE_UNMANAGED, + settings={"wallet.type": "indy", "wallet.key": "test_key"}, + ) + wallet_profile = InMemoryProfile.test_profile() + + self.manager._instances["test"] = wallet_profile + retrieve_by_id.return_value = wallet_record + get_wallet_profile.return_value = wallet_profile + + await self.manager.remove_wallet("test") + + assert "test" not in self.manager._instances + get_wallet_profile.assert_called_once_with( + self.profile.context, wallet_record, {"wallet.key": "test_key"} + ) + remove_profile.assert_called_once_with() + assert wallet_delete_record.call_count == 1 + delete_all_records.assert_called_once_with( + 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( + wallet_id="test_wallet", + key_management_mode=WalletRecord.MODE_UNMANAGED, + settings={"wallet.type": "indy"}, + ) + + with self.assertRaises(WalletKeyMissingError): + await self.manager.create_auth_token(wallet_record) + + async def test_create_auth_token_managed(self): + self.profile.settings["multitenant.jwt_secret"] = "very_secret_jwt" + wallet_record = WalletRecord( + wallet_id="test_wallet", + key_management_mode=WalletRecord.MODE_MANAGED, + settings={}, + ) + + expected_token = jwt.encode( + {"wallet_id": wallet_record.wallet_id}, "very_secret_jwt" + ).decode() + + token = self.manager.create_auth_token(wallet_record) + + 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_id="test_wallet", + key_management_mode=WalletRecord.MODE_UNMANAGED, + settings={"wallet.type": "indy"}, + ) + + expected_token = jwt.encode( + {"wallet_id": wallet_record.wallet_id, "wallet_key": "test_key"}, + "very_secret_jwt", + ).decode() + + token = self.manager.create_auth_token(wallet_record, "test_key") + + assert expected_token == token + + 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() + + with self.assertRaises(jwt.InvalidTokenError): + await self.manager.get_profile_for_token(self.profile.context, token) + + async def test_get_profile_for_token_wallet_key_missing_raises(self): + self.profile.settings["multitenant.jwt_secret"] = "very_secret_jwt" + wallet_record = WalletRecord( + key_management_mode=WalletRecord.MODE_UNMANAGED, + settings={"wallet.type": "indy"}, + ) + session = await self.profile.session() + 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) + + async def test_get_profile_for_token_managed_wallet(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"}, + ) + + session = await self.profile.session() + await wallet_record.save(session) + + token = jwt.encode( + {"wallet_id": wallet_record.wallet_id}, "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_unmanaged_wallet(self): + self.profile.settings["multitenant.jwt_secret"] = "very_secret_jwt" + wallet_record = WalletRecord( + key_management_mode=WalletRecord.MODE_UNMANAGED, + settings={"wallet.type": "indy"}, + ) + + session = await self.profile.session() + await wallet_record.save(session) + + token = jwt.encode( + {"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" + ) 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, + {"wallet.key": "wallet_key"}, + ) + + assert profile == mock_profile + + async def test_get_wallets_by_message_missing_wire_format_raises(self): + with self.assertRaises( + InjectionError, + ): + await self.manager.get_wallets_by_message({}) + + async def test_get_wallets_by_message(self): + message_body = async_mock.MagicMock() + recipient_keys = ["1", "2", "3", "4"] + + mock_wire_format = async_mock.MagicMock( + get_recipient_keys=lambda mesage_body: recipient_keys + ) + + return_wallets = [ + WalletRecord(settings={}), + None, + None, + WalletRecord(settings={}), + ] + + with async_mock.patch.object( + BaseMultitenantManager, "_get_wallet_by_key" + ) as get_wallet_by_key: + get_wallet_by_key.side_effect = return_wallets + + wallets = await self.manager.get_wallets_by_message( + message_body, mock_wire_format + ) + + assert len(wallets) == 2 + assert wallets[0] == return_wallets[0] + assert wallets[1] == return_wallets[3] + assert get_wallet_by_key.call_count == 4 diff --git a/aries_cloudagent/multitenant/tests/test_manager.py b/aries_cloudagent/multitenant/tests/test_manager.py index 6238587d79..60ef8c624d 100644 --- a/aries_cloudagent/multitenant/tests/test_manager.py +++ b/aries_cloudagent/multitenant/tests/test_manager.py @@ -1,26 +1,10 @@ from asynctest import TestCase as AsyncTestCase from asynctest import mock as async_mock -import jwt - from ...core.in_memory import InMemoryProfile -from ...config.base import InjectionError 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, -) -from ...wallet.key_type import KeyType -from ...wallet.did_method import DIDMethod -from ..manager import MultitenantManager, MultitenantManagerError -from ..error import WalletKeyMissingError +from ..manager import MultitenantManager class TestMultitenantManager(AsyncTestCase): @@ -33,60 +17,6 @@ async def setUp(self): self.manager = MultitenantManager(self.profile) - async def test_init_throws_no_profile(self): - with self.assertRaises(MultitenantManagerError): - MultitenantManager(None) - - async def test_get_default_mediator(self): - with async_mock.patch.object( - MediationManager, "get_default_mediator" - ) as get_default_mediator: - mediaton_record = MediationRecord() - - # has default mediator - get_default_mediator.return_value = mediaton_record - default_mediator = await self.manager.get_default_mediator() - assert default_mediator is mediaton_record - - # Doesn't have default mediator - get_default_mediator.return_value = None - default_mediator = await self.manager.get_default_mediator() - assert default_mediator is None - - async def test_get_webhook_urls_dispatch_type_base(self): - wallet_record = WalletRecord( - settings={ - "wallet.dispatch_type": "base", - "wallet.webhook_urls": ["subwallet-webhook-url"], - }, - ) - self.context.update_settings({"admin.webhook_urls": ["base-webhook-url"]}) - webhook_urls = self.manager.get_webhook_urls(self.context, wallet_record) - assert webhook_urls == ["base-webhook-url"] - - async def test_get_webhook_urls_dispatch_type_default(self): - wallet_record = WalletRecord( - settings={ - "wallet.dispatch_type": "default", - "wallet.webhook_urls": ["subwallet-webhook-url"], - }, - ) - self.context.update_settings({"admin.webhook_urls": ["base-webhook-url"]}) - webhook_urls = self.manager.get_webhook_urls(self.context, wallet_record) - assert webhook_urls == ["subwallet-webhook-url"] - - async def test_get_webhook_urls_dispatch_type_both(self): - wallet_record = WalletRecord( - settings={ - "wallet.dispatch_type": "both", - "wallet.webhook_urls": ["subwallet-webhook-url"], - }, - ) - self.context.update_settings({"admin.webhook_urls": ["base-webhook-url"]}) - webhook_urls = self.manager.get_webhook_urls(self.context, wallet_record) - assert "base-webhook-url" in webhook_urls - assert "subwallet-webhook-url" in webhook_urls - async def test_get_wallet_profile_returns_from_cache(self): wallet_record = WalletRecord(wallet_id="test") self.manager._instances["test"] = InMemoryProfile.test_profile() @@ -243,488 +173,3 @@ def side_effect(context, provision): assert profile.settings.get("mediation.invite") == "http://invite.com" assert profile.settings.get("mediation.default_id") == "24a96ef5" assert profile.settings.get("mediation.clear") == True - - async def test_wallet_exists_name_is_root_profile_name(self): - session = InMemoryProfile.test_session({"wallet.name": "test_wallet"}) - - wallet_name_exists = await self.manager._wallet_name_exists( - session, "test_wallet" - ) - assert wallet_name_exists is True - - async def test_wallet_exists_in_wallet_record(self): - session = InMemoryProfile.test_session({"wallet.name": "test_wallet"}) - - # create wallet record with existing wallet_name - wallet_record = WalletRecord( - key_management_mode="managed", - settings={"wallet.name": "another_test_wallet"}, - ) - await wallet_record.save(session) - - wallet_name_exists = await self.manager._wallet_name_exists( - session, "another_test_wallet" - ) - assert wallet_name_exists is True - - async def test_wallet_exists_false(self): - session = InMemoryProfile.test_session({"wallet.name": "test_wallet"}) - - wallet_name_exists = await self.manager._wallet_name_exists( - session, "another_test_wallet" - ) - assert wallet_name_exists is False - - async def test_get_wallet_by_key_routing_record_does_not_exist(self): - 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) - - assert wallet is None - retrieve_by_id.assert_not_called() - - 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" - wallet_id = "test-wallet-id" - - route_record = RouteRecord(wallet_id=wallet_id, recipient_key=recipient_key) - async with self.profile.session() as session: - await route_record.save(session) - - with self.assertRaises(StorageNotFoundError): - await self.manager._get_wallet_by_key(recipient_key) - - async def test_get_wallet_by_key(self): - recipient_key = "test-recipient-key" - - wallet_record = WalletRecord(settings={}) - async with self.profile.session() as session: - await wallet_record.save(session) - - route_record = RouteRecord( - wallet_id=wallet_record.wallet_id, recipient_key=recipient_key - ) - await route_record.save(session) - - wallet = await self.manager._get_wallet_by_key(recipient_key) - - assert isinstance(wallet, WalletRecord) - - async def test_create_wallet_removes_key_only_unmanaged_mode(self): - with async_mock.patch.object( - MultitenantManager, "get_wallet_profile" - ) as get_wallet_profile: - get_wallet_profile.return_value = InMemoryProfile.test_profile() - - unmanaged_wallet_record = await self.manager.create_wallet( - {"wallet.key": "test_key"}, WalletRecord.MODE_UNMANAGED - ) - managed_wallet_record = await self.manager.create_wallet( - {"wallet.key": "test_key"}, WalletRecord.MODE_MANAGED - ) - - assert unmanaged_wallet_record.settings.get("wallet.key") is None - assert managed_wallet_record.settings.get("wallet.key") == "test_key" - - async def test_create_wallet_fails_if_wallet_name_exists(self): - with async_mock.patch.object( - MultitenantManager, "_wallet_name_exists" - ) as _wallet_name_exists: - _wallet_name_exists.return_value = True - - with self.assertRaises( - MultitenantManagerError, - msg="Wallet with name test_wallet already exists", - ): - await self.manager.create_wallet( - {"wallet.name": "test_wallet"}, WalletRecord.MODE_MANAGED - ) - - 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( - MultitenantManager, "get_wallet_profile" - ) as get_wallet_profile, async_mock.patch.object( - MultitenantManager, "add_key" - ) as add_key: - get_wallet_profile.return_value = InMemoryProfile.test_profile() - - wallet_record = await self.manager.create_wallet( - {"wallet.name": "test_wallet", "wallet.key": "test_key"}, - WalletRecord.MODE_MANAGED, - ) - - wallet_record_save.assert_called_once() - get_wallet_profile.assert_called_once_with( - self.profile.context, - wallet_record, - {"wallet.key": "test_key"}, - provision=True, - ) - add_key.assert_not_called() - assert isinstance(wallet_record, WalletRecord) - assert wallet_record.wallet_name == "test_wallet" - assert wallet_record.key_management_mode == WalletRecord.MODE_MANAGED - assert wallet_record.wallet_key == "test_key" - - async def test_create_wallet_adds_wallet_route(self): - did_info = DIDInfo( - did="public-did", - verkey="test_verkey", - metadata={"meta": "data"}, - method=DIDMethod.SOV, - key_type=KeyType.ED25519, - ) - - with async_mock.patch.object( - WalletRecord, "save" - ) as wallet_record_save, async_mock.patch.object( - MultitenantManager, "get_wallet_profile" - ) as get_wallet_profile, async_mock.patch.object( - MultitenantManager, "add_key" - ) as add_key, async_mock.patch.object( - InMemoryWallet, "get_public_did" - ) as get_public_did: - get_wallet_profile.return_value = InMemoryProfile.test_profile() - get_public_did.return_value = did_info - - wallet_record = await self.manager.create_wallet( - {"wallet.name": "test_wallet", "wallet.key": "test_key"}, - WalletRecord.MODE_MANAGED, - ) - - add_key.assert_called_once_with( - wallet_record.wallet_id, did_info.verkey, skip_if_exists=True - ) - - wallet_record_save.assert_called_once() - get_wallet_profile.assert_called_once_with( - self.profile.context, - wallet_record, - {"wallet.key": "test_key"}, - provision=True, - ) - assert isinstance(wallet_record, WalletRecord) - assert wallet_record.wallet_name == "test_wallet" - 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): - 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={ - "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_fails_no_wallet_key_but_required(self): - with async_mock.patch.object(WalletRecord, "retrieve_by_id") as retrieve_by_id: - retrieve_by_id.return_value = WalletRecord( - wallet_id="test", - key_management_mode=WalletRecord.MODE_UNMANAGED, - settings={"wallet.type": "indy"}, - ) - - with self.assertRaises(WalletKeyMissingError): - await self.manager.remove_wallet("test") - - 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( - MultitenantManager, "get_wallet_profile" - ) as get_wallet_profile, async_mock.patch.object( - InMemoryProfile, "remove" - ) as remove_profile, async_mock.patch.object( - WalletRecord, "delete_record" - ) as wallet_delete_record, async_mock.patch.object( - InMemoryStorage, "delete_all_records" - ) as delete_all_records: - wallet_record = WalletRecord( - wallet_id="test", - key_management_mode=WalletRecord.MODE_UNMANAGED, - settings={"wallet.type": "indy", "wallet.key": "test_key"}, - ) - wallet_profile = InMemoryProfile.test_profile() - - self.manager._instances["test"] = wallet_profile - retrieve_by_id.return_value = wallet_record - get_wallet_profile.return_value = wallet_profile - - await self.manager.remove_wallet("test") - - assert "test" not in self.manager._instances - get_wallet_profile.assert_called_once_with( - self.profile.context, wallet_record, {"wallet.key": "test_key"} - ) - remove_profile.assert_called_once_with() - assert wallet_delete_record.call_count == 1 - delete_all_records.assert_called_once_with( - 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( - wallet_id="test_wallet", - key_management_mode=WalletRecord.MODE_UNMANAGED, - settings={"wallet.type": "indy"}, - ) - - with self.assertRaises(WalletKeyMissingError): - await self.manager.create_auth_token(wallet_record) - - async def test_create_auth_token_managed(self): - self.profile.settings["multitenant.jwt_secret"] = "very_secret_jwt" - wallet_record = WalletRecord( - wallet_id="test_wallet", - key_management_mode=WalletRecord.MODE_MANAGED, - settings={}, - ) - - expected_token = jwt.encode( - {"wallet_id": wallet_record.wallet_id}, "very_secret_jwt" - ).decode() - - token = self.manager.create_auth_token(wallet_record) - - 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_id="test_wallet", - key_management_mode=WalletRecord.MODE_UNMANAGED, - settings={"wallet.type": "indy"}, - ) - - expected_token = jwt.encode( - {"wallet_id": wallet_record.wallet_id, "wallet_key": "test_key"}, - "very_secret_jwt", - ).decode() - - token = self.manager.create_auth_token(wallet_record, "test_key") - - assert expected_token == token - - 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() - - with self.assertRaises(jwt.InvalidTokenError): - await self.manager.get_profile_for_token(self.profile.context, token) - - async def test_get_profile_for_token_wallet_key_missing_raises(self): - self.profile.settings["multitenant.jwt_secret"] = "very_secret_jwt" - wallet_record = WalletRecord( - key_management_mode=WalletRecord.MODE_UNMANAGED, - settings={"wallet.type": "indy"}, - ) - session = await self.profile.session() - 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) - - async def test_get_profile_for_token_managed_wallet(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"}, - ) - - session = await self.profile.session() - await wallet_record.save(session) - - token = jwt.encode( - {"wallet_id": wallet_record.wallet_id}, "very_secret_jwt", algorithm="HS256" - ).decode() - - with async_mock.patch.object( - MultitenantManager, "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_unmanaged_wallet(self): - self.profile.settings["multitenant.jwt_secret"] = "very_secret_jwt" - wallet_record = WalletRecord( - key_management_mode=WalletRecord.MODE_UNMANAGED, - settings={"wallet.type": "indy"}, - ) - - session = await self.profile.session() - await wallet_record.save(session) - - token = jwt.encode( - {"wallet_id": wallet_record.wallet_id, "wallet_key": "wallet_key"}, - "very_secret_jwt", - algorithm="HS256", - ).decode() - - with async_mock.patch.object( - MultitenantManager, "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, - {"wallet.key": "wallet_key"}, - ) - - assert profile == mock_profile - - async def test_get_wallets_by_message_missing_wire_format_raises(self): - with self.assertRaises( - InjectionError, - ): - await self.manager.get_wallets_by_message({}) - - async def test_get_wallets_by_message(self): - message_body = async_mock.MagicMock() - recipient_keys = ["1", "2", "3", "4"] - - mock_wire_format = async_mock.MagicMock( - get_recipient_keys=lambda mesage_body: recipient_keys - ) - - return_wallets = [ - WalletRecord(settings={}), - None, - None, - WalletRecord(settings={}), - ] - - with async_mock.patch.object( - MultitenantManager, "_get_wallet_by_key" - ) as get_wallet_by_key: - get_wallet_by_key.side_effect = return_wallets - - wallets = await self.manager.get_wallets_by_message( - message_body, mock_wire_format - ) - - assert len(wallets) == 2 - assert wallets[0] == return_wallets[0] - assert wallets[1] == return_wallets[3] - assert get_wallet_by_key.call_count == 4 diff --git a/aries_cloudagent/multitenant/tests/test_manager_provider.py b/aries_cloudagent/multitenant/tests/test_manager_provider.py new file mode 100644 index 0000000000..8a22a7da6e --- /dev/null +++ b/aries_cloudagent/multitenant/tests/test_manager_provider.py @@ -0,0 +1,38 @@ +from asynctest import TestCase as AsyncTestCase + +from ...config.injection_context import InjectionContext +from ...config.base import InjectionError +from ..manager_provider import MultitenantManagerProvider +from ...core.in_memory import InMemoryProfile + + +class TestProfileManagerProvider(AsyncTestCase): + async def test_provide_manager(self): + profile = InMemoryProfile.test_profile() + provider = MultitenantManagerProvider(profile) + context = InjectionContext() + + self.assertEqual( + provider.provide(context.settings, context.injector).__class__.__name__, + "MultitenantManager", + ) + + async def test_provide_askar_profile_manager(self): + profile = InMemoryProfile.test_profile() + provider = MultitenantManagerProvider(profile) + context = InjectionContext() + context.settings["multitenant.wallet_type"] = "askar-profile" + + self.assertEqual( + provider.provide(context.settings, context.injector).__class__.__name__, + "AskarProfileMultitenantManager", + ) + + async def test_invalid_manager_type(self): + profile = InMemoryProfile.test_profile() + provider = MultitenantManagerProvider(profile) + context = InjectionContext() + context.settings["multitenant.wallet_type"] = "not-valid" + + with self.assertRaises(InjectionError): + provider.provide(context.settings, context.injector) diff --git a/aries_cloudagent/protocols/actionmenu/v1_0/controller.py b/aries_cloudagent/protocols/actionmenu/v1_0/controller.py index 4060c10d24..0faac26e63 100644 --- a/aries_cloudagent/protocols/actionmenu/v1_0/controller.py +++ b/aries_cloudagent/protocols/actionmenu/v1_0/controller.py @@ -16,6 +16,6 @@ def __init__(self, protocol: str): async def determine_roles(self, context: InjectionContext) -> Sequence[str]: """Determine what action menu roles are defined.""" - service = context.inject(BaseMenuService, required=False) + service = context.inject_or(BaseMenuService) if service: return ["provider"] diff --git a/aries_cloudagent/protocols/actionmenu/v1_0/handlers/menu_request_handler.py b/aries_cloudagent/protocols/actionmenu/v1_0/handlers/menu_request_handler.py index e25f9d007d..41414c5b75 100644 --- a/aries_cloudagent/protocols/actionmenu/v1_0/handlers/menu_request_handler.py +++ b/aries_cloudagent/protocols/actionmenu/v1_0/handlers/menu_request_handler.py @@ -26,7 +26,7 @@ async def handle(self, context: RequestContext, responder: BaseResponder): self._logger.info("Received action menu request") - service: BaseMenuService = context.inject(BaseMenuService, required=False) + service: BaseMenuService = context.inject_or(BaseMenuService) if service: menu = await service.get_active_menu( context.profile, diff --git a/aries_cloudagent/protocols/actionmenu/v1_0/handlers/perform_handler.py b/aries_cloudagent/protocols/actionmenu/v1_0/handlers/perform_handler.py index 1f6747fb2d..e8338111d2 100644 --- a/aries_cloudagent/protocols/actionmenu/v1_0/handlers/perform_handler.py +++ b/aries_cloudagent/protocols/actionmenu/v1_0/handlers/perform_handler.py @@ -26,7 +26,7 @@ async def handle(self, context: RequestContext, responder: BaseResponder): self._logger.info("Received action menu perform request") - service: BaseMenuService = context.inject(BaseMenuService, required=False) + service: BaseMenuService = context.inject_or(BaseMenuService) if service: reply = await service.perform_menu_action( context.profile, diff --git a/aries_cloudagent/protocols/connections/v1_0/manager.py b/aries_cloudagent/protocols/connections/v1_0/manager.py index bf49f01d6b..556efa076b 100644 --- a/aries_cloudagent/protocols/connections/v1_0/manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/manager.py @@ -13,6 +13,7 @@ from ....core.error import BaseError from ....core.profile import ProfileSession from ....messaging.responder import BaseResponder +from ....multitenant.base import BaseMultitenantManager from ....storage.error import StorageError, StorageNotFoundError from ....transport.inbound.receipt import MessageReceipt from ....wallet.base import BaseWallet @@ -22,7 +23,6 @@ from ....wallet.did_method import DIDMethod from ....wallet.error import WalletNotFoundError from ....wallet.util import bytes_to_b58 -from ....multitenant.manager import MultitenantManager from ...routing.v1_0.manager import RoutingManager from ...coordinate_mediation.v1_0.manager import MediationManager @@ -132,7 +132,7 @@ async def create_invitation( image_url = self._session.context.settings.get("image_url") # Multitenancy setup - multitenant_mgr = self._session.inject(MultitenantManager, required=False) + multitenant_mgr = self._session.inject_or(BaseMultitenantManager) wallet_id = self._session.settings.get("wallet.id") if not my_label: @@ -246,7 +246,7 @@ async def create_invitation( ) if keylist_updates: - responder = self._session.inject(BaseResponder, required=False) + responder = self._session.inject_or(BaseResponder) await responder.send( keylist_updates, connection_id=mediation_record.connection_id ) @@ -336,7 +336,7 @@ async def receive_invitation( if connection.accept == ConnRecord.ACCEPT_AUTO: request = await self.create_request(connection, mediation_id=mediation_id) - responder = self._session.inject(BaseResponder, required=False) + responder = self._session.inject_or(BaseResponder) if responder: await responder.send(request, connection_id=connection.connection_id) # refetch connection for accurate state @@ -377,7 +377,7 @@ async def create_request( or_default=True, ) - multitenant_mgr = self._session.inject(MultitenantManager, required=False) + multitenant_mgr = self._session.inject_or(BaseMultitenantManager) wallet_id = self._session.settings.get("wallet.id") base_mediation_record = None @@ -437,7 +437,7 @@ async def create_request( # Notify mediator of keylist changes if keylist_updates and mediation_record: # send a update keylist message with new recipient keys. - responder = self._session.inject(BaseResponder, required=False) + responder = self._session.inject_or(BaseResponder) await responder.send( keylist_updates, connection_id=mediation_record.connection_id ) @@ -474,7 +474,7 @@ async def receive_request( my_info = None # Multitenancy setup - multitenant_mgr = self._session.inject(MultitenantManager, required=False) + multitenant_mgr = self._session.inject_or(BaseMultitenantManager) wallet_id = self._session.settings.get("wallet.id") wallet = self._session.inject(BaseWallet) @@ -602,14 +602,14 @@ async def receive_request( # Send keylist updates to mediator mediation_record = await mediation_record_if_id(self._session, mediation_id) if keylist_updates and mediation_record: - responder = self._session.inject(BaseResponder, required=False) + responder = self._session.inject_or(BaseResponder) await responder.send( keylist_updates, connection_id=mediation_record.connection_id ) if connection.accept == ConnRecord.ACCEPT_AUTO: response = await self.create_response(connection, mediation_id=mediation_id) - responder = self._session.inject(BaseResponder, required=False) + responder = self._session.inject_or(BaseResponder) if responder: await responder.send_reply( response, connection_id=connection.connection_id @@ -651,7 +651,7 @@ async def create_response( mediation_record = await mediation_record_if_id(self._session, mediation_id) # Multitenancy setup - multitenant_mgr = self._session.inject(MultitenantManager, required=False) + multitenant_mgr = self._session.inject_or(BaseMultitenantManager) wallet_id = self._session.settings.get("wallet.id") base_mediation_record = None @@ -722,7 +722,7 @@ async def create_response( # Update mediator if necessary if keylist_updates and mediation_record: - responder = self._session.inject(BaseResponder, required=False) + responder = self._session.inject_or(BaseResponder) await responder.send( keylist_updates, connection_id=mediation_record.connection_id ) @@ -881,7 +881,7 @@ async def create_static_connection( wallet = self._session.inject(BaseWallet) # Multitenancy setup - multitenant_mgr = self._session.inject(MultitenantManager, required=False) + multitenant_mgr = self._session.inject_or(BaseMultitenantManager) wallet_id = self._session.settings.get("wallet.id") base_mediation_record = None @@ -1008,7 +1008,7 @@ async def find_inbound_connection(self, receipt: MessageReceipt) -> ConnRecord: f"connection_by_verkey::{receipt.sender_verkey}" f"::{receipt.recipient_verkey}" ) - cache = self._session.inject(BaseCache, required=False) + cache = self._session.inject_or(BaseCache) if cache: async with cache.acquire(cache_key) as entry: if entry.result: @@ -1092,7 +1092,7 @@ async def get_connection_targets( """ if not connection_id: connection_id = connection.connection_id - cache = self._session.inject(BaseCache, required=False) + cache = self._session.inject_or(BaseCache) cache_key = f"connection_target::{connection_id}" if cache: async with cache.acquire(cache_key) as entry: diff --git a/aries_cloudagent/protocols/connections/v1_0/messages/problem_report.py b/aries_cloudagent/protocols/connections/v1_0/messages/problem_report.py index 27170f5f5a..8a5e548583 100644 --- a/aries_cloudagent/protocols/connections/v1_0/messages/problem_report.py +++ b/aries_cloudagent/protocols/connections/v1_0/messages/problem_report.py @@ -7,7 +7,9 @@ from ..message_types import PROBLEM_REPORT -HANDLER_CLASS = "aries_cloudagent.messaging.problem_report.handler.ProblemReportHandler" +HANDLER_CLASS = ( + "aries_cloudagent.protocols.problem_report.v1_0.handler.ProblemReportHandler" +) class ProblemReportReason(Enum): diff --git a/aries_cloudagent/protocols/connections/v1_0/routes.py b/aries_cloudagent/protocols/connections/v1_0/routes.py index 58c9bc9330..0070a0f0e3 100644 --- a/aries_cloudagent/protocols/connections/v1_0/routes.py +++ b/aries_cloudagent/protocols/connections/v1_0/routes.py @@ -155,7 +155,7 @@ class ConnectionStaticResultSchema(OpenAPISchema): """Result schema for new static connection.""" my_did = fields.Str(description="Local DID", required=True, **INDY_DID) - mv_verkey = fields.Str( + my_verkey = fields.Str( description="My verification key", required=True, **INDY_RAW_PUBLIC_KEY ) my_endpoint = fields.Str(description="My URL endpoint", required=True, **ENDPOINT) 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 6dba6c5fbc..648f54b5b9 100644 --- a/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py @@ -14,6 +14,7 @@ from .....core.in_memory import InMemoryProfile from .....did.did_key import DIDKey from .....messaging.responder import BaseResponder, MockResponder +from .....multitenant.base import BaseMultitenantManager from .....multitenant.manager import MultitenantManager from .....protocols.routing.v1_0.manager import RoutingManager from .....resolver.did_resolver import DIDResolver @@ -83,7 +84,7 @@ async def setUp(self): self.multitenant_mgr = async_mock.MagicMock(MultitenantManager, autospec=True) self.session.context.injector.bind_instance( - MultitenantManager, self.multitenant_mgr + BaseMultitenantManager, self.multitenant_mgr ) self.test_mediator_routing_keys = [ diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/mediation_grant_handler.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/mediation_grant_handler.py index 98756f2c2d..06899735f7 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/mediation_grant_handler.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/mediation_grant_handler.py @@ -3,7 +3,7 @@ from .....messaging.base_handler import BaseHandler, HandlerException from .....messaging.request_context import RequestContext from .....messaging.responder import BaseResponder -from .....multitenant.manager import MultitenantManager +from .....multitenant.base import BaseMultitenantManager from .....storage.error import StorageNotFoundError from ..manager import MediationManager @@ -33,7 +33,7 @@ async def handle(self, context: RequestContext, responder: BaseResponder): await mgr.request_granted(record, context.message) # Multitenancy setup - multitenant_mgr = context.profile.inject(MultitenantManager, required=False) + multitenant_mgr = context.profile.inject_or(BaseMultitenantManager) wallet_id = context.profile.settings.get("wallet.id") if multitenant_mgr and wallet_id: 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 41872d1043..c4c5d1a074 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 @@ -1,19 +1,21 @@ """Test mediate grant message handler.""" + import pytest from asynctest import TestCase as AsyncTestCase from asynctest import mock as async_mock -from .. import mediation_grant_handler as test_module - from ......connections.models.conn_record import ConnRecord from ......messaging.base_handler import HandlerException from ......messaging.request_context import RequestContext from ......messaging.responder import MockResponder -from ......multitenant.manager import MultitenantManager +from ......multitenant.base import BaseMultitenantManager + from ...messages.mediate_grant import MediationGrant from ...models.mediation_record import MediationRecord from ...manager import MediationManager + from ..mediation_grant_handler import MediationGrantHandler +from .. import mediation_grant_handler as test_module TEST_CONN_ID = "conn-id" TEST_VERKEY = "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx" @@ -84,7 +86,7 @@ async def test_handler_multitenant_base_mediation(self): ) multitenant_mgr = async_mock.CoroutineMock() - profile.context.injector.bind_instance(MultitenantManager, multitenant_mgr) + profile.context.injector.bind_instance(BaseMultitenantManager, multitenant_mgr) default_base_mediator = MediationRecord(routing_keys=["key1", "key2"]) multitenant_mgr.get_default_mediator = async_mock.CoroutineMock() diff --git a/aries_cloudagent/protocols/didexchange/v1_0/manager.py b/aries_cloudagent/protocols/didexchange/v1_0/manager.py index 46067e8917..9b7e1c38cc 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/manager.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/manager.py @@ -11,6 +11,7 @@ from ....core.profile import ProfileSession 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 @@ -19,7 +20,6 @@ from ....wallet.did_method import DIDMethod from ....wallet.did_posture import DIDPosture from ....did.did_key import DIDKey -from ....multitenant.manager import MultitenantManager from ...coordinate_mediation.v1_0.manager import MediationManager from ...out_of_band.v1_0.messages.invitation import ( @@ -142,7 +142,7 @@ async def receive_invitation( if conn_rec.accept == ConnRecord.ACCEPT_AUTO: request = await self.create_request(conn_rec, mediation_id=mediation_id) - responder = self._session.inject(BaseResponder, required=False) + responder = self._session.inject_or(BaseResponder) if responder: await responder.send_reply( request, @@ -208,7 +208,7 @@ async def create_request_implicit( conn_rec.request_id = request._id conn_rec.state = ConnRecord.State.REQUEST.rfc23 await conn_rec.save(self._session, reason="Created connection request") - responder = self._session.inject(BaseResponder, required=False) + responder = self._session.inject_or(BaseResponder) if responder: await responder.send(request, connection_id=conn_rec.connection_id) @@ -246,7 +246,7 @@ async def create_request( base_mediation_record = None # Multitenancy setup - multitenant_mgr = self._session.inject(MultitenantManager, required=False) + multitenant_mgr = self._session.inject_or(BaseMultitenantManager) wallet_id = self._session.settings.get("wallet.id") if multitenant_mgr and wallet_id: base_mediation_record = await multitenant_mgr.get_default_mediator() @@ -311,7 +311,7 @@ async def create_request( # Notify Mediator if keylist_updates and mediation_record: - responder = self._session.inject(BaseResponder, required=False) + responder = self._session.inject_or(BaseResponder) await responder.send( keylist_updates, connection_id=mediation_record.connection_id ) @@ -357,7 +357,7 @@ async def receive_request( wallet = self._session.inject(BaseWallet) # Multitenancy setup - multitenant_mgr = self._session.inject(MultitenantManager, required=False) + multitenant_mgr = self._session.inject_or(BaseMultitenantManager) wallet_id = self._session.settings.get("wallet.id") # Determine what key will need to sign the response @@ -514,7 +514,7 @@ async def receive_request( # Send keylist updates to mediator mediation_record = await mediation_record_if_id(self._session, mediation_id) if keylist_updates and mediation_record: - responder = self._session.inject(BaseResponder, required=False) + responder = self._session.inject_or(BaseResponder) await responder.send( keylist_updates, connection_id=mediation_record.connection_id ) @@ -525,7 +525,7 @@ async def receive_request( my_endpoint, mediation_id=mediation_id, ) - responder = self._session.inject(BaseResponder, required=False) + responder = self._session.inject_or(BaseResponder) if responder: await responder.send_reply( response, connection_id=conn_rec.connection_id @@ -568,7 +568,7 @@ async def create_response( base_mediation_record = None # Multitenancy setup - multitenant_mgr = self._session.inject(MultitenantManager, required=False) + multitenant_mgr = self._session.inject_or(BaseMultitenantManager) wallet_id = self._session.settings.get("wallet.id") if multitenant_mgr and wallet_id: base_mediation_record = await multitenant_mgr.get_default_mediator() @@ -629,7 +629,7 @@ async def create_response( # Update Mediator if necessary if keylist_updates and mediation_record: - responder = self._session.inject(BaseResponder, required=False) + responder = self._session.inject_or(BaseResponder) await responder.send( keylist_updates, connection_id=mediation_record.connection_id ) @@ -737,7 +737,7 @@ async def accept_response( # create and send connection-complete message complete = DIDXComplete() complete.assign_thread_from(response) - responder = self._session.inject(BaseResponder, required=False) + responder = self._session.inject_or(BaseResponder) if responder: await responder.send_reply(complete, connection_id=conn_rec.connection_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 d3088eadb4..7d08792fa0 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py @@ -16,6 +16,7 @@ from .....ledger.base import BaseLedger from .....messaging.responder import BaseResponder, MockResponder from .....messaging.decorators.attach_decorator import AttachDecorator +from .....multitenant.base import BaseMultitenantManager from .....multitenant.manager import MultitenantManager from .....storage.error import StorageNotFoundError from .....transport.inbound.receipt import MessageReceipt @@ -103,7 +104,7 @@ async def setUp(self): self.multitenant_mgr = async_mock.MagicMock(MultitenantManager, autospec=True) self.session.context.injector.bind_instance( - MultitenantManager, self.multitenant_mgr + BaseMultitenantManager, self.multitenant_mgr ) self.manager = DIDXManager(self.session) diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py index f569432748..7b86c2de5f 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py @@ -5,7 +5,6 @@ import uuid from asyncio import shield -from time import time from ....connections.models.conn_record import ConnRecord from ....core.error import BaseError @@ -13,9 +12,6 @@ from ....indy.issuer import IndyIssuerError from ....ledger.base import BaseLedger from ....ledger.error import LedgerError -from ....messaging.credential_definitions.util import CRED_DEF_SENT_RECORD_TYPE -from ....messaging.schemas.util import SCHEMA_SENT_RECORD_TYPE -from ....storage.base import StorageRecord from ....storage.error import StorageNotFoundError from ....transport.inbound.receipt import MessageReceipt @@ -688,24 +684,11 @@ async def store_record_in_wallet(self, ledger_response: dict = None): raise TransactionManagerError(reason) # write the wallet non-secrets record - # TODO refactor this code (duplicated from ledger.indy.py) if ledger_response["result"]["txn"]["type"] == "101": # schema transaction schema_id = ledger_response["result"]["txnMetadata"]["txnId"] - schema_id_parts = schema_id.split(":") public_did = ledger_response["result"]["txn"]["metadata"]["from"] - schema_tags = { - "schema_id": schema_id, - "schema_issuer_did": public_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) - # TODO refactor this code? - async with ledger: - storage = ledger.get_indy_storage() - await storage.add_record(record) + await ledger.add_schema_non_secrets_record(schema_id, public_did) elif ledger_response["result"]["txn"]["type"] == "102": # cred def transaction @@ -717,25 +700,11 @@ async def store_record_in_wallet(self, ledger_response: dict = None): raise TransactionManagerError(err.roll_up) from err schema_id = schema_response["id"] - schema_id_parts = schema_id.split(":") public_did = ledger_response["result"]["txn"]["metadata"]["from"] credential_definition_id = ledger_response["result"]["txnMetadata"]["txnId"] - 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_did, - "cred_def_id": credential_definition_id, - "epoch": str(int(time())), - } - record = StorageRecord( - CRED_DEF_SENT_RECORD_TYPE, credential_definition_id, cred_def_tags + await ledger.add_cred_def_non_secrets_record( + schema_id, public_did, credential_definition_id ) - # TODO refactor this code? - async with ledger: - storage = ledger.get_indy_storage() - await storage.add_record(record) else: # TODO unknown ledger transaction type, just ignore for now ... diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/messages/transaction_acknowledgement.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/messages/transaction_acknowledgement.py index fdb56ee470..96f28193e0 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/messages/transaction_acknowledgement.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/messages/transaction_acknowledgement.py @@ -2,9 +2,10 @@ from marshmallow import EXCLUDE, fields -from .....messaging.ack.message import Ack, AckSchema from .....messaging.valid import UUIDFour +from ....notification.v1_0.messages.ack import V10Ack, V10AckSchema + from ..message_types import TRANSACTION_ACKNOWLEDGEMENT, PROTOCOL_PACKAGE HANDLER_CLASS = ( @@ -13,7 +14,7 @@ ) -class TransactionAcknowledgement(Ack): +class TransactionAcknowledgement(V10Ack): """Class representing a transaction acknowledgement message.""" class Meta: @@ -41,7 +42,7 @@ def __init__( self.ledger_response = ledger_response -class TransactionAcknowledgementSchema(AckSchema): +class TransactionAcknowledgementSchema(V10AckSchema): """Transaction Acknowledgement schema class.""" class Meta: diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py index 4f3abc372b..d461acd67c 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py @@ -1,6 +1,7 @@ """Endorse Transaction handling admin routes.""" import json +from typing import Optional from aiohttp import web from aiohttp_apispec import ( @@ -292,7 +293,7 @@ async def endorse_transaction_response(request: web.BaseRequest): outbound_handler = request["outbound_message_router"] session = await context.session() - wallet: BaseWallet = session.inject(BaseWallet, required=False) + wallet: BaseWallet = session.inject_or(BaseWallet) if not wallet: raise web.HTTPForbidden(reason="No wallet available") @@ -337,7 +338,7 @@ async def endorse_transaction_response(request: web.BaseRequest): transaction_mgr = TransactionManager(session) transaction_json = transaction.messages_attach[0]["data"]["json"] - ledger = context.inject(BaseLedger, required=False) + ledger = context.inject_or(BaseLedger) if not ledger: reason = "No ledger available" if not context.settings.get_value("wallet.type"): @@ -394,7 +395,7 @@ async def refuse_transaction_response(request: web.BaseRequest): outbound_handler = request["outbound_message_router"] session = await context.session() - wallet: BaseWallet = session.inject(BaseWallet, required=False) + wallet: Optional[BaseWallet] = session.inject_or(BaseWallet) if not wallet: raise web.HTTPForbidden(reason="No wallet available") diff --git a/aries_cloudagent/protocols/introduction/v0_1/handlers/invitation_handler.py b/aries_cloudagent/protocols/introduction/v0_1/handlers/invitation_handler.py index 58a93ac341..dd0066db85 100644 --- a/aries_cloudagent/protocols/introduction/v0_1/handlers/invitation_handler.py +++ b/aries_cloudagent/protocols/introduction/v0_1/handlers/invitation_handler.py @@ -1,5 +1,6 @@ """Handler for incoming invitation messages.""" +from typing import Optional from .....messaging.base_handler import ( BaseHandler, BaseResponder, @@ -22,8 +23,8 @@ async def handle(self, context: RequestContext, responder: BaseResponder): if not context.connection_ready: raise HandlerException("No connection established for invitation message") - service: BaseIntroductionService = context.inject( - BaseIntroductionService, required=False + service: Optional[BaseIntroductionService] = context.inject_or( + BaseIntroductionService ) if service: await service.return_invitation( diff --git a/aries_cloudagent/protocols/introduction/v0_1/handlers/tests/test_invitation_handler.py b/aries_cloudagent/protocols/introduction/v0_1/handlers/tests/test_invitation_handler.py index 80aee5213d..49fe5c3f29 100644 --- a/aries_cloudagent/protocols/introduction/v0_1/handlers/tests/test_invitation_handler.py +++ b/aries_cloudagent/protocols/introduction/v0_1/handlers/tests/test_invitation_handler.py @@ -46,7 +46,7 @@ async def test_handle(self): responder = MockResponder() with async_mock.patch.object( - self.context, "inject", async_mock.MagicMock() + self.context, "inject_or", async_mock.MagicMock() ) as mock_ctx_inject: mock_ctx_inject.return_value = async_mock.MagicMock( return_invitation=async_mock.CoroutineMock() diff --git a/aries_cloudagent/protocols/introduction/v0_1/routes.py b/aries_cloudagent/protocols/introduction/v0_1/routes.py index 76a13042e8..b3ac7c0aaa 100644 --- a/aries_cloudagent/protocols/introduction/v0_1/routes.py +++ b/aries_cloudagent/protocols/introduction/v0_1/routes.py @@ -1,6 +1,7 @@ """Introduction service admin routes.""" import logging +from typing import Optional from aiohttp import web from aiohttp_apispec import docs, match_info_schema, querystring_schema, response_schema @@ -64,8 +65,8 @@ async def introduction_start(request: web.BaseRequest): target_connection_id = request.query.get("target_connection_id") message = request.query.get("message") - service: BaseIntroductionService = context.inject( - BaseIntroductionService, required=False + service: Optional[BaseIntroductionService] = context.inject_or( + BaseIntroductionService ) if not service: raise web.HTTPForbidden(reason="Introduction service not available") diff --git a/aries_cloudagent/protocols/introduction/v0_1/tests/test_routes.py b/aries_cloudagent/protocols/introduction/v0_1/tests/test_routes.py index 348bca3225..0aacbb20d5 100644 --- a/aries_cloudagent/protocols/introduction/v0_1/tests/test_routes.py +++ b/aries_cloudagent/protocols/introduction/v0_1/tests/test_routes.py @@ -64,7 +64,7 @@ async def test_introduction_start(self): mock_conn_rec.serialize = async_mock.MagicMock() with async_mock.patch.object( - self.context, "inject", async_mock.MagicMock() + self.context, "inject_or", async_mock.MagicMock() ) as mock_ctx_inject, async_mock.patch.object( test_module.web, "json_response" ) as mock_response: @@ -104,7 +104,7 @@ async def test_introduction_start_x(self): mock_conn_rec.serialize = async_mock.MagicMock() with async_mock.patch.object( - self.context, "inject", async_mock.MagicMock() + self.context, "inject_or", async_mock.MagicMock() ) as mock_ctx_inject: mock_ctx_inject.return_value = async_mock.MagicMock( start_introduction=async_mock.CoroutineMock( diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/manager.py b/aries_cloudagent/protocols/issue_credential/v1_0/manager.py index b470a753ac..06da5a4530 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/manager.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/manager.py @@ -273,7 +273,7 @@ async def _create(cred_def_id): credential_offer = None cache_key = f"credential_offer::{cred_def_id}" - cache = self._profile.inject(BaseCache, required=False) + cache = self._profile.inject_or(BaseCache) if cache: async with cache.acquire(cache_key) as entry: if entry.result: @@ -419,7 +419,7 @@ async def _create(): f"credential_request::{credential_definition_id}::{holder_did}::{nonce}" ) cred_req_result = None - cache = self._profile.inject(BaseCache, required=False) + cache = self._profile.inject_or(BaseCache) if cache: async with cache.acquire(cache_key) as entry: if entry.result: @@ -826,7 +826,7 @@ async def send_credential_ack( except StorageError as err: LOGGER.exception(err) # holder still owes an ack: carry on - responder = self._profile.inject(BaseResponder, required=False) + responder = self._profile.inject_or(BaseResponder) if responder: await responder.send_reply( credential_ack_message, diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/messages/credential_ack.py b/aries_cloudagent/protocols/issue_credential/v1_0/messages/credential_ack.py index 6b5c947998..fd7b8939df 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/messages/credential_ack.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/messages/credential_ack.py @@ -2,7 +2,7 @@ from marshmallow import EXCLUDE -from .....messaging.ack.message import Ack, AckSchema +from ....notification.v1_0.messages.ack import V10Ack, V10AckSchema from ..message_types import CREDENTIAL_ACK, PROTOCOL_PACKAGE @@ -11,7 +11,7 @@ ) -class CredentialAck(Ack): +class CredentialAck(V10Ack): """Class representing a credential ack message.""" class Meta: @@ -26,7 +26,7 @@ def __init__(self, **kwargs): super().__init__(**kwargs) -class CredentialAckSchema(AckSchema): +class CredentialAckSchema(V10AckSchema): """Credential ack schema.""" class Meta: 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 bceee98f54..ed77023c87 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 @@ -185,7 +185,7 @@ async def create_offer( issuer = self.profile.inject(IndyIssuer) ledger = self.profile.inject(BaseLedger) - cache = self.profile.inject(BaseCache, required=False) + cache = self.profile.inject_or(BaseCache) cred_proposal_message = cred_ex_record.cred_proposal @@ -267,7 +267,7 @@ async def _create(): cache_key = f"credential_request::{cred_def_id}::{holder_did}::{nonce}" cred_req_result = None - cache = self.profile.inject(BaseCache, required=False) + cache = self.profile.inject_or(BaseCache) if cache: async with cache.acquire(cache_key) as entry: if entry.result: diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/manager.py b/aries_cloudagent/protocols/issue_credential/v2_0/manager.py index 70d30e9717..b0885e0840 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( except StorageError as err: LOGGER.exception(err) # holder still owes an ack: carry on - responder = self._profile.inject(BaseResponder, required=False) + responder = self._profile.inject_or(BaseResponder) if responder: await responder.send_reply( cred_ack_message, diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/messages/cred_ack.py b/aries_cloudagent/protocols/issue_credential/v2_0/messages/cred_ack.py index ac98ed7345..79bdc1e1a9 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/messages/cred_ack.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/messages/cred_ack.py @@ -2,14 +2,14 @@ from marshmallow import EXCLUDE -from .....messaging.ack.message import Ack, AckSchema +from ....notification.v1_0.messages.ack import V10Ack, V10AckSchema from ..message_types import CRED_20_ACK, PROTOCOL_PACKAGE HANDLER_CLASS = f"{PROTOCOL_PACKAGE}.handlers.cred_ack_handler.V20CredAckHandler" -class V20CredAck(Ack): +class V20CredAck(V10Ack): """Credential ack.""" class Meta: @@ -24,7 +24,7 @@ def __init__(self, **kwargs): super().__init__(**kwargs) -class V20CredAckSchema(AckSchema): +class V20CredAckSchema(V10AckSchema): """Credential ack schema.""" class Meta: diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/routes.py b/aries_cloudagent/protocols/issue_credential/v2_0/routes.py index ebff90debd..a098047c6c 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/routes.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/routes.py @@ -261,7 +261,7 @@ class V20CredRequestFreeSchema(AdminAPIMessageTracingSchema): ) -class V20CredSendRequestSchema(V20IssueCredSchemaCore): +class V20CredExFreeSchema(V20IssueCredSchemaCore): """Request schema for sending credential admin message.""" connection_id = fields.UUID( @@ -575,7 +575,7 @@ async def credential_exchange_create(request: web.BaseRequest): tags=["issue-credential v2.0"], summary="Send holder a credential, automating entire flow", ) -@request_schema(V20CredSendRequestSchema()) +@request_schema(V20CredExFreeSchema()) @response_schema(V20CredExRecordSchema(), 200, description="") async def credential_exchange_send(request: web.BaseRequest): """ @@ -683,7 +683,7 @@ async def credential_exchange_send(request: web.BaseRequest): tags=["issue-credential v2.0"], summary="Send issuer a credential proposal", ) -@request_schema(V20IssueCredSchemaCore()) +@request_schema(V20CredExFreeSchema()) @response_schema(V20CredExRecordSchema(), 200, description="") async def credential_exchange_send_proposal(request: web.BaseRequest): """ diff --git a/aries_cloudagent/messaging/ack/tests/__init__.py b/aries_cloudagent/protocols/notification/__init__.py similarity index 100% rename from aries_cloudagent/messaging/ack/tests/__init__.py rename to aries_cloudagent/protocols/notification/__init__.py diff --git a/aries_cloudagent/protocols/notification/definition.py b/aries_cloudagent/protocols/notification/definition.py new file mode 100644 index 0000000000..1f92b7d0d2 --- /dev/null +++ b/aries_cloudagent/protocols/notification/definition.py @@ -0,0 +1,10 @@ +"""Version definitions for this protocol.""" + +versions = [ + { + "major_version": 1, + "minimum_minor_version": 0, + "current_minor_version": 0, + "path": "v1_0", + }, +] diff --git a/aries_cloudagent/protocols/notification/v1_0/__init__.py b/aries_cloudagent/protocols/notification/v1_0/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/aries_cloudagent/protocols/notification/v1_0/handlers/__init__.py b/aries_cloudagent/protocols/notification/v1_0/handlers/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/aries_cloudagent/protocols/notification/v1_0/handlers/ack_handler.py b/aries_cloudagent/protocols/notification/v1_0/handlers/ack_handler.py new file mode 100644 index 0000000000..8063056695 --- /dev/null +++ b/aries_cloudagent/protocols/notification/v1_0/handlers/ack_handler.py @@ -0,0 +1,36 @@ +"""Generic ack message handler.""" + +from .....messaging.base_handler import BaseHandler +from .....messaging.request_context import RequestContext +from .....messaging.responder import BaseResponder +from .....utils.tracing import trace_event, get_timer + +from ..messages.ack import V10Ack + + +class V10AckHandler(BaseHandler): + """Message handler class for generic acks.""" + + async def handle(self, context: RequestContext, responder: BaseResponder): + """ + Message handler logic for presentation acks. + + Args: + context: request context + responder: responder callback + """ + r_time = get_timer() + + self._logger.debug("V20PresAckHandler called with context %s", context) + assert isinstance(context.message, V10Ack) + self._logger.info( + "Received v1.0 notification ack message: %s", + context.message.serialize(as_string=True), + ) + + trace_event( + context.settings, + context.message, + outcome="V10AckHandler.handle.END", + perf_counter=r_time, + ) diff --git a/aries_cloudagent/protocols/notification/v1_0/handlers/tests/__init__.py b/aries_cloudagent/protocols/notification/v1_0/handlers/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/aries_cloudagent/protocols/notification/v1_0/handlers/tests/test_ack_handler.py b/aries_cloudagent/protocols/notification/v1_0/handlers/tests/test_ack_handler.py new file mode 100644 index 0000000000..eb63e04b37 --- /dev/null +++ b/aries_cloudagent/protocols/notification/v1_0/handlers/tests/test_ack_handler.py @@ -0,0 +1,21 @@ +from asynctest import mock as async_mock, TestCase as AsyncTestCase + +from ......messaging.request_context import RequestContext +from ......messaging.responder import MockResponder +from ......transport.inbound.receipt import MessageReceipt + +from ...messages.ack import V10Ack + +from .. import ack_handler as test_module + + +class TestNotificationAckHandler(AsyncTestCase): + async def test_called(self): + request_context = RequestContext.test_context() + request_context.message_receipt = MessageReceipt() + request_context.connection_record = async_mock.MagicMock() + + request_context.message = V10Ack(status="OK") + handler = test_module.V10AckHandler() + responder = MockResponder() + await handler.handle(request_context, responder) diff --git a/aries_cloudagent/protocols/notification/v1_0/message_types.py b/aries_cloudagent/protocols/notification/v1_0/message_types.py new file mode 100644 index 0000000000..363d43e949 --- /dev/null +++ b/aries_cloudagent/protocols/notification/v1_0/message_types.py @@ -0,0 +1,19 @@ +"""Message and inner object type identifiers for present-proof protocol v2.0.""" + +from ...didcomm_prefix import DIDCommPrefix + +SPEC_URI = ( + "https://github.com/hyperledger/aries-rfcs/tree/" + "560ffd23361f16a01e34ccb7dcc908ec28c5ddb1/features/0015-acks" +) + +# Message types +NOTIF_10_ACK = "notification/1.0/ack" + +PROTOCOL_PACKAGE = "aries_cloudagent.protocols.notification.v1_0" + +MESSAGE_TYPES = DIDCommPrefix.qualify_all( + { + NOTIF_10_ACK: f"{PROTOCOL_PACKAGE}.messages.ack.V10Ack", + } +) diff --git a/aries_cloudagent/protocols/notification/v1_0/messages/__init__.py b/aries_cloudagent/protocols/notification/v1_0/messages/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/aries_cloudagent/protocols/notification/v1_0/messages/ack.py b/aries_cloudagent/protocols/notification/v1_0/messages/ack.py new file mode 100644 index 0000000000..da050c83ac --- /dev/null +++ b/aries_cloudagent/protocols/notification/v1_0/messages/ack.py @@ -0,0 +1,43 @@ +"""Represents an explicit RFC 15 ack message, adopted into present-proof protocol.""" + +from marshmallow import EXCLUDE, fields + +from .....messaging.agent_message import AgentMessage, AgentMessageSchema + +from ..message_types import NOTIF_10_ACK, PROTOCOL_PACKAGE + +HANDLER_CLASS = f"{PROTOCOL_PACKAGE}.handlers.ack_handler.V10AckHandler" + + +class V10Ack(AgentMessage): + """Base class representing an explicit ack message for no specific protocol.""" + + class Meta: + """V10Ack metadata.""" + + handler_class = HANDLER_CLASS + message_type = NOTIF_10_ACK + schema_class = "V10AckSchema" + + def __init__(self, status: str = None, **kwargs): + """ + Initialize an explicit ack message instance. + + Args: + status: Status (default OK) + + """ + super().__init__(**kwargs) + self.status = status or "OK" + + +class V10AckSchema(AgentMessageSchema): + """Schema for V10Ack class.""" + + class Meta: + """V10Ack schema metadata.""" + + model_class = V10Ack + unknown = EXCLUDE + + status = fields.Str(required=True, description="Status", example="OK") 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 33261c1bf6..adb62b7deb 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,7 @@ 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.manager import MultitenantManager +from ....multitenant.base import BaseMultitenantManager from ....storage.error import StorageNotFoundError from ....transport.inbound.receipt import MessageReceipt from ....wallet.base import BaseWallet @@ -139,7 +139,7 @@ async def create_invitation( wallet = self._session.inject(BaseWallet) # Multitenancy setup - multitenant_mgr = self._session.inject(MultitenantManager, required=False) + multitenant_mgr = self._session.inject_or(BaseMultitenantManager) wallet_id = self._session.settings.get("wallet.id") accept = bool( @@ -315,7 +315,7 @@ async def create_invitation( ) if keylist_updates: - responder = self._session.inject(BaseResponder, required=False) + responder = self._session.inject_or(BaseResponder) await responder.send( keylist_updates, connection_id=mediation_record.connection_id ) @@ -363,17 +363,17 @@ async def create_invitation( async def receive_invitation( self, - invi_msg: InvitationMessage, + invitation: InvitationMessage, use_existing_connection: bool = True, auto_accept: bool = None, alias: str = None, mediation_id: str = None, - ) -> dict: + ) -> ConnRecord: """ Receive an out of band invitation message. Args: - invi_msg: invitation message + invitation: invitation message use_existing_connection: whether to use existing connection if possible auto_accept: whether to accept the invitation automatically alias: Alias for connection record @@ -390,15 +390,15 @@ async def receive_invitation( mediation_id = None # There must be exactly 1 service entry - if len(invi_msg.services) != 1: + if len(invitation.services) != 1: raise OutOfBandManagerError("service array must have exactly one element") - if not (invi_msg.requests_attach or invi_msg.handshake_protocols): + if not (invitation.requests_attach or invitation.handshake_protocols): raise OutOfBandManagerError( "Invitation must specify handshake_protocols, requests_attach, or both" ) # Get the single service item - oob_service_item = invi_msg.services[0] + oob_service_item = invitation.services[0] if isinstance(oob_service_item, ServiceMessage): service = oob_service_item public_did = None @@ -437,7 +437,7 @@ async def receive_invitation( for hsp in dict.fromkeys( [ DIDCommPrefix.unqualify(proto) - for proto in invi_msg.handshake_protocols + for proto in invitation.handshake_protocols ] ) ] @@ -454,7 +454,7 @@ async def receive_invitation( ) if conn_rec is not None: num_included_protocols = len(unq_handshake_protos) - num_included_req_attachments = len(invi_msg.requests_attach) + num_included_req_attachments = len(invitation.requests_attach) # With handshake protocol, request attachment; use existing connection if ( num_included_protocols >= 1 @@ -462,7 +462,7 @@ async def receive_invitation( and use_existing_connection ): await self.create_handshake_reuse_message( - invi_msg=invi_msg, + invi_msg=invitation, conn_record=conn_rec, ) try: @@ -526,7 +526,7 @@ async def receive_invitation( if proto is HSProto.RFC23: didx_mgr = DIDXManager(self._session) conn_rec = await didx_mgr.receive_invitation( - invitation=invi_msg, + invitation=invitation, their_public_did=public_did, auto_accept=auto_accept, alias=alias, @@ -543,9 +543,9 @@ async def receive_invitation( ] or [] connection_invitation = ConnectionInvitation.deserialize( { - "@id": invi_msg._id, + "@id": invitation._id, "@type": DIDCommPrefix.qualify_current(proto.name), - "label": invi_msg.label, + "label": invitation.label, "recipientKeys": service.recipient_keys, "serviceEndpoint": service.service_endpoint, "routingKeys": service.routing_keys, @@ -563,8 +563,8 @@ async def receive_invitation( break # Request Attach - if len(invi_msg.requests_attach) >= 1 and conn_rec is not None: - req_attach = invi_msg.requests_attach[0] + 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( @@ -575,14 +575,14 @@ async def receive_invitation( req_attach=req_attach, service=service, conn_rec=conn_rec, - trace=(invi_msg._trace is not None), + 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=(invi_msg._trace is not None), + trace=(invitation._trace is not None), ) elif unq_req_attach_type == CREDENTIAL_OFFER: if auto_accept or self._session.settings.get( @@ -597,12 +597,12 @@ async def receive_invitation( LOGGER.warning( "Connection not ready to receive credential, " f"For connection_id:{conn_rec.connection_id} and " - f"invitation_msg_id {invi_msg._id}", + f"invitation_msg_id {invitation._id}", ) await self._process_cred_offer_v1( req_attach=req_attach, conn_rec=conn_rec, - trace=(invi_msg._trace is not None), + trace=(invitation._trace is not None), ) elif unq_req_attach_type == CRED_20_OFFER: if auto_accept or self._session.settings.get( @@ -617,12 +617,12 @@ async def receive_invitation( LOGGER.warning( "Connection not ready to receive credential, " f"For connection_id:{conn_rec.connection_id} and " - f"invitation_msg_id {invi_msg._id}", + f"invitation_msg_id {invitation._id}", ) await self._process_cred_offer_v2( req_attach=req_attach, conn_rec=conn_rec, - trace=(invi_msg._trace is not None), + trace=(invitation._trace is not None), ) else: raise OutOfBandManagerError( @@ -636,7 +636,7 @@ async def receive_invitation( else: raise OutOfBandManagerError("requests~attach is not properly formatted") - return conn_rec.serialize() + return conn_rec async def _process_pres_request_v1( self, @@ -707,7 +707,7 @@ async def _process_pres_request_v1( ) ), ) - responder = self._session.inject(BaseResponder, required=False) + responder = self._session.inject_or(BaseResponder) if responder: await responder.send( message=presentation_message, @@ -768,7 +768,7 @@ async def _process_pres_request_v2( f", pres_ex_record: {pres_ex_record.pres_ex_id}" ), ) - responder = self._session.inject(BaseResponder, required=False) + responder = self._session.inject_or(BaseResponder) if responder: await responder.send( message=pres_msg, @@ -812,7 +812,7 @@ async def _process_cred_offer_v1( cred_ex_record=cred_ex_record, holder_did=conn_rec.my_did, ) - responder = self._session.inject(BaseResponder, required=False) + responder = self._session.inject_or(BaseResponder) if responder: await responder.send( message=cred_request_message, @@ -856,7 +856,7 @@ async def _process_cred_offer_v2( cred_ex_record=cred_ex_record, holder_did=conn_rec.my_did, ) - responder = self._session.inject(BaseResponder, required=False) + responder = self._session.inject_or(BaseResponder) if responder: await responder.send( message=cred_request_message, @@ -971,7 +971,7 @@ async def create_handshake_reuse_message( connection_targets = await self.fetch_connection_targets( connection=conn_record ) - responder = self._session.inject(BaseResponder, required=False) + responder = self._session.inject_or(BaseResponder) if responder: await responder.send( message=reuse_msg, @@ -1021,7 +1021,7 @@ async def receive_reuse_message( conn_record = await self.find_existing_connection( tag_filter=tag_filter, post_filter=post_filter ) - responder = self._session.inject(BaseResponder, required=False) + responder = self._session.inject_or(BaseResponder) if conn_record is not None: # For ConnRecords created using did-exchange reuse_accept_msg = HandshakeReuseAccept() 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 8903f458a1..388da7500d 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/routes.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/routes.py @@ -227,7 +227,7 @@ async def invitation_receive(request: web.BaseRequest): except (DIDXManagerError, StorageError, BaseModelError) as err: raise web.HTTPBadRequest(reason=err.roll_up) from err - return web.json_response(result) + return web.json_response(result.serialize()) async def register(app: web.Application): 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 6173705576..a8a60fa83b 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,4 +1,5 @@ """Test OOB Manager.""" + import asyncio import json @@ -21,6 +22,7 @@ from .....messaging.decorators.attach_decorator import AttachDecorator from .....messaging.responder import BaseResponder, MockResponder from .....messaging.util import str_to_epoch +from .....multitenant.base import BaseMultitenantManager from .....multitenant.manager import MultitenantManager from .....protocols.coordinate_mediation.v1_0.models.mediation_record import ( MediationRecord, @@ -362,11 +364,11 @@ def setUp(self): self.session.context.injector.bind_instance(BaseResponder, self.responder) self.mt_mgr = async_mock.MagicMock() self.mt_mgr = async_mock.create_autospec(MultitenantManager) - self.session.context.injector.bind_instance(MultitenantManager, self.mt_mgr) + self.session.context.injector.bind_instance(BaseMultitenantManager, self.mt_mgr) self.multitenant_mgr = async_mock.MagicMock(MultitenantManager, autospec=True) self.session.context.injector.bind_instance( - MultitenantManager, self.multitenant_mgr + BaseMultitenantManager, self.multitenant_mgr ) self.manager = OutOfBandManager(self.session) @@ -852,7 +854,7 @@ async def test_dif_req_v2_attach_pres_existing_conn_auto_present_pres_msg_with_c conn_rec = await self.manager.receive_invitation( mock_oob_invi, use_existing_connection=True ) - assert ConnRecord.deserialize(conn_rec) + assert conn_rec is not None async def test_dif_req_v2_attach_pres_existing_conn_auto_present_pres_msg_with_nonce( self, @@ -1018,7 +1020,7 @@ async def test_dif_req_v2_attach_pres_existing_conn_auto_present_pres_msg_with_n conn_rec = await self.manager.receive_invitation( mock_oob_invi, use_existing_connection=True ) - assert ConnRecord.deserialize(conn_rec) + assert conn_rec is not None async def test_create_invitation_public_x_no_public_invites(self): self.session.context.update_settings({"public_invites": False}) @@ -1185,7 +1187,7 @@ async def test_receive_invitation_with_valid_mediation(self): ) invi_msg = invite.invitation invitee_record = await self.manager.receive_invitation( - invi_msg=invi_msg, + invitation=invi_msg, mediation_id=mediation_record._id, ) mock_didx_recv_invi.assert_called_once_with( @@ -1300,14 +1302,14 @@ async def test_receive_invitation_connection(self): ) result = await self.manager.receive_invitation( - invi_msg=oob_invi_rec.invitation, + invitation=oob_invi_rec.invitation, use_existing_connection=True, auto_accept=True, ) - connection_id = UUID(result.get("connection_id"), version=4) + connection_id = UUID(result.connection_id, version=4) assert ( - connection_id.hex == result.get("connection_id").replace("-", "") - and len(result.get("connection_id")) > 5 + connection_id.hex == result.connection_id.replace("-", "") + and len(result.connection_id) > 5 ) async def test_receive_invitation_services_with_neither_service_blocks_nor_dids( @@ -1397,10 +1399,10 @@ async def test_receive_invitation_req_pres_v1_0_attachment_x(self): with self.assertRaises(OutOfBandManagerError) as context: result = await self.manager.receive_invitation(mock_oob_invi) - connection_id = UUID(result.get("connection_id"), version=4) + connection_id = UUID(result.connection_id, version=4) assert ( - connection_id.hex == result.get("connection_id") - and len(result.get("connection_id")) > 5 + connection_id.hex == result.connection_id + and len(result.connection_id) > 5 ) assert "requests~attach is not properly formatted" in str(context.exception) @@ -2016,9 +2018,7 @@ async def test_existing_conn_record_public_did(self): ) is None ) - assert ( - result.get("connection_id") == retrieved_conn_records[0].connection_id - ) + assert result.connection_id == retrieved_conn_records[0].connection_id async def test_existing_conn_record_public_did_not_accepted(self): self.session.context.update_settings({"public_invites": True}) @@ -2117,9 +2117,7 @@ async def test_existing_conn_record_public_did_not_accepted(self): ) == "not_accepted" ) - assert ( - result.get("connection_id") != retrieved_conn_records[0].connection_id - ) + assert result.connection_id != retrieved_conn_records[0].connection_id async def test_existing_conn_record_public_did_inverse_cases(self): self.session.context.update_settings({"public_invites": True}) @@ -2134,11 +2132,6 @@ async def test_existing_conn_record_public_did_inverse_cases(self): await test_exist_conn.save(self.session) await test_exist_conn.metadata_set(self.session, "reuse_msg_state", "initial") await test_exist_conn.metadata_set(self.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( DIDXManager, "receive_invitation", autospec=True @@ -2180,9 +2173,7 @@ async def test_existing_conn_record_public_did_inverse_cases(self): }, alt=True, ) - assert ( - result.get("connection_id") != retrieved_conn_records[0].connection_id - ) + assert result.connection_id != retrieved_conn_records[0].connection_id async def test_existing_conn_record_public_did_timeout(self): self.session.context.update_settings({"public_invites": True}) @@ -2475,7 +2466,7 @@ async def test_req_v1_attach_presentation_existing_conn_auto_present_pres_msg(se conn_rec = await self.manager.receive_invitation( mock_oob_invi, use_existing_connection=True ) - assert ConnRecord.deserialize(conn_rec) + assert conn_rec is not None async def test_req_v1_attach_pres_catch_value_error(self): self.session.context.update_settings({"public_invites": True}) @@ -2780,7 +2771,7 @@ async def test_req_v2_attach_presentation_existing_conn_auto_present_pres_msg(se conn_rec = await self.manager.receive_invitation( mock_oob_invi, use_existing_connection=True ) - assert ConnRecord.deserialize(conn_rec) + assert conn_rec is not None async def test_req_v2_attach_pres_catch_value_error(self): self.session.context.update_settings({"public_invites": True}) @@ -2989,7 +2980,7 @@ async def test_req_attach_cred_offer_v1(self): conn_rec = await self.manager.receive_invitation( mock_oob_invi, use_existing_connection=True ) - assert ConnRecord.deserialize(conn_rec) + assert conn_rec is not None async def test_req_attach_cred_offer_v1_no_issue(self): self.session.context.update_settings({"public_invites": True}) @@ -3172,7 +3163,7 @@ async def test_req_attach_cred_offer_v2(self): conn_rec = await self.manager.receive_invitation( mock_oob_invi, use_existing_connection=True ) - assert ConnRecord.deserialize(conn_rec) + assert conn_rec is not None async def test_req_attach_cred_offer_v2_no_issue(self): self.session.context.update_settings({"public_invites": True}) @@ -3382,11 +3373,6 @@ async def test_request_attach_cred_offer_v1_check_conn_rec_active_timeout(self): await test_exist_conn.metadata_set(self.session, "reuse_msg_state", "initial") await test_exist_conn.metadata_set(self.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() @@ -3396,7 +3382,7 @@ async def test_request_attach_cred_offer_v1_check_conn_rec_active_timeout(self): DIDXManager, "receive_invitation", autospec=True, - ) as didx_mgr_receive_invitation, async_mock.patch.object( + ), async_mock.patch.object( V10CredManager, "receive_offer", autospec=True, @@ -3407,7 +3393,7 @@ async def test_request_attach_cred_offer_v1_check_conn_rec_active_timeout(self): OutOfBandManager, "fetch_connection_targets", autospec=True, - ) as oob_mgr_fetch_conn, async_mock.patch.object( + ), async_mock.patch.object( OutOfBandManager, "find_existing_connection", autospec=True, @@ -3415,7 +3401,7 @@ async def test_request_attach_cred_offer_v1_check_conn_rec_active_timeout(self): OutOfBandManager, "check_reuse_msg_state", autospec=True, - ) as oob_mgr_check_reuse_state, async_mock.patch.object( + ), async_mock.patch.object( OutOfBandManager, "conn_rec_is_active", autospec=True, @@ -3423,19 +3409,19 @@ async def test_request_attach_cred_offer_v1_check_conn_rec_active_timeout(self): OutOfBandManager, "create_handshake_reuse_message", autospec=True, - ) as oob_mgr_create_reuse_msg, async_mock.patch.object( + ), async_mock.patch.object( OutOfBandManager, "receive_reuse_message", autospec=True, - ) as oob_mgr_receive_reuse_msg, async_mock.patch.object( + ), async_mock.patch.object( OutOfBandManager, "receive_reuse_accepted_message", autospec=True, - ) as oob_mgr_receive_accept_msg, async_mock.patch.object( + ), async_mock.patch.object( OutOfBandManager, "receive_problem_report", autospec=True, - ) as oob_mgr_receive_problem_report, async_mock.patch.object( + ), async_mock.patch.object( V10CredManager, "create_request", autospec=True, @@ -3458,7 +3444,7 @@ async def test_request_attach_cred_offer_v1_check_conn_rec_active_timeout(self): mock_oob_invi, use_existing_connection=True ) mock_logger_warning.assert_called_once() - assert ConnRecord.deserialize(conn_rec) + assert conn_rec is not None async def test_request_attach_cred_offer_v2_check_conn_rec_active_timeout(self): self.session.context.update_settings({"public_invites": True}) @@ -3552,4 +3538,4 @@ async def test_request_attach_cred_offer_v2_check_conn_rec_active_timeout(self): mock_oob_invi, use_existing_connection=True ) mock_logger_warning.assert_called_once() - assert ConnRecord.deserialize(conn_rec) + assert conn_rec is not None 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 cf7a29795e..d7aaffabf8 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 @@ -1,9 +1,8 @@ -import json - from asynctest import TestCase as AsyncTestCase from asynctest import mock as async_mock from .....admin.request_context import AdminRequestContext +from .....connections.models.conn_record import ConnRecord from .. import routes as test_module @@ -86,20 +85,23 @@ async def test_invitation_create_x(self): async def test_invitation_receive(self): self.request.json = async_mock.CoroutineMock() + expected_connection_record = ConnRecord(connection_id="some-id") with async_mock.patch.object( test_module, "OutOfBandManager", autospec=True ) as mock_oob_mgr, async_mock.patch.object( test_module.InvitationMessage, "deserialize", async_mock.Mock() - ) as mock_invi_deser, async_mock.patch.object( + ), async_mock.patch.object( test_module.web, "json_response", async_mock.Mock() ) as mock_json_response: mock_oob_mgr.return_value.receive_invitation = async_mock.CoroutineMock( - return_value={"abc": "123"} + return_value=expected_connection_record ) - result = await test_module.invitation_receive(self.request) - mock_json_response.assert_called_once_with({"abc": "123"}) + await test_module.invitation_receive(self.request) + mock_json_response.assert_called_once_with( + expected_connection_record.serialize() + ) async def test_invitation_receive_forbidden_x(self): self.context.update_settings({"admin.no_receive_invites": True}) 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 7bf07c49bb..27dff27c32 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 @@ -76,7 +76,7 @@ def profile(): @pytest.fixture(scope="class") async def setup_tuple(profile): async with profile.session() as session: - wallet = session.inject(BaseWallet, required=False) + wallet = session.inject_or(BaseWallet) await wallet.create_local_did( method=DIDMethod.SOV, key_type=KeyType.ED25519, did="WgWxqztrNooG92RXvxSTWv" ) diff --git a/aries_cloudagent/protocols/present_proof/v1_0/manager.py b/aries_cloudagent/protocols/present_proof/v1_0/manager.py index 6c19ad356d..db7f1df132 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/manager.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/manager.py @@ -426,7 +426,7 @@ async def send_presentation_ack( presentation_exchange_record: presentation exchange record with thread id """ - responder = self._profile.inject(BaseResponder, required=False) + responder = self._profile.inject_or(BaseResponder) if responder: presentation_ack_message = PresentationAck() 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 41a65adc8f..36f181d56e 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 @@ -2,7 +2,7 @@ from marshmallow import EXCLUDE -from .....messaging.ack.message import Ack, AckSchema +from ....notification.v1_0.messages.ack import V10Ack, V10AckSchema from ..message_types import PRESENTATION_ACK, PROTOCOL_PACKAGE @@ -11,7 +11,7 @@ ) -class PresentationAck(Ack): +class PresentationAck(V10Ack): """Base class representing an explicit ack message for present-proof protocol.""" class Meta: @@ -32,7 +32,7 @@ def __init__(self, status: str = None, **kwargs): super().__init__(status, **kwargs) -class PresentationAckSchema(AckSchema): +class PresentationAckSchema(V10AckSchema): """Schema for PresentationAck class.""" class Meta: diff --git a/aries_cloudagent/protocols/present_proof/v1_0/routes.py b/aries_cloudagent/protocols/present_proof/v1_0/routes.py index cd1b68a134..e986aac1f0 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/routes.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/routes.py @@ -817,16 +817,6 @@ async def presentation_exchange_verify_presentation(request: web.BaseRequest): ) ) - connection_id = pres_ex_record.connection_id - - try: - connection_record = await ConnRecord.retrieve_by_id(session, connection_id) - except StorageError 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") - presentation_manager = PresentationManager(context.profile) try: pres_ex_record = await presentation_manager.verify_presentation(pres_ex_record) 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 f8335341c4..41424eb20e 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 @@ -1299,92 +1299,6 @@ async def test_presentation_exchange_verify_presentation_px_rec_not_found(self): ) assert "no such record" in str(context.exception) - async def test_presentation_exchange_verify_presentation_not_found(self): - self.request.match_info = {"pres_ex_id": "dummy"} - self.profile.context.injector.bind_instance( - BaseLedger, - async_mock.MagicMock( - __aenter__=async_mock.CoroutineMock(), - __aexit__=async_mock.CoroutineMock(), - ), - ) - self.profile.context.injector.bind_instance( - IndyVerifier, - async_mock.MagicMock( - verify_presentation=async_mock.CoroutineMock(), - ), - ) - - with async_mock.patch( - "aries_cloudagent.connections.models.conn_record.ConnRecord", - autospec=True, - ) as mock_connection_record, async_mock.patch( - "aries_cloudagent.protocols.present_proof.v1_0.manager.PresentationManager", - autospec=True, - ) as mock_presentation_manager, async_mock.patch( - ( - "aries_cloudagent.protocols.present_proof.v1_0." - "models.presentation_exchange.V10PresentationExchange" - ), - autospec=True, - ) as mock_presentation_exchange: - - # Since we are mocking import - importlib.reload(test_module) - - mock_presentation_exchange.retrieve_by_id = async_mock.CoroutineMock( - return_value=async_mock.MagicMock( - state=mock_presentation_exchange.STATE_PRESENTATION_RECEIVED, - connection_id="dummy", - ) - ) - - mock_connection_record.retrieve_by_id = async_mock.CoroutineMock( - side_effect=StorageNotFoundError - ) - - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.presentation_exchange_verify_presentation( - self.request - ) - - async def test_presentation_exchange_verify_presentation_not_ready(self): - self.request.match_info = {"pres_ex_id": "dummy"} - - with async_mock.patch( - "aries_cloudagent.connections.models.conn_record.ConnRecord", - autospec=True, - ) as mock_connection_record, async_mock.patch( - "aries_cloudagent.protocols.present_proof.v1_0.manager.PresentationManager", - autospec=True, - ) as mock_presentation_manager, async_mock.patch( - ( - "aries_cloudagent.protocols.present_proof.v1_0." - "models.presentation_exchange.V10PresentationExchange" - ), - autospec=True, - ) as mock_presentation_exchange: - - # Since we are mocking import - importlib.reload(test_module) - - mock_presentation_exchange.retrieve_by_id = async_mock.CoroutineMock( - return_value=async_mock.MagicMock( - state=mock_presentation_exchange.STATE_PRESENTATION_RECEIVED, - connection_id="dummy", - ) - ) - - mock_connection_record.is_ready = False - mock_connection_record.retrieve_by_id = async_mock.CoroutineMock( - return_value=mock_connection_record - ) - - with self.assertRaises(test_module.web.HTTPForbidden): - await test_module.presentation_exchange_verify_presentation( - self.request - ) - async def test_presentation_exchange_verify_presentation_bad_state(self): self.request.json = async_mock.CoroutineMock() self.request.match_info = {"pres_ex_id": "dummy"} diff --git a/aries_cloudagent/protocols/present_proof/v2_0/manager.py b/aries_cloudagent/protocols/present_proof/v2_0/manager.py index 69bd99b296..b92ca2ecb6 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/manager.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/manager.py @@ -376,7 +376,7 @@ async def send_pres_ack(self, pres_ex_record: V20PresExRecord): pres_ex_record: presentation exchange record with thread id """ - responder = self._profile.inject(BaseResponder, required=False) + responder = self._profile.inject_or(BaseResponder) if responder: pres_ack_message = V20PresAck() 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 af22b75216..d9032f8d6f 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 @@ -2,14 +2,14 @@ from marshmallow import EXCLUDE -from .....messaging.ack.message import Ack, AckSchema +from ....notification.v1_0.messages.ack import V10Ack, V10AckSchema from ..message_types import PRES_20_ACK, PROTOCOL_PACKAGE HANDLER_CLASS = f"{PROTOCOL_PACKAGE}.handlers.pres_ack_handler.V20PresAckHandler" -class V20PresAck(Ack): +class V20PresAck(V10Ack): """Base class representing an explicit ack message for present-proof protocol.""" class Meta: @@ -30,7 +30,7 @@ def __init__(self, status: str = None, **kwargs): super().__init__(status, **kwargs) -class V20PresAckSchema(AckSchema): +class V20PresAckSchema(V10AckSchema): """Schema for V20PresAck class.""" class Meta: diff --git a/aries_cloudagent/protocols/present_proof/v2_0/routes.py b/aries_cloudagent/protocols/present_proof/v2_0/routes.py index 14024379d6..afcf317ab7 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/routes.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/routes.py @@ -1093,16 +1093,6 @@ async def present_proof_verify_presentation(request: web.BaseRequest): ) ) - connection_id = pres_ex_record.connection_id - - try: - conn_record = await ConnRecord.retrieve_by_id(session, connection_id) - except StorageError 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") - pres_manager = V20PresManager(context.profile) try: pres_ex_record = await pres_manager.verify_pres(pres_ex_record) 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 297e9a0eda..6c54e7742f 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 @@ -2082,59 +2082,6 @@ async def test_present_proof_verify_presentation_px_rec_not_found(self): await test_module.present_proof_verify_presentation(self.request) assert "no such record" in str(context.exception) - async def test_present_proof_verify_presentation_not_found(self): - self.request.match_info = {"pres_ex_id": "dummy"} - - with async_mock.patch.object( - test_module, "ConnRecord", autospec=True - ) as mock_conn_rec_cls, async_mock.patch.object( - test_module, "V20PresExRecord", autospec=True - ) as mock_px_rec_cls: - mock_px_rec_inst = async_mock.MagicMock( - connection_id="dummy", - state=test_module.V20PresExRecord.STATE_PRESENTATION_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( - side_effect=StorageNotFoundError() - ) - - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.present_proof_verify_presentation(self.request) - - async def test_present_proof_verify_presentation_not_ready(self): - self.request.match_info = {"pres_ex_id": "dummy"} - - with async_mock.patch.object( - test_module, "ConnRecord", autospec=True - ) as mock_conn_rec_cls, async_mock.patch.object( - test_module, "V20PresExRecord", autospec=True - ) as mock_px_rec_cls: - mock_px_rec_inst = async_mock.MagicMock( - connection_id="dummy", - state=test_module.V20PresExRecord.STATE_PRESENTATION_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=False) - mock_conn_rec_cls.retrieve_by_id = async_mock.CoroutineMock( - return_value=mock_conn_rec_inst - ) - - with self.assertRaises(test_module.web.HTTPForbidden): - await test_module.present_proof_verify_presentation(self.request) - async def test_present_proof_verify_presentation_bad_state(self): self.request.match_info = {"pres_ex_id": "dummy"} diff --git a/aries_cloudagent/resolver/__init__.py b/aries_cloudagent/resolver/__init__.py index 51a297e43b..d7dceffb03 100644 --- a/aries_cloudagent/resolver/__init__.py +++ b/aries_cloudagent/resolver/__init__.py @@ -12,7 +12,7 @@ async def setup(context: InjectionContext): """Set up default resolvers.""" - registry = context.inject(DIDResolverRegistry, required=False) + registry = context.inject_or(DIDResolverRegistry) if not registry: LOGGER.warning("No DID Resolver Registry instance found in context") return diff --git a/aries_cloudagent/resolver/base.py b/aries_cloudagent/resolver/base.py index 1374a50578..994bfe94b2 100644 --- a/aries_cloudagent/resolver/base.py +++ b/aries_cloudagent/resolver/base.py @@ -1,10 +1,11 @@ """Base Class for DID Resolvers.""" +import re +import warnings + from abc import ABC, abstractmethod from enum import Enum -import re from typing import NamedTuple, Pattern, Sequence, Union -import warnings from pydid import DID @@ -116,7 +117,8 @@ async def supports(self, profile: Profile, did: str) -> bool: try: supported_did_regex = self.supported_did_regex except NotImplementedError as error: - if not self.supported_methods: + methods = self.supported_methods + if not methods: raise error warnings.warn( "BaseResolver.supported_methods is deprecated; " @@ -125,7 +127,7 @@ async def supports(self, profile: Profile, did: str) -> bool: ) supported_did_regex = re.compile( - "^did:(?:{}):.*$".format("|".join(self.supported_methods)) + "^did:(?:{}):.*$".format("|".join(methods)) ) return bool(supported_did_regex.match(did)) diff --git a/aries_cloudagent/resolver/default/indy.py b/aries_cloudagent/resolver/default/indy.py index b6df684f9c..75f24e3c2c 100644 --- a/aries_cloudagent/resolver/default/indy.py +++ b/aries_cloudagent/resolver/default/indy.py @@ -3,7 +3,7 @@ Resolution is performed using the IndyLedger class. """ -from typing import Sequence, Pattern +from typing import Pattern from pydid import DID, DIDDocumentBuilder from pydid.verification_method import Ed25519VerificationKey2018 @@ -34,15 +34,6 @@ def __init__(self): async def setup(self, context: InjectionContext): """Perform required setup for Indy DID resolution.""" - @property - def supported_methods(self) -> Sequence[str]: - """ - Return supported methods of Indy DID Resolver. - - DEPRECATED: Use supported_did_regex instead. - """ - return ["sov"] - @property def supported_did_regex(self) -> Pattern: """Return supported_did_regex of Indy DID Resolver.""" @@ -50,7 +41,7 @@ def supported_did_regex(self) -> Pattern: async def _resolve(self, profile: Profile, did: str) -> dict: """Resolve an indy DID.""" - ledger = profile.inject(BaseLedger, required=False) + ledger = profile.inject_or(BaseLedger) if not ledger: raise NoIndyLedger("No Indy ledger instance is configured.") diff --git a/aries_cloudagent/resolver/default/key.py b/aries_cloudagent/resolver/default/key.py index 53286b1896..2f9f10edf9 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 Sequence, Pattern +from typing import Pattern from ...did.did_key import DIDKey from ...config.injection_context import InjectionContext @@ -23,15 +23,6 @@ def __init__(self): async def setup(self, context: InjectionContext): """Perform required setup for Key DID resolution.""" - @property - def supported_methods(self) -> Sequence[str]: - """ - Return supported methods of Key DID Resolver. - - DEPRECATED: Use supported_did_regex instead. - """ - return ["key"] - @property def supported_did_regex(self) -> Pattern: """Return supported_did_regex of Key DID Resolver.""" diff --git a/aries_cloudagent/resolver/default/tests/test_indy.py b/aries_cloudagent/resolver/default/tests/test_indy.py index 468e60ad37..a82f7ba427 100644 --- a/aries_cloudagent/resolver/default/tests/test_indy.py +++ b/aries_cloudagent/resolver/default/tests/test_indy.py @@ -44,12 +44,6 @@ def profile(ledger): class TestIndyResolver: - @pytest.mark.asyncio - async def test_supported_methods(self, profile, resolver: IndyDIDResolver): - """Test the supported_methods.""" - assert resolver.supported_methods == ["sov"] - assert await resolver.supports(profile, TEST_DID0) - @pytest.mark.asyncio async def test_supported_did_regex(self, profile, resolver: IndyDIDResolver): """Test the supported_did_regex.""" diff --git a/aries_cloudagent/resolver/default/tests/test_key.py b/aries_cloudagent/resolver/default/tests/test_key.py index 2d06a8517d..2ec6587006 100644 --- a/aries_cloudagent/resolver/default/tests/test_key.py +++ b/aries_cloudagent/resolver/default/tests/test_key.py @@ -27,16 +27,6 @@ def profile(): yield profile -@pytest.mark.asyncio -async def test_supported_methods(profile, resolver: KeyDIDResolver): - """Test the supported_methods.""" - assert resolver.supported_methods == ["key"] - assert await resolver.supports( - profile, - TEST_DID0, - ) - - @pytest.mark.asyncio async def test_supported_did_regex(profile, resolver: KeyDIDResolver): """Test the supported_did_regex.""" diff --git a/aries_cloudagent/resolver/default/web.py b/aries_cloudagent/resolver/default/web.py index 3475ca2a8d..30aa5ce440 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 Sequence, Pattern +from typing import Pattern import aiohttp @@ -35,10 +35,6 @@ def supported_did_regex(self) -> Pattern: """Return supported_did_regex of Web DID Resolver.""" return DIDWeb.PATTERN - def supported_methods(self) -> Sequence[str]: - """Return list of supported methods.""" - return ["web"] - def __transform_to_url(self, did): """ Transform did to url. diff --git a/aries_cloudagent/resolver/routes.py b/aries_cloudagent/resolver/routes.py index 78a1943188..17c91eeff1 100644 --- a/aries_cloudagent/resolver/routes.py +++ b/aries_cloudagent/resolver/routes.py @@ -1,32 +1,42 @@ """ Resolve did document admin routes. -"/resolver/resolve/{did}": { - "get": { - "responses": { - "200": { - "schema": { - "$ref": "#/definitions/DIDDoc" - }, - "description": null - } - }, - "parameters": [ - { - "in": "path", - "name": "did", - "required": true, - "type": "string", - "pattern": "did:([a-z]+):((?:[a-zA-Z0-9._-]*:)*[a-zA-Z0-9._-]+)", - "description": "decentralize identifier(DID)", - "example": "did:ted:WgWxqztrNooG92RXvxSTWv" - } - ], - "tags": [ "resolver" ], - "summary": "Retrieve doc for requested did", - "produces": [ "application/json" ] + "/resolver/resolve/{did}": { + "get": { + "responses": { + "200": { + "schema": { + "$ref": "#/definitions/DIDDoc" + + }, + "description": null + + } + + }, + "parameters": [ + + { + "in": "path", + "name": "did", + "required": true, + "type": "string", + "pattern": "did:([a-z]+):((?:[a-zA-Z0-9._-]*:)*[a-zA-Z0-9._-]+)", + "description": "decentralize identifier(DID)", + "example": "did:ted:WgWxqztrNooG92RXvxSTWv" + + } + + ], + + "tags": [ "resolver" ], + "summary": "Retrieve doc for requested did", + "produces": [ "application/json" ] + + } + } -} + """ from aiohttp import web diff --git a/aries_cloudagent/resolver/tests/test_base.py b/aries_cloudagent/resolver/tests/test_base.py index 4f049c16b6..cdae76a91e 100644 --- a/aries_cloudagent/resolver/tests/test_base.py +++ b/aries_cloudagent/resolver/tests/test_base.py @@ -18,10 +18,6 @@ def __init__(self): async def setup(self, context): pass - @property - def supported_methods(self): - return ["test"] - @property def supported_did_regex(self): return re.compile("^did:example:[a-zA-Z0-9_.-]+$") @@ -66,3 +62,22 @@ async def test_resolve_x(native_resolver): with pytest.raises(DIDMethodNotSupported) as x_did: await native_resolver.resolve(None, "did:nosuchmethod:xxx") assert "does not support DID method" in str(x_did.value) + + +@pytest.mark.asyncio +async def test_supported_methods(): + class TestDIDResolver(BaseDIDResolver): + async def setup(self, context): + pass + + @property + def supported_methods(self): + return ["example"] + + async def _resolve(self, profile, did) -> DIDDocument: + return DIDDocument("did:example:123") + + with pytest.deprecated_call(): + assert await TestDIDResolver().supports( + profile, "did:example:WgWxqztrNooG92RXvxSTWv" + ) diff --git a/aries_cloudagent/resolver/tests/test_did_resolver.py b/aries_cloudagent/resolver/tests/test_did_resolver.py index 3a7fae83fe..b08480e805 100644 --- a/aries_cloudagent/resolver/tests/test_did_resolver.py +++ b/aries_cloudagent/resolver/tests/test_did_resolver.py @@ -1,5 +1,9 @@ """Test did resolver registry.""" +from typing import Pattern + +import re + import pytest from asynctest import mock as async_mock @@ -64,16 +68,18 @@ class MockResolver(BaseDIDResolver): def __init__(self, supported_methods, resolved=None, native: bool = False): super().__init__(ResolverType.NATIVE if native else ResolverType.NON_NATIVE) - self._supported_methods = supported_methods + self._did_regex = re.compile( + "^did:(?:{}):.*$".format("|".join(supported_methods)) + ) self.resolved = resolved + @property + def supported_did_regex(self) -> Pattern: + return self._did_regex + async def setup(self, context): pass - @property - def supported_methods(self): - return self._supported_methods - async def _resolve(self, profile, did): if isinstance(self.resolved, Exception): raise self.resolved diff --git a/aries_cloudagent/revocation/routes.py b/aries_cloudagent/revocation/routes.py index 3cb0943f1d..cfbda8c3ec 100644 --- a/aries_cloudagent/revocation/routes.py +++ b/aries_cloudagent/revocation/routes.py @@ -12,9 +12,6 @@ request_schema, response_schema, ) -from aries_cloudagent.protocols.endorse_transaction.v1_0.manager import ( - TransactionManager, -) from marshmallow import fields, validate, validates_schema from marshmallow.exceptions import ValidationError @@ -35,12 +32,14 @@ WHOLE_NUM, UUIDFour, ) +from ..protocols.endorse_transaction.v1_0.manager import TransactionManager from ..protocols.endorse_transaction.v1_0.models.transaction_record import ( TransactionRecordSchema, ) 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 from .manager import RevocationManager, RevocationManagerError @@ -709,7 +708,7 @@ async def upload_tails_file(request: web.BaseRequest): rev_reg_id = request.match_info["rev_reg_id"] - tails_server = context.inject(BaseTailsServer, required=False) + tails_server = context.inject_or(BaseTailsServer) if not tails_server: raise web.HTTPForbidden(reason="No tails server configured") diff --git a/aries_cloudagent/storage/askar.py b/aries_cloudagent/storage/askar.py index 63036b3591..75683d4af9 100644 --- a/aries_cloudagent/storage/askar.py +++ b/aries_cloudagent/storage/askar.py @@ -263,8 +263,8 @@ def __init__( self.type_filter = type_filter self.page_size = page_size or DEFAULT_PAGE_SIZE self._done = False - self._scan = None self._profile = profile + self._scan = None @property def opened(self) -> bool: @@ -364,7 +364,11 @@ async def _open(self): if self._scan: return try: - self._scan = self._profile.store.scan(self.type_filter, self.tag_query) + self._scan = self._profile.store.scan( + self.type_filter, + self.tag_query, + profile=self._profile.settings.get("wallet.askar_profile"), + ) except AskarError as err: raise StorageSearchError("Error opening search query") from err diff --git a/aries_cloudagent/storage/tests/test_askar_storage.py b/aries_cloudagent/storage/tests/test_askar_storage.py index b6b5dd5c70..4cc013dfae 100644 --- a/aries_cloudagent/storage/tests/test_askar_storage.py +++ b/aries_cloudagent/storage/tests/test_askar_storage.py @@ -4,6 +4,8 @@ from asynctest import mock as async_mock +from asynctest import TestCase as AsyncTestCase + from ...askar.profile import AskarProfileManager from ...config.injection_context import InjectionContext @@ -349,3 +351,28 @@ async def test_postgres_wallet_storage_works(self): await postgres_wallet.close() await postgres_wallet.remove() + + +class TestAskarStorageSearchSession(AsyncTestCase): + @pytest.mark.asyncio + async def test_askar_storage_search_session(self): + profile = "profileId" + + with async_mock.patch( + "aries_cloudagent.storage.askar.AskarProfile" + ) as AskarProfile: + askar_profile = AskarProfile(None, True) + askar_profile_scan = async_mock.MagicMock() + askar_profile.store.scan.return_value = askar_profile_scan + askar_profile.settings.get.return_value = profile + + storageSearchSession = test_module.AskarStorageSearchSession( + askar_profile, "filter", "tagQuery" + ) + await storageSearchSession._open() + + assert storageSearchSession._scan == askar_profile_scan + askar_profile.settings.get.assert_called_once_with("wallet.askar_profile") + askar_profile.store.scan.assert_called_once_with( + "filter", "tagQuery", profile=profile + ) diff --git a/aries_cloudagent/transport/inbound/session.py b/aries_cloudagent/transport/inbound/session.py index 88df01be8e..d6214acda7 100644 --- a/aries_cloudagent/transport/inbound/session.py +++ b/aries_cloudagent/transport/inbound/session.py @@ -7,7 +7,7 @@ from ...admin.server import AdminResponder from ...core.profile import Profile from ...messaging.responder import BaseResponder -from ...multitenant.manager import MultitenantManager +from ...multitenant.base import BaseMultitenantManager from ..error import WireFormatError from ..outbound.message import OutboundMessage @@ -159,7 +159,7 @@ def response_buffered(self) -> bool: async def handle_relay_context(self, payload_enc: Union[str, bytes]): """Update the session profile based on the recipients of an incoming message.""" - multitenant_mgr = self.profile.context.inject(MultitenantManager) + multitenant_mgr = self.profile.context.inject(BaseMultitenantManager) try: [wallet] = await multitenant_mgr.get_wallets_by_message( diff --git a/aries_cloudagent/transport/inbound/tests/test_session.py b/aries_cloudagent/transport/inbound/tests/test_session.py index 17cb970e4e..e1e5fcd9cd 100644 --- a/aries_cloudagent/transport/inbound/tests/test_session.py +++ b/aries_cloudagent/transport/inbound/tests/test_session.py @@ -6,6 +6,7 @@ from ....admin.server import AdminResponder from ....core.in_memory import InMemoryProfile from ....messaging.responder import BaseResponder +from ....multitenant.base import BaseMultitenantManager from ....multitenant.manager import MultitenantManager from ...error import WireFormatError @@ -120,7 +121,7 @@ async def test_receive(self): return_value=self.profile ) self.profile.context.injector.bind_instance( - MultitenantManager, self.multitenant_mgr + BaseMultitenantManager, self.multitenant_mgr ) self.profile.context.update_settings({"multitenant.enabled": True}) self.base_responder = async_mock.MagicMock(AdminResponder, autospec=True) @@ -153,7 +154,7 @@ async def test_receive_no_wallet_found(self): return_value=self.profile ) self.profile.context.injector.bind_instance( - MultitenantManager, self.multitenant_mgr + BaseMultitenantManager, self.multitenant_mgr ) self.profile.context.update_settings({"multitenant.enabled": True}) diff --git a/aries_cloudagent/transport/outbound/manager.py b/aries_cloudagent/transport/outbound/manager.py index 58a1a0d3e7..b9086445cb 100644 --- a/aries_cloudagent/transport/outbound/manager.py +++ b/aries_cloudagent/transport/outbound/manager.py @@ -173,7 +173,7 @@ def register_class( async def start_transport(self, transport_id: str): """Start a registered transport.""" transport = self.registered_transports[transport_id]() - transport.collector = self.context.inject(Collector, required=False) + transport.collector = self.context.inject_or(Collector) await transport.start() self.running_transports[transport_id] = transport diff --git a/aries_cloudagent/transport/outbound/queue/base.py b/aries_cloudagent/transport/outbound/queue/base.py index 23fa18674d..9cd1c53927 100644 --- a/aries_cloudagent/transport/outbound/queue/base.py +++ b/aries_cloudagent/transport/outbound/queue/base.py @@ -37,10 +37,6 @@ async def start(self): async def stop(self): """Stop the queue.""" - @abstractmethod - async def push(self, key: bytes, message: bytes): - """Push a ``message`` to queue on ``key``.""" - @abstractmethod async def enqueue_message( self, diff --git a/aries_cloudagent/transport/pack_format.py b/aries_cloudagent/transport/pack_format.py index 9c3ad8300f..2423e47a40 100644 --- a/aries_cloudagent/transport/pack_format.py +++ b/aries_cloudagent/transport/pack_format.py @@ -108,7 +108,7 @@ async def unpack( receipt: MessageReceipt, ): """Look up the wallet instance and perform the message unpack.""" - wallet = session.inject(BaseWallet, required=False) + wallet = session.inject_or(BaseWallet) if not wallet: raise WireFormatParseError("Wallet not defined in profile session") @@ -170,7 +170,7 @@ async def pack( if not sender_key or not recipient_keys: raise WireFormatEncodeError("Cannot pack message without associated keys") - wallet = session.inject(BaseWallet, required=False) + wallet = session.inject_or(BaseWallet) if not wallet: raise WireFormatEncodeError("No wallet instance") diff --git a/aries_cloudagent/vc/ld_proofs/__init__.py b/aries_cloudagent/vc/ld_proofs/__init__.py index 0b8c47dcc6..9843f069be 100644 --- a/aries_cloudagent/vc/ld_proofs/__init__.py +++ b/aries_cloudagent/vc/ld_proofs/__init__.py @@ -1,5 +1,5 @@ from .ld_proofs import sign, verify, derive -from .ProofSet import ProofSet +from .proof_set import ProofSet from .purposes import ( ProofPurpose, ControllerProofPurpose, @@ -28,34 +28,34 @@ from .check import get_properties_without_context __all__ = [ - sign, - verify, - derive, - ProofSet, + "sign", + "verify", + "derive", + "ProofSet", # Proof purposes - ProofPurpose, - ControllerProofPurpose, - AssertionProofPurpose, - AuthenticationProofPurpose, - CredentialIssuancePurpose, + "ProofPurpose", + "ControllerProofPurpose", + "AssertionProofPurpose", + "AuthenticationProofPurpose", + "CredentialIssuancePurpose", # Suites - LinkedDataProof, - LinkedDataSignature, - JwsLinkedDataSignature, - Ed25519Signature2018, - BbsBlsSignature2020, - BbsBlsSignatureProof2020, + "LinkedDataProof", + "LinkedDataSignature", + "JwsLinkedDataSignature", + "Ed25519Signature2018", + "BbsBlsSignature2020", + "BbsBlsSignatureProof2020", # Key pairs - KeyPair, - WalletKeyPair, + "KeyPair", + "WalletKeyPair", # Document Loaders - DocumentLoaderMethod, - DocumentLoader, + "DocumentLoaderMethod", + "DocumentLoader", # Exceptions - LinkedDataProofException, + "LinkedDataProofException", # Validation results - DocumentVerificationResult, - ProofResult, - PurposeResult, - get_properties_without_context, + "DocumentVerificationResult", + "ProofResult", + "PurposeResult", + "get_properties_without_context", ] diff --git a/aries_cloudagent/vc/ld_proofs/crypto/__init__.py b/aries_cloudagent/vc/ld_proofs/crypto/__init__.py index 13842f4eea..ccb10f82da 100644 --- a/aries_cloudagent/vc/ld_proofs/crypto/__init__.py +++ b/aries_cloudagent/vc/ld_proofs/crypto/__init__.py @@ -1,4 +1,4 @@ -from .KeyPair import KeyPair -from .WalletKeyPair import WalletKeyPair +from .key_pair import KeyPair +from .wallet_key_pair import WalletKeyPair -__all__ = [KeyPair, WalletKeyPair] +__all__ = ["KeyPair", "WalletKeyPair"] diff --git a/aries_cloudagent/vc/ld_proofs/crypto/KeyPair.py b/aries_cloudagent/vc/ld_proofs/crypto/key_pair.py similarity index 100% rename from aries_cloudagent/vc/ld_proofs/crypto/KeyPair.py rename to aries_cloudagent/vc/ld_proofs/crypto/key_pair.py diff --git a/aries_cloudagent/vc/ld_proofs/crypto/tests/test_WalletKeyPair.py b/aries_cloudagent/vc/ld_proofs/crypto/tests/test_wallet_key_pair.py similarity index 95% rename from aries_cloudagent/vc/ld_proofs/crypto/tests/test_WalletKeyPair.py rename to aries_cloudagent/vc/ld_proofs/crypto/tests/test_wallet_key_pair.py index ba9d125fd1..705ca244be 100644 --- a/aries_cloudagent/vc/ld_proofs/crypto/tests/test_WalletKeyPair.py +++ b/aries_cloudagent/vc/ld_proofs/crypto/tests/test_wallet_key_pair.py @@ -1,9 +1,10 @@ -from asynctest import TestCase -from asynctest import mock as async_mock +from asynctest import TestCase, mock as async_mock from .....wallet.key_pair import KeyType + from ...error import LinkedDataProofException -from ..WalletKeyPair import WalletKeyPair + +from ..wallet_key_pair import WalletKeyPair class TestWalletKeyPair(TestCase): diff --git a/aries_cloudagent/vc/ld_proofs/crypto/WalletKeyPair.py b/aries_cloudagent/vc/ld_proofs/crypto/wallet_key_pair.py similarity index 98% rename from aries_cloudagent/vc/ld_proofs/crypto/WalletKeyPair.py rename to aries_cloudagent/vc/ld_proofs/crypto/wallet_key_pair.py index 43e6df5eeb..f666b739ac 100644 --- a/aries_cloudagent/vc/ld_proofs/crypto/WalletKeyPair.py +++ b/aries_cloudagent/vc/ld_proofs/crypto/wallet_key_pair.py @@ -5,8 +5,10 @@ from ....wallet.util import b58_to_bytes from ....wallet.key_type import KeyType from ....wallet.base import BaseWallet + from ..error import LinkedDataProofException -from .KeyPair import KeyPair + +from .key_pair import KeyPair class WalletKeyPair(KeyPair): diff --git a/aries_cloudagent/vc/ld_proofs/document_loader.py b/aries_cloudagent/vc/ld_proofs/document_loader.py index 355486353d..2f4eab5e4e 100644 --- a/aries_cloudagent/vc/ld_proofs/document_loader.py +++ b/aries_cloudagent/vc/ld_proofs/document_loader.py @@ -28,7 +28,7 @@ def __init__(self, profile: Profile, cache_ttl: int = 300) -> None: """ self.profile = profile self.resolver = profile.inject(DIDResolver) - self.cache = profile.inject(BaseCache, required=False) + self.cache = profile.inject_or(BaseCache) self.requests_loader = requests.requests_document_loader() self.executor = concurrent.futures.ThreadPoolExecutor(max_workers=1) self.cache_ttl = cache_ttl @@ -112,4 +112,4 @@ def __call__(self, url: str, options: dict): DocumentLoaderMethod = Callable[[str, dict], dict] -__all__ = [DocumentLoaderMethod, DocumentLoader] +__all__ = ["DocumentLoaderMethod", "DocumentLoader"] diff --git a/aries_cloudagent/vc/ld_proofs/ld_proofs.py b/aries_cloudagent/vc/ld_proofs/ld_proofs.py index cdb284fc73..284e328f61 100644 --- a/aries_cloudagent/vc/ld_proofs/ld_proofs.py +++ b/aries_cloudagent/vc/ld_proofs/ld_proofs.py @@ -2,11 +2,11 @@ from typing import List -from .validation_result import DocumentVerificationResult from .document_loader import DocumentLoaderMethod -from .ProofSet import ProofSet +from .proof_set import ProofSet from .purposes import ProofPurpose from .suites import LinkedDataProof +from .validation_result import DocumentVerificationResult async def sign( diff --git a/aries_cloudagent/vc/ld_proofs/ProofSet.py b/aries_cloudagent/vc/ld_proofs/proof_set.py similarity index 99% rename from aries_cloudagent/vc/ld_proofs/ProofSet.py rename to aries_cloudagent/vc/ld_proofs/proof_set.py index 8eace9e6c4..4ff176d679 100644 --- a/aries_cloudagent/vc/ld_proofs/ProofSet.py +++ b/aries_cloudagent/vc/ld_proofs/proof_set.py @@ -1,14 +1,15 @@ """Class to represent a Linked Data proof set.""" from typing import List, Union + from pyld.jsonld import JsonLdProcessor -from .error import LinkedDataProofException -from .validation_result import DocumentVerificationResult, ProofResult from .constants import SECURITY_CONTEXT_URL from .document_loader import DocumentLoaderMethod -from .purposes.ProofPurpose import ProofPurpose +from .error import LinkedDataProofException +from .purposes.proof_purpose import ProofPurpose from .suites import LinkedDataProof +from .validation_result import DocumentVerificationResult, ProofResult class ProofSet: diff --git a/aries_cloudagent/vc/ld_proofs/purposes/__init__.py b/aries_cloudagent/vc/ld_proofs/purposes/__init__.py index 7b24e9323b..091c726be9 100644 --- a/aries_cloudagent/vc/ld_proofs/purposes/__init__.py +++ b/aries_cloudagent/vc/ld_proofs/purposes/__init__.py @@ -1,13 +1,14 @@ -from .ProofPurpose import ProofPurpose -from .ControllerProofPurpose import ControllerProofPurpose -from .AssertionProofPurpose import AssertionProofPurpose -from .AuthenticationProofPurpose import AuthenticationProofPurpose -from .CredentialIssuancePurpose import CredentialIssuancePurpose +from .proof_purpose import ProofPurpose + +from .assertion_proof_purpose import AssertionProofPurpose +from .authentication_proof_purpose import AuthenticationProofPurpose +from .controller_proof_purpose import ControllerProofPurpose +from .credential_issuance_purpose import CredentialIssuancePurpose __all__ = [ - ProofPurpose, - ControllerProofPurpose, - AssertionProofPurpose, - AuthenticationProofPurpose, - CredentialIssuancePurpose, + "ProofPurpose", + "ControllerProofPurpose", + "AssertionProofPurpose", + "AuthenticationProofPurpose", + "CredentialIssuancePurpose", ] diff --git a/aries_cloudagent/vc/ld_proofs/purposes/AssertionProofPurpose.py b/aries_cloudagent/vc/ld_proofs/purposes/assertion_proof_purpose.py similarity index 89% rename from aries_cloudagent/vc/ld_proofs/purposes/AssertionProofPurpose.py rename to aries_cloudagent/vc/ld_proofs/purposes/assertion_proof_purpose.py index 1f36127496..c988b3f2a2 100644 --- a/aries_cloudagent/vc/ld_proofs/purposes/AssertionProofPurpose.py +++ b/aries_cloudagent/vc/ld_proofs/purposes/assertion_proof_purpose.py @@ -2,7 +2,7 @@ from datetime import datetime, timedelta -from .ControllerProofPurpose import ControllerProofPurpose +from .controller_proof_purpose import ControllerProofPurpose class AssertionProofPurpose(ControllerProofPurpose): diff --git a/aries_cloudagent/vc/ld_proofs/purposes/AuthenticationProofPurpose.py b/aries_cloudagent/vc/ld_proofs/purposes/authentication_proof_purpose.py similarity index 92% rename from aries_cloudagent/vc/ld_proofs/purposes/AuthenticationProofPurpose.py rename to aries_cloudagent/vc/ld_proofs/purposes/authentication_proof_purpose.py index 8dfa13a840..4ed8c0731a 100644 --- a/aries_cloudagent/vc/ld_proofs/purposes/AuthenticationProofPurpose.py +++ b/aries_cloudagent/vc/ld_proofs/purposes/authentication_proof_purpose.py @@ -1,12 +1,17 @@ """Authentication proof purpose class.""" from datetime import datetime, timedelta +from typing import TYPE_CHECKING +from ..document_loader import DocumentLoaderMethod from ..error import LinkedDataProofException from ..validation_result import PurposeResult -from ..document_loader import DocumentLoaderMethod -from ..suites import LinkedDataProof -from .ControllerProofPurpose import ControllerProofPurpose + +from .controller_proof_purpose import ControllerProofPurpose + +# Avoid circular dependency +if TYPE_CHECKING: + from ..suites import LinkedDataProof class AuthenticationProofPurpose(ControllerProofPurpose): @@ -37,7 +42,7 @@ def validate( *, proof: dict, document: dict, - suite: LinkedDataProof, + suite: "LinkedDataProof", verification_method: dict, document_loader: DocumentLoaderMethod, ) -> PurposeResult: diff --git a/aries_cloudagent/vc/ld_proofs/purposes/ControllerProofPurpose.py b/aries_cloudagent/vc/ld_proofs/purposes/controller_proof_purpose.py similarity index 93% rename from aries_cloudagent/vc/ld_proofs/purposes/ControllerProofPurpose.py rename to aries_cloudagent/vc/ld_proofs/purposes/controller_proof_purpose.py index fa0cb6fd0e..84167348ac 100644 --- a/aries_cloudagent/vc/ld_proofs/purposes/ControllerProofPurpose.py +++ b/aries_cloudagent/vc/ld_proofs/purposes/controller_proof_purpose.py @@ -1,14 +1,20 @@ """Controller proof purpose class.""" +from typing import TYPE_CHECKING + from pyld.jsonld import JsonLdProcessor from pyld import jsonld -from ..error import LinkedDataProofException -from ..validation_result import PurposeResult from ..constants import SECURITY_CONTEXT_URL -from ..suites import LinkedDataProof from ..document_loader import DocumentLoaderMethod -from .ProofPurpose import ProofPurpose +from ..error import LinkedDataProofException +from ..validation_result import PurposeResult + +from .proof_purpose import ProofPurpose + +# Avoid circular dependency +if TYPE_CHECKING: + from ..suites import LinkedDataProof class ControllerProofPurpose(ProofPurpose): @@ -19,7 +25,7 @@ def validate( *, proof: dict, document: dict, - suite: LinkedDataProof, + suite: "LinkedDataProof", verification_method: dict, document_loader: DocumentLoaderMethod, ) -> PurposeResult: diff --git a/aries_cloudagent/vc/ld_proofs/purposes/CredentialIssuancePurpose.py b/aries_cloudagent/vc/ld_proofs/purposes/credential_issuance_purpose.py similarity index 90% rename from aries_cloudagent/vc/ld_proofs/purposes/CredentialIssuancePurpose.py rename to aries_cloudagent/vc/ld_proofs/purposes/credential_issuance_purpose.py index 5e24cf3de7..151857fbf2 100644 --- a/aries_cloudagent/vc/ld_proofs/purposes/CredentialIssuancePurpose.py +++ b/aries_cloudagent/vc/ld_proofs/purposes/credential_issuance_purpose.py @@ -1,15 +1,20 @@ """Credential Issuance proof purpose class.""" -from typing import List +from typing import List, TYPE_CHECKING + from pyld.jsonld import JsonLdProcessor from pyld import jsonld +from ..constants import CREDENTIALS_ISSUER_URL +from ..document_loader import DocumentLoaderMethod from ..error import LinkedDataProofException from ..validation_result import PurposeResult -from ..suites import LinkedDataProof -from ..document_loader import DocumentLoaderMethod -from ..constants import CREDENTIALS_ISSUER_URL -from .AssertionProofPurpose import AssertionProofPurpose + +from .assertion_proof_purpose import AssertionProofPurpose + +# Avoid circular dependency +if TYPE_CHECKING: + from ..suites import LinkedDataProof class CredentialIssuancePurpose(AssertionProofPurpose): @@ -20,7 +25,7 @@ def validate( *, proof: dict, document: dict, - suite: LinkedDataProof, + suite: "LinkedDataProof", verification_method: dict, document_loader: DocumentLoaderMethod, ) -> PurposeResult: diff --git a/aries_cloudagent/vc/ld_proofs/purposes/ProofPurpose.py b/aries_cloudagent/vc/ld_proofs/purposes/proof_purpose.py similarity index 93% rename from aries_cloudagent/vc/ld_proofs/purposes/ProofPurpose.py rename to aries_cloudagent/vc/ld_proofs/purposes/proof_purpose.py index 8d9b7168ef..49fc7a48c8 100644 --- a/aries_cloudagent/vc/ld_proofs/purposes/ProofPurpose.py +++ b/aries_cloudagent/vc/ld_proofs/purposes/proof_purpose.py @@ -1,11 +1,16 @@ """Base Proof Purpose class.""" from datetime import datetime, timedelta +from typing import TYPE_CHECKING from ....messaging.util import str_to_datetime -from ..validation_result import PurposeResult + from ..document_loader import DocumentLoaderMethod -from ..suites import LinkedDataProof +from ..validation_result import PurposeResult + +# Avoid circular dependency +if TYPE_CHECKING: + from ..suites import LinkedDataProof class ProofPurpose: @@ -24,7 +29,7 @@ def validate( *, proof: dict, document: dict, - suite: LinkedDataProof, + suite: "LinkedDataProof", verification_method: dict, document_loader: DocumentLoaderMethod, ) -> PurposeResult: diff --git a/aries_cloudagent/vc/ld_proofs/purposes/tests/test_AuthenticationProofPurpose.py b/aries_cloudagent/vc/ld_proofs/purposes/tests/test_authentication_proof_purpose.py similarity index 97% rename from aries_cloudagent/vc/ld_proofs/purposes/tests/test_AuthenticationProofPurpose.py rename to aries_cloudagent/vc/ld_proofs/purposes/tests/test_authentication_proof_purpose.py index 4f902db3a4..957c5d56c1 100644 --- a/aries_cloudagent/vc/ld_proofs/purposes/tests/test_AuthenticationProofPurpose.py +++ b/aries_cloudagent/vc/ld_proofs/purposes/tests/test_authentication_proof_purpose.py @@ -2,8 +2,8 @@ from asynctest import TestCase, mock as async_mock from ...validation_result import PurposeResult -from ..ControllerProofPurpose import ControllerProofPurpose -from ..AuthenticationProofPurpose import AuthenticationProofPurpose +from ..controller_proof_purpose import ControllerProofPurpose +from ..authentication_proof_purpose import AuthenticationProofPurpose class TestAuthenticationProofPurpose(TestCase): diff --git a/aries_cloudagent/vc/ld_proofs/purposes/tests/test_ControllerProofPurpose.py b/aries_cloudagent/vc/ld_proofs/purposes/tests/test_controller_proof_purpose.py similarity index 97% rename from aries_cloudagent/vc/ld_proofs/purposes/tests/test_ControllerProofPurpose.py rename to aries_cloudagent/vc/ld_proofs/purposes/tests/test_controller_proof_purpose.py index 156f4a8dbc..8ae1dc33c6 100644 --- a/aries_cloudagent/vc/ld_proofs/purposes/tests/test_ControllerProofPurpose.py +++ b/aries_cloudagent/vc/ld_proofs/purposes/tests/test_controller_proof_purpose.py @@ -3,8 +3,8 @@ from ....tests.data import TEST_VC_DOCUMENT_SIGNED_DID_KEY_ED25519 from ....tests.document_loader import custom_document_loader -from ..ProofPurpose import ProofPurpose -from ..ControllerProofPurpose import ControllerProofPurpose +from ..proof_purpose import ProofPurpose +from ..controller_proof_purpose import ControllerProofPurpose class TestControllerProofPurpose(TestCase): diff --git a/aries_cloudagent/vc/ld_proofs/purposes/tests/test_CredentialIssuancePurpose.py b/aries_cloudagent/vc/ld_proofs/purposes/tests/test_credential_issuance_purpose.py similarity index 97% rename from aries_cloudagent/vc/ld_proofs/purposes/tests/test_CredentialIssuancePurpose.py rename to aries_cloudagent/vc/ld_proofs/purposes/tests/test_credential_issuance_purpose.py index 8d0ee90886..60442b73da 100644 --- a/aries_cloudagent/vc/ld_proofs/purposes/tests/test_CredentialIssuancePurpose.py +++ b/aries_cloudagent/vc/ld_proofs/purposes/tests/test_credential_issuance_purpose.py @@ -2,8 +2,8 @@ from asynctest import TestCase, mock as async_mock from ...validation_result import PurposeResult -from ..AssertionProofPurpose import AssertionProofPurpose -from ..CredentialIssuancePurpose import CredentialIssuancePurpose +from ..assertion_proof_purpose import AssertionProofPurpose +from ..credential_issuance_purpose import CredentialIssuancePurpose from ....tests.data import TEST_VC_DOCUMENT_SIGNED_ED25519 from ....tests.document_loader import custom_document_loader diff --git a/aries_cloudagent/vc/ld_proofs/purposes/tests/test_ProofPurpose.py b/aries_cloudagent/vc/ld_proofs/purposes/tests/test_proof_purpose.py similarity index 98% rename from aries_cloudagent/vc/ld_proofs/purposes/tests/test_ProofPurpose.py rename to aries_cloudagent/vc/ld_proofs/purposes/tests/test_proof_purpose.py index 7d6dcef890..ffde15d366 100644 --- a/aries_cloudagent/vc/ld_proofs/purposes/tests/test_ProofPurpose.py +++ b/aries_cloudagent/vc/ld_proofs/purposes/tests/test_proof_purpose.py @@ -2,7 +2,7 @@ from asynctest import TestCase, mock as async_mock from .....messaging.util import datetime_to_str -from ..ProofPurpose import ProofPurpose +from ..proof_purpose import ProofPurpose class TestProofPurpose(TestCase): diff --git a/aries_cloudagent/vc/ld_proofs/suites/__init__.py b/aries_cloudagent/vc/ld_proofs/suites/__init__.py index a6d6c74578..3e680e142b 100644 --- a/aries_cloudagent/vc/ld_proofs/suites/__init__.py +++ b/aries_cloudagent/vc/ld_proofs/suites/__init__.py @@ -1,15 +1,15 @@ -from .LinkedDataProof import LinkedDataProof -from .LinkedDataSignature import LinkedDataSignature -from .JwsLinkedDataSignature import JwsLinkedDataSignature -from .Ed25519Signature2018 import Ed25519Signature2018 -from .BbsBlsSignature2020 import BbsBlsSignature2020 -from .BbsBlsSignatureProof2020 import BbsBlsSignatureProof2020 +from .linked_data_proof import LinkedDataProof +from .linked_data_signature import LinkedDataSignature +from .jws_linked_data_signature import JwsLinkedDataSignature +from .ed25519_signature_2018 import Ed25519Signature2018 +from .bbs_bls_signature_2020 import BbsBlsSignature2020 +from .bbs_bls_signature_proof_2020 import BbsBlsSignatureProof2020 __all__ = [ - LinkedDataProof, - LinkedDataSignature, - JwsLinkedDataSignature, - Ed25519Signature2018, - BbsBlsSignature2020, - BbsBlsSignatureProof2020, + "LinkedDataProof", + "LinkedDataSignature", + "JwsLinkedDataSignature", + "Ed25519Signature2018", + "BbsBlsSignature2020", + "BbsBlsSignatureProof2020", ] diff --git a/aries_cloudagent/vc/ld_proofs/suites/BbsBlsSignature2020.py b/aries_cloudagent/vc/ld_proofs/suites/bbs_bls_signature_2020.py similarity index 96% rename from aries_cloudagent/vc/ld_proofs/suites/BbsBlsSignature2020.py rename to aries_cloudagent/vc/ld_proofs/suites/bbs_bls_signature_2020.py index a0d1ddcbe8..ff363a9f2b 100644 --- a/aries_cloudagent/vc/ld_proofs/suites/BbsBlsSignature2020.py +++ b/aries_cloudagent/vc/ld_proofs/suites/bbs_bls_signature_2020.py @@ -1,16 +1,18 @@ """BbsBlsSignature2020 class.""" -from datetime import datetime +from datetime import datetime, timezone +from pytz import utc from typing import List, Union - from ....wallet.util import b64_to_bytes, bytes_to_b64 + from ..crypto import KeyPair -from ..error import LinkedDataProofException -from ..validation_result import ProofResult from ..document_loader import DocumentLoaderMethod +from ..error import LinkedDataProofException from ..purposes import ProofPurpose -from .BbsBlsSignature2020Base import BbsBlsSignature2020Base +from ..validation_result import ProofResult + +from .bbs_bls_signature_2020_base import BbsBlsSignature2020Base class BbsBlsSignature2020(BbsBlsSignature2020Base): @@ -58,7 +60,9 @@ async def create_proof( # Set created if not already set if not proof.get("created"): # Use class date, or now - date = self.date or datetime.now() + date = self.date or datetime.now(timezone.utc) + if not date.tzinfo: + date = utc.localize(date) proof["created"] = date.isoformat() # Allow purpose to update the proof; the `proof` is in the diff --git a/aries_cloudagent/vc/ld_proofs/suites/BbsBlsSignature2020Base.py b/aries_cloudagent/vc/ld_proofs/suites/bbs_bls_signature_2020_base.py similarity index 98% rename from aries_cloudagent/vc/ld_proofs/suites/BbsBlsSignature2020Base.py rename to aries_cloudagent/vc/ld_proofs/suites/bbs_bls_signature_2020_base.py index b236df21b1..4c0a1e8938 100644 --- a/aries_cloudagent/vc/ld_proofs/suites/BbsBlsSignature2020Base.py +++ b/aries_cloudagent/vc/ld_proofs/suites/bbs_bls_signature_2020_base.py @@ -1,13 +1,16 @@ """BbsBlsSignature2020Base class.""" from abc import ABCMeta, abstractmethod -from pyld import jsonld from typing import List -from ..error import LinkedDataProofException +from pyld import jsonld + from ....utils.dependencies import is_ursa_bbs_signatures_module_installed + from ..document_loader import DocumentLoaderMethod -from .LinkedDataProof import LinkedDataProof +from ..error import LinkedDataProofException + +from .linked_data_proof import LinkedDataProof class BbsBlsSignature2020Base(LinkedDataProof, metaclass=ABCMeta): diff --git a/aries_cloudagent/vc/ld_proofs/suites/BbsBlsSignatureProof2020.py b/aries_cloudagent/vc/ld_proofs/suites/bbs_bls_signature_proof_2020.py similarity index 98% rename from aries_cloudagent/vc/ld_proofs/suites/BbsBlsSignatureProof2020.py rename to aries_cloudagent/vc/ld_proofs/suites/bbs_bls_signature_proof_2020.py index 3b00e8ed5f..ad951e7bab 100644 --- a/aries_cloudagent/vc/ld_proofs/suites/BbsBlsSignatureProof2020.py +++ b/aries_cloudagent/vc/ld_proofs/suites/bbs_bls_signature_proof_2020.py @@ -3,9 +3,11 @@ import re from os import urandom -from pyld import jsonld from typing import List -from .BbsBlsSignature2020Base import BbsBlsSignature2020Base + +from pyld import jsonld + +from .bbs_bls_signature_2020_base import BbsBlsSignature2020Base if BbsBlsSignature2020Base.BBS_SUPPORTED: from ursa_bbs_signatures import ( @@ -21,13 +23,15 @@ from ....utils.dependencies import assert_ursa_bbs_signatures_installed from ....wallet.util import b64_to_bytes, bytes_to_b64 + from ..crypto import KeyPair from ..error import LinkedDataProofException from ..validation_result import ProofResult from ..document_loader import DocumentLoaderMethod from ..purposes import ProofPurpose -from .BbsBlsSignature2020 import BbsBlsSignature2020 -from .LinkedDataProof import DeriveProofResult + +from .bbs_bls_signature_2020 import BbsBlsSignature2020 +from .linked_data_proof import DeriveProofResult class BbsBlsSignatureProof2020(BbsBlsSignature2020Base): diff --git a/aries_cloudagent/vc/ld_proofs/suites/Ed25519Signature2018.py b/aries_cloudagent/vc/ld_proofs/suites/ed25519_signature_2018.py similarity index 95% rename from aries_cloudagent/vc/ld_proofs/suites/Ed25519Signature2018.py rename to aries_cloudagent/vc/ld_proofs/suites/ed25519_signature_2018.py index 203b2bff91..df77ff8b75 100644 --- a/aries_cloudagent/vc/ld_proofs/suites/Ed25519Signature2018.py +++ b/aries_cloudagent/vc/ld_proofs/suites/ed25519_signature_2018.py @@ -4,7 +4,8 @@ from typing import Union from ..crypto import KeyPair -from .JwsLinkedDataSignature import JwsLinkedDataSignature + +from .jws_linked_data_signature import JwsLinkedDataSignature class Ed25519Signature2018(JwsLinkedDataSignature): diff --git a/aries_cloudagent/vc/ld_proofs/suites/JwsLinkedDataSignature.py b/aries_cloudagent/vc/ld_proofs/suites/jws_linked_data_signature.py similarity index 99% rename from aries_cloudagent/vc/ld_proofs/suites/JwsLinkedDataSignature.py rename to aries_cloudagent/vc/ld_proofs/suites/jws_linked_data_signature.py index e40399bb72..9aa749a8ee 100644 --- a/aries_cloudagent/vc/ld_proofs/suites/JwsLinkedDataSignature.py +++ b/aries_cloudagent/vc/ld_proofs/suites/jws_linked_data_signature.py @@ -1,15 +1,19 @@ """JWS Linked Data class.""" -from pyld.jsonld import JsonLdProcessor +import json + from datetime import datetime from typing import Union -import json + +from pyld.jsonld import JsonLdProcessor from ....wallet.util import b64_to_bytes, bytes_to_b64, str_to_b64, b64_to_str + from ..crypto import KeyPair from ..document_loader import DocumentLoaderMethod from ..error import LinkedDataProofException -from .LinkedDataSignature import LinkedDataSignature + +from .linked_data_signature import LinkedDataSignature class JwsLinkedDataSignature(LinkedDataSignature): diff --git a/aries_cloudagent/vc/ld_proofs/suites/LinkedDataProof.py b/aries_cloudagent/vc/ld_proofs/suites/linked_data_proof.py similarity index 95% rename from aries_cloudagent/vc/ld_proofs/suites/LinkedDataProof.py rename to aries_cloudagent/vc/ld_proofs/suites/linked_data_proof.py index 979b28b260..1c5c5cd63a 100644 --- a/aries_cloudagent/vc/ld_proofs/suites/LinkedDataProof.py +++ b/aries_cloudagent/vc/ld_proofs/suites/linked_data_proof.py @@ -2,21 +2,18 @@ from abc import ABC -from pyld import jsonld -from typing import List, TYPE_CHECKING, Union +from typing import List, Union +from pyld import jsonld from typing_extensions import TypedDict from ..check import get_properties_without_context from ..constants import SECURITY_CONTEXT_URL -from ..error import LinkedDataProofException from ..document_loader import DocumentLoaderMethod +from ..error import LinkedDataProofException +from ..purposes import ProofPurpose from ..validation_result import ProofResult -# ProofPurpose and LinkedDataProof depend on each other -if TYPE_CHECKING: - from ..purposes.ProofPurpose import ProofPurpose - class DeriveProofResult(TypedDict): """Result dict for deriving a proof.""" @@ -44,7 +41,7 @@ async def create_proof( self, *, document: dict, - purpose: "ProofPurpose", + purpose: ProofPurpose, document_loader: DocumentLoaderMethod, ) -> dict: """Create proof for document. @@ -67,7 +64,7 @@ async def verify_proof( *, proof: dict, document: dict, - purpose: "ProofPurpose", + purpose: ProofPurpose, document_loader: DocumentLoaderMethod, ) -> ProofResult: """Verify proof against document and proof purpose. diff --git a/aries_cloudagent/vc/ld_proofs/suites/LinkedDataSignature.py b/aries_cloudagent/vc/ld_proofs/suites/linked_data_signature.py similarity index 96% rename from aries_cloudagent/vc/ld_proofs/suites/LinkedDataSignature.py rename to aries_cloudagent/vc/ld_proofs/suites/linked_data_signature.py index 6d29daf945..f6764d5bc7 100644 --- a/aries_cloudagent/vc/ld_proofs/suites/LinkedDataSignature.py +++ b/aries_cloudagent/vc/ld_proofs/suites/linked_data_signature.py @@ -1,16 +1,18 @@ """Linked Data Signature class.""" -from datetime import datetime +from abc import abstractmethod, ABCMeta +from datetime import datetime, timezone from hashlib import sha256 +from pytz import utc from typing import Union -from abc import abstractmethod, ABCMeta -from ..error import LinkedDataProofException -from ..validation_result import ProofResult +from ..constants import SECURITY_CONTEXT_URL from ..document_loader import DocumentLoaderMethod +from ..error import LinkedDataProofException from ..purposes import ProofPurpose -from ..constants import SECURITY_CONTEXT_URL -from .LinkedDataProof import LinkedDataProof +from ..validation_result import ProofResult + +from .linked_data_proof import LinkedDataProof class LinkedDataSignature(LinkedDataProof, metaclass=ABCMeta): @@ -96,7 +98,9 @@ async def create_proof( # Set created if not already set if not proof.get("created"): # Use class date, or now - date = self.date or datetime.now() + date = self.date or datetime.now(timezone.utc) + if not date.tzinfo: + date = utc.localize(date) proof["created"] = date.isoformat() # Allow purpose to update the proof; the `proof` is in the diff --git a/aries_cloudagent/vc/ld_proofs/suites/tests/test_BbsBlsSignature2020.py b/aries_cloudagent/vc/ld_proofs/suites/tests/test_bbs_bls_signature_2020.py similarity index 96% rename from aries_cloudagent/vc/ld_proofs/suites/tests/test_BbsBlsSignature2020.py rename to aries_cloudagent/vc/ld_proofs/suites/tests/test_bbs_bls_signature_2020.py index 484a028180..f8bfdf6533 100644 --- a/aries_cloudagent/vc/ld_proofs/suites/tests/test_BbsBlsSignature2020.py +++ b/aries_cloudagent/vc/ld_proofs/suites/tests/test_bbs_bls_signature_2020.py @@ -1,7 +1,6 @@ from asynctest import TestCase, mock as async_mock import pytest - from .....did.did_key import DIDKey from .....wallet.key_pair import KeyType from .....wallet.in_memory import InMemoryWallet @@ -14,12 +13,13 @@ TEST_VC_DOCUMENT, TEST_VC_DOCUMENT_SIGNED_BBS, ) -from ...error import LinkedDataProofException -from ...crypto.WalletKeyPair import WalletKeyPair -from ...purposes.AssertionProofPurpose import AssertionProofPurpose +from ...error import LinkedDataProofException +from ...crypto.wallet_key_pair import WalletKeyPair +from ...purposes.assertion_proof_purpose import AssertionProofPurpose from ...ld_proofs import sign, verify -from ..BbsBlsSignature2020 import BbsBlsSignature2020 + +from ..bbs_bls_signature_2020 import BbsBlsSignature2020 @pytest.mark.ursa_bbs_signatures diff --git a/aries_cloudagent/vc/ld_proofs/suites/tests/test_BbsBlsSignatureProof2020.py b/aries_cloudagent/vc/ld_proofs/suites/tests/test_bbs_bls_signature_proof_2020.py similarity index 97% rename from aries_cloudagent/vc/ld_proofs/suites/tests/test_BbsBlsSignatureProof2020.py rename to aries_cloudagent/vc/ld_proofs/suites/tests/test_bbs_bls_signature_proof_2020.py index b72d0daeec..e8a79e2297 100644 --- a/aries_cloudagent/vc/ld_proofs/suites/tests/test_BbsBlsSignatureProof2020.py +++ b/aries_cloudagent/vc/ld_proofs/suites/tests/test_bbs_bls_signature_proof_2020.py @@ -21,13 +21,14 @@ TEST_VC_DOCUMENT_PARTIAL_PROOF_BBS, TEST_VC_DOCUMENT_NESTED_REVEAL, ) -from ...crypto.WalletKeyPair import WalletKeyPair -from ...purposes.AssertionProofPurpose import AssertionProofPurpose +from ....vc_ld import derive_credential, verify_credential +from ...crypto.wallet_key_pair import WalletKeyPair +from ...purposes.assertion_proof_purpose import AssertionProofPurpose from ...error import LinkedDataProofException from ...ld_proofs import verify, derive -from ....vc_ld import derive_credential, verify_credential -from ..BbsBlsSignatureProof2020 import BbsBlsSignatureProof2020 + +from ..bbs_bls_signature_proof_2020 import BbsBlsSignatureProof2020 @pytest.mark.ursa_bbs_signatures diff --git a/aries_cloudagent/vc/ld_proofs/suites/tests/test_Ed25519Signature2018.py b/aries_cloudagent/vc/ld_proofs/suites/tests/test_ed25519_signature_2018.py similarity index 95% rename from aries_cloudagent/vc/ld_proofs/suites/tests/test_Ed25519Signature2018.py rename to aries_cloudagent/vc/ld_proofs/suites/tests/test_ed25519_signature_2018.py index 7999a07da6..613aec46ab 100644 --- a/aries_cloudagent/vc/ld_proofs/suites/tests/test_Ed25519Signature2018.py +++ b/aries_cloudagent/vc/ld_proofs/suites/tests/test_ed25519_signature_2018.py @@ -5,6 +5,7 @@ from .....wallet.key_pair import KeyType from .....wallet.in_memory import InMemoryWallet from .....core.in_memory import InMemoryProfile + from ....tests.document_loader import custom_document_loader from ....tests.data import ( TEST_LD_DOCUMENT, @@ -13,11 +14,12 @@ TEST_VC_DOCUMENT, TEST_VC_DOCUMENT_SIGNED_ED25519, ) -from ...crypto.WalletKeyPair import WalletKeyPair -from ...purposes.AssertionProofPurpose import AssertionProofPurpose +from ...crypto.wallet_key_pair import WalletKeyPair +from ...purposes.assertion_proof_purpose import AssertionProofPurpose from ...ld_proofs import sign, verify -from ..Ed25519Signature2018 import Ed25519Signature2018 + +from ..ed25519_signature_2018 import Ed25519Signature2018 class TestEd25519Signature2018(TestCase): diff --git a/aries_cloudagent/vc/ld_proofs/tests/test_doc.py b/aries_cloudagent/vc/ld_proofs/tests/test_doc.py index eef13356d3..1681173830 100644 --- a/aries_cloudagent/vc/ld_proofs/tests/test_doc.py +++ b/aries_cloudagent/vc/ld_proofs/tests/test_doc.py @@ -30,10 +30,10 @@ "image": "https://manu.sporny.org/images/manu.png", "proof": { "proofPurpose": "assertionMethod", - "created": "2019-12-11T03:50:55", + "created": "2019-12-11T03:50:55+00:00", "type": "Ed25519Signature2018", "verificationMethod": "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", - "jws": "eyJhbGciOiAiRWREU0EiLCAiYjY0IjogZmFsc2UsICJjcml0IjogWyJiNjQiXX0..Q6amIrxGiSbM7Ce6DxlfwLCjVcYyclas8fMxaecspXFUcFW9DAAxKzgHx93FWktnlZjM_biitkMgZdStgvivAQ", + "jws": "eyJhbGciOiAiRWREU0EiLCAiYjY0IjogZmFsc2UsICJjcml0IjogWyJiNjQiXX0..FX3xSAN3BxpHnclqtiCKsHa3f6O1pi_fulEoCNs2YQplYBU7lYSdnIm1BoPo_YCw8AS25pOQo1ufW05mXJlxAw", }, } @@ -131,10 +131,10 @@ "image": "https://manu.sporny.org/images/manu.png", "proof": { "proofPurpose": "assertionMethod", - "created": "2019-12-11T03:50:55", + "created": "2019-12-11T03:50:55+00:00", "type": "Ed25519Signature2018", "verificationMethod": "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", - "jws": "eyJhbGciOiAiRWREU0EiLCAiYjY0IjogZmFsc2UsICJjcml0IjogWyJiNjQiXX0..Q6amIrxGiSbM7Ce6DxlfwLCjVcYyclas8fMxaecspXFUcFW9DAAxKzgHx93FWktnlZjM_biitkMgZdStgvivAQ", + "jws": "eyJhbGciOiAiRWREU0EiLCAiYjY0IjogZmFsc2UsICJjcml0IjogWyJiNjQiXX0..FX3xSAN3BxpHnclqtiCKsHa3f6O1pi_fulEoCNs2YQplYBU7lYSdnIm1BoPo_YCw8AS25pOQo1ufW05mXJlxAw", }, }, results=[ @@ -151,10 +151,10 @@ }, ], "proofPurpose": "assertionMethod", - "created": "2019-12-11T03:50:55", + "created": "2019-12-11T03:50:55+00:00", "type": "Ed25519Signature2018", "verificationMethod": "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", - "jws": "eyJhbGciOiAiRWREU0EiLCAiYjY0IjogZmFsc2UsICJjcml0IjogWyJiNjQiXX0..Q6amIrxGiSbM7Ce6DxlfwLCjVcYyclas8fMxaecspXFUcFW9DAAxKzgHx93FWktnlZjM_biitkMgZdStgvivAQ", + "jws": "eyJhbGciOiAiRWREU0EiLCAiYjY0IjogZmFsc2UsICJjcml0IjogWyJiNjQiXX0..FX3xSAN3BxpHnclqtiCKsHa3f6O1pi_fulEoCNs2YQplYBU7lYSdnIm1BoPo_YCw8AS25pOQo1ufW05mXJlxAw", }, purpose_result=PurposeResult( valid=True, 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 cf9e2b21ef..15d9ea5e90 100644 --- a/aries_cloudagent/vc/ld_proofs/tests/test_ld_proofs.py +++ b/aries_cloudagent/vc/ld_proofs/tests/test_ld_proofs.py @@ -1,6 +1,6 @@ import pytest -from datetime import datetime +from datetime import datetime, timezone from asynctest import TestCase @@ -65,7 +65,7 @@ async def test_sign_Ed25519Signature2018(self): key_type=KeyType.ED25519, public_key_base58=self.ed25519_key_info.verkey, ), - date=datetime.strptime("2019-12-11T03:50:55Z", "%Y-%m-%dT%H:%M:%SZ"), + date=datetime(2019, 12, 11, 3, 50, 55, 0, timezone.utc), ) signed = await sign( document=DOC_TEMPLATE, @@ -103,7 +103,7 @@ async def test_sign_BbsBlsSignature2020(self): key_type=KeyType.BLS12381G2, public_key_base58=self.bls12381g2_key_info.verkey, ), - date=datetime.strptime("2019-12-11T03:50:55Z", "%Y-%m-%dT%H:%M:%SZ"), + date=datetime(2019, 12, 11, 3, 50, 55, 0), ) signed = await sign( document=DOC_TEMPLATE_BBS, diff --git a/aries_cloudagent/vc/tests/contexts/__init__.py b/aries_cloudagent/vc/tests/contexts/__init__.py index 55fa0437d8..1009e27b06 100644 --- a/aries_cloudagent/vc/tests/contexts/__init__.py +++ b/aries_cloudagent/vc/tests/contexts/__init__.py @@ -12,15 +12,15 @@ __all__ = [ - DID_V1, - SECURITY_V1, - SECURITY_V2, - SECURITY_V3_UNSTABLE, - BBS_V1, - CREDENTIALS_V1, - CITIZENSHIP_V1, - VACCINATION_V1, - EXAMPLES_V1, - ODRL, - SCHEMA_ORG, + "DID_V1", + "SECURITY_V1", + "SECURITY_V2", + "SECURITY_V3_UNSTABLE", + "BBS_V1", + "CREDENTIALS_V1", + "CITIZENSHIP_V1", + "VACCINATION_V1", + "EXAMPLES_V1", + "ODRL", + "SCHEMA_ORG", ] diff --git a/aries_cloudagent/vc/tests/data/__init__.py b/aries_cloudagent/vc/tests/data/__init__.py index dede5caabd..34abdb19ab 100644 --- a/aries_cloudagent/vc/tests/data/__init__.py +++ b/aries_cloudagent/vc/tests/data/__init__.py @@ -44,37 +44,37 @@ from .test_vc_document_nested_reveal import TEST_VC_DOCUMENT_NESTED_REVEAL __all__ = [ - BBS_NESTED_VC_FULL_REVEAL_DOCUMENT_MATTR, - BBS_NESTED_VC_MATTR, - BBS_NESTED_VC_REVEAL_DOCUMENT_MATTR, - BBS_PARTIAL_PROOF_NESTED_VC_MATTR, - BBS_PARTIAL_PROOF_VC_MATTR, - BBS_PROOF_VC_MATTR, - BBS_PROOF_NESTED_VC_MATTR, - BBS_SIGNED_VC_MATTR, - BBS_SIGNED_NESTED_VC_MATTR, - BBS_VC_MATTR, - BBS_VC_REVEAL_DOCUMENT_MATTR, - TEST_LD_DOCUMENT_BAD_SIGNED_BBS, - TEST_LD_DOCUMENT_SIGNED_BBS, - TEST_LD_DOCUMENT_BAD_SIGNED_ED25519, - TEST_LD_DOCUMENT_SIGNED_ED25519, - TEST_LD_DOCUMENT, - TEST_LD_DOCUMENT_REVEAL, - TEST_LD_DOCUMENT_REVEAL_ALL, - TEST_LD_DOCUMENT_PROOF_BBS, - TEST_LD_DOCUMENT_PARTIAL_PROOF_BBS, - TEST_LD_DOCUMENT_BAD_PARTIAL_PROOF_BBS, - TEST_VC_DOCUMENT_NESTED_PARTIAL_PROOF_BBS, - TEST_VC_DOCUMENT_NESTED_PROOF_BBS, - TEST_VC_DOCUMENT_NESTED_SIGNED_BBS, - TEST_VC_DOCUMENT_NESTED, - TEST_VC_DOCUMENT_PARTIAL_PROOF_BBS, - TEST_VC_DOCUMENT_REVEAL, - TEST_VC_DOCUMENT_SIGNED_BBS, - TEST_VC_DOCUMENT_SIGNED_ED25519, - TEST_VC_DOCUMENT, - TEST_VC_DOCUMENT_SIGNED_DID_KEY_ED25519, - TEST_VC_DOCUMENT_DID_KEY_ED25519, - TEST_VC_DOCUMENT_NESTED_REVEAL, + "BBS_NESTED_VC_FULL_REVEAL_DOCUMENT_MATTR", + "BBS_NESTED_VC_MATTR", + "BBS_NESTED_VC_REVEAL_DOCUMENT_MATTR", + "BBS_PARTIAL_PROOF_NESTED_VC_MATTR", + "BBS_PARTIAL_PROOF_VC_MATTR", + "BBS_PROOF_VC_MATTR", + "BBS_PROOF_NESTED_VC_MATTR", + "BBS_SIGNED_VC_MATTR", + "BBS_SIGNED_NESTED_VC_MATTR", + "BBS_VC_MATTR", + "BBS_VC_REVEAL_DOCUMENT_MATTR", + "TEST_LD_DOCUMENT_BAD_SIGNED_BBS", + "TEST_LD_DOCUMENT_SIGNED_BBS", + "TEST_LD_DOCUMENT_BAD_SIGNED_ED25519", + "TEST_LD_DOCUMENT_SIGNED_ED25519", + "TEST_LD_DOCUMENT", + "TEST_LD_DOCUMENT_REVEAL", + "TEST_LD_DOCUMENT_REVEAL_ALL", + "TEST_LD_DOCUMENT_PROOF_BBS", + "TEST_LD_DOCUMENT_PARTIAL_PROOF_BBS", + "TEST_LD_DOCUMENT_BAD_PARTIAL_PROOF_BBS", + "TEST_VC_DOCUMENT_NESTED_PARTIAL_PROOF_BBS", + "TEST_VC_DOCUMENT_NESTED_PROOF_BBS", + "TEST_VC_DOCUMENT_NESTED_SIGNED_BBS", + "TEST_VC_DOCUMENT_NESTED", + "TEST_VC_DOCUMENT_PARTIAL_PROOF_BBS", + "TEST_VC_DOCUMENT_REVEAL", + "TEST_VC_DOCUMENT_SIGNED_BBS", + "TEST_VC_DOCUMENT_SIGNED_ED25519", + "TEST_VC_DOCUMENT", + "TEST_VC_DOCUMENT_SIGNED_DID_KEY_ED25519", + "TEST_VC_DOCUMENT_DID_KEY_ED25519", + "TEST_VC_DOCUMENT_NESTED_REVEAL", ] diff --git a/aries_cloudagent/vc/tests/dids/__init__.py b/aries_cloudagent/vc/tests/dids/__init__.py index 3a34cad782..07ed6649c0 100644 --- a/aries_cloudagent/vc/tests/dids/__init__.py +++ b/aries_cloudagent/vc/tests/dids/__init__.py @@ -8,8 +8,8 @@ from .did_sov_QqEfJxe752NCmWqR5TssZ5 import DID_SOV_QqEfJxe752NCmWqR5TssZ5 __all__ = [ - DID_z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL, - DID_zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa, - DID_EXAMPLE_48939859, - DID_SOV_QqEfJxe752NCmWqR5TssZ5, + "DID_z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", + "DID_zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa", + "DID_EXAMPLE_48939859", + "DID_SOV_QqEfJxe752NCmWqR5TssZ5", ] diff --git a/aries_cloudagent/vc/vc_ld/__init__.py b/aries_cloudagent/vc/vc_ld/__init__.py index 3aedd593be..e16eb34255 100644 --- a/aries_cloudagent/vc/vc_ld/__init__.py +++ b/aries_cloudagent/vc/vc_ld/__init__.py @@ -11,16 +11,16 @@ ) __all__ = [ - issue, - verify_presentation, - verify_credential, - create_presentation, - sign_presentation, - derive_credential, - PresentationVerificationResult, - VerifiableCredential, - LDProof, - LinkedDataProofSchema, - CredentialSchema, - VerifiableCredentialSchema, + "issue", + "verify_presentation", + "verify_credential", + "create_presentation", + "sign_presentation", + "derive_credential", + "PresentationVerificationResult", + "VerifiableCredential", + "LDProof", + "LinkedDataProofSchema", + "CredentialSchema", + "VerifiableCredentialSchema", ] diff --git a/aries_cloudagent/vc/vc_ld/models/__init__.py b/aries_cloudagent/vc/vc_ld/models/__init__.py index 615b3c9e1e..33b66aab88 100644 --- a/aries_cloudagent/vc/vc_ld/models/__init__.py +++ b/aries_cloudagent/vc/vc_ld/models/__init__.py @@ -9,9 +9,9 @@ ) __all__ = [ - VerifiableCredential, - CredentialSchema, - VerifiableCredentialSchema, - LDProof, - LinkedDataProofSchema, + "VerifiableCredential", + "CredentialSchema", + "VerifiableCredentialSchema", + "LDProof", + "LinkedDataProofSchema", ] diff --git a/aries_cloudagent/vc/vc_ld/models/credential.py b/aries_cloudagent/vc/vc_ld/models/credential.py index 99d0b0d834..232d17a3a5 100644 --- a/aries_cloudagent/vc/vc_ld/models/credential.py +++ b/aries_cloudagent/vc/vc_ld/models/credential.py @@ -1,6 +1,7 @@ """Verifiable Credential marshmallow schema classes.""" from datetime import datetime +from pytz import utc from typing import List, Optional, Union from marshmallow import INCLUDE, fields, post_dump, ValidationError @@ -168,6 +169,8 @@ def issuance_date(self): def issuance_date(self, date: Union[str, datetime]): """Setter for issuance date.""" if isinstance(date, datetime): + if not date.tzinfo: + date = utc.localize(date) date = date.isoformat() self._issuance_date = date @@ -181,6 +184,8 @@ def expiration_date(self): def expiration_date(self, date: Union[str, datetime, None]): """Setter for expiration date.""" if isinstance(date, datetime): + if not date.tzinfo: + date = utc.localize(date) date = date.isoformat() self._expiration_date = date diff --git a/aries_cloudagent/vc/vc_ld/models/tests/test_credential.py b/aries_cloudagent/vc/vc_ld/models/tests/test_credential.py index c37119597f..c0eff0d7ba 100644 --- a/aries_cloudagent/vc/vc_ld/models/tests/test_credential.py +++ b/aries_cloudagent/vc/vc_ld/models/tests/test_credential.py @@ -1,4 +1,4 @@ -from datetime import datetime +from datetime import datetime, timezone from unittest import TestCase from marshmallow.utils import INCLUDE @@ -149,17 +149,29 @@ def test_properties(self): with self.assertRaises(Exception): credential.issuer = {"not-id": "not-id"} - date = datetime.now() + date = datetime.now(timezone.utc) credential.issuance_date = date assert credential.issuance_date == date.isoformat() credential.issuance_date = date.isoformat() assert credential.issuance_date == date.isoformat() + date = datetime(2019, 12, 11, 3, 50, 55, 0) + credential.issuance_date = date + assert ( + credential.issuance_date + == datetime(2019, 12, 11, 3, 50, 55, 0, timezone.utc).isoformat() + ) - date = datetime.now() + date = datetime.now(timezone.utc) credential.expiration_date = date assert credential.expiration_date == date.isoformat() credential.expiration_date = date.isoformat() assert credential.expiration_date == date.isoformat() + date = datetime(2019, 12, 11, 3, 50, 55, 0) + credential.expiration_date = date + assert ( + credential.expiration_date + == datetime(2019, 12, 11, 3, 50, 55, 0, timezone.utc).isoformat() + ) assert not credential.credential_subject assert not credential.credential_subject_ids diff --git a/aries_cloudagent/vc/vc_ld/tests/test_credential.py b/aries_cloudagent/vc/vc_ld/tests/test_credential.py index d2b6c17a1c..17e35dd76b 100644 --- a/aries_cloudagent/vc/vc_ld/tests/test_credential.py +++ b/aries_cloudagent/vc/vc_ld/tests/test_credential.py @@ -37,9 +37,9 @@ "proof": { "type": "Ed25519Signature2018", "verificationMethod": "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", - "created": "2019-12-11T03:50:55", + "created": "2019-12-11T03:50:55+00:00", "proofPurpose": "assertionMethod", - "jws": "eyJhbGciOiAiRWREU0EiLCAiYjY0IjogZmFsc2UsICJjcml0IjogWyJiNjQiXX0..lKJU0Df_keblRKhZAS9Qq6zybm-HqUXNVZ8vgEPNTAjQKBhQDxvXNo7nvtUBb_Eq1Ch6YBKY7UBAjg6iBX5qBQ", + "jws": "eyJhbGciOiAiRWREU0EiLCAiYjY0IjogZmFsc2UsICJjcml0IjogWyJiNjQiXX0..tELgWSCo0jSOKLpHWLOe2ZWj6WxLFc6FcqBZzgQFdcaawC5mARYmdr0XA37TMUp2Q9jUwriNP4gQm3GFIHV9BQ", }, } @@ -80,9 +80,9 @@ "proof": { "type": "BbsBlsSignature2020", "verificationMethod": "did:key:zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa#zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa", - "created": "2019-12-11T03:50:55", + "created": "2019-12-11T03:50:55+00:00", "proofPurpose": "assertionMethod", - "proofValue": "saCdwWlzoYB0Ayo7xthCGK462T4x95lkI7yINra+r/DRY8PH4udviBebYIMA0pHkFX/nW+ilcdipr8jdN+WbHElg2wIrpWEqdvbT/vrjTWM2iXS7MmsMvpQbLfJVohDCBrm4b6BuE6QYO4Va6tYsKw==", + "proofValue": "ke/4UsKMbYyFQP2WAsQs0QGJ0PCfvN3tWMoD8oBjroY3mrgXA7FmUqVy0HaZIoKvFHl1FnE1jqh0OnqErooFBMTu0a4qe3klziRSuRuLdUhgbgzOJeJiWPlrs6Y5/D5d0CRp8ifPj7xZBIy3Iuoz4g==", }, } @@ -107,9 +107,9 @@ "proof": { "type": "Ed25519Signature2018", "verificationMethod": "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", - "created": "2019-12-11T03:50:55", + "created": "2019-12-11T03:50:55+00:00", "proofPurpose": "assertionMethod", - "jws": "eyJhbGciOiAiRWREU0EiLCAiYjY0IjogZmFsc2UsICJjcml0IjogWyJiNjQiXX0..lKJU0Df_keblRKhZAS9Qq6zybm-HqUXNVZ8vgEPNTAjQKBhQDxvXNo7nvtUBb_Eq1Ch6YBKY7UBAjg6iBX5qBQ", + "jws": "eyJhbGciOiAiRWREU0EiLCAiYjY0IjogZmFsc2UsICJjcml0IjogWyJiNjQiXX0..tELgWSCo0jSOKLpHWLOe2ZWj6WxLFc6FcqBZzgQFdcaawC5mARYmdr0XA37TMUp2Q9jUwriNP4gQm3GFIHV9BQ", }, }, results=[ @@ -122,9 +122,9 @@ ], "type": "Ed25519Signature2018", "verificationMethod": "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", - "created": "2019-12-11T03:50:55", + "created": "2019-12-11T03:50:55+00:00", "proofPurpose": "assertionMethod", - "jws": "eyJhbGciOiAiRWREU0EiLCAiYjY0IjogZmFsc2UsICJjcml0IjogWyJiNjQiXX0..lKJU0Df_keblRKhZAS9Qq6zybm-HqUXNVZ8vgEPNTAjQKBhQDxvXNo7nvtUBb_Eq1Ch6YBKY7UBAjg6iBX5qBQ", + "jws": "eyJhbGciOiAiRWREU0EiLCAiYjY0IjogZmFsc2UsICJjcml0IjogWyJiNjQiXX0..tELgWSCo0jSOKLpHWLOe2ZWj6WxLFc6FcqBZzgQFdcaawC5mARYmdr0XA37TMUp2Q9jUwriNP4gQm3GFIHV9BQ", }, purpose_result=PurposeResult( valid=True, @@ -188,9 +188,9 @@ "proof": { "type": "Ed25519Signature2018", "verificationMethod": "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", - "created": "2019-12-11T03:50:55", + "created": "2019-12-11T03:50:55+00:00", "proofPurpose": "assertionMethod", - "jws": "eyJhbGciOiAiRWREU0EiLCAiYjY0IjogZmFsc2UsICJjcml0IjogWyJiNjQiXX0..lKJU0Df_keblRKhZAS9Qq6zybm-HqUXNVZ8vgEPNTAjQKBhQDxvXNo7nvtUBb_Eq1Ch6YBKY7UBAjg6iBX5qBQ", + "jws": "eyJhbGciOiAiRWREU0EiLCAiYjY0IjogZmFsc2UsICJjcml0IjogWyJiNjQiXX0..tELgWSCo0jSOKLpHWLOe2ZWj6WxLFc6FcqBZzgQFdcaawC5mARYmdr0XA37TMUp2Q9jUwriNP4gQm3GFIHV9BQ", }, } ], @@ -221,18 +221,18 @@ "proof": { "type": "Ed25519Signature2018", "verificationMethod": "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", - "created": "2019-12-11T03:50:55", + "created": "2019-12-11T03:50:55+00:00", "proofPurpose": "assertionMethod", - "jws": "eyJhbGciOiAiRWREU0EiLCAiYjY0IjogZmFsc2UsICJjcml0IjogWyJiNjQiXX0..lKJU0Df_keblRKhZAS9Qq6zybm-HqUXNVZ8vgEPNTAjQKBhQDxvXNo7nvtUBb_Eq1Ch6YBKY7UBAjg6iBX5qBQ", + "jws": "eyJhbGciOiAiRWREU0EiLCAiYjY0IjogZmFsc2UsICJjcml0IjogWyJiNjQiXX0..tELgWSCo0jSOKLpHWLOe2ZWj6WxLFc6FcqBZzgQFdcaawC5mARYmdr0XA37TMUp2Q9jUwriNP4gQm3GFIHV9BQ", }, } ], "proof": { "type": "Ed25519Signature2018", "verificationMethod": "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", - "created": "2020-12-11T03:50:55", + "created": "2020-12-11T03:50:55+00:00", "proofPurpose": "authentication", "challenge": "2b1bbff6-e608-4368-bf84-67471b27e41c", - "jws": "eyJhbGciOiAiRWREU0EiLCAiYjY0IjogZmFsc2UsICJjcml0IjogWyJiNjQiXX0..IOjNkdpk4lF8uU8N7n0OMc3opqU1wtCTu3KbJcdDIKvSt6QLEy-ofRDVgN2xo-21yxzx36mXVjiilWdB6A-dDg", + "jws": "eyJhbGciOiAiRWREU0EiLCAiYjY0IjogZmFsc2UsICJjcml0IjogWyJiNjQiXX0..vLk6iXrPxdt4xWq_Lwnd85cy17Npol1ALxYoMhZrKpmkaWDOoz_exP1ggurPik7Rhpa7a82AfrT0OnigJnJsAQ", }, } diff --git a/aries_cloudagent/vc/vc_ld/tests/test_validation_result.py b/aries_cloudagent/vc/vc_ld/tests/test_validation_result.py index d9e69269f4..26f027d4a6 100644 --- a/aries_cloudagent/vc/vc_ld/tests/test_validation_result.py +++ b/aries_cloudagent/vc/vc_ld/tests/test_validation_result.py @@ -1,7 +1,4 @@ from asynctest import TestCase -from datetime import datetime - -import pytest from ..validation_result import PresentationVerificationResult diff --git a/aries_cloudagent/vc/vc_ld/verify.py b/aries_cloudagent/vc/vc_ld/verify.py index 3c98205205..d589dd23c5 100644 --- a/aries_cloudagent/vc/vc_ld/verify.py +++ b/aries_cloudagent/vc/vc_ld/verify.py @@ -183,4 +183,4 @@ async def verify_presentation( return PresentationVerificationResult(verified=False, errors=[e]) -__all__ = [verify_presentation, verify_credential] +__all__ = ["verify_presentation", "verify_credential"] diff --git a/aries_cloudagent/version.py b/aries_cloudagent/version.py index d6d16e16b1..b264670880 100644 --- a/aries_cloudagent/version.py +++ b/aries_cloudagent/version.py @@ -1,3 +1,3 @@ """Library version information.""" -__version__ = "0.7.0" +__version__ = "0.7.1" diff --git a/aries_cloudagent/wallet/routes.py b/aries_cloudagent/wallet/routes.py index 903d07c905..e1d5c318e7 100644 --- a/aries_cloudagent/wallet/routes.py +++ b/aries_cloudagent/wallet/routes.py @@ -23,13 +23,14 @@ ENDPOINT_TYPE, INDY_RAW_PUBLIC_KEY, ) -from ..multitenant.manager import MultitenantManager -from .key_type import KeyType -from .did_method import DIDMethod +from ..multitenant.base import BaseMultitenantManager + from .base import BaseWallet from .did_info import DIDInfo from .did_posture import DIDPosture +from .did_method import DIDMethod from .error import WalletError, WalletNotFoundError +from .key_type import KeyType class WalletModuleResponseSchema(OpenAPISchema): @@ -198,7 +199,7 @@ async def wallet_did_list(request: web.BaseRequest): """ context: AdminRequestContext = request["context"] session = await context.session() - wallet = session.inject(BaseWallet, required=False) + wallet = session.inject_or(BaseWallet) if not wallet: raise web.HTTPForbidden(reason="No wallet available") filter_did = request.query.get("did") @@ -323,7 +324,7 @@ async def wallet_create_did(request: web.BaseRequest): ) session = await context.session() - wallet = session.inject(BaseWallet, required=False) + wallet = session.inject_or(BaseWallet) if not wallet: raise web.HTTPForbidden(reason="No wallet available") try: @@ -350,7 +351,7 @@ async def wallet_get_public_did(request: web.BaseRequest): """ context: AdminRequestContext = request["context"] session = await context.session() - wallet = session.inject(BaseWallet, required=False) + wallet = session.inject_or(BaseWallet) if not wallet: raise web.HTTPForbidden(reason="No wallet available") try: @@ -377,7 +378,7 @@ async def wallet_set_public_did(request: web.BaseRequest): """ context: AdminRequestContext = request["context"] session = await context.session() - wallet = session.inject(BaseWallet, required=False) + wallet = session.inject_or(BaseWallet) if not wallet: raise web.HTTPForbidden(reason="No wallet available") did = request.query.get("did") @@ -385,11 +386,11 @@ async def wallet_set_public_did(request: web.BaseRequest): raise web.HTTPBadRequest(reason="Request query must include DID") # Multitenancy setup - multitenant_mgr = session.inject(MultitenantManager, required=False) + multitenant_mgr = session.inject_or(BaseMultitenantManager) wallet_id = session.settings.get("wallet.id") try: - ledger = session.inject(BaseLedger, required=False) + ledger = session.inject_or(BaseLedger) if not ledger: reason = "No ledger available" if not session.settings.get_value("wallet.type"): @@ -441,7 +442,7 @@ async def wallet_set_did_endpoint(request: web.BaseRequest): """ context: AdminRequestContext = request["context"] session = await context.session() - wallet = session.inject(BaseWallet, required=False) + wallet = session.inject_or(BaseWallet) if not wallet: raise web.HTTPForbidden(reason="No wallet available") @@ -453,7 +454,7 @@ async def wallet_set_did_endpoint(request: web.BaseRequest): ) try: - ledger = session.inject(BaseLedger, required=False) + ledger = session.inject_or(BaseLedger) await wallet.set_did_endpoint(did, endpoint, ledger, endpoint_type) except WalletNotFoundError as err: raise web.HTTPNotFound(reason=err.roll_up) from err @@ -481,7 +482,7 @@ async def wallet_get_did_endpoint(request: web.BaseRequest): """ context: AdminRequestContext = request["context"] session = await context.session() - wallet = session.inject(BaseWallet, required=False) + wallet = session.inject_or(BaseWallet) if not wallet: raise web.HTTPForbidden(reason="No wallet available") did = request.query.get("did") @@ -514,7 +515,7 @@ async def wallet_rotate_did_keypair(request: web.BaseRequest): """ context: AdminRequestContext = request["context"] session = await context.session() - wallet = session.inject(BaseWallet, required=False) + wallet = session.inject_or(BaseWallet) if not wallet: raise web.HTTPForbidden(reason="No wallet available") did = request.query.get("did") diff --git a/aries_cloudagent/wallet/tests/test_routes.py b/aries_cloudagent/wallet/tests/test_routes.py index 5da9561b39..c534cf03bf 100644 --- a/aries_cloudagent/wallet/tests/test_routes.py +++ b/aries_cloudagent/wallet/tests/test_routes.py @@ -3,9 +3,11 @@ from ...admin.request_context import AdminRequestContext from ...ledger.base import BaseLedger +from ...multitenant.base import BaseMultitenantManager from ...multitenant.manager import MultitenantManager from ...wallet.key_type import KeyType from ...wallet.did_method import DIDMethod + from .. import routes as test_module from ..base import BaseWallet from ..did_info import DIDInfo @@ -409,7 +411,7 @@ async def test_set_public_did_multitenant(self): self.session_inject[BaseLedger] = ledger multitenant_mgr = async_mock.MagicMock(MultitenantManager, autospec=True) - self.session_inject[MultitenantManager] = multitenant_mgr + self.session_inject[BaseMultitenantManager] = multitenant_mgr with async_mock.patch.object( test_module.web, "json_response", async_mock.Mock() diff --git a/demo/AliceGetsAPhone.md b/demo/AliceGetsAPhone.md index 1f4fa15108..4f0b63d710 100644 --- a/demo/AliceGetsAPhone.md +++ b/demo/AliceGetsAPhone.md @@ -19,6 +19,7 @@ This demo also introduces revocation of credentials. - [Present the Proof](#present-the-proof) - [Review the Proof](#review-the-proof) - [Revoke the Credential and Send Another Proof Request](#revoke-the-credential-and-send-another-proof-request) +- [Send a Connectionless Proof Request](#send-a-connectionless-proof-request) - [Conclusion](#conclusion) ## Getting Started @@ -301,6 +302,18 @@ Once that is done, try sending another proof request and see what happens! Exper Revocation +## Send a Connectionless Proof Request + +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.) + +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). + +Then in the faber demo, select option `2a` - Faber will display a QR code which you can scan with your mobile agent. You will see the same proof request displayed in your mobile agent, which you can respond to. + +Behind the scenes, the Faber controller delivers the proof request information (linked from the url encoded in the QR code) directly to your mobile agent, without establishing and agent-to-agent connection first. If you are interested in the underlying mechanics, you can review the `faber.py` code in the repository. + ## Conclusion That’s the Faber-Mobile Alice demo. Feel free to play with the Swagger API and experiment further and figure out what an instance of a controller has to do to make things work. diff --git a/demo/AriesOpenAPIDemo.md b/demo/AriesOpenAPIDemo.md index f42a1597c1..8edfb71dff 100644 --- a/demo/AriesOpenAPIDemo.md +++ b/demo/AriesOpenAPIDemo.md @@ -502,8 +502,8 @@ Finally, we need put into the JSON the data values for the `credential_preview` "value": "Maths" }, { - "name": "age", - "value": "24" + "name": "birthdate_dateint", + "value": "19640101" } ``` @@ -640,9 +640,9 @@ From the Faber browser tab, get ready to execute the **`POST /present-proof/send }, "requested_predicates": { "0_age_GE_uuid": { - "name": "age", - "p_type": ">=", - "p_value": 18, + "name": "birthdate_dateint", + "p_type": "<=", + "p_value": 20030101, "restrictions": [ { "cred_def_id": "SsX9siFWXJyCAmXnHY514N:3:CL:8:faber.agent.degree_schema" diff --git a/demo/README.md b/demo/README.md index a561633cdb..ae8a32ad69 100644 --- a/demo/README.md +++ b/demo/README.md @@ -176,6 +176,8 @@ When ready to test the credentials exchange protocols, go to the Faber prompt, e You don't need to do anything with Alice's agent - her agent is implemented to automatically receive credentials and respond to proof requests. +Note there is an option "2a" to initiate a connectionless proof - you can execute this option but it woll only work end-to-end when [connecting to Faber from a mobile agent](AliceGetsAPhone.md). + ## Additional Options in the Alice/Faber demo You can enable support for various aca-py features by providing additional command-line arguements when starting up `alice` or `faber`. diff --git a/demo/run_bdd b/demo/run_bdd index 8ce5297ea5..d7d356661b 100755 --- a/demo/run_bdd +++ b/demo/run_bdd @@ -175,6 +175,7 @@ fi if ! [ -z "$POSTGRES" ]; then DOCKER_ENV="${DOCKER_ENV} -e POSTGRES=1 -e RUST_BACKTRACE=1" fi +# e.g. LEDGER_URL=http://test.bcovrin.vonx.io if ! [ -z "$LEDGER_URL" ]; then GENESIS_URL="${LEDGER_URL}/genesis" DOCKER_ENV="${DOCKER_ENV} -e LEDGER_URL=${LEDGER_URL}" @@ -205,6 +206,7 @@ if ! [ -z "$TAILS_NETWORK" ]; then DOCKER_ENV="${DOCKER_ENV} -e TAILS_NETWORK=${TAILS_NETWORK}" DOCKER_ENV="${DOCKER_ENV} -e TAILS_NGROK_NAME=ngrok-tails-server" fi +# e.g. PUBLIC_TAILS_URL=https://tails.vonx.io if ! [ -z "$PUBLIC_TAILS_URL" ]; then DOCKER_ENV="${DOCKER_ENV} -e PUBLIC_TAILS_URL=${PUBLIC_TAILS_URL}" fi diff --git a/demo/runners/faber.py b/demo/runners/faber.py index dcb14d8095..39a1ae5e1d 100644 --- a/demo/runners/faber.py +++ b/demo/runners/faber.py @@ -4,8 +4,10 @@ import os import sys import time +import datetime from aiohttp import ClientError +from qrcode import QRCode sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) @@ -67,6 +69,303 @@ async def detect_connection(self): def connection_ready(self): return self._connection_ready.done() and self._connection_ready.result() + def generate_credential_offer(self, aip, cred_type, cred_def_id, exchange_tracing): + age = 24 + d = datetime.date.today() + birth_date = datetime.date(d.year - age, d.month, d.day) + birth_date_format = "%Y%m%d" + if aip == 10: + # define attributes to send for credential + self.cred_attrs[cred_def_id] = { + "name": "Alice Smith", + "date": "2018-05-28", + "degree": "Maths", + "birthdate_dateint": birth_date.strftime(birth_date_format), + "timestamp": str(int(time.time())), + } + + cred_preview = { + "@type": CRED_PREVIEW_TYPE, + "attributes": [ + {"name": n, "value": v} + for (n, v) in self.cred_attrs[cred_def_id].items() + ], + } + offer_request = { + "connection_id": self.connection_id, + "cred_def_id": cred_def_id, + "comment": f"Offer on cred def id {cred_def_id}", + "auto_remove": False, + "credential_preview": cred_preview, + "trace": exchange_tracing, + } + return offer_request + + elif aip == 20: + if cred_type == CRED_FORMAT_INDY: + self.cred_attrs[cred_def_id] = { + "name": "Alice Smith", + "date": "2018-05-28", + "degree": "Maths", + "birthdate_dateint": birth_date.strftime(birth_date_format), + "timestamp": str(int(time.time())), + } + + cred_preview = { + "@type": CRED_PREVIEW_TYPE, + "attributes": [ + {"name": n, "value": v} + for (n, v) in self.cred_attrs[cred_def_id].items() + ], + } + offer_request = { + "connection_id": self.connection_id, + "comment": f"Offer on cred def id {cred_def_id}", + "auto_remove": False, + "credential_preview": cred_preview, + "filter": {"indy": {"cred_def_id": cred_def_id}}, + "trace": exchange_tracing, + } + return offer_request + + elif cred_type == CRED_FORMAT_JSON_LD: + offer_request = { + "connection_id": self.connection_id, + "filter": { + "ld_proof": { + "credential": { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://w3id.org/citizenship/v1", + ], + "type": [ + "VerifiableCredential", + "PermanentResident", + ], + "id": "https://credential.example.com/residents/1234567890", + "issuer": self.did, + "issuanceDate": "2020-01-01T12:00:00Z", + "credentialSubject": { + "type": ["PermanentResident"], + "givenName": "ALICE", + "familyName": "SMITH", + "gender": "Female", + "birthCountry": "Bahamas", + "birthDate": "1958-07-17", + }, + }, + "options": {"proofType": SIG_TYPE_BLS}, + } + }, + } + return offer_request + + else: + raise Exception(f"Error invalid credential type: {self.cred_type}") + + else: + raise Exception(f"Error invalid AIP level: {self.aip}") + + def generate_proof_request_web_request( + self, aip, cred_type, revocation, exchange_tracing, connectionless=False + ): + age = 18 + d = datetime.date.today() + birth_date = datetime.date(d.year - age, d.month, d.day) + birth_date_format = "%Y%m%d" + if aip == 10: + req_attrs = [ + { + "name": "name", + "restrictions": [{"schema_name": "degree schema"}], + }, + { + "name": "date", + "restrictions": [{"schema_name": "degree schema"}], + }, + ] + if revocation: + req_attrs.append( + { + "name": "degree", + "restrictions": [{"schema_name": "degree schema"}], + "non_revoked": {"to": int(time.time() - 1)}, + }, + ) + else: + req_attrs.append( + { + "name": "degree", + "restrictions": [{"schema_name": "degree schema"}], + } + ) + if SELF_ATTESTED: + # test self-attested claims + req_attrs.append( + {"name": "self_attested_thing"}, + ) + req_preds = [ + # test zero-knowledge proofs + { + "name": "birthdate_dateint", + "p_type": "<=", + "p_value": int(birth_date.strftime(birth_date_format)), + "restrictions": [{"schema_name": "degree schema"}], + } + ] + indy_proof_request = { + "name": "Proof of Education", + "version": "1.0", + "requested_attributes": { + f"0_{req_attr['name']}_uuid": req_attr for req_attr in req_attrs + }, + "requested_predicates": { + f"0_{req_pred['name']}_GE_uuid": req_pred for req_pred in req_preds + }, + } + + if revocation: + indy_proof_request["non_revoked"] = {"to": int(time.time())} + + proof_request_web_request = { + "proof_request": indy_proof_request, + "trace": exchange_tracing, + } + if not connectionless: + proof_request_web_request["connection_id"] = self.connection_id + return proof_request_web_request + + elif aip == 20: + if cred_type == CRED_FORMAT_INDY: + req_attrs = [ + { + "name": "name", + "restrictions": [{"schema_name": "degree schema"}], + }, + { + "name": "date", + "restrictions": [{"schema_name": "degree schema"}], + }, + ] + if revocation: + req_attrs.append( + { + "name": "degree", + "restrictions": [{"schema_name": "degree schema"}], + "non_revoked": {"to": int(time.time() - 1)}, + }, + ) + else: + req_attrs.append( + { + "name": "degree", + "restrictions": [{"schema_name": "degree schema"}], + } + ) + if SELF_ATTESTED: + # test self-attested claims + req_attrs.append( + {"name": "self_attested_thing"}, + ) + req_preds = [ + # test zero-knowledge proofs + { + "name": "birthdate_dateint", + "p_type": "<=", + "p_value": int(birth_date.strftime(birth_date_format)), + "restrictions": [{"schema_name": "degree schema"}], + } + ] + indy_proof_request = { + "name": "Proof of Education", + "version": "1.0", + "requested_attributes": { + f"0_{req_attr['name']}_uuid": req_attr for req_attr in req_attrs + }, + "requested_predicates": { + f"0_{req_pred['name']}_GE_uuid": req_pred + for req_pred in req_preds + }, + } + + if revocation: + indy_proof_request["non_revoked"] = {"to": int(time.time())} + + proof_request_web_request = { + "presentation_request": {"indy": indy_proof_request}, + "trace": exchange_tracing, + } + if not connectionless: + proof_request_web_request["connection_id"] = self.connection_id + return proof_request_web_request + + elif cred_type == CRED_FORMAT_JSON_LD: + proof_request_web_request = { + "comment": "test proof request for json-ld", + "presentation_request": { + "dif": { + "options": { + "challenge": "3fa85f64-5717-4562-b3fc-2c963f66afa7", + "domain": "4jt78h47fh47", + }, + "presentation_definition": { + "id": "32f54163-7166-48f1-93d8-ff217bdb0654", + "format": {"ldp_vp": {"proof_type": [SIG_TYPE_BLS]}}, + "input_descriptors": [ + { + "id": "citizenship_input_1", + "name": "EU Driver's License", + "schema": [ + { + "uri": "https://www.w3.org/2018/credentials#VerifiableCredential" + }, + { + "uri": "https://w3id.org/citizenship#PermanentResident" + }, + ], + "constraints": { + "limit_disclosure": "required", + "is_holder": [ + { + "directive": "required", + "field_id": [ + "1f44d55f-f161-4938-a659-f8026467f126" + ], + } + ], + "fields": [ + { + "id": "1f44d55f-f161-4938-a659-f8026467f126", + "path": [ + "$.credentialSubject.familyName" + ], + "purpose": "The claim must be from one of the specified person", + "filter": {"const": "SMITH"}, + }, + { + "path": [ + "$.credentialSubject.givenName" + ], + "purpose": "The claim must be from one of the specified person", + }, + ], + }, + } + ], + }, + } + }, + } + if not connectionless: + proof_request_web_request["connection_id"] = self.connection_id + return proof_request_web_request + + else: + raise Exception(f"Error invalid credential type: {self.cred_type}") + + else: + raise Exception(f"Error invalid AIP level: {self.aip}") + async def main(args): faber_agent = await create_agent_with_args(args, ident="faber") @@ -97,7 +396,13 @@ async def main(args): if faber_agent.cred_type == CRED_FORMAT_INDY: faber_agent.public_did = True faber_schema_name = "degree schema" - faber_schema_attrs = ["name", "date", "degree", "age", "timestamp"] + faber_schema_attrs = [ + "name", + "date", + "degree", + "birthdate_dateint", + "timestamp", + ] await faber_agent.initialize( the_agent=agent, schema_name=faber_schema_name, @@ -116,6 +421,7 @@ async def main(args): options = ( " (1) Issue Credential\n" " (2) Send Proof Request\n" + " (2a) Send *Connectionless* Proof Request (requires a Mobile client)\n" " (3) Send Message\n" " (4) Create New Invitation\n" ) @@ -174,97 +480,29 @@ async def main(args): log_status("#13 Issue credential offer to X") if faber_agent.aip == 10: - # define attributes to send for credential - faber_agent.agent.cred_attrs[faber_agent.cred_def_id] = { - "name": "Alice Smith", - "date": "2018-05-28", - "degree": "Maths", - "age": "24", - "timestamp": str(int(time.time())), - } - - cred_preview = { - "@type": CRED_PREVIEW_TYPE, - "attributes": [ - {"name": n, "value": v} - for (n, v) in faber_agent.agent.cred_attrs[ - faber_agent.cred_def_id - ].items() - ], - } - offer_request = { - "connection_id": faber_agent.agent.connection_id, - "cred_def_id": faber_agent.cred_def_id, - "comment": f"Offer on cred def id {faber_agent.cred_def_id}", - "auto_remove": False, - "credential_preview": cred_preview, - "trace": exchange_tracing, - } + offer_request = faber_agent.agent.generate_credential_offer( + faber_agent.aip, None, faber_agent.cred_def_id, exchange_tracing + ) await faber_agent.agent.admin_POST( "/issue-credential/send-offer", offer_request ) elif faber_agent.aip == 20: if faber_agent.cred_type == CRED_FORMAT_INDY: - faber_agent.agent.cred_attrs[faber_agent.cred_def_id] = { - "name": "Alice Smith", - "date": "2018-05-28", - "degree": "Maths", - "age": "24", - "timestamp": str(int(time.time())), - } - - cred_preview = { - "@type": CRED_PREVIEW_TYPE, - "attributes": [ - {"name": n, "value": v} - for (n, v) in faber_agent.agent.cred_attrs[ - faber_agent.cred_def_id - ].items() - ], - } - offer_request = { - "connection_id": faber_agent.agent.connection_id, - "comment": f"Offer on cred def id {faber_agent.cred_def_id}", - "auto_remove": False, - "credential_preview": cred_preview, - "filter": { - "indy": {"cred_def_id": faber_agent.cred_def_id} - }, - "trace": exchange_tracing, - } + offer_request = faber_agent.agent.generate_credential_offer( + faber_agent.aip, + faber_agent.cred_type, + faber_agent.cred_def_id, + exchange_tracing, + ) elif faber_agent.cred_type == CRED_FORMAT_JSON_LD: - offer_request = { - "connection_id": faber_agent.agent.connection_id, - "filter": { - "ld_proof": { - "credential": { - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://w3id.org/citizenship/v1", - ], - "type": [ - "VerifiableCredential", - "PermanentResident", - ], - "id": "https://credential.example.com/residents/1234567890", - "issuer": faber_agent.agent.did, - "issuanceDate": "2020-01-01T12:00:00Z", - "credentialSubject": { - "type": ["PermanentResident"], - # "id": "", - "givenName": "ALICE", - "familyName": "SMITH", - "gender": "Female", - "birthCountry": "Bahamas", - "birthDate": "1958-07-17", - }, - }, - "options": {"proofType": SIG_TYPE_BLS}, - } - }, - } + offer_request = faber_agent.agent.generate_credential_offer( + faber_agent.aip, + faber_agent.cred_type, + None, + exchange_tracing, + ) else: raise Exception( @@ -281,65 +519,14 @@ async def main(args): elif option == "2": log_status("#20 Request proof of degree from alice") if faber_agent.aip == 10: - req_attrs = [ - { - "name": "name", - "restrictions": [{"schema_name": "degree schema"}], - }, - { - "name": "date", - "restrictions": [{"schema_name": "degree schema"}], - }, - ] - if faber_agent.revocation: - req_attrs.append( - { - "name": "degree", - "restrictions": [{"schema_name": "degree schema"}], - "non_revoked": {"to": int(time.time() - 1)}, - }, - ) - else: - req_attrs.append( - { - "name": "degree", - "restrictions": [{"schema_name": "degree schema"}], - } - ) - if SELF_ATTESTED: - # test self-attested claims - req_attrs.append( - {"name": "self_attested_thing"}, + proof_request_web_request = ( + faber_agent.agent.generate_proof_request_web_request( + faber_agent.aip, + faber_agent.cred_type, + faber_agent.revocation, + exchange_tracing, ) - req_preds = [ - # test zero-knowledge proofs - { - "name": "age", - "p_type": ">=", - "p_value": 18, - "restrictions": [{"schema_name": "degree schema"}], - } - ] - indy_proof_request = { - "name": "Proof of Education", - "version": "1.0", - "requested_attributes": { - f"0_{req_attr['name']}_uuid": req_attr - for req_attr in req_attrs - }, - "requested_predicates": { - f"0_{req_pred['name']}_GE_uuid": req_pred - for req_pred in req_preds - }, - } - - if faber_agent.revocation: - indy_proof_request["non_revoked"] = {"to": int(time.time())} - proof_request_web_request = { - "connection_id": faber_agent.agent.connection_id, - "proof_request": indy_proof_request, - "trace": exchange_tracing, - } + ) await faber_agent.agent.admin_POST( "/present-proof/send-request", proof_request_web_request ) @@ -347,132 +534,24 @@ async def main(args): elif faber_agent.aip == 20: if faber_agent.cred_type == CRED_FORMAT_INDY: - req_attrs = [ - { - "name": "name", - "restrictions": [{"schema_name": faber_schema_name}], - }, - { - "name": "date", - "restrictions": [{"schema_name": faber_schema_name}], - }, - ] - if faber_agent.revocation: - req_attrs.append( - { - "name": "degree", - "restrictions": [ - {"schema_name": faber_schema_name} - ], - "non_revoked": {"to": int(time.time() - 1)}, - }, + proof_request_web_request = ( + faber_agent.agent.generate_proof_request_web_request( + faber_agent.aip, + faber_agent.cred_type, + faber_agent.revocation, + exchange_tracing, ) - else: - req_attrs.append( - { - "name": "degree", - "restrictions": [ - {"schema_name": faber_schema_name} - ], - } - ) - if SELF_ATTESTED: - # test self-attested claims - req_attrs.append( - {"name": "self_attested_thing"}, - ) - req_preds = [ - # test zero-knowledge proofs - { - "name": "age", - "p_type": ">=", - "p_value": 18, - "restrictions": [{"schema_name": faber_schema_name}], - } - ] - indy_proof_request = { - "name": "Proof of Education", - "version": "1.0", - "requested_attributes": { - f"0_{req_attr['name']}_uuid": req_attr - for req_attr in req_attrs - }, - "requested_predicates": { - f"0_{req_pred['name']}_GE_uuid": req_pred - for req_pred in req_preds - }, - } - - if faber_agent.revocation: - indy_proof_request["non_revoked"] = {"to": int(time.time())} - proof_request_web_request = { - "connection_id": faber_agent.agent.connection_id, - "presentation_request": {"indy": indy_proof_request}, - "trace": exchange_tracing, - } + ) elif faber_agent.cred_type == CRED_FORMAT_JSON_LD: - proof_request_web_request = { - "comment": "test proof request for json-ld", - "connection_id": faber_agent.agent.connection_id, - "presentation_request": { - "dif": { - "options": { - "challenge": "3fa85f64-5717-4562-b3fc-2c963f66afa7", - "domain": "4jt78h47fh47", - }, - "presentation_definition": { - "id": "32f54163-7166-48f1-93d8-ff217bdb0654", - "format": { - "ldp_vp": {"proof_type": [SIG_TYPE_BLS]} - }, - "input_descriptors": [ - { - "id": "citizenship_input_1", - "name": "EU Driver's License", - "schema": [ - { - "uri": "https://www.w3.org/2018/credentials#VerifiableCredential" - }, - { - "uri": "https://w3id.org/citizenship#PermanentResident" - }, - ], - "constraints": { - "limit_disclosure": "required", - "is_holder": [ - { - "directive": "required", - "field_id": [ - "1f44d55f-f161-4938-a659-f8026467f126" - ], - } - ], - "fields": [ - { - "id": "1f44d55f-f161-4938-a659-f8026467f126", - "path": [ - "$.credentialSubject.familyName" - ], - "purpose": "The claim must be from one of the specified person", - "filter": { - "const": "SMITH" - }, - }, - { - "path": [ - "$.credentialSubject.givenName" - ], - "purpose": "The claim must be from one of the specified person", - }, - ], - }, - } - ], - }, - } - }, - } + proof_request_web_request = ( + faber_agent.agent.generate_proof_request_web_request( + faber_agent.aip, + faber_agent.cred_type, + faber_agent.revocation, + exchange_tracing, + ) + ) else: raise Exception( @@ -486,6 +565,88 @@ async def main(args): else: raise Exception(f"Error invalid AIP level: {faber_agent.aip}") + elif option == "2a": + log_status("#20 Request * Connectionless * proof of degree from alice") + if faber_agent.aip == 10: + proof_request_web_request = ( + faber_agent.agent.generate_proof_request_web_request( + faber_agent.aip, + faber_agent.cred_type, + faber_agent.revocation, + exchange_tracing, + connectionless=True, + ) + ) + proof_request = await faber_agent.agent.admin_POST( + "/present-proof/create-request", proof_request_web_request + ) + 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 + + "/" + ) + log_msg(f"Proof request url: {url}") + qr = QRCode(border=1) + qr.add_data(url) + log_msg( + "Scan the following QR code to accept the proof request from a mobile agent." + ) + qr.print_ascii(invert=True) + + elif faber_agent.aip == 20: + if faber_agent.cred_type == CRED_FORMAT_INDY: + proof_request_web_request = ( + faber_agent.agent.generate_proof_request_web_request( + faber_agent.aip, + faber_agent.cred_type, + faber_agent.revocation, + exchange_tracing, + connectionless=True, + ) + ) + elif faber_agent.cred_type == CRED_FORMAT_JSON_LD: + proof_request_web_request = ( + faber_agent.agent.generate_proof_request_web_request( + faber_agent.aip, + faber_agent.cred_type, + faber_agent.revocation, + exchange_tracing, + connectionless=True, + ) + ) + else: + raise Exception( + "Error invalid credential type:" + faber_agent.cred_type + ) + + proof_request = await faber_agent.agent.admin_POST( + "/present-proof-2.0/create-request", proof_request_web_request + ) + pres_req_id = proof_request["pres_ex_id"] + url = ( + "http://" + + os.getenv("DOCKERHOST").replace( + "{PORT}", str(faber_agent.agent.admin_port + 1) + ) + + "/webhooks/pres_req/" + + pres_req_id + + "/" + ) + log_msg(f"Proof request url: {url}") + qr = QRCode(border=1) + qr.add_data(url) + log_msg( + "Scan the following QR code to accept the proof request from a mobile agent." + ) + qr.print_ascii(invert=True) + else: + raise Exception(f"Error invalid AIP level: {faber_agent.aip}") + elif option == "3": msg = await prompt("Enter message: ") await faber_agent.agent.admin_POST( diff --git a/demo/runners/support/agent.py b/demo/runners/support/agent.py index 93b661e3d3..f7fe103117 100644 --- a/demo/runners/support/agent.py +++ b/demo/runners/support/agent.py @@ -8,6 +8,7 @@ import subprocess import sys from timeit import default_timer +import base64 from aiohttp import ( web, @@ -693,8 +694,16 @@ async def listen_webhooks(self, webhook_port): f"http://{self.external_host}:{str(webhook_port)}/webhooks" ) app = web.Application() - app.add_routes([web.post("/webhooks/topic/{topic}/", self._receive_webhook)]) - + app.add_routes( + [ + web.post("/webhooks/topic/{topic}/", self._receive_webhook), + # route for fetching proof request for connectionless requests + web.get( + "/webhooks/pres_req/{pres_req_id}/", + self._send_connectionless_proof_req, + ), + ] + ) runner = web.AppRunner(app) await runner.setup() self.webhook_site = web.TCPSite(runner, "0.0.0.0", webhook_port) @@ -706,6 +715,36 @@ async def _receive_webhook(self, request: ClientRequest): await self.handle_webhook(topic, payload, request.headers) return web.Response(status=200) + async def service_decorator(self): + # add a service decorator + did_url = "/wallet/did/public" + agent_public_did = await self.admin_GET(did_url) + endpoint_url = ( + "/wallet/get-did-endpoint" + "?did=" + agent_public_did["result"]["did"] + ) + agent_endpoint = await self.admin_GET(endpoint_url) + decorator = { + "recipientKeys": [agent_public_did["result"]["verkey"]], + # "routingKeys": [agent_public_did["result"]["verkey"]], + "serviceEndpoint": agent_endpoint["endpoint"], + } + return decorator + + async def _send_connectionless_proof_req(self, request: ClientRequest): + pres_req_id = request.match_info["pres_req_id"] + url = "/present-proof/records/" + pres_req_id + proof_exch = await self.admin_GET(url) + if not proof_exch: + return web.Response(status=404) + proof_reg_txn = proof_exch["presentation_request_dict"] + proof_reg_txn["~service"] = await self.service_decorator() + objJsonStr = json.dumps(proof_reg_txn) + objJsonB64 = base64.b64encode(objJsonStr.encode("ascii")) + service_url = self.webhook_url + redirect_url = service_url + "/?m=" + objJsonB64.decode("ascii") + log_msg(f"Redirecting to: {redirect_url}") + raise web.HTTPFound(redirect_url) + async def handle_webhook(self, topic: str, payload, headers: dict): if topic != "webhook": # would recurse handler = f"handle_{topic}" diff --git a/docs/README.md b/docs/README.md index 628db74f00..6c59cfd2f1 100644 --- a/docs/README.md +++ b/docs/README.md @@ -21,7 +21,7 @@ updated, as noted below. To test generate and view the RTD documentation locally, you must install [Sphinx](https://www.sphinx-doc.org/en/master/) and the [Sphinx RTD theme](https://pypi.org/project/sphinx-rtd-theme/). Both can be installed from PyPi using pip. For example: -``` +``` bash pip install -U sphinx pip install -U sphinx-rtd-theme ``` @@ -29,7 +29,8 @@ pip install -U sphinx-rtd-theme ### Generate Module Files To rebuild the project and settings from scratch (you'll need to move the generated index file up a level): -``` + +``` bash rm -rf generated sphinx-apidoc -f -M -o ./generated ../aries_cloudagent/ $(find ../aries_cloudagent/ -name '*tests*') ``` @@ -42,12 +43,12 @@ Check the `git status` in your repo to see if the generator updates, adds or re To auto-generate the module documentation locally run: -``` +``` bash sphinx-build -b html -a -E -c ./ ./ ./_build ``` Once generated, go into the `_build` folder and open `index.html` in a browser. Note that the `_build` is -`.gitignore'd` and so will not be part of a git push. +`.gitignore`'d and so will not be part of a git push. ### Look for Errors diff --git a/docs/assets/inbound-messaging.png b/docs/assets/inbound-messaging.png new file mode 100644 index 0000000000..804c291e86 Binary files /dev/null and b/docs/assets/inbound-messaging.png differ diff --git a/docs/assets/inbound-messaging.puml b/docs/assets/inbound-messaging.puml new file mode 100644 index 0000000000..4607463b28 --- /dev/null +++ b/docs/assets/inbound-messaging.puml @@ -0,0 +1,37 @@ +@startuml + +participant "Inbound\nMessage\nHandler" as oag +participant "http\nTransport" as ht +participant "Internal\nTransport\nManager" as itm +participant "Inbound\nSession" as is +participant "Conductor" as con +participant "Dispatcher" as disp +participant "Responder" as resp +participant "Message\nProtocol\nHandler" as mh + + +oag -> ht: "inbound_message_handler()" +ht->itm: "create_session()" +itm -> is: "create" +is --> itm +itm --> ht +ht --> is: "receive()" +is --> is: "parse_inbound()" +is --> is: "receive_inbound()" +is --> is: "process_inbound()" +is --> is: "inbound_handler()" +is --> con: "inbound_message_router()" +con --> disp: "queue_message()" +disp --> disp: "handle_message()" +disp --> disp: "make_message()" +disp --> resp: "create()" +disp --> mh: "handle()" +mh-->resp: "send_reply()" +mh --> disp: "" +disp --> con: "" +con --> con: "dispatch_complete()" +con --> is +is --> ht + + +@enduml diff --git a/docs/conf.py b/docs/conf.py index 4e19b7d0fc..9c3c64c116 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -22,18 +22,26 @@ "aiohttp_cors", "aiohttp", "aiohttp_apispec", - "marshmallow", "base58", "msgpack", "pytest", "asynctest", "markdown", "prompt_toolkit", - "aries_cloudagent.base_handler", - "aries_cloudagent.logging", "multicodec", "configargparse", "pyld", + "pydid", + "aries_askar", + "indy_vdr", + "aioredis", + "deepmerge", + "ecdsa", + "indy_credx", + "dateutil", + "jsonpath_ng", + "unflatten", + "aries_cloudagent.vc", ] # "aries_cloudagent.tests.test_conductor", diff --git a/docs/generated/aries_cloudagent.askar.rst b/docs/generated/aries_cloudagent.askar.rst new file mode 100644 index 0000000000..c289fdfd38 --- /dev/null +++ b/docs/generated/aries_cloudagent.askar.rst @@ -0,0 +1,26 @@ +aries\_cloudagent.askar package +=============================== + +.. automodule:: aries_cloudagent.askar + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +aries\_cloudagent.askar.profile module +-------------------------------------- + +.. automodule:: aries_cloudagent.askar.profile + :members: + :undoc-members: + :show-inheritance: + +aries\_cloudagent.askar.store module +------------------------------------ + +.. automodule:: aries_cloudagent.askar.store + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.config.rst b/docs/generated/aries_cloudagent.config.rst index 336d2b64b0..8fae3838c6 100644 --- a/docs/generated/aries_cloudagent.config.rst +++ b/docs/generated/aries_cloudagent.config.rst @@ -17,6 +17,14 @@ aries\_cloudagent.config.argparse module :undoc-members: :show-inheritance: +aries\_cloudagent.config.banner module +-------------------------------------- + +.. automodule:: aries_cloudagent.config.banner + :members: + :undoc-members: + :show-inheritance: + aries\_cloudagent.config.base module ------------------------------------ diff --git a/docs/generated/aries_cloudagent.core.rst b/docs/generated/aries_cloudagent.core.rst index e83baa6b13..efcf60f2d0 100644 --- a/docs/generated/aries_cloudagent.core.rst +++ b/docs/generated/aries_cloudagent.core.rst @@ -33,6 +33,14 @@ aries\_cloudagent.core.error module :undoc-members: :show-inheritance: +aries\_cloudagent.core.event\_bus module +---------------------------------------- + +.. automodule:: aries_cloudagent.core.event_bus + :members: + :undoc-members: + :show-inheritance: + aries\_cloudagent.core.in\_memory module ---------------------------------------- diff --git a/docs/generated/aries_cloudagent.crypto.rst b/docs/generated/aries_cloudagent.crypto.rst new file mode 100644 index 0000000000..18aa244b14 --- /dev/null +++ b/docs/generated/aries_cloudagent.crypto.rst @@ -0,0 +1,26 @@ +aries\_cloudagent.crypto package +================================ + +.. automodule:: aries_cloudagent.crypto + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +aries\_cloudagent.crypto.derive1PU module +----------------------------------------- + +.. automodule:: aries_cloudagent.crypto.derive1PU + :members: + :undoc-members: + :show-inheritance: + +aries\_cloudagent.crypto.deriveECDH module +------------------------------------------ + +.. automodule:: aries_cloudagent.crypto.deriveECDH + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.did.rst b/docs/generated/aries_cloudagent.did.rst new file mode 100644 index 0000000000..4eb9394a21 --- /dev/null +++ b/docs/generated/aries_cloudagent.did.rst @@ -0,0 +1,18 @@ +aries\_cloudagent.did package +============================= + +.. automodule:: aries_cloudagent.did + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +aries\_cloudagent.did.did\_key module +------------------------------------- + +.. automodule:: aries_cloudagent.did.did_key + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.indy.credx.rst b/docs/generated/aries_cloudagent.indy.credx.rst new file mode 100644 index 0000000000..b80d4017f7 --- /dev/null +++ b/docs/generated/aries_cloudagent.indy.credx.rst @@ -0,0 +1,34 @@ +aries\_cloudagent.indy.credx package +==================================== + +.. automodule:: aries_cloudagent.indy.credx + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +aries\_cloudagent.indy.credx.holder module +------------------------------------------ + +.. automodule:: aries_cloudagent.indy.credx.holder + :members: + :undoc-members: + :show-inheritance: + +aries\_cloudagent.indy.credx.issuer module +------------------------------------------ + +.. automodule:: aries_cloudagent.indy.credx.issuer + :members: + :undoc-members: + :show-inheritance: + +aries\_cloudagent.indy.credx.verifier module +-------------------------------------------- + +.. automodule:: aries_cloudagent.indy.credx.verifier + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.indy.models.rst b/docs/generated/aries_cloudagent.indy.models.rst new file mode 100644 index 0000000000..ae04af6ddb --- /dev/null +++ b/docs/generated/aries_cloudagent.indy.models.rst @@ -0,0 +1,122 @@ +aries\_cloudagent.indy.models package +===================================== + +.. automodule:: aries_cloudagent.indy.models + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +aries\_cloudagent.indy.models.cred module +----------------------------------------- + +.. automodule:: aries_cloudagent.indy.models.cred + :members: + :undoc-members: + :show-inheritance: + +aries\_cloudagent.indy.models.cred\_abstract module +--------------------------------------------------- + +.. automodule:: aries_cloudagent.indy.models.cred_abstract + :members: + :undoc-members: + :show-inheritance: + +aries\_cloudagent.indy.models.cred\_def module +---------------------------------------------- + +.. automodule:: aries_cloudagent.indy.models.cred_def + :members: + :undoc-members: + :show-inheritance: + +aries\_cloudagent.indy.models.cred\_precis module +------------------------------------------------- + +.. automodule:: aries_cloudagent.indy.models.cred_precis + :members: + :undoc-members: + :show-inheritance: + +aries\_cloudagent.indy.models.cred\_request module +-------------------------------------------------- + +.. automodule:: aries_cloudagent.indy.models.cred_request + :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: + +aries\_cloudagent.indy.models.predicate module +---------------------------------------------- + +.. automodule:: aries_cloudagent.indy.models.predicate + :members: + :undoc-members: + :show-inheritance: + +aries\_cloudagent.indy.models.pres\_preview module +-------------------------------------------------- + +.. automodule:: aries_cloudagent.indy.models.pres_preview + :members: + :undoc-members: + :show-inheritance: + +aries\_cloudagent.indy.models.proof module +------------------------------------------ + +.. automodule:: aries_cloudagent.indy.models.proof + :members: + :undoc-members: + :show-inheritance: + +aries\_cloudagent.indy.models.proof\_request module +--------------------------------------------------- + +.. automodule:: aries_cloudagent.indy.models.proof_request + :members: + :undoc-members: + :show-inheritance: + +aries\_cloudagent.indy.models.requested\_creds module +----------------------------------------------------- + +.. automodule:: aries_cloudagent.indy.models.requested_creds + :members: + :undoc-members: + :show-inheritance: + +aries\_cloudagent.indy.models.revocation module +----------------------------------------------- + +.. automodule:: aries_cloudagent.indy.models.revocation + :members: + :undoc-members: + :show-inheritance: + +aries\_cloudagent.indy.models.schema module +------------------------------------------- + +.. automodule:: aries_cloudagent.indy.models.schema + :members: + :undoc-members: + :show-inheritance: + +aries\_cloudagent.indy.models.xform module +------------------------------------------ + +.. automodule:: aries_cloudagent.indy.models.xform + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.indy.rst b/docs/generated/aries_cloudagent.indy.rst index 84bc785ff9..0e5c50f637 100644 --- a/docs/generated/aries_cloudagent.indy.rst +++ b/docs/generated/aries_cloudagent.indy.rst @@ -12,6 +12,8 @@ Subpackages .. toctree:: :maxdepth: 4 + aries_cloudagent.indy.credx + aries_cloudagent.indy.models aries_cloudagent.indy.sdk Submodules diff --git a/docs/generated/aries_cloudagent.ledger.rst b/docs/generated/aries_cloudagent.ledger.rst index 8f97462469..66a6bda1c8 100644 --- a/docs/generated/aries_cloudagent.ledger.rst +++ b/docs/generated/aries_cloudagent.ledger.rst @@ -41,6 +41,14 @@ aries\_cloudagent.ledger.indy module :undoc-members: :show-inheritance: +aries\_cloudagent.ledger.indy\_vdr module +----------------------------------------- + +.. automodule:: aries_cloudagent.ledger.indy_vdr + :members: + :undoc-members: + :show-inheritance: + aries\_cloudagent.ledger.routes module -------------------------------------- diff --git a/docs/generated/aries_cloudagent.messaging.jsonld.rst b/docs/generated/aries_cloudagent.messaging.jsonld.rst index d5f38aaef0..02ce783dd1 100644 --- a/docs/generated/aries_cloudagent.messaging.jsonld.rst +++ b/docs/generated/aries_cloudagent.messaging.jsonld.rst @@ -25,6 +25,14 @@ aries\_cloudagent.messaging.jsonld.credential module :undoc-members: :show-inheritance: +aries\_cloudagent.messaging.jsonld.error module +----------------------------------------------- + +.. automodule:: aries_cloudagent.messaging.jsonld.error + :members: + :undoc-members: + :show-inheritance: + aries\_cloudagent.messaging.jsonld.routes module ------------------------------------------------ diff --git a/docs/generated/aries_cloudagent.messaging.rst b/docs/generated/aries_cloudagent.messaging.rst index 7fdab6639c..573c8dc9d1 100644 --- a/docs/generated/aries_cloudagent.messaging.rst +++ b/docs/generated/aries_cloudagent.messaging.rst @@ -38,6 +38,14 @@ aries\_cloudagent.messaging.base\_handler module :undoc-members: :show-inheritance: +aries\_cloudagent.messaging.base\_message module +------------------------------------------------ + +.. automodule:: aries_cloudagent.messaging.base_message + :members: + :undoc-members: + :show-inheritance: + aries\_cloudagent.messaging.error module ---------------------------------------- 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 c247b37fe3..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 @@ -64,3 +64,11 @@ aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.handlers.mediation\_requ :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: 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 f73c606138..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 @@ -72,3 +72,11 @@ aries\_cloudagent.protocols.coordinate\_mediation.v1\_0.messages.mediate\_reques :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: 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 6a50c190fe..bcd9ffdf7a 100644 --- a/docs/generated/aries_cloudagent.protocols.didexchange.v1_0.messages.rst +++ b/docs/generated/aries_cloudagent.protocols.didexchange.v1_0.messages.rst @@ -17,10 +17,10 @@ aries\_cloudagent.protocols.didexchange.v1\_0.messages.complete module :undoc-members: :show-inheritance: -aries\_cloudagent.protocols.didexchange.v1\_0.messages.problem\_report module ------------------------------------------------------------------------------ +aries\_cloudagent.protocols.didexchange.v1\_0.messages.problem\_report\_reason module +------------------------------------------------------------------------------------- -.. automodule:: aries_cloudagent.protocols.didexchange.v1_0.messages.problem_report +.. automodule:: aries_cloudagent.protocols.didexchange.v1_0.messages.problem_report_reason :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 new file mode 100644 index 0000000000..d940678a45 --- /dev/null +++ b/docs/generated/aries_cloudagent.protocols.endorse_transaction.rst @@ -0,0 +1,26 @@ +aries\_cloudagent.protocols.endorse\_transaction package +======================================================== + +.. automodule:: aries_cloudagent.protocols.endorse_transaction + :members: + :undoc-members: + :show-inheritance: + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + aries_cloudagent.protocols.endorse_transaction.v1_0 + +Submodules +---------- + +aries\_cloudagent.protocols.endorse\_transaction.definition module +------------------------------------------------------------------ + +.. automodule:: aries_cloudagent.protocols.endorse_transaction.definition + :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 new file mode 100644 index 0000000000..e76ffa3ddc --- /dev/null +++ b/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.handlers.rst @@ -0,0 +1,66 @@ +aries\_cloudagent.protocols.endorse\_transaction.v1\_0.handlers package +======================================================================= + +.. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.handlers + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +aries\_cloudagent.protocols.endorse\_transaction.v1\_0.handlers.endorsed\_transaction\_response\_handler module +--------------------------------------------------------------------------------------------------------------- + +.. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.handlers.endorsed_transaction_response_handler + :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: + +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: + +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: + +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: + +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: + +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: 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 new file mode 100644 index 0000000000..3e14c4194c --- /dev/null +++ b/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.messages.rst @@ -0,0 +1,74 @@ +aries\_cloudagent.protocols.endorse\_transaction.v1\_0.messages package +======================================================================= + +.. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.messages + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +aries\_cloudagent.protocols.endorse\_transaction.v1\_0.messages.cancel\_transaction module +------------------------------------------------------------------------------------------ + +.. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.messages.cancel_transaction + :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: + +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: + +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: + +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: + +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: + +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: + +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: 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 new file mode 100644 index 0000000000..a120e79ce3 --- /dev/null +++ b/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.models.rst @@ -0,0 +1,18 @@ +aries\_cloudagent.protocols.endorse\_transaction.v1\_0.models package +===================================================================== + +.. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.models + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +aries\_cloudagent.protocols.endorse\_transaction.v1\_0.models.transaction\_record module +---------------------------------------------------------------------------------------- + +.. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.models.transaction_record + :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 new file mode 100644 index 0000000000..07a19405a7 --- /dev/null +++ b/docs/generated/aries_cloudagent.protocols.endorse_transaction.v1_0.rst @@ -0,0 +1,52 @@ +aries\_cloudagent.protocols.endorse\_transaction.v1\_0 package +============================================================== + +.. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0 + :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 + +Submodules +---------- + +aries\_cloudagent.protocols.endorse\_transaction.v1\_0.manager module +--------------------------------------------------------------------- + +.. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.manager + :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: + +aries\_cloudagent.protocols.endorse\_transaction.v1\_0.routes module +-------------------------------------------------------------------- + +.. automodule:: aries_cloudagent.protocols.endorse_transaction.v1_0.routes + :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: 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 5f343986e0..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 @@ -33,6 +33,14 @@ aries\_cloudagent.protocols.issue\_credential.v1\_0.handlers.credential\_offer\_ :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: + aries\_cloudagent.protocols.issue\_credential.v1\_0.handlers.credential\_proposal\_handler module ------------------------------------------------------------------------------------------------- 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 20e7a38dde..8279cea992 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 @@ -41,6 +41,14 @@ aries\_cloudagent.protocols.issue\_credential.v1\_0.messages.credential\_offer m :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: + aries\_cloudagent.protocols.issue\_credential.v1\_0.messages.credential\_proposal module ---------------------------------------------------------------------------------------- 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 new file mode 100644 index 0000000000..34fdbaf253 --- /dev/null +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.indy.rst @@ -0,0 +1,18 @@ +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: + +Submodules +---------- + +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: 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 new file mode 100644 index 0000000000..771f548b4e --- /dev/null +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.models.rst @@ -0,0 +1,26 @@ +aries\_cloudagent.protocols.issue\_credential.v2\_0.formats.ld\_proof.models package +==================================================================================== + +.. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.models + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +aries\_cloudagent.protocols.issue\_credential.v2\_0.formats.ld\_proof.models.cred\_detail module +------------------------------------------------------------------------------------------------ + +.. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.models.cred_detail + :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: 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 new file mode 100644 index 0000000000..594e20f4d8 --- /dev/null +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.rst @@ -0,0 +1,26 @@ +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: + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.models + +Submodules +---------- + +aries\_cloudagent.protocols.issue\_credential.v2\_0.formats.ld\_proof.handler module +------------------------------------------------------------------------------------ + +.. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.formats.ld_proof.handler + :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 new file mode 100644 index 0000000000..31bc906fed --- /dev/null +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.formats.rst @@ -0,0 +1,27 @@ +aries\_cloudagent.protocols.issue\_credential.v2\_0.formats package +=================================================================== + +.. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.formats + :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 + +Submodules +---------- + +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: 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 26b96528be..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 @@ -33,6 +33,14 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.handlers.cred\_offer\_handle :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: + aries\_cloudagent.protocols.issue\_credential.v2\_0.handlers.cred\_proposal\_handler module ------------------------------------------------------------------------------------------- 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 8056e9b432..c4d72caf1d 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 @@ -49,6 +49,14 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.messages.cred\_offer module :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: + aries\_cloudagent.protocols.issue\_credential.v2\_0.messages.cred\_proposal module ---------------------------------------------------------------------------------- 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 3fb867b36e..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 @@ -9,18 +9,18 @@ aries\_cloudagent.protocols.issue\_credential.v2\_0.models.detail package Submodules ---------- -aries\_cloudagent.protocols.issue\_credential.v2\_0.models.detail.dif module ----------------------------------------------------------------------------- +aries\_cloudagent.protocols.issue\_credential.v2\_0.models.detail.indy module +----------------------------------------------------------------------------- -.. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.models.detail.dif +.. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.models.detail.indy :members: :undoc-members: :show-inheritance: -aries\_cloudagent.protocols.issue\_credential.v2\_0.models.detail.indy module ------------------------------------------------------------------------------ +aries\_cloudagent.protocols.issue\_credential.v2\_0.models.detail.ld\_proof module +---------------------------------------------------------------------------------- -.. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.models.detail.indy +.. automodule:: aries_cloudagent.protocols.issue_credential.v2_0.models.detail.ld_proof :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 a72d2c2df8..b218bf8826 100644 --- a/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.rst +++ b/docs/generated/aries_cloudagent.protocols.issue_credential.v2_0.rst @@ -12,6 +12,7 @@ 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 diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.dif.rst b/docs/generated/aries_cloudagent.protocols.present_proof.dif.rst new file mode 100644 index 0000000000..5d83cee955 --- /dev/null +++ b/docs/generated/aries_cloudagent.protocols.present_proof.dif.rst @@ -0,0 +1,50 @@ +aries\_cloudagent.protocols.present\_proof.dif package +====================================================== + +.. automodule:: aries_cloudagent.protocols.present_proof.dif + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +aries\_cloudagent.protocols.present\_proof.dif.pres\_exch module +---------------------------------------------------------------- + +.. automodule:: aries_cloudagent.protocols.present_proof.dif.pres_exch + :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: + +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: + +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: + +aries\_cloudagent.protocols.present\_proof.dif.pres\_schema module +------------------------------------------------------------------ + +.. automodule:: aries_cloudagent.protocols.present_proof.dif.pres_schema + :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 new file mode 100644 index 0000000000..a1954d2b86 --- /dev/null +++ b/docs/generated/aries_cloudagent.protocols.present_proof.indy.rst @@ -0,0 +1,18 @@ +aries\_cloudagent.protocols.present\_proof.indy package +======================================================= + +.. automodule:: aries_cloudagent.protocols.present_proof.indy + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +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: diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.rst b/docs/generated/aries_cloudagent.protocols.present_proof.rst index 25119660d3..e9b69e7de6 100644 --- a/docs/generated/aries_cloudagent.protocols.present_proof.rst +++ b/docs/generated/aries_cloudagent.protocols.present_proof.rst @@ -12,7 +12,10 @@ 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 Submodules ---------- 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 0b04c34bfd..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 @@ -25,6 +25,14 @@ aries\_cloudagent.protocols.present\_proof.v1\_0.handlers.presentation\_handler :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: + aries\_cloudagent.protocols.present\_proof.v1\_0.handlers.presentation\_proposal\_handler module ------------------------------------------------------------------------------------------------ diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.messages.inner.rst b/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.messages.inner.rst deleted file mode 100644 index a7f7901e80..0000000000 --- a/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.messages.inner.rst +++ /dev/null @@ -1,18 +0,0 @@ -aries\_cloudagent.protocols.present\_proof.v1\_0.messages.inner package -======================================================================= - -.. automodule:: aries_cloudagent.protocols.present_proof.v1_0.messages.inner - :members: - :undoc-members: - :show-inheritance: - -Submodules ----------- - -aries\_cloudagent.protocols.present\_proof.v1\_0.messages.inner.presentation\_preview module --------------------------------------------------------------------------------------------- - -.. automodule:: aries_cloudagent.protocols.present_proof.v1_0.messages.inner.presentation_preview - :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 cdd07ed1ab..a00cb2d278 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 @@ -6,14 +6,6 @@ aries\_cloudagent.protocols.present\_proof.v1\_0.messages package :undoc-members: :show-inheritance: -Subpackages ------------ - -.. toctree:: - :maxdepth: 4 - - aries_cloudagent.protocols.present_proof.v1_0.messages.inner - Submodules ---------- @@ -33,6 +25,14 @@ aries\_cloudagent.protocols.present\_proof.v1\_0.messages.presentation\_ack modu :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: + aries\_cloudagent.protocols.present\_proof.v1\_0.messages.presentation\_proposal module --------------------------------------------------------------------------------------- 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 606bff9fc6..7fb3dd5dd0 100644 --- a/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.rst +++ b/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.rst @@ -15,7 +15,6 @@ Subpackages 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.util Submodules ---------- diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.util.rst b/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.util.rst deleted file mode 100644 index ec2c0d41b4..0000000000 --- a/docs/generated/aries_cloudagent.protocols.present_proof.v1_0.util.rst +++ /dev/null @@ -1,26 +0,0 @@ -aries\_cloudagent.protocols.present\_proof.v1\_0.util package -============================================================= - -.. automodule:: aries_cloudagent.protocols.present_proof.v1_0.util - :members: - :undoc-members: - :show-inheritance: - -Submodules ----------- - -aries\_cloudagent.protocols.present\_proof.v1\_0.util.indy module ------------------------------------------------------------------ - -.. automodule:: aries_cloudagent.protocols.present_proof.v1_0.util.indy - :members: - :undoc-members: - :show-inheritance: - -aries\_cloudagent.protocols.present\_proof.v1\_0.util.predicate module ----------------------------------------------------------------------- - -.. automodule:: aries_cloudagent.protocols.present_proof.v1_0.util.predicate - :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 new file mode 100644 index 0000000000..06b74fc41a --- /dev/null +++ b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.formats.dif.rst @@ -0,0 +1,18 @@ +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: + +Submodules +---------- + +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: 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 new file mode 100644 index 0000000000..fb215abc4e --- /dev/null +++ b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.formats.indy.rst @@ -0,0 +1,18 @@ +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: + +Submodules +---------- + +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: 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 new file mode 100644 index 0000000000..5d44e018a1 --- /dev/null +++ b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.formats.rst @@ -0,0 +1,27 @@ +aries\_cloudagent.protocols.present\_proof.v2\_0.formats package +================================================================ + +.. automodule:: aries_cloudagent.protocols.present_proof.v2_0.formats + :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 + +Submodules +---------- + +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: 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 new file mode 100644 index 0000000000..6be81bcd53 --- /dev/null +++ b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.handlers.rst @@ -0,0 +1,50 @@ +aries\_cloudagent.protocols.present\_proof.v2\_0.handlers package +================================================================= + +.. automodule:: aries_cloudagent.protocols.present_proof.v2_0.handlers + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +aries\_cloudagent.protocols.present\_proof.v2\_0.handlers.pres\_ack\_handler module +----------------------------------------------------------------------------------- + +.. automodule:: aries_cloudagent.protocols.present_proof.v2_0.handlers.pres_ack_handler + :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: + +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: + +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: + +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: 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 new file mode 100644 index 0000000000..bcaf052ad7 --- /dev/null +++ b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.messages.rst @@ -0,0 +1,58 @@ +aries\_cloudagent.protocols.present\_proof.v2\_0.messages package +================================================================= + +.. automodule:: aries_cloudagent.protocols.present_proof.v2_0.messages + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +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: + +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: + +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: + +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: + +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: + +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: 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 new file mode 100644 index 0000000000..342773342d --- /dev/null +++ b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.models.rst @@ -0,0 +1,18 @@ +aries\_cloudagent.protocols.present\_proof.v2\_0.models package +=============================================================== + +.. automodule:: aries_cloudagent.protocols.present_proof.v2_0.models + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +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: diff --git a/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.rst b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.rst new file mode 100644 index 0000000000..19a5e02281 --- /dev/null +++ b/docs/generated/aries_cloudagent.protocols.present_proof.v2_0.rst @@ -0,0 +1,45 @@ +aries\_cloudagent.protocols.present\_proof.v2\_0 package +======================================================== + +.. automodule:: aries_cloudagent.protocols.present_proof.v2_0 + :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 + +Submodules +---------- + +aries\_cloudagent.protocols.present\_proof.v2\_0.manager module +--------------------------------------------------------------- + +.. automodule:: aries_cloudagent.protocols.present_proof.v2_0.manager + :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: + +aries\_cloudagent.protocols.present\_proof.v2\_0.routes module +-------------------------------------------------------------- + +.. automodule:: aries_cloudagent.protocols.present_proof.v2_0.routes + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.protocols.rst b/docs/generated/aries_cloudagent.protocols.rst index d7e944310c..f1d39cd49a 100644 --- a/docs/generated/aries_cloudagent.protocols.rst +++ b/docs/generated/aries_cloudagent.protocols.rst @@ -18,6 +18,7 @@ Subpackages 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.out_of_band diff --git a/docs/generated/aries_cloudagent.resolver.default.rst b/docs/generated/aries_cloudagent.resolver.default.rst new file mode 100644 index 0000000000..375c99ee0f --- /dev/null +++ b/docs/generated/aries_cloudagent.resolver.default.rst @@ -0,0 +1,34 @@ +aries\_cloudagent.resolver.default package +========================================== + +.. automodule:: aries_cloudagent.resolver.default + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +aries\_cloudagent.resolver.default.indy module +---------------------------------------------- + +.. automodule:: aries_cloudagent.resolver.default.indy + :members: + :undoc-members: + :show-inheritance: + +aries\_cloudagent.resolver.default.key module +--------------------------------------------- + +.. automodule:: aries_cloudagent.resolver.default.key + :members: + :undoc-members: + :show-inheritance: + +aries\_cloudagent.resolver.default.web module +--------------------------------------------- + +.. automodule:: aries_cloudagent.resolver.default.web + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.resolver.rst b/docs/generated/aries_cloudagent.resolver.rst new file mode 100644 index 0000000000..c3aa28279c --- /dev/null +++ b/docs/generated/aries_cloudagent.resolver.rst @@ -0,0 +1,50 @@ +aries\_cloudagent.resolver package +================================== + +.. automodule:: aries_cloudagent.resolver + :members: + :undoc-members: + :show-inheritance: + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + aries_cloudagent.resolver.default + +Submodules +---------- + +aries\_cloudagent.resolver.base module +-------------------------------------- + +.. automodule:: aries_cloudagent.resolver.base + :members: + :undoc-members: + :show-inheritance: + +aries\_cloudagent.resolver.did\_resolver module +----------------------------------------------- + +.. automodule:: aries_cloudagent.resolver.did_resolver + :members: + :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 +---------------------------------------- + +.. automodule:: aries_cloudagent.resolver.routes + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.rst b/docs/generated/aries_cloudagent.rst index b52ea8e93c..b48b739bc4 100644 --- a/docs/generated/aries_cloudagent.rst +++ b/docs/generated/aries_cloudagent.rst @@ -13,22 +13,27 @@ Subpackages :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.crypto + 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 diff --git a/docs/generated/aries_cloudagent.storage.rst b/docs/generated/aries_cloudagent.storage.rst index c7145c8539..bd06d4d460 100644 --- a/docs/generated/aries_cloudagent.storage.rst +++ b/docs/generated/aries_cloudagent.storage.rst @@ -6,9 +6,25 @@ aries\_cloudagent.storage package :undoc-members: :show-inheritance: +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + aries_cloudagent.storage.vc_holder + Submodules ---------- +aries\_cloudagent.storage.askar module +-------------------------------------- + +.. automodule:: aries_cloudagent.storage.askar + :members: + :undoc-members: + :show-inheritance: + aries\_cloudagent.storage.base module ------------------------------------- diff --git a/docs/generated/aries_cloudagent.storage.vc_holder.rst b/docs/generated/aries_cloudagent.storage.vc_holder.rst new file mode 100644 index 0000000000..7bd5098aff --- /dev/null +++ b/docs/generated/aries_cloudagent.storage.vc_holder.rst @@ -0,0 +1,58 @@ +aries\_cloudagent.storage.vc\_holder package +============================================ + +.. automodule:: aries_cloudagent.storage.vc_holder + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +aries\_cloudagent.storage.vc\_holder.askar module +------------------------------------------------- + +.. automodule:: aries_cloudagent.storage.vc_holder.askar + :members: + :undoc-members: + :show-inheritance: + +aries\_cloudagent.storage.vc\_holder.base module +------------------------------------------------ + +.. automodule:: aries_cloudagent.storage.vc_holder.base + :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: + +aries\_cloudagent.storage.vc\_holder.indy module +------------------------------------------------ + +.. automodule:: aries_cloudagent.storage.vc_holder.indy + :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: + +aries\_cloudagent.storage.vc\_holder.xform module +------------------------------------------------- + +.. automodule:: aries_cloudagent.storage.vc_holder.xform + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.transport.outbound.queue.rst b/docs/generated/aries_cloudagent.transport.outbound.queue.rst new file mode 100644 index 0000000000..b23440d0dc --- /dev/null +++ b/docs/generated/aries_cloudagent.transport.outbound.queue.rst @@ -0,0 +1,34 @@ +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 c76149e83b..eb749ee452 100644 --- a/docs/generated/aries_cloudagent.transport.outbound.rst +++ b/docs/generated/aries_cloudagent.transport.outbound.rst @@ -6,6 +6,14 @@ aries\_cloudagent.transport.outbound package :undoc-members: :show-inheritance: +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + aries_cloudagent.transport.outbound.queue + Submodules ---------- @@ -41,6 +49,14 @@ aries\_cloudagent.transport.outbound.message module :undoc-members: :show-inheritance: +aries\_cloudagent.transport.outbound.status module +-------------------------------------------------- + +.. automodule:: aries_cloudagent.transport.outbound.status + :members: + :undoc-members: + :show-inheritance: + aries\_cloudagent.transport.outbound.ws module ---------------------------------------------- diff --git a/docs/generated/aries_cloudagent.utils.rst b/docs/generated/aries_cloudagent.utils.rst index c12a56d352..f5241f39d8 100644 --- a/docs/generated/aries_cloudagent.utils.rst +++ b/docs/generated/aries_cloudagent.utils.rst @@ -17,6 +17,22 @@ aries\_cloudagent.utils.classloader module :undoc-members: :show-inheritance: +aries\_cloudagent.utils.dependencies module +------------------------------------------- + +.. automodule:: aries_cloudagent.utils.dependencies + :members: + :undoc-members: + :show-inheritance: + +aries\_cloudagent.utils.env module +---------------------------------- + +.. automodule:: aries_cloudagent.utils.env + :members: + :undoc-members: + :show-inheritance: + aries\_cloudagent.utils.http module ----------------------------------- @@ -25,6 +41,14 @@ aries\_cloudagent.utils.http module :undoc-members: :show-inheritance: +aries\_cloudagent.utils.jwe module +---------------------------------- + +.. automodule:: aries_cloudagent.utils.jwe + :members: + :undoc-members: + :show-inheritance: + aries\_cloudagent.utils.outofband module ---------------------------------------- diff --git a/docs/generated/aries_cloudagent.vc.ld_proofs.crypto.rst b/docs/generated/aries_cloudagent.vc.ld_proofs.crypto.rst new file mode 100644 index 0000000000..66c04359cd --- /dev/null +++ b/docs/generated/aries_cloudagent.vc.ld_proofs.crypto.rst @@ -0,0 +1,26 @@ +aries\_cloudagent.vc.ld\_proofs.crypto package +============================================== + +.. automodule:: aries_cloudagent.vc.ld_proofs.crypto + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +aries\_cloudagent.vc.ld\_proofs.crypto.key\_pair module +------------------------------------------------------- + +.. automodule:: aries_cloudagent.vc.ld_proofs.crypto.key_pair + :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: diff --git a/docs/generated/aries_cloudagent.vc.ld_proofs.purposes.rst b/docs/generated/aries_cloudagent.vc.ld_proofs.purposes.rst new file mode 100644 index 0000000000..169a889c5b --- /dev/null +++ b/docs/generated/aries_cloudagent.vc.ld_proofs.purposes.rst @@ -0,0 +1,50 @@ +aries\_cloudagent.vc.ld\_proofs.purposes package +================================================ + +.. automodule:: aries_cloudagent.vc.ld_proofs.purposes + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +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: + +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: + +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: + +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: + +aries\_cloudagent.vc.ld\_proofs.purposes.proof\_purpose module +-------------------------------------------------------------- + +.. automodule:: aries_cloudagent.vc.ld_proofs.purposes.proof_purpose + :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 new file mode 100644 index 0000000000..ca7276b1fc --- /dev/null +++ b/docs/generated/aries_cloudagent.vc.ld_proofs.rst @@ -0,0 +1,76 @@ +aries\_cloudagent.vc.ld\_proofs package +======================================= + +.. automodule:: aries_cloudagent.vc.ld_proofs + :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 + +Submodules +---------- + +aries\_cloudagent.vc.ld\_proofs.check module +-------------------------------------------- + +.. automodule:: aries_cloudagent.vc.ld_proofs.check + :members: + :undoc-members: + :show-inheritance: + +aries\_cloudagent.vc.ld\_proofs.constants module +------------------------------------------------ + +.. automodule:: aries_cloudagent.vc.ld_proofs.constants + :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: + +aries\_cloudagent.vc.ld\_proofs.error module +-------------------------------------------- + +.. automodule:: aries_cloudagent.vc.ld_proofs.error + :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: + +aries\_cloudagent.vc.ld\_proofs.proof\_set module +------------------------------------------------- + +.. automodule:: aries_cloudagent.vc.ld_proofs.proof_set + :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: diff --git a/docs/generated/aries_cloudagent.vc.ld_proofs.suites.rst b/docs/generated/aries_cloudagent.vc.ld_proofs.suites.rst new file mode 100644 index 0000000000..cbda566bca --- /dev/null +++ b/docs/generated/aries_cloudagent.vc.ld_proofs.suites.rst @@ -0,0 +1,66 @@ +aries\_cloudagent.vc.ld\_proofs.suites package +============================================== + +.. automodule:: aries_cloudagent.vc.ld_proofs.suites + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +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: + +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: + +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: + +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: + +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: + +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: + +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: diff --git a/docs/generated/aries_cloudagent.vc.rst b/docs/generated/aries_cloudagent.vc.rst new file mode 100644 index 0000000000..f3c1e09d88 --- /dev/null +++ b/docs/generated/aries_cloudagent.vc.rst @@ -0,0 +1,16 @@ +aries\_cloudagent.vc package +============================ + +.. automodule:: aries_cloudagent.vc + :members: + :undoc-members: + :show-inheritance: + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + 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 new file mode 100644 index 0000000000..83bcd876d2 --- /dev/null +++ b/docs/generated/aries_cloudagent.vc.vc_ld.models.rst @@ -0,0 +1,26 @@ +aries\_cloudagent.vc.vc\_ld.models package +========================================== + +.. automodule:: aries_cloudagent.vc.vc_ld.models + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +aries\_cloudagent.vc.vc\_ld.models.credential module +---------------------------------------------------- + +.. automodule:: aries_cloudagent.vc.vc_ld.models.credential + :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: diff --git a/docs/generated/aries_cloudagent.vc.vc_ld.rst b/docs/generated/aries_cloudagent.vc.vc_ld.rst new file mode 100644 index 0000000000..a89699f5cc --- /dev/null +++ b/docs/generated/aries_cloudagent.vc.vc_ld.rst @@ -0,0 +1,50 @@ +aries\_cloudagent.vc.vc\_ld package +=================================== + +.. automodule:: aries_cloudagent.vc.vc_ld + :members: + :undoc-members: + :show-inheritance: + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + aries_cloudagent.vc.vc_ld.models + +Submodules +---------- + +aries\_cloudagent.vc.vc\_ld.issue module +---------------------------------------- + +.. automodule:: aries_cloudagent.vc.vc_ld.issue + :members: + :undoc-members: + :show-inheritance: + +aries\_cloudagent.vc.vc\_ld.prove module +---------------------------------------- + +.. automodule:: aries_cloudagent.vc.vc_ld.prove + :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: + +aries\_cloudagent.vc.vc\_ld.verify module +----------------------------------------- + +.. automodule:: aries_cloudagent.vc.vc_ld.verify + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generated/aries_cloudagent.wallet.rst b/docs/generated/aries_cloudagent.wallet.rst index a40bace18d..f5c66a5c5b 100644 --- a/docs/generated/aries_cloudagent.wallet.rst +++ b/docs/generated/aries_cloudagent.wallet.rst @@ -17,6 +17,14 @@ Subpackages Submodules ---------- +aries\_cloudagent.wallet.askar module +------------------------------------- + +.. automodule:: aries_cloudagent.wallet.askar + :members: + :undoc-members: + :show-inheritance: + aries\_cloudagent.wallet.base module ------------------------------------ @@ -25,6 +33,14 @@ aries\_cloudagent.wallet.base module :undoc-members: :show-inheritance: +aries\_cloudagent.wallet.bbs module +----------------------------------- + +.. automodule:: aries_cloudagent.wallet.bbs + :members: + :undoc-members: + :show-inheritance: + aries\_cloudagent.wallet.crypto module -------------------------------------- @@ -33,6 +49,22 @@ aries\_cloudagent.wallet.crypto module :undoc-members: :show-inheritance: +aries\_cloudagent.wallet.did\_info module +----------------------------------------- + +.. automodule:: aries_cloudagent.wallet.did_info + :members: + :undoc-members: + :show-inheritance: + +aries\_cloudagent.wallet.did\_method module +------------------------------------------- + +.. automodule:: aries_cloudagent.wallet.did_method + :members: + :undoc-members: + :show-inheritance: + aries\_cloudagent.wallet.did\_posture module -------------------------------------------- @@ -65,6 +97,22 @@ aries\_cloudagent.wallet.indy module :undoc-members: :show-inheritance: +aries\_cloudagent.wallet.key\_pair module +----------------------------------------- + +.. automodule:: aries_cloudagent.wallet.key_pair + :members: + :undoc-members: + :show-inheritance: + +aries\_cloudagent.wallet.key\_type module +----------------------------------------- + +.. automodule:: aries_cloudagent.wallet.key_type + :members: + :undoc-members: + :show-inheritance: + aries\_cloudagent.wallet.routes module -------------------------------------- diff --git a/open-api/openapi.json b/open-api/openapi.json index b08f3c519d..b63c0078ac 100644 --- a/open-api/openapi.json +++ b/open-api/openapi.json @@ -1,7 +1,7 @@ { "swagger" : "2.0", "info" : { - "version" : "v0.6.0", + "version" : "v0.7.1", "title" : "Aries Cloud Agent" }, "tags" : [ { @@ -288,6 +288,13 @@ "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", @@ -308,7 +315,7 @@ "description" : "Connection state", "required" : false, "type" : "string", - "enum" : [ "start", "invitation", "completed", "response", "abandoned", "error", "init", "active", "request" ] + "enum" : [ "start", "abandoned", "active", "completed", "response", "init", "invitation", "error", "request" ] }, { "name" : "their_did", "in" : "query", @@ -512,7 +519,7 @@ "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]*)?(/[^?&#]+)?$" + "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", @@ -547,7 +554,7 @@ "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]*)?(/[^?&#]+)?$" + "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" } ], "responses" : { "200" : { @@ -871,6 +878,29 @@ } } }, + "/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" ], @@ -987,7 +1017,7 @@ "200" : { "description" : "", "schema" : { - "$ref" : "#/definitions/CredBrief" + "$ref" : "#/definitions/IndyCredInfo" } } } @@ -1044,7 +1074,7 @@ "200" : { "description" : "", "schema" : { - "$ref" : "#/definitions/CredBriefList" + "$ref" : "#/definitions/CredInfoList" } } } @@ -1097,15 +1127,15 @@ "/didexchange/create-request" : { "post" : { "tags" : [ "did-exchange" ], - "summary" : "Create request against public DID's implicit invitation", + "summary" : "Create and send a request against public DID's implicit invitation", "produces" : [ "application/json" ], "parameters" : [ { "name" : "their_public_did", "in" : "query", - "description" : "Public DID to which to request connection", + "description" : "Qualified public DID to which to request connection", "required" : true, "type" : "string", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + "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" : "mediation_id", "in" : "query", @@ -1119,19 +1149,25 @@ "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]*)?(/[^?&#]+)?$" + "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/DIDXRequest" + "$ref" : "#/definitions/ConnRecord" } } } @@ -1174,7 +1210,7 @@ "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]*)?(/[^?&#]+)?$" + "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" } ], "responses" : { "200" : { @@ -1203,7 +1239,7 @@ "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]*)?(/[^?&#]+)?$" + "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", @@ -1245,7 +1281,7 @@ "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]*)?(/[^?&#]+)?$" + "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" } ], "responses" : { "200" : { @@ -1282,7 +1318,7 @@ "/issue-credential-2.0/create" : { "post" : { "tags" : [ "issue-credential v2.0" ], - "summary" : "Send holder a credential, automating entire flow", + "summary" : "Create credential from attribute values", "produces" : [ "application/json" ], "parameters" : [ { "in" : "body", @@ -1302,6 +1338,29 @@ } } }, + "/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" ], @@ -1486,6 +1545,13 @@ "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", @@ -1543,7 +1609,7 @@ "name" : "body", "required" : false, "schema" : { - "$ref" : "#/definitions/V20IssueCredSchemaCore" + "$ref" : "#/definitions/V20CredExFree" } } ], "responses" : { @@ -1589,7 +1655,7 @@ "name" : "body", "required" : false, "schema" : { - "$ref" : "#/definitions/V20IssueCredSchemaCore" + "$ref" : "#/definitions/V20CredExFree" } } ], "responses" : { @@ -1648,6 +1714,29 @@ } } }, + "/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" ], @@ -2702,7 +2791,7 @@ "name" : "body", "required" : false, "schema" : { - "$ref" : "#/definitions/InvitationReceiveRequest" + "$ref" : "#/definitions/InvitationMessage" } }, { "name" : "alias", @@ -3410,16 +3499,16 @@ "parameters" : [ { "name" : "did", "in" : "path", - "description" : "decentralize identifier(DID)", + "description" : "DID", "required" : true, "type" : "string", - "pattern" : "^did:([a-z0-9]+):((?:[a-zA-Z0-9._-]*:)*[a-zA-Z0-9._-]+)$" + "pattern" : "^did:([a-z0-9]+):((?:[a-zA-Z0-9._%-]*:)*[a-zA-Z0-9._%-]+)$" } ], "responses" : { "200" : { "description" : "null", "schema" : { - "$ref" : "#/definitions/DIDDoc" + "$ref" : "#/definitions/ResolutionResult" } } } @@ -3543,12 +3632,24 @@ "schema" : { "$ref" : "#/definitions/PublishRevocations" } + }, { + "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/PublishRevocations" + "$ref" : "#/definitions/TxnOrPublishRevocationsResult" } } } @@ -3647,12 +3748,24 @@ "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" + "$ref" : "#/definitions/TxnOrRevRegResult" } } } @@ -3670,6 +3783,18 @@ "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" : { @@ -3823,7 +3948,7 @@ }, { "name" : "create_transaction_for_endorser", "in" : "query", - "description" : "Create Transaction For Endorser's signature", + "description" : "Create Transaction For Endorser's signature", "required" : false, "type" : "boolean" } ], @@ -3903,6 +4028,29 @@ } } }, + "/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" ], @@ -4055,6 +4203,12 @@ "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" : { @@ -4220,7 +4374,7 @@ "/transactions/{tran_id}/write" : { "post" : { "tags" : [ "endorse-transaction" ], - "summary" : "For Author to write an endorsed transaction to the ledger", + "summary" : "For Author / Endorser to write an endorsed transaction to the ledger", "produces" : [ "application/json" ], "parameters" : [ { "name" : "tran_id", @@ -4590,7 +4744,7 @@ }, "lastmod_time" : { "type" : "string", - "example" : "2021-05-05 12:53:31Z", + "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|)$" }, @@ -4727,6 +4881,35 @@ "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" : { @@ -4764,9 +4947,15 @@ "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-05-05 12:53:31Z", + "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|)$" }, @@ -4849,7 +5038,7 @@ }, "updated_at" : { "type" : "string", - "example" : "2021-05-05 12:53:31Z", + "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|)$" } @@ -4977,7 +5166,7 @@ "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]*)?(/[^?&#]+)?$" + "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", @@ -5013,7 +5202,7 @@ "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]*)?(/[^?&#]+)?$" + "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" @@ -5032,6 +5221,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" : { @@ -5136,7 +5363,7 @@ "properties" : { "created_at" : { "type" : "string", - "example" : "2021-05-05 12:53:31Z", + "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|)$" }, @@ -5162,7 +5389,7 @@ }, "updated_at" : { "type" : "string", - "example" : "2021-05-05 12:53:31Z", + "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|)$" }, @@ -5215,63 +5442,114 @@ } } }, - "CredBrief" : { + "CredDefValue" : { "type" : "object", "properties" : { - "attrs" : { - "type" : "object", - "description" : "Attribute names mapped to their raw values", - "additionalProperties" : { - "type" : "string", - "description" : "Attribute value" - } + "primary" : { + "$ref" : "#/definitions/CredDefValue_primary" }, - "cred_def_id" : { + "revocation" : { + "$ref" : "#/definitions/CredDefValue_revocation" + } + } + }, + "CredDefValuePrimary" : { + "type" : "object", + "properties" : { + "n" : { "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.]+)):(.+)?$" + "example" : "0", + "pattern" : "^[0-9]*$" }, - "cred_rev_id" : { + "r" : { + "$ref" : "#/definitions/Generated" + }, + "rctxt" : { "type" : "string", - "example" : "12345", - "description" : "Credential revocation identifier", - "pattern" : "^[1-9][0-9]*$" + "example" : "0", + "pattern" : "^[0-9]*$" }, - "referent" : { + "s" : { "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Credential referent" + "example" : "0", + "pattern" : "^[0-9]*$" }, - "rev_reg_id" : { + "z" : { "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:(.+$)" + "example" : "0", + "pattern" : "^[0-9]*$" + } + } + }, + "CredDefValueRevocation" : { + "type" : "object", + "properties" : { + "g" : { + "type" : "string", + "example" : "1 1F14F&ECB578F 2 095E45DDF417D" }, - "schema_id" : { + "g_dash" : { "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", - "description" : "Schema identifier", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" + "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" } } }, - "CredBriefList" : { + "CredInfoList" : { "type" : "object", "properties" : { "results" : { "type" : "array", "items" : { - "$ref" : "#/definitions/CredBrief" + "$ref" : "#/definitions/IndyCredInfo" } } } }, "CredRevRecordResult" : { - "type" : "object" - }, - "CredRevokedResult" : { + "type" : "object", + "properties" : { + "result" : { + "$ref" : "#/definitions/IssuerCredRevRecord" + } + } + }, + "CredRevokedResult" : { "type" : "object", "properties" : { "revoked" : { @@ -5351,9 +5629,7 @@ "description" : "Signature type: CL for Camenisch-Lysyanskaya" }, "value" : { - "type" : "object", - "description" : "Credential definition primary and revocation values", - "properties" : { } + "$ref" : "#/definitions/CredentialDefinition_value" }, "ver" : { "type" : "string", @@ -5424,6 +5700,37 @@ } } }, + "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" ], @@ -5563,18 +5870,6 @@ } } }, - "DIDDoc" : { - "type" : "object", - "required" : [ "did_doc" ], - "properties" : { - "did_doc" : { - "type" : "string", - "example" : "*", - "description" : "decentralize identifier(DID) document", - "pattern" : "^did:([a-z0-9]+):((?:[a-zA-Z0-9._-]*:)*[a-zA-Z0-9._-]+)$" - } - } - }, "DIDEndpoint" : { "type" : "object", "required" : [ "did" ], @@ -5589,7 +5884,7 @@ "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]*)?(/[^?&#]+)?$" + "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" } } }, @@ -5607,7 +5902,7 @@ "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]*)?(/[^?&#]+)?$" + "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", @@ -5668,30 +5963,110 @@ } } }, - "DIFPresProposal" : { + "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" : { - "some_dif" : { + "directive" : { "type" : "string", - "description" : "Placeholder for W3C/DIF/JSON-LD presentation proposal format" + "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}" + } } } }, - "DIFPresRequest" : { + "DIFOptions" : { "type" : "object", "properties" : { - "some_dif" : { + "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", - "description" : "Placeholder for W3C/DIF/JSON-LD presentation request format" + "example" : "4jt78h47fh47", + "description" : "Domain protect against replay attack" } } }, "DIFPresSpec" : { "type" : "object", "properties" : { - "some_dif" : { + "issuer_id" : { "type" : "string", - "description" : "Placeholder for W3C/DIF/JSON-LD presentation format" + "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" : { } + } + } + }, + "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" } } }, @@ -5707,6 +6082,20 @@ } } }, + "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" ], @@ -5728,27 +6117,88 @@ "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]*)?(/[^?&#]+)?$" + "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]*)?(/[^?&#]+)?$" + "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", - "required" : [ "credential", "options" ], "properties" : { - "credential" : { - "type" : "object", - "description" : "Credential to sign", - "properties" : { } + "master_secret" : { + "type" : "string", + "example" : "0", + "pattern" : "^[0-9]*$" }, - "options" : { - "$ref" : "#/definitions/Generated" + "number" : { + "type" : "string", + "example" : "0", + "pattern" : "^[0-9]*$" + }, + "remainder" : { + "type" : "string", + "example" : "0", + "pattern" : "^[0-9]*$" } } }, @@ -5759,7 +6209,7 @@ "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]*)?(/[^?&#]+)?$", + "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$", "x-nullable" : true } } @@ -5790,6 +6240,49 @@ "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" : { @@ -5798,7 +6291,7 @@ "description" : "Attribute names and value", "additionalProperties" : { "type" : "string", - "example" : "24" + "example" : "alice" } }, "cred_def_id" : { @@ -5807,11 +6300,12 @@ "description" : "Credential definition identifier", "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" }, - "cred_rev" : { + "cred_rev_id" : { "type" : "string", "example" : "12345", "description" : "Credential revocation identifier", - "pattern" : "^[1-9][0-9]*$" + "pattern" : "^[1-9][0-9]*$", + "x-nullable" : true }, "referent" : { "type" : "string", @@ -5822,7 +6316,8 @@ "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:(.+$)" + "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", @@ -5851,40 +6346,43 @@ } } }, - "IndyPresAttrSpec" : { + "IndyCredRequest" : { "type" : "object", - "required" : [ "name" ], + "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.]+)):(.+)?$" }, - "mime-type" : { - "type" : "string", - "example" : "image/jpeg", - "description" : "MIME type (default null)" - }, - "name" : { - "type" : "string", - "example" : "favourite_drink", - "description" : "Attribute name" - }, - "referent" : { + "nonce" : { "type" : "string", "example" : "0", - "description" : "Credential referent" + "description" : "Nonce in credential request", + "pattern" : "^[0-9]*$" }, - "value" : { + "prover_did" : { "type" : "string", - "example" : "martini", - "description" : "Attribute value" + "example" : "WgWxqztrNooG92RXvxSTWv", + "description" : "Prover DID", + "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" } } }, - "IndyPresPredSpec" : { + "IndyCredential" : { "type" : "object", - "required" : [ "name", "predicate", "threshold" ], + "required" : [ "cred_def_id", "schema_id", "signature", "signature_correctness_proof", "values" ], "properties" : { "cred_def_id" : { "type" : "string", @@ -5892,32 +6390,291 @@ "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" + "rev_reg" : { + "type" : "object", + "description" : "Revocation registry state", + "properties" : { }, + "x-nullable" : true }, - "predicate" : { + "rev_reg_id" : { "type" : "string", - "example" : ">=", - "description" : "Predicate type ('<', '<=', '>=', or '>')", - "enum" : [ "<", "<=", ">=", ">" ] + "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 }, - "threshold" : { - "type" : "integer", - "format" : "int32", - "description" : "Threshold value" - } - } - }, - "IndyPresPreview" : { - "type" : "object", - "required" : [ "attributes", "predicates" ], - "properties" : { - "@type" : { + "schema_id" : { "type" : "string", - "example" : "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/present-proof/1.0/presentation-preview", - "description" : "Message type identifier" + "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" : "0", + "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", @@ -5967,6 +6724,119 @@ } } }, + "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" : { @@ -5999,15 +6869,22 @@ } } }, - "IndyProofReqNonRevoked" : { + "IndyProofReqAttrSpecNonRevoked" : { "type" : "object", - "required" : [ "to" ], "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" : 1620219211, - "description" : "Timestamp of interest for non-revocation proof", + "example" : 1640995199, + "description" : "Latest time of interest in non-revocation interval", "minimum" : 0, "maximum" : 18446744073709551615 } @@ -6027,134 +6904,384 @@ }, "p_type" : { "type" : "string", - "example" : ">=", - "description" : "Predicate type ('<', '<=', '>=', or '>')", - "enum" : [ "<", "<=", ">=", ">" ] + "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)" }, - "p_value" : { + "timestamp" : { "type" : "integer", "format" : "int32", - "description" : "Threshold value" - }, - "restrictions" : { - "type" : "array", - "description" : "If present, credential must satisfy one of given restrictions", - "items" : { - "$ref" : "#/definitions/IndyProofReqPredSpecRestrictions" - } + "example" : 1640995199, + "description" : "Epoch timestamp of interest for non-revocation proof", + "minimum" : 0, + "maximum" : 18446744073709551615 } } }, - "IndyProofReqPredSpecRestrictions" : { + "IndyRevRegDef" : { "type" : "object", "properties" : { - "cred_def_id" : { + "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.]+)):(.+)?$" }, - "issuer_did" : { + "id" : { "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "Credential issuer DID", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + "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:(.+$)" }, - "schema_id" : { + "revocDefType" : { "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", - "description" : "Schema identifier", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" + "example" : "CL_ACCUM", + "description" : "Revocation registry type (specify CL_ACCUM)", + "enum" : [ "CL_ACCUM" ] }, - "schema_issuer_did" : { + "tag" : { "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "Schema issuer (origin) DID", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" + "description" : "Revocation registry tag" }, - "schema_name" : { - "type" : "string", - "example" : "transcript", - "description" : "Schema name" + "value" : { + "$ref" : "#/definitions/IndyRevRegDef_value" }, - "schema_version" : { + "ver" : { "type" : "string", "example" : "1.0", - "description" : "Schema version", + "description" : "Version of revocation registry definition", "pattern" : "^[0-9.]+$" } } }, - "IndyProofRequest" : { + "IndyRevRegDefValue" : { "type" : "object", - "required" : [ "requested_attributes", "requested_predicates" ], "properties" : { - "name" : { + "issuanceType" : { "type" : "string", - "example" : "Proof request", - "description" : "Proof request name" + "description" : "Issuance type", + "enum" : [ "ISSUANCE_ON_DEMAND", "ISSUANCE_BY_DEFAULT" ] }, - "non_revoked" : { - "$ref" : "#/definitions/IndyProofReqAttrSpec_non_revoked" + "maxCredNum" : { + "type" : "integer", + "format" : "int32", + "example" : 10, + "description" : "Maximum number of credentials; registry size", + "minimum" : 1 }, - "nonce" : { - "type" : "string", - "example" : "1234567890", - "description" : "Nonce" + "publicKeys" : { + "$ref" : "#/definitions/IndyRevRegDefValue_publicKeys" }, - "requested_attributes" : { - "type" : "object", - "description" : "Requested attribute specifications of proof request", - "additionalProperties" : { - "$ref" : "#/definitions/IndyProofReqAttrSpec" - } + "tailsHash" : { + "type" : "string", + "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", + "description" : "Tails hash value", + "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" }, - "requested_predicates" : { - "type" : "object", - "description" : "Requested predicate specifications of proof request", - "additionalProperties" : { - "$ref" : "#/definitions/IndyProofReqPredSpec" - } + "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" }, - "version" : { + "ver" : { "type" : "string", "example" : "1.0", - "description" : "Proof request version", + "description" : "Version of revocation registry entry", "pattern" : "^[0-9.]+$" } } }, - "IndyRequestedCredsRequestedAttr" : { + "IndyRevRegEntryValue" : { "type" : "object", - "required" : [ "cred_id" ], "properties" : { - "cred_id" : { + "accum" : { "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Wallet credential identifier (typically but not necessarily a UUID)" + "example" : "21 11792B036AED0AAA12A4 4 298B2571FFC63A737", + "description" : "Accumulator value" }, - "revealed" : { - "type" : "boolean", - "description" : "Whether to reveal attribute in proof (default true)" + "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" + } } } }, - "IndyRequestedCredsRequestedPred" : { + "InputDescriptors" : { "type" : "object", - "required" : [ "cred_id" ], "properties" : { - "cred_id" : { + "constraints" : { + "$ref" : "#/definitions/Constraints" + }, + "group" : { + "type" : "array", + "items" : { + "type" : "string", + "description" : "Group" + } + }, + "id" : { "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Wallet credential identifier (typically but not necessarily a UUID)" + "description" : "ID" }, - "timestamp" : { - "type" : "integer", - "format" : "int32", - "example" : 1620219211, - "description" : "Epoch timestamp of interest for non-revocation proof", - "minimum" : 0, - "maximum" : 18446744073709551615 + "metadata" : { + "type" : "object", + "description" : "Metadata dictionary", + "properties" : { } + }, + "name" : { + "type" : "string", + "description" : "Name" + }, + "purpose" : { + "type" : "string", + "description" : "Purpose" + }, + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/SchemaInputDescriptor" + } } } }, @@ -6207,7 +7334,7 @@ } } }, - "InvitationReceiveRequest" : { + "InvitationMessage" : { "type" : "object", "properties" : { "@id" : { @@ -6243,25 +7370,16 @@ }, "services" : { "type" : "array", - "description" : "DIDComm service object or a DID", + "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" : { - "anyOf": [ - { - "type" : "array", - "items" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "Service DID", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - } - }, - { - "type" : "array", - "items" : { - "$ref" : "#/definitions/Service" - } - } - ] + "description" : "Either a DIDComm service object (as per RFC0067) or a DID string." } } } @@ -6271,7 +7389,7 @@ "properties" : { "created_at" : { "type" : "string", - "example" : "2021-05-05 12:53:31Z", + "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|)$" }, @@ -6281,9 +7399,7 @@ "description" : "Invitation message identifier" }, "invitation" : { - "type" : "object", - "description" : "Out of band invitation object", - "properties" : { } + "$ref" : "#/definitions/InvitationRecord_invitation" }, "invitation_id" : { "type" : "string", @@ -6306,7 +7422,7 @@ }, "updated_at" : { "type" : "string", - "example" : "2021-05-05 12:53:31Z", + "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|)$" } @@ -6320,19 +7436,163 @@ "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", "description" : "Connection identifier" }, - "invitation" : { - "$ref" : "#/definitions/ConnectionInvitation" + "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" }, - "invitation_url" : { + "updated_at" : { "type" : "string", - "example" : "http://192.168.56.101:8020/invite?c_i=eyJAdHlwZSI6Li4ufQ==", - "description" : "Invitation URL" + "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|)$" } } }, - "IssueCredentialModuleResponse" : { - "type" : "object" - }, "Keylist" : { "type" : "object", "properties" : { @@ -6474,7 +7734,7 @@ }, "created" : { "type" : "string", - "example" : "2021-05-05 12:53:31Z", + "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|)$" }, @@ -6512,7 +7772,7 @@ }, "created" : { "type" : "string", - "example" : "2021-05-05 12:53:31Z", + "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|)$" }, @@ -6655,7 +7915,7 @@ }, "created_at" : { "type" : "string", - "example" : "2021-05-05 12:53:31Z", + "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|)$" }, @@ -6695,7 +7955,7 @@ }, "updated_at" : { "type" : "string", - "example" : "2021-05-05 12:53:31Z", + "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|)$" } @@ -6900,6 +8160,93 @@ } } }, + "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" : { @@ -6932,6 +8279,21 @@ } } }, + "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" : { @@ -7011,6 +8373,22 @@ } } }, + "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" : { @@ -7043,7 +8421,12 @@ } }, "RevRegResult" : { - "type" : "object" + "type" : "object", + "properties" : { + "result" : { + "$ref" : "#/definitions/IssuerRevRegRecord" + } + } }, "RevRegUpdateTailsFileUri" : { "type" : "object", @@ -7110,7 +8493,7 @@ }, "created_at" : { "type" : "string", - "example" : "2021-05-05 12:53:31Z", + "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|)$" }, @@ -7130,7 +8513,7 @@ }, "updated_at" : { "type" : "string", - "example" : "2021-05-05 12:53:31Z", + "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|)$" }, @@ -7191,6 +8574,19 @@ } } }, + "SchemaInputDescriptor" : { + "type" : "object", + "properties" : { + "required" : { + "type" : "boolean", + "description" : "Required" + }, + "uri" : { + "type" : "string", + "description" : "URI" + } + } + }, "SchemaSendRequest" : { "type" : "object", "required" : [ "attributes", "schema_name", "schema_version" ], @@ -7219,12 +8615,10 @@ }, "SchemaSendResult" : { "type" : "object", - "required" : [ "schema", "schema_id" ], + "required" : [ "schema_id" ], "properties" : { "schema" : { - "type" : "object", - "description" : "Schema result", - "properties" : { } + "$ref" : "#/definitions/SchemaSendResult_schema" }, "schema_id" : { "type" : "string", @@ -7267,57 +8661,12 @@ } } }, - "Service" : { - "type" : "object", - "required" : [ "id", "type" ], - "properties" : { - "did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "Service DID", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "id" : { - "type" : "string", - "description" : "Service identifier" - }, - "recipientKeys" : { - "type" : "array", - "description" : "List of recipient keys", - "items" : { - "type" : "string", - "example" : "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH", - "description" : "Recipient public key", - "pattern" : "^did:key:z[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+$" - } - }, - "routingKeys" : { - "type" : "array", - "description" : "List of routing keys", - "items" : { - "type" : "string", - "example" : "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH", - "description" : "Routing key", - "pattern" : "^did:key:z[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+$" - } - }, - "serviceEndpoint" : { - "type" : "string", - "example" : "http://192.168.56.101:8020", - "description" : "Service endpoint at which to reach this agent" - }, - "type" : { - "type" : "string", - "description" : "Service type" - } - } - }, "SignRequest" : { "type" : "object", "required" : [ "doc", "verkey" ], "properties" : { "doc" : { - "$ref" : "#/definitions/Generated" + "$ref" : "#/definitions/Doc" }, "verkey" : { "type" : "string", @@ -7339,6 +8688,82 @@ } } }, + "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" : { @@ -7362,7 +8787,7 @@ "time" : { "type" : "integer", "format" : "int32", - "example" : 1620219211, + "example" : 1640995199, "minimum" : 0, "maximum" : 18446744073709551615 } @@ -7437,11 +8862,6 @@ "TransactionRecord" : { "type" : "object", "properties" : { - "_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Transaction identifier" - }, "_type" : { "type" : "string", "example" : "101", @@ -7454,10 +8874,15 @@ }, "created_at" : { "type" : "string", - "example" : "2021-05-05 12:53:31Z", + "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" : { @@ -7466,7 +8891,9 @@ "attach_id" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", "format" : "dif/endorse-transaction/request@v1.0" }, - "properties" : { } + "additionalProperties" : { + "type" : "string" + } } }, "messages_attach" : { @@ -7531,9 +8958,14 @@ "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-05-05 12:53:31Z", + "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|)$" } @@ -7550,6 +8982,28 @@ } } }, + "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" : { @@ -7599,6 +9053,38 @@ } } }, + "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" ], @@ -7681,14 +9167,12 @@ }, "created_at" : { "type" : "string", - "example" : "2021-05-05 12:53:31Z", + "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" : { - "type" : "object", - "description" : "Credential as stored", - "properties" : { } + "$ref" : "#/definitions/V10CredentialExchange_credential" }, "credential_definition_id" : { "type" : "string", @@ -7707,24 +9191,16 @@ "description" : "Credential identifier" }, "credential_offer" : { - "type" : "object", - "description" : "(Indy) credential offer", - "properties" : { } + "$ref" : "#/definitions/V10CredentialExchange_credential_offer" }, "credential_offer_dict" : { - "type" : "object", - "description" : "Serialized credential offer message", - "properties" : { } + "$ref" : "#/definitions/V10CredentialExchange_credential_offer_dict" }, "credential_proposal_dict" : { - "type" : "object", - "description" : "Serialized credential proposal message", - "properties" : { } + "$ref" : "#/definitions/V10CredentialExchange_credential_proposal_dict" }, "credential_request" : { - "type" : "object", - "description" : "(Indy) credential request", - "properties" : { } + "$ref" : "#/definitions/V10CredentialExchange_credential_request" }, "credential_request_metadata" : { "type" : "object", @@ -7748,9 +9224,7 @@ "description" : "Parent thread identifier" }, "raw_credential" : { - "type" : "object", - "description" : "Credential as received, prior to storage in holder wallet", - "properties" : { } + "$ref" : "#/definitions/V10CredentialExchange_raw_credential" }, "revoc_reg_id" : { "type" : "string", @@ -7788,7 +9262,7 @@ }, "updated_at" : { "type" : "string", - "example" : "2021-05-05 12:53:31Z", + "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|)$" } @@ -8033,7 +9507,7 @@ }, "created_at" : { "type" : "string", - "example" : "2021-05-05 12:53:31Z", + "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|)$" }, @@ -8049,9 +9523,7 @@ "enum" : [ "self", "external" ] }, "presentation" : { - "type" : "object", - "description" : "(Indy) presentation (also known as proof)", - "properties" : { } + "$ref" : "#/definitions/V10PresentationExchange_presentation" }, "presentation_exchange_id" : { "type" : "string", @@ -8059,19 +9531,13 @@ "description" : "Presentation exchange identifier" }, "presentation_proposal_dict" : { - "type" : "object", - "description" : "Serialized presentation proposal message", - "properties" : { } + "$ref" : "#/definitions/V10PresentationExchange_presentation_proposal_dict" }, "presentation_request" : { - "type" : "object", - "description" : "(Indy) presentation request (also known as proof request)", - "properties" : { } + "$ref" : "#/definitions/V10PresentationExchange_presentation_request" }, "presentation_request_dict" : { - "type" : "object", - "description" : "Serialized presentation request message", - "properties" : { } + "$ref" : "#/definitions/V10PresentationExchange_presentation_request_dict" }, "role" : { "type" : "string", @@ -8095,7 +9561,7 @@ }, "updated_at" : { "type" : "string", - "example" : "2021-05-05 12:53:31Z", + "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|)$" }, @@ -8214,6 +9680,37 @@ } } }, + "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" : { @@ -8233,10 +9730,7 @@ "description" : "Issuer choice to remove this credential exchange record when complete" }, "by_format" : { - "type" : "object", - "description" : "Attachment content by format for proposal, offer, request, and issue", - "readOnly" : true, - "properties" : { } + "$ref" : "#/definitions/V20CredExRecord_by_format" }, "connection_id" : { "type" : "string", @@ -8245,7 +9739,7 @@ }, "created_at" : { "type" : "string", - "example" : "2021-05-05 12:53:31Z", + "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|)$" }, @@ -8254,36 +9748,20 @@ "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", "description" : "Credential exchange identifier" }, - "cred_id_stored" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Credential identifier stored in wallet" - }, "cred_issue" : { - "type" : "object", - "description" : "Serialized credential issue message", - "properties" : { } + "$ref" : "#/definitions/V20CredExRecord_cred_issue" }, "cred_offer" : { - "type" : "object", - "description" : "Serialized credential offer message", - "properties" : { } + "$ref" : "#/definitions/V10CredentialExchange_credential_offer_dict" }, "cred_preview" : { - "type" : "object", - "description" : "Serialized credential preview from credential proposal", - "readOnly" : true, - "properties" : { } + "$ref" : "#/definitions/V20CredExRecord_cred_preview" }, "cred_proposal" : { - "type" : "object", - "description" : "Serialized credential proposal message", - "properties" : { } + "$ref" : "#/definitions/V10CredentialExchange_credential_proposal_dict" }, "cred_request" : { - "type" : "object", - "description" : "Serialized credential request message", - "properties" : { } + "$ref" : "#/definitions/V20CredExRecord_cred_request" }, "error_msg" : { "type" : "string", @@ -8324,12 +9802,33 @@ }, "updated_at" : { "type" : "string", - "example" : "2021-05-05 12:53:31Z", + "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" : { @@ -8349,7 +9848,7 @@ "properties" : { "created_at" : { "type" : "string", - "example" : "2021-05-05 12:53:31Z", + "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|)$" }, @@ -8392,7 +9891,7 @@ }, "updated_at" : { "type" : "string", - "example" : "2021-05-05 12:53:31Z", + "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|)$" } @@ -8403,7 +9902,7 @@ "properties" : { "created_at" : { "type" : "string", - "example" : "2021-05-05 12:53:31Z", + "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|)$" }, @@ -8429,7 +9928,7 @@ }, "updated_at" : { "type" : "string", - "example" : "2021-05-05 12:53:31Z", + "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|)$" } @@ -8507,6 +10006,63 @@ } } }, + "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" ], @@ -8526,6 +10082,79 @@ } } }, + "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" ], @@ -8561,19 +10190,94 @@ } } }, - "V20CredPreview" : { + "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" : [ "attributes" ], + "required" : [ "formats", "requests~attach" ], "properties" : { + "@id" : { + "type" : "string", + "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "description" : "Message identifier" + }, "@type" : { "type" : "string", - "example" : "issue-credential/2.0/credential-preview", - "description" : "Message type identifier" + "example" : "https://didcomm.org/my-family/1.0/my-message-type", + "description" : "Message type", + "readOnly" : true }, - "attributes" : { + "comment" : { + "type" : "string", + "description" : "Human-readable comment", + "x-nullable" : true + }, + "formats" : { "type" : "array", + "description" : "Acceptable attachment formats", "items" : { - "$ref" : "#/definitions/V20CredAttrSpec" + "$ref" : "#/definitions/V20CredFormat" + } + }, + "requests~attach" : { + "type" : "array", + "description" : "Request attachments", + "items" : { + "$ref" : "#/definitions/AttachDecorator" } } } @@ -8600,6 +10304,12 @@ "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, @@ -8607,6 +10317,17 @@ } } }, + "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" : { @@ -8643,6 +10364,41 @@ "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" ], @@ -8670,10 +10426,7 @@ "description" : "Prover choice to auto-present proof as verifier requests" }, "by_format" : { - "type" : "object", - "description" : "Attachment content by format for proposal, request, and presentation", - "readOnly" : true, - "properties" : { } + "$ref" : "#/definitions/V20PresExRecord_by_format" }, "connection_id" : { "type" : "string", @@ -8682,7 +10435,7 @@ }, "created_at" : { "type" : "string", - "example" : "2021-05-05 12:53:31Z", + "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|)$" }, @@ -8698,9 +10451,7 @@ "enum" : [ "self", "external" ] }, "pres" : { - "type" : "object", - "description" : "Serialized presentation message", - "properties" : { } + "$ref" : "#/definitions/V20PresExRecord_pres" }, "pres_ex_id" : { "type" : "string", @@ -8708,14 +10459,10 @@ "description" : "Presentation exchange identifier" }, "pres_proposal" : { - "type" : "object", - "description" : "Serialized presentation proposal message", - "properties" : { } + "$ref" : "#/definitions/V10PresentationExchange_presentation_proposal_dict" }, "pres_request" : { - "type" : "object", - "description" : "Serialized presentation request message", - "properties" : { } + "$ref" : "#/definitions/V10PresentationExchange_presentation_request_dict" }, "role" : { "type" : "string", @@ -8739,7 +10486,7 @@ }, "updated_at" : { "type" : "string", - "example" : "2021-05-05 12:53:31Z", + "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|)$" }, @@ -8751,6 +10498,23 @@ } } }, + "V20PresExRecordByFormat" : { + "type" : "object", + "properties" : { + "pres" : { + "type" : "object", + "properties" : { } + }, + "pres_proposal" : { + "type" : "object", + "properties" : { } + }, + "pres_request" : { + "type" : "object", + "properties" : { } + } + } + }, "V20PresExRecordList" : { "type" : "object", "properties" : { @@ -8763,6 +10527,22 @@ } } }, + "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" ], @@ -8772,6 +10552,40 @@ } } }, + "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" : { @@ -8812,6 +10626,44 @@ } } }, + "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" : { @@ -8874,7 +10726,7 @@ "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]*)?(/[^?&#]+)?$" + "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" } }, "cred_tags" : { @@ -8889,6 +10741,14 @@ "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", @@ -8927,14 +10787,6 @@ "example" : "did:example:ebfeb1f712ebc6f1c276e12ec21", "description" : "Subject identifier" } - }, - "types" : { - "type" : "array", - "items" : { - "type" : "string", - "example" : "VerifiableCredential", - "description" : "Type" - } } } }, @@ -8954,9 +10806,7 @@ "required" : [ "doc" ], "properties" : { "doc" : { - "type" : "object", - "description" : "Credential to verify", - "properties" : { } + "$ref" : "#/definitions/VerifyRequest_doc" }, "verkey" : { "type" : "string", @@ -8986,7 +10836,7 @@ "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]*)?(/[^?&#]+)?$" + "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" } }, "given_id" : { @@ -9017,7 +10867,7 @@ "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]*)?(/[^?&#]+)?$" + "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" } }, "subject_ids" : { @@ -9042,7 +10892,7 @@ "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]*)?(/[^?&#]+)?$" + "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" } } } @@ -9068,7 +10918,7 @@ "properties" : { "created_at" : { "type" : "string", - "example" : "2021-05-05 12:53:31Z", + "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|)$" }, @@ -9089,7 +10939,7 @@ }, "updated_at" : { "type" : "string", - "example" : "2021-05-05 12:53:31Z", + "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|)$" }, @@ -9108,11 +10958,23 @@ "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" @@ -9121,6 +10983,14 @@ "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" @@ -9129,10 +10999,60 @@ "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" @@ -9151,14 +11071,30 @@ "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" @@ -9171,6 +11107,46 @@ "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" @@ -9179,6 +11155,22 @@ "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" @@ -9191,6 +11183,18 @@ "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" @@ -9209,11 +11213,15 @@ }, "V20PresSpecByFormatRequest_dif" : { "type" : "object", - "description" : "Presentation specification for DIF" + "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 diff --git a/requirements.askar.txt b/requirements.askar.txt index 55721751ad..3c14ab546b 100644 --- a/requirements.askar.txt +++ b/requirements.askar.txt @@ -1,3 +1,3 @@ -aries-askar~=0.2.0 +aries-askar~=0.2.2 indy-credx~=0.3 -indy-vdr~=0.3 +indy-vdr~=0.3.2