Skip to content

Commit

Permalink
Showing 59 changed files with 1,489 additions and 285 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ repos:
args: ["--config", ".commitlint.config.js"]
additional_dependencies: ['@commitlint/config-conventional']
- repo: https://github.com/psf/black
rev: 20.8b1
rev: 22.3.0
hooks:
- id: black
stages: [commit]
148 changes: 148 additions & 0 deletions Multitenancy.md
Original file line number Diff line number Diff line change
@@ -24,6 +24,9 @@ This allows ACA-Py to be used for a wider range of use cases. One use case could
- [Getting a token](#getting-a-token)
- [JWT Secret](#jwt-secret)
- [SwaggerUI](#swaggerui)
- [Tenant Management](#tenant-management)
- [Update a tenant](#update-a-tenant)
- [Remove a tenant](#remove-a-tenant)

## General Concept

@@ -203,6 +206,79 @@ The `Authorization` header is in addition to the Admin API key. So if the `admin

A token can be obtained in two ways. The first method is the `token` parameter from the response of the create wallet (`POST /multitenancy/wallet`) endpoint. The second option is using the get wallet token endpoint (`POST /multitenancy/wallet/{wallet_id}/token`) endpoint.

#### Method 1: Register new tenant

This is the method you use to obtain a token when you haven't already registered a tenant. In this process you will first register a tenant then an object containing your tenant `token` as well as other useful information like your `wallet id` will be returned to you.

Example

```jsonc
new_tenant='{
"image_url": "https://aries.ca/images/sample.png",
"key_management_mode": "managed",
"label": "example-label-02",
"wallet_dispatch_type": "default",
"wallet_key": "example-encryption-key-02",
"wallet_name": "example-name-02",
"wallet_type": "askar",
"wallet_webhook_urls": [
"https://example.com/webhook"
]
}'
```

```
echo $new_tenant | curl -X POST "${ACAPY_ADMIN_URL}/multitenancy/wallet" \
-H "Content-Type: application/json" \
-H "X-Api-Key: $ACAPY_ADMIN_URL_API_KEY" \
-d @-
```

**`Response`**

```jsonc
{
"settings": {
"wallet.type": "askar",
"wallet.name": "example-name-02",
"wallet.webhook_urls": [
"https://example.com/webhook"
],
"wallet.dispatch_type": "default",
"default_label": "example-label-02",
"image_url": "https://aries.ca/images/sample.png",
"wallet.id": "3b64ad0d-f556-4c04-92bc-cd95bfde58cd"
},
"key_management_mode": "managed",
"updated_at": "2022-04-01T15:12:35.474975Z",
"wallet_id": "3b64ad0d-f556-4c04-92bc-cd95bfde58cd",
"created_at": "2022-04-01T15:12:35.474975Z",
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3YWxsZXRfaWQiOiIzYjY0YWQwZC1mNTU2LTRjMDQtOTJiYy1jZDk1YmZkZTU4Y2QifQ.A4eWbSR2M1Z6mbjcSLOlciBuUejehLyytCVyeUlxI0E"
}
```


#### Method 2: Get tenant token

This method allows you to retrieve a tenant `token` for an already registered tenant. To retrieve a token you will need an Admin API key (if your admin is protected with one), `wallet_key` and the `wallet_id` of the tenant.

Example

```
curl -X POST "${ACAPY_ADMIN_URL}/multitenancy/wallet/{wallet_id}/token" \
-H "Content-Type: application/json" \
-H "X-Api-Key: $ACAPY_ADMIN_URL_API_KEY" \
-d { "wallet_key": "example-encryption-key-02" }
```

**`Response`**

```jsonc
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3YWxsZXRfaWQiOiIzYjY0YWQwZC1mNTU2LTRjMDQtOTJiYy1jZDk1YmZkZTU4Y2QifQ.A4eWbSR2M1Z6mbjcSLOlciBuUejehLyytCVyeUlxI0E"
}
```

In unmanaged mode, the get token endpoint also requires the `wallet_key` parameter to be included in the request body. The wallet key will be included in the JWT so the wallet can be unlocked when making requests to the admin API.

```jsonc
@@ -224,3 +300,75 @@ For deterministic JWT creation and verification between restarts and multiple in
When using the SwaggerUI you can click the :lock: icon next to each of the endpoints or the `Authorize` button at the top to set the correct authentication headers. Make sure to also include the `Bearer ` part in the input field. This won't be automatically added.

![](/docs/assets/adminApiAuthentication.png)

## Tenant Management

After registering a tenant which effectively creates a subwallet, you may need to update the tenant information or delete it. The following describes how to accomplish both goals.

### Update a tenant

The following properties can be updated: `image_url`, `label`, `wallet_dispatch_type`, and `wallet_webhook_urls` for tenants of a multitenancy wallet. To update these properties you will `PUT` a request json containing the properties you wish to update along with the updated values to the `/multitenancy/wallet/${TENANT_WALLET_ID}` admin endpoint. If the Admin API endoint is protected, you will also include the Admin API Key in the request header.

Example

```jsonc
update_tenant='{
"image_url": "https://aries.ca/images/sample-updated.png",
"label": "example-label-02-updated",
"wallet_webhook_urls": [
"https://example.com/webhook/updated"
]
}'
```

```
echo $update_tenant | curl -X PUT "${ACAPY_ADMIN_URL}/multitenancy/wallet/${TENANT_WALLET_ID}" \
-H "Content-Type: application/json" \
-H "x-api-key: $ACAPY_ADMIN_URL_API_KEY" \
-d @-
```

**`Response`**

```jsonc
{
"settings": {
"wallet.type": "askar",
"wallet.name": "example-name-02",
"wallet.webhook_urls": [
"https://example.com/webhook/updated"
],
"wallet.dispatch_type": "default",
"default_label": "example-label-02-updated",
"image_url": "https://aries.ca/images/sample-updated.png",
"wallet.id": "3b64ad0d-f556-4c04-92bc-cd95bfde58cd"
},
"key_management_mode": "managed",
"updated_at": "2022-04-01T16:23:58.642004Z",
"wallet_id": "3b64ad0d-f556-4c04-92bc-cd95bfde58cd",
"created_at": "2022-04-01T15:12:35.474975Z"
}
```
> An Admin API Key is all that is ALLOWED to be included in a request header during an update. Inluding the Bearer token header will result in a 404: Unauthorized error
## Remove a tenant

The following information is required to delete a tenant:
- wallet_id
- wallet_key
- {Admin_Api_Key} if admin is protected

Example

```
curl -X POST "${ACAPY_ADMIN_URL}/multitenancy/wallet/{wallet_id}/remove" \
-H "Content-Type: application/json" \
-H "x-api-key: $ACAPY_ADMIN_URL_API_KEY" \
-d '{ "wallet_key": "example-encryption-key-02" }'
```

**`Response`**

```jsonc
{}
```
11 changes: 11 additions & 0 deletions aries_cloudagent/config/argparse.py
Original file line number Diff line number Diff line change
@@ -1423,6 +1423,15 @@ def add_arguments(self, parser: ArgumentParser):
"to use with a Hyperledger Indy ledger."
),
)
parser.add_argument(
"--wallet-allow-insecure-seed",
action="store_true",
env_var="ACAPY_WALLET_ALLOW_INSECURE_SEED",
help=(
"If this parameter is set, allows to use a custom seed "
"to create a local DID"
),
)
parser.add_argument(
"--wallet-key",
type=str,
@@ -1543,6 +1552,8 @@ def get_settings(self, args: Namespace) -> dict:
settings["wallet.seed"] = args.seed
if args.wallet_local_did:
settings["wallet.local_did"] = True
if args.wallet_allow_insecure_seed:
settings["wallet.allow_insecure_seed"] = True
if args.wallet_key:
settings["wallet.key"] = args.wallet_key
if args.wallet_rekey:
13 changes: 11 additions & 2 deletions aries_cloudagent/indy/models/pres_preview.py
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@
from ...messaging.models.base import BaseModel, BaseModelSchema
from ...messaging.util import canon
from ...messaging.valid import INDY_CRED_DEF_ID, INDY_PREDICATE
from ...multitenant.base import BaseMultitenantManager
from ...protocols.didcomm_prefix import DIDCommPrefix
from ...wallet.util import b64_to_str

@@ -351,7 +352,11 @@ def non_revoc(cred_def_id: str) -> IndyNonRevocationInterval:
revoc_support = False
if cd_id:
if profile:
ledger_exec_inst = profile.inject(IndyLedgerRequestsExecutor)
multitenant_mgr = profile.inject_or(BaseMultitenantManager)
if multitenant_mgr:
ledger_exec_inst = IndyLedgerRequestsExecutor(profile)
else:
ledger_exec_inst = profile.inject(IndyLedgerRequestsExecutor)
ledger = (
await ledger_exec_inst.get_ledger_for_identifier(
cd_id,
@@ -410,7 +415,11 @@ def non_revoc(cred_def_id: str) -> IndyNonRevocationInterval:
revoc_support = False
if cd_id:
if profile:
ledger_exec_inst = profile.inject(IndyLedgerRequestsExecutor)
multitenant_mgr = profile.inject_or(BaseMultitenantManager)
if multitenant_mgr:
ledger_exec_inst = IndyLedgerRequestsExecutor(profile)
else:
ledger_exec_inst = profile.inject(IndyLedgerRequestsExecutor)
ledger = (
await ledger_exec_inst.get_ledger_for_identifier(
cd_id,
6 changes: 6 additions & 0 deletions aries_cloudagent/indy/models/tests/test_pres_preview.py
Original file line number Diff line number Diff line change
@@ -13,6 +13,8 @@
IndyLedgerRequestsExecutor,
)
from ....messaging.util import canon
from ....multitenant.base import BaseMultitenantManager
from ....multitenant.manager import MultitenantManager
from ....protocols.didcomm_prefix import DIDCommPrefix


@@ -443,6 +445,10 @@ async def test_to_indy_proof_request_revo(self):
context.injector.bind_instance(
IndyLedgerRequestsExecutor, IndyLedgerRequestsExecutor(mock_profile)
)
context.injector.bind_instance(
BaseMultitenantManager,
async_mock.MagicMock(MultitenantManager, autospec=True),
)
with async_mock.patch.object(
IndyLedgerRequestsExecutor, "get_ledger_for_identifier"
) as mock_get_ledger:
4 changes: 4 additions & 0 deletions aries_cloudagent/indy/sdk/verifier.py
Original file line number Diff line number Diff line change
@@ -47,6 +47,8 @@ async def verify_presentation(
rev_reg_entries: revocation registry entries
"""

LOGGER.debug(f">>> received presentation: {pres}")
LOGGER.debug(f">>> for pres_req: {pres_req}")
try:
self.non_revoc_intervals(pres_req, pres, credential_definitions)
await self.check_timestamps(self.profile, pres_req, pres, rev_reg_defs)
@@ -58,6 +60,8 @@ async def verify_presentation(
)
return False

LOGGER.debug(f">>> verifying presentation: {pres}")
LOGGER.debug(f">>> for pres_req: {pres_req}")
try:
verified = await indy.anoncreds.verifier_verify_proof(
json.dumps(pres_req),
24 changes: 24 additions & 0 deletions aries_cloudagent/indy/tests/test_verifier.py
Original file line number Diff line number Diff line change
@@ -10,6 +10,8 @@
from ...ledger.multiple_ledger.ledger_requests_executor import (
IndyLedgerRequestsExecutor,
)
from ...multitenant.base import BaseMultitenantManager
from ...multitenant.manager import MultitenantManager

from .. import verifier as test_module
from ..verifier import IndyVerifier
@@ -332,6 +334,28 @@ def setUp(self):
self.verifier = MockVerifier()

async def test_check_timestamps(self):
# multitenant
mock_profile = InMemoryProfile.test_profile()
context = mock_profile.context
context.injector.bind_instance(
IndyLedgerRequestsExecutor,
IndyLedgerRequestsExecutor(mock_profile),
)
context.injector.bind_instance(
BaseMultitenantManager,
async_mock.MagicMock(MultitenantManager, autospec=True),
)
with async_mock.patch.object(
IndyLedgerRequestsExecutor, "get_ledger_for_identifier"
) as mock_get_ledger:
mock_get_ledger.return_value = (None, self.ledger)
await self.verifier.check_timestamps(
mock_profile,
INDY_PROOF_REQ_NAME,
INDY_PROOF_NAME,
REV_REG_DEFS,
)

# all clear, with timestamps
mock_profile = InMemoryProfile.test_profile()
context = mock_profile.context
Loading

0 comments on commit 76c886c

Please sign in to comment.