From c5ffa46dbbbff35a5980ff26b3254b12117502ce Mon Sep 17 00:00:00 2001 From: Adam Burdett Date: Tue, 15 Mar 2022 16:05:44 -0600 Subject: [PATCH 1/8] multitenant key derivation method Signed-off-by: Adam Burdett --- aries_cloudagent/config/argparse.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index a8b6ae5110..8c203a04e0 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -1623,7 +1623,7 @@ def add_arguments(self, parser: ArgumentParser): help=( 'Specify multitenancy configuration ("wallet_type" and "wallet_name"). ' 'For example: "{"wallet_type":"askar-profile","wallet_name":' - '"askar-profile-name"}"' + '"askar-profile-name", "key":"key", "key_derivation_method":"RAW"}"' '"wallet_name" is only used when "wallet_type" is "askar-profile"' ), ) @@ -1656,6 +1656,16 @@ def get_settings(self, args: Namespace): settings["multitenant.wallet_name"] = multitenancyConfig.get( "wallet_name" ) + + if multitenancyConfig.get("key"): + settings["multitenant.key"] = multitenancyConfig.get( + "key" + ) + + if multitenancyConfig.get("key_derivation_method"): + settings["multitenant.key_derivation_method"] = multitenancyConfig.get( + "key_derivation_method" + ) return settings From 69b913d238c893931380fcf69e5e23b2e2b3874d Mon Sep 17 00:00:00 2001 From: Adam Burdett Date: Wed, 23 Mar 2022 08:35:33 -0600 Subject: [PATCH 2/8] multitenant routes with key der Signed-off-by: Adam Burdett --- aries_cloudagent/config/argparse.py | 14 ++++------ aries_cloudagent/multitenant/admin/routes.py | 29 ++++++++++++-------- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index 8c203a04e0..e9a3a82641 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -1656,16 +1656,14 @@ def get_settings(self, args: Namespace): settings["multitenant.wallet_name"] = multitenancyConfig.get( "wallet_name" ) - + if multitenancyConfig.get("key"): - settings["multitenant.key"] = multitenancyConfig.get( - "key" - ) - + settings["multitenant.key"] = multitenancyConfig.get("key") + if multitenancyConfig.get("key_derivation_method"): - settings["multitenant.key_derivation_method"] = multitenancyConfig.get( - "key_derivation_method" - ) + settings[ + "multitenant.key_derivation_method" + ] = multitenancyConfig.get("key_derivation_method") return settings diff --git a/aries_cloudagent/multitenant/admin/routes.py b/aries_cloudagent/multitenant/admin/routes.py index fd0a8a6b3e..959cc36b4e 100644 --- a/aries_cloudagent/multitenant/admin/routes.py +++ b/aries_cloudagent/multitenant/admin/routes.py @@ -3,25 +3,23 @@ from aiohttp import web from aiohttp_apispec import ( docs, - request_schema, match_info_schema, - response_schema, querystring_schema, + request_schema, + response_schema, ) -from marshmallow import fields, validate, validates_schema, ValidationError +from marshmallow import ValidationError, fields, validate, validates_schema from ...admin.request_context import AdminRequestContext -from ...messaging.valid import JSONWebToken, UUIDFour +from ...core.error import BaseError +from ...core.profile import ProfileManagerProvider from ...messaging.models.base import BaseModelError from ...messaging.models.openapi import OpenAPISchema +from ...messaging.valid import JSONWebToken, UUIDFour from ...multitenant.base import BaseMultitenantManager from ...storage.error import StorageError, StorageNotFoundError -from ...wallet.models.wallet_record import WalletRecord, WalletRecordSchema from ...wallet.error import WalletSettingsError - -from ...core.error import BaseError -from ...core.profile import ProfileManagerProvider - +from ...wallet.models.wallet_record import WalletRecord, WalletRecordSchema from ..error import WalletKeyMissingError @@ -58,6 +56,13 @@ class CreateWalletRequestSchema(OpenAPISchema): description="Master key used for key derivation.", example="MySecretKey123" ) + wallet_key_derivation = fields.Str( + description="Key derivation", + example="RAW", + default="ARGON2I_MOD", + validate=validate.OneOf(["ARGON2I_MOD", "ARGON2I_INT", "RAW"]), + ) + wallet_type = fields.Str( description="Type of the wallet to create", example="indy", @@ -225,8 +230,7 @@ async def wallets_list(request: web.BaseRequest): profile = context.profile query = {} - wallet_name = request.query.get("wallet_name") - if wallet_name: + if wallet_name := request.query.get("wallet_name"): query["wallet_name"] = wallet_name try: @@ -297,6 +301,9 @@ async def wallet_create(request: web.BaseRequest): "wallet.type": body.get("wallet_type") or "in_memory", "wallet.name": body.get("wallet_name"), "wallet.key": wallet_key, + "wallet.key_derivation_method": body.get( + "wallet_key_derivation", "ARGON2I_MOD" + ), "wallet.webhook_urls": wallet_webhook_urls, "wallet.dispatch_type": wallet_dispatch_type, } From 70f535417fbcf05ff2432469630882bd78eb02f9 Mon Sep 17 00:00:00 2001 From: Adam Burdett Date: Wed, 23 Mar 2022 08:51:38 -0600 Subject: [PATCH 3/8] multitenant wallet record key derivation prop Signed-off-by: Adam Burdett --- aries_cloudagent/wallet/models/wallet_record.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/wallet/models/wallet_record.py b/aries_cloudagent/wallet/models/wallet_record.py index 3421968d34..3620fb54f3 100644 --- a/aries_cloudagent/wallet/models/wallet_record.py +++ b/aries_cloudagent/wallet/models/wallet_record.py @@ -36,7 +36,7 @@ def __init__( wallet_id: str = None, key_management_mode: str = None, settings: dict = None, - # MTODO: how to make this a tag without making it + # TODO: how to make this a tag without making it # a constructor param wallet_name: str = None, **kwargs, @@ -81,6 +81,11 @@ def wallet_key(self) -> Optional[str]: """Accessor for the key of the wallet.""" return self.settings.get("wallet.key") + @property + def wallet_key_derivation_method(self): + """Accessor for the key derivation method of the wallet.""" + return self.settings.get("wallet.key_derivation_method") + @property def record_value(self) -> dict: """Accessor for the JSON record value generated for this record.""" From ce58a8f9d30172a7931755d0f2870b25f6ea095c Mon Sep 17 00:00:00 2001 From: Adam Burdett Date: Wed, 30 Mar 2022 00:32:46 -0600 Subject: [PATCH 4/8] fixed tests assertions Signed-off-by: Adam Burdett --- aries_cloudagent/multitenant/admin/routes.py | 3 ++- aries_cloudagent/multitenant/admin/tests/test_routes.py | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/multitenant/admin/routes.py b/aries_cloudagent/multitenant/admin/routes.py index 959cc36b4e..890eb29cc1 100644 --- a/aries_cloudagent/multitenant/admin/routes.py +++ b/aries_cloudagent/multitenant/admin/routes.py @@ -230,7 +230,8 @@ async def wallets_list(request: web.BaseRequest): profile = context.profile query = {} - if wallet_name := request.query.get("wallet_name"): + wallet_name = request.query.get("wallet_name") + if wallet_name: query["wallet_name"] = wallet_name try: diff --git a/aries_cloudagent/multitenant/admin/tests/test_routes.py b/aries_cloudagent/multitenant/admin/tests/test_routes.py index c1f50f49c3..d2299b8ff5 100644 --- a/aries_cloudagent/multitenant/admin/tests/test_routes.py +++ b/aries_cloudagent/multitenant/admin/tests/test_routes.py @@ -144,6 +144,7 @@ async def test_wallet_create(self): "wallet_name": "test", "wallet_type": "indy", "wallet_key": "test", + "key_derivation_method": "ARGON2I_MOD", "key_management_mode": "managed", "wallet_webhook_urls": [], "wallet_dispatch_type": "base", @@ -175,6 +176,7 @@ async def test_wallet_create(self): "wallet.name": body["wallet_name"], "wallet.type": body["wallet_type"], "wallet.key": body["wallet_key"], + 'wallet.key_derivation_method': body["key_derivation_method"], "wallet.webhook_urls": body["wallet_webhook_urls"], "wallet.dispatch_type": body["wallet_dispatch_type"], }, @@ -206,6 +208,7 @@ async def test_wallet_create_optional_default_fields(self): body = { "wallet_name": "test", "wallet_key": "test", + "key_derivation_method": "ARGON2I_MOD", "wallet_webhook_urls": [], "wallet_dispatch_type": "base", "label": "my_test_label", @@ -223,6 +226,7 @@ async def test_wallet_create_optional_default_fields(self): "wallet.name": body["wallet_name"], "wallet.type": "in_memory", "wallet.key": body["wallet_key"], + 'wallet.key_derivation_method': body["key_derivation_method"], "default_label": body["label"], "image_url": body["image_url"], "wallet.webhook_urls": body["wallet_webhook_urls"], From 65ea610a4d6ff21522cca1654ef74045ebe75248 Mon Sep 17 00:00:00 2001 From: Adam Burdett Date: Mon, 4 Apr 2022 17:25:17 -0600 Subject: [PATCH 5/8] atomic subwallet creation Signed-off-by: Adam Burdett --- aries_cloudagent/config/argparse.py | 5 +-- aries_cloudagent/multitenant/admin/routes.py | 10 ++--- .../multitenant/admin/tests/test_routes.py | 33 ++++++++++++++-- aries_cloudagent/multitenant/base.py | 39 ++++++++++--------- 4 files changed, 56 insertions(+), 31 deletions(-) diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index e9a3a82641..b3d7d7f3dc 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -1623,7 +1623,7 @@ def add_arguments(self, parser: ArgumentParser): help=( 'Specify multitenancy configuration ("wallet_type" and "wallet_name"). ' 'For example: "{"wallet_type":"askar-profile","wallet_name":' - '"askar-profile-name", "key":"key", "key_derivation_method":"RAW"}"' + '"askar-profile-name", "key_derivation_method":"RAW"}"' '"wallet_name" is only used when "wallet_type" is "askar-profile"' ), ) @@ -1657,9 +1657,6 @@ def get_settings(self, args: Namespace): "wallet_name" ) - if multitenancyConfig.get("key"): - settings["multitenant.key"] = multitenancyConfig.get("key") - if multitenancyConfig.get("key_derivation_method"): settings[ "multitenant.key_derivation_method" diff --git a/aries_cloudagent/multitenant/admin/routes.py b/aries_cloudagent/multitenant/admin/routes.py index 890eb29cc1..98ebaa2b36 100644 --- a/aries_cloudagent/multitenant/admin/routes.py +++ b/aries_cloudagent/multitenant/admin/routes.py @@ -58,8 +58,8 @@ class CreateWalletRequestSchema(OpenAPISchema): wallet_key_derivation = fields.Str( description="Key derivation", + required=False, example="RAW", - default="ARGON2I_MOD", validate=validate.OneOf(["ARGON2I_MOD", "ARGON2I_INT", "RAW"]), ) @@ -107,7 +107,7 @@ class CreateWalletRequestSchema(OpenAPISchema): description="Key management method to use for this wallet.", example=WalletRecord.MODE_MANAGED, default=WalletRecord.MODE_MANAGED, - # MTODO: add unmanaged mode once implemented + # TODO: add unmanaged mode once implemented validate=validate.OneOf((WalletRecord.MODE_MANAGED,)), ) @@ -302,19 +302,19 @@ async def wallet_create(request: web.BaseRequest): "wallet.type": body.get("wallet_type") or "in_memory", "wallet.name": body.get("wallet_name"), "wallet.key": wallet_key, - "wallet.key_derivation_method": body.get( - "wallet_key_derivation", "ARGON2I_MOD" - ), "wallet.webhook_urls": wallet_webhook_urls, "wallet.dispatch_type": wallet_dispatch_type, } label = body.get("label") image_url = body.get("image_url") + key_derivation = body.get("wallet_key_derivation") if label: settings["default_label"] = label if image_url: settings["image_url"] = image_url + if key_derivation: # allow lower levels to handle default + settings["wallet.key_derivation_method"] = key_derivation try: multitenant_mgr = context.profile.inject(BaseMultitenantManager) diff --git a/aries_cloudagent/multitenant/admin/tests/test_routes.py b/aries_cloudagent/multitenant/admin/tests/test_routes.py index d2299b8ff5..cf447e5cbb 100644 --- a/aries_cloudagent/multitenant/admin/tests/test_routes.py +++ b/aries_cloudagent/multitenant/admin/tests/test_routes.py @@ -142,9 +142,9 @@ async def test_wallets_list_query(self): async def test_wallet_create(self): body = { "wallet_name": "test", + "default_label": "test_label", "wallet_type": "indy", "wallet_key": "test", - "key_derivation_method": "ARGON2I_MOD", "key_management_mode": "managed", "wallet_webhook_urls": [], "wallet_dispatch_type": "base", @@ -176,7 +176,6 @@ async def test_wallet_create(self): "wallet.name": body["wallet_name"], "wallet.type": body["wallet_type"], "wallet.key": body["wallet_key"], - 'wallet.key_derivation_method': body["key_derivation_method"], "wallet.webhook_urls": body["wallet_webhook_urls"], "wallet.dispatch_type": body["wallet_dispatch_type"], }, @@ -208,7 +207,7 @@ async def test_wallet_create_optional_default_fields(self): body = { "wallet_name": "test", "wallet_key": "test", - "key_derivation_method": "ARGON2I_MOD", + "wallet_key_derivation": "ARGON2I_MOD", "wallet_webhook_urls": [], "wallet_dispatch_type": "base", "label": "my_test_label", @@ -226,11 +225,37 @@ async def test_wallet_create_optional_default_fields(self): "wallet.name": body["wallet_name"], "wallet.type": "in_memory", "wallet.key": body["wallet_key"], - 'wallet.key_derivation_method': body["key_derivation_method"], "default_label": body["label"], "image_url": body["image_url"], "wallet.webhook_urls": body["wallet_webhook_urls"], "wallet.dispatch_type": body["wallet_dispatch_type"], + "wallet.key_derivation_method": body["wallet_key_derivation"], + }, + WalletRecord.MODE_MANAGED, + ) + + + async def test_wallet_create_raw_key_derivation(self): + body = { + "wallet_name": "test", + "wallet_key": "test", + "wallet_key_derivation": "RAW", + } + self.request.json = async_mock.CoroutineMock(return_value=body) + + with async_mock.patch.object(test_module.web, "json_response") as mock_response: + self.mock_multitenant_mgr.create_wallet = async_mock.CoroutineMock() + self.mock_multitenant_mgr.create_auth_token = async_mock.Mock() + + await test_module.wallet_create(self.request) + self.mock_multitenant_mgr.create_wallet.assert_called_once_with( + { + "wallet.type": "in_memory", + "wallet.name": body["wallet_name"], + "wallet.key": body["wallet_key"], + "wallet.key_derivation_method": body["wallet_key_derivation"], + "wallet.webhook_urls": [], + "wallet.dispatch_type": "base", }, WalletRecord.MODE_MANAGED, ) diff --git a/aries_cloudagent/multitenant/base.py b/aries_cloudagent/multitenant/base.py index 5bdd9a9787..35cf332b9d 100644 --- a/aries_cloudagent/multitenant/base.py +++ b/aries_cloudagent/multitenant/base.py @@ -183,26 +183,29 @@ async def create_wallet( ) await wallet_record.save(session) + try: + # provision wallet + profile = await self.get_wallet_profile( + self._profile.context, + wallet_record, + { + "wallet.key": wallet_key, + }, + provision=True, + ) - # provision wallet - profile = await self.get_wallet_profile( - self._profile.context, - wallet_record, - { - "wallet.key": wallet_key, - }, - provision=True, - ) - - # subwallet context - async with profile.session() as session: - wallet = session.inject(BaseWallet) - public_did_info = await wallet.get_public_did() + # subwallet context + async with profile.session() as session: + wallet = session.inject(BaseWallet) + public_did_info = await wallet.get_public_did() - if public_did_info: - await self.add_key( - wallet_record.wallet_id, public_did_info.verkey, skip_if_exists=True - ) + if public_did_info: + await self.add_key( + wallet_record.wallet_id, public_did_info.verkey, skip_if_exists=True + ) + except Exception: + await wallet_record.delete_record(session) + raise return wallet_record From d90c141b9223e7be81b2b19101e79cba389e65e8 Mon Sep 17 00:00:00 2001 From: Adam Burdett Date: Mon, 4 Apr 2022 17:31:29 -0600 Subject: [PATCH 6/8] Todo for subwallets Signed-off-by: Adam Burdett --- aries_cloudagent/multitenant/admin/routes.py | 2 +- aries_cloudagent/wallet/models/wallet_record.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/multitenant/admin/routes.py b/aries_cloudagent/multitenant/admin/routes.py index 98ebaa2b36..5d016b9fb6 100644 --- a/aries_cloudagent/multitenant/admin/routes.py +++ b/aries_cloudagent/multitenant/admin/routes.py @@ -107,7 +107,7 @@ class CreateWalletRequestSchema(OpenAPISchema): description="Key management method to use for this wallet.", example=WalletRecord.MODE_MANAGED, default=WalletRecord.MODE_MANAGED, - # TODO: add unmanaged mode once implemented + # MTODO: add unmanaged mode once implemented validate=validate.OneOf((WalletRecord.MODE_MANAGED,)), ) diff --git a/aries_cloudagent/wallet/models/wallet_record.py b/aries_cloudagent/wallet/models/wallet_record.py index 3620fb54f3..52aa8496ab 100644 --- a/aries_cloudagent/wallet/models/wallet_record.py +++ b/aries_cloudagent/wallet/models/wallet_record.py @@ -36,7 +36,7 @@ def __init__( wallet_id: str = None, key_management_mode: str = None, settings: dict = None, - # TODO: how to make this a tag without making it + # MTODO: how to make this a tag without making it # a constructor param wallet_name: str = None, **kwargs, From d725a139e8794ae38a4619ee52b58310a1119df1 Mon Sep 17 00:00:00 2001 From: Adam Burdett Date: Thu, 7 Apr 2022 09:02:07 -0600 Subject: [PATCH 7/8] formatting Signed-off-by: Adam Burdett --- aries_cloudagent/multitenant/admin/tests/test_routes.py | 1 - 1 file changed, 1 deletion(-) diff --git a/aries_cloudagent/multitenant/admin/tests/test_routes.py b/aries_cloudagent/multitenant/admin/tests/test_routes.py index cf447e5cbb..faae8d6d3b 100644 --- a/aries_cloudagent/multitenant/admin/tests/test_routes.py +++ b/aries_cloudagent/multitenant/admin/tests/test_routes.py @@ -234,7 +234,6 @@ async def test_wallet_create_optional_default_fields(self): WalletRecord.MODE_MANAGED, ) - async def test_wallet_create_raw_key_derivation(self): body = { "wallet_name": "test", From 45de35637ce6cc24799551568ebdb933986122a9 Mon Sep 17 00:00:00 2001 From: Adam Burdett Date: Thu, 14 Apr 2022 15:06:17 -0600 Subject: [PATCH 8/8] async mock,'As it should be' Signed-off-by: Adam Burdett --- aries_cloudagent/multitenant/admin/tests/test_routes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aries_cloudagent/multitenant/admin/tests/test_routes.py b/aries_cloudagent/multitenant/admin/tests/test_routes.py index 37887d5667..98ad47dca2 100644 --- a/aries_cloudagent/multitenant/admin/tests/test_routes.py +++ b/aries_cloudagent/multitenant/admin/tests/test_routes.py @@ -244,7 +244,7 @@ async def test_wallet_create_raw_key_derivation(self): with async_mock.patch.object(test_module.web, "json_response") as mock_response: self.mock_multitenant_mgr.create_wallet = async_mock.CoroutineMock() - self.mock_multitenant_mgr.create_auth_token = async_mock.Mock() + self.mock_multitenant_mgr.create_auth_token = async_mock.CoroutineMock() await test_module.wallet_create(self.request) self.mock_multitenant_mgr.create_wallet.assert_called_once_with(