Skip to content

Commit 65a1099

Browse files
authored
Merge branch 'main' into feature/enable-aggr-vcs
2 parents 20c811c + 2dafaaa commit 65a1099

12 files changed

Lines changed: 247 additions & 23 deletions

File tree

aries_cloudagent/core/conductor.py

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from ..config.logging import LoggingConfigurator
2727
from ..config.provider import ClassProvider
2828
from ..config.wallet import wallet_config
29+
from ..connections.models.conn_record import ConnRecord
2930
from ..core.profile import Profile
3031
from ..indy.verifier import IndyVerifier
3132
from ..ledger.base import BaseLedger
@@ -450,14 +451,23 @@ async def start(self) -> None:
450451
if mediation_connections_invite
451452
else OutOfBandManager(self.root_profile)
452453
)
453-
454-
conn_record = await mgr.receive_invitation(
455-
invitation=invitation_handler.from_url(
456-
mediation_invite_record.invite
457-
),
458-
auto_accept=True,
459-
)
460454
async with self.root_profile.session() as session:
455+
invitation = invitation_handler.from_url(
456+
mediation_invite_record.invite
457+
)
458+
if isinstance(mgr, OutOfBandManager):
459+
oob_record = await mgr.receive_invitation(
460+
invitation=invitation,
461+
auto_accept=True,
462+
)
463+
conn_record = await ConnRecord.retrieve_by_id(
464+
session, oob_record.connection_id
465+
)
466+
else:
467+
conn_record = await mgr.receive_invitation(
468+
invitation=invitation,
469+
auto_accept=True,
470+
)
461471
await (
462472
MediationInviteStore(
463473
session.context.inject(BaseStorage)

aries_cloudagent/core/dispatcher.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,10 +296,10 @@ async def make_message(
296296
if not isinstance(parsed_msg, dict):
297297
raise MessageParseError("Expected a JSON object")
298298
message_type = parsed_msg.get("@type")
299-
message_type_rec_version = get_version_from_message_type(message_type)
300299

301300
if not message_type:
302301
raise MessageParseError("Message does not contain '@type' parameter")
302+
message_type_rec_version = get_version_from_message_type(message_type)
303303

304304
registry: ProtocolRegistry = self.profile.inject(ProtocolRegistry)
305305
try:

aries_cloudagent/core/tests/test_conductor.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1161,7 +1161,9 @@ async def test_mediator_invitation_0434(self, mock_from_url, _):
11611161
"test": async_mock.MagicMock(schemes=["http"])
11621162
}
11631163
await conductor.setup()
1164-
1164+
conductor.root_profile.context.update_settings(
1165+
{"mediation.connections_invite": False}
1166+
)
11651167
conn_record = ConnRecord(
11661168
invitation_key="3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx",
11671169
their_label="Hello",
@@ -1170,12 +1172,15 @@ async def test_mediator_invitation_0434(self, mock_from_url, _):
11701172
)
11711173
conn_record.accept = ConnRecord.ACCEPT_MANUAL
11721174
await conn_record.save(await conductor.root_profile.session())
1175+
oob_record = async_mock.MagicMock(
1176+
connection_id=conn_record.connection_id,
1177+
)
11731178
with async_mock.patch.object(
11741179
test_module,
11751180
"OutOfBandManager",
11761181
async_mock.MagicMock(
11771182
return_value=async_mock.MagicMock(
1178-
receive_invitation=async_mock.AsyncMock(return_value=conn_record)
1183+
receive_invitation=async_mock.AsyncMock(return_value=oob_record)
11791184
)
11801185
),
11811186
) as mock_mgr, async_mock.patch.object(
@@ -1185,10 +1190,10 @@ async def test_mediator_invitation_0434(self, mock_from_url, _):
11851190
return_value=async_mock.MagicMock(value=f"v{__version__}")
11861191
),
11871192
):
1193+
assert not conductor.root_profile.settings["mediation.connections_invite"]
11881194
await conductor.start()
11891195
await conductor.stop()
11901196
mock_from_url.assert_called_once_with("test-invite")
1191-
mock_mgr.return_value.receive_invitation.assert_called_once()
11921197

11931198
@async_mock.patch.object(test_module, "MediationInviteStore")
11941199
@async_mock.patch.object(test_module.ConnectionInvitation, "from_url")

aries_cloudagent/protocols/connections/v1_0/routes.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
request_schema,
1111
response_schema,
1212
)
13-
13+
from typing import cast
1414
from marshmallow import fields, validate, validates_schema
1515

1616
from ....admin.request_context import AdminRequestContext
@@ -115,7 +115,7 @@ class CreateInvitationRequestSchema(OpenAPISchema):
115115
mediation_id = fields.Str(
116116
required=False,
117117
description="Identifier for active mediation record to be used",
118-
**MEDIATION_ID_SCHEMA
118+
**MEDIATION_ID_SCHEMA,
119119
)
120120

121121

@@ -247,7 +247,7 @@ class ReceiveInvitationQueryStringSchema(OpenAPISchema):
247247
mediation_id = fields.Str(
248248
required=False,
249249
description="Identifier for active mediation record to be used",
250-
**MEDIATION_ID_SCHEMA
250+
**MEDIATION_ID_SCHEMA,
251251
)
252252

253253

@@ -261,7 +261,7 @@ class AcceptInvitationQueryStringSchema(OpenAPISchema):
261261
mediation_id = fields.Str(
262262
required=False,
263263
description="Identifier for active mediation record to be used",
264-
**MEDIATION_ID_SCHEMA
264+
**MEDIATION_ID_SCHEMA,
265265
)
266266

267267

@@ -536,11 +536,16 @@ async def connections_create_invitation(request: web.BaseRequest):
536536
metadata=metadata,
537537
mediation_id=mediation_id,
538538
)
539-
539+
invitation_url = invitation.to_url(base_url)
540+
base_endpoint = service_endpoint or cast(
541+
str, profile.settings.get("default_endpoint")
542+
)
540543
result = {
541544
"connection_id": connection and connection.connection_id,
542545
"invitation": invitation.serialize(),
543-
"invitation_url": invitation.to_url(base_url),
546+
"invitation_url": f"{base_endpoint}{invitation_url}"
547+
if invitation_url.startswith("?")
548+
else invitation_url,
544549
}
545550
except (ConnectionManagerError, StorageError, BaseModelError) as err:
546551
raise web.HTTPBadRequest(reason=err.roll_up) from err

aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -764,11 +764,15 @@ async def on_startup_event(profile: Profile, event: Event):
764764
invite = InvitationMessage.from_url(endorser_invitation)
765765
if invite:
766766
oob_mgr = OutOfBandManager(profile)
767-
conn_record = await oob_mgr.receive_invitation(
767+
oob_record = await oob_mgr.receive_invitation(
768768
invitation=invite,
769769
auto_accept=True,
770770
alias=endorser_alias,
771771
)
772+
async with profile.session() as session:
773+
conn_record = await ConnRecord.retrieve_by_id(
774+
session, oob_record.connection_id
775+
)
772776
else:
773777
invite = ConnectionInvitation.from_url(endorser_invitation)
774778
if invite:

aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -503,7 +503,7 @@ async def receive_credential(
503503

504504
# Remove values from cred that are not part of detail
505505
cred_dict.pop("proof")
506-
credential_status = cred_dict.pop("credentialStatus", None)
506+
credential_status = cred_dict.get("credentialStatus", None)
507507
detail_status = detail.options.credential_status
508508

509509
if cred_dict != detail_dict["credential"]:

aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/tests/test_handler.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -770,13 +770,14 @@ async def test_receive_credential_x_credential_status_ne(self):
770770

771771
async def test_receive_credential_x_credential_status_ne_both_set(self):
772772
detail = deepcopy(LD_PROOF_VC_DETAIL)
773+
status_entry = {"type": "SomeRandomType"}
773774

774-
# Set credential status so it's only set on the detail
775-
# not the issued credential
775+
# Set credential status in both request and reference credential
776776
detail["options"]["credentialStatus"] = {"type": "CredentialStatusType"}
777+
detail["credential"]["credentialStatus"] = deepcopy(status_entry)
777778

778779
vc = deepcopy(LD_PROOF_VC)
779-
vc["credentialStatus"] = {"type": "SomeRandomType"}
780+
vc["credentialStatus"] = deepcopy(status_entry)
780781

781782
cred_issue = V20CredIssue(
782783
formats=[

demo/docker-agent/Dockerfile.acapy

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
FROM bcgovimages/aries-cloudagent:py36-1.16-1_1.0.0-rc0
2+
3+
USER root
4+
5+
ADD https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64 ./jq
6+
RUN chmod +x ./jq
7+
COPY ngrok-wait.sh ngrok-wait.sh
8+
RUN chmod +x ./ngrok-wait.sh
9+
10+
USER $user
11+
12+
# temporary until this PR gets merged/released
13+
RUN pip uninstall -y aries-cloudagent
14+
RUN pip install aries-cloudagent[indy,bbs,askar]@git+https://github.com/ianco/aries-cloudagent-python@endorser-write-did

demo/docker-agent/README.md

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# Running an Author Agent and connecting to an Endorser
2+
3+
This directory contains scripts to run an aca-py agent as an Author, that can conenct to an Endorser service.
4+
5+
## Running the Author Agent
6+
7+
The docker-compose script runs ngrok to expose the agent's port publicly, and stores wallet data in a postgres database.
8+
9+
To run the Author agent in this repo, open a command shell in this directory and run:
10+
11+
- to build the containers:
12+
13+
```bash
14+
docker-compose build
15+
```
16+
17+
- to run the author agent:
18+
19+
```bash
20+
docker-compose up
21+
```
22+
23+
You can connect to the [agent's api service here](http://localhost:8010).
24+
25+
Note that all the configuration settings are hard-coded in the docker-compose file and ngrok-wait.sh script, so if you change any configs you need to rebuild the docker images.
26+
27+
- to shut down the agent:
28+
29+
```bash
30+
docker-compose stop
31+
docker-compose rm -f
32+
```
33+
34+
This will leave the agent's wallet data, so if you restart the agent it will maintain any created data.
35+
36+
- to remove the agent's wallet:
37+
38+
```bash
39+
docker volume rm docker-agent_wallet-db-data
40+
```
41+
42+
Note that the Author agent is not (yet) configured with revocations enabled or a tails server, so revocation is not supported.
43+
44+
## Connecting to an Endorser Service
45+
46+
For this example, we will connect to [this endorser service](https://github.com/bcgov/aries-endorser-service), which you can connect to locally at `http://localhost:5050/endorser/docs`.
47+
48+
Make sure you start the endorser service on the same ledger as your author, and make sure the endorser has a public DID with ENDORSER role.
49+
50+
For example start the endorser service as `LEDGER_URL=http://test.bcovrin.vonx.io TAILS_SERVER_URL=https://tails-test.vonx.io ./manage start --logs` and then make sure the Author agent is started with `--genesis_url http://test.bcovrin.vonx.io/genesis`.
51+
52+
### Connecting the Author to the Endorser
53+
54+
Endorser Service: Use the `GET /v1/admin/config` endpoint to fetch the endorser's configuration, including the public DID (which the author will need to know). Also confirm whether the `ENDORSER_AUTO_ACCEPT_CONNECTIONS` and `ENDORSER_AUTO_ENDORSE_REQUESTS` settings are `True` or `False` - for the following we will assume that both are `False` and the endorser must explicitely respond to all requests.
55+
56+
Author Agent: Use the `POST /didexchange/create-request` to request a connection with the endorser, using the endorser's public DID. Set the `alias` to `Endorser` - this *MUST* match the `--endorser-alias 'Endorser'` setting (in the ngrok-wait.sh script). Use the `GET /connections` endpoint to verify the connection is in `request` state.
57+
58+
Endorser Service: Use the `GET /v1/connections` endpoint to see the connection request (state `request`). Using the `connection_id`, call the `POST /connections/{connection_id}/accept` endpoint to accept the request. Verify that the connection state goes to `active`.
59+
60+
Author Agent: Verify the connection state goes to `active`. Use the `POST /transactions/{conn_id}/set-endorser-role` to set the connection role to `TRANSACTION_AUTHOR`, and then use `POST /transactions/{conn_id}/set-endorser-info` to set the endorser's alias to `Endorser` and the public DID to the endorser's public DID. Verify the settings using the `GET /connections/{conn_id}/meta-data` endpoint.
61+
62+
The connection is now setup between the two agents!
63+
64+
### Creating a Public Author DID
65+
66+
Author Agent: Use the `POST /wallet/did/create` (use an empty `{}` POST body) to create a local did. Then use `POST /ledger/register-nym` to send the data to the ledger - this will create a transaction and send it to the endorser service.
67+
68+
Endorser Service: Use the `GET /v1/endorse/transactions` endpoint to see the endorse request - it should be in state `request_received`. Using the `POST /v1/endorse/transactions/{transaction_id}/endorse` endpoint and the `transaction_id`, approve the request. The state should now (eventually) go to `transaction_acked`.
69+
70+
Author Service: Use the `GET /transactions` endpoint to verify the transaction is in `transaction_acked` state. Then use the `POST /wallet/did/public` to set the new DID to be the Author's public DID. This will generate another endorser transaction to set the DID's endpoint (ATTRIB transaction) on the ledger.
71+
72+
Endorser Service: Use the same endpoints as above (`GET /v1/endorse/transactions` and then `POST /v1/endorse/transactions/{transaction_id}/endorse`) to view the endorse request and approve it.
73+
74+
### Endorsing Author Requests
75+
76+
Author requests to create schema, create credential definition and create revocation registries will all now generate endorse requests to the endorser.
77+
78+
Author Agent: To create a schema use the `POST /schemas` endpoint. This will create an endorse request.
79+
80+
Endorser Service: Use the same endpoints as above (`GET /v1/endorse/transactions` and then `POST /v1/endorse/transactions/{transaction_id}/endorse`) to view the endorse request and approve it.
81+
82+
Author Agent: To create a cred def use the `POST /credential-definitions` endpoint. This will create an endorse request.
83+
84+
Endorser Service: Use the same endpoints as above (`GET /v1/endorse/transactions` and then `POST /v1/endorse/transactions/{transaction_id}/endorse`) to view the endorse request and approve it.
85+
86+
87+
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Sample docker-compose to start a local aca-py author agent
2+
# To start aca-py and the postgres database, just run `docker-compose up`
3+
# To shut down the services run `docker-compose rm` - this will retain the postgres database, so you can change aca-py startup parameters
4+
# and restart the docker containers without losing your wallet data
5+
# If you want to delete your wallet data just run `docker volume ls -q | xargs docker volume rm`
6+
version: "3"
7+
services:
8+
ngrok-agent:
9+
image: wernight/ngrok
10+
ports:
11+
- 4067:4040
12+
command: ngrok http author-agent:8001 --log stdout
13+
14+
author-agent:
15+
build:
16+
context: .
17+
dockerfile: Dockerfile.acapy
18+
environment:
19+
- NGROK_NAME=ngrok-agent
20+
ports:
21+
- 8010:8010
22+
- 8001:8001
23+
depends_on:
24+
- wallet-db
25+
entrypoint: /bin/bash
26+
command: [
27+
"-c",
28+
"sleep 5; \
29+
./ngrok-wait.sh"
30+
]
31+
volumes:
32+
- ./ngrok-wait.sh:/home/indy/ngrok-wait.sh
33+
34+
wallet-db:
35+
image: vcr-postgresql
36+
environment:
37+
- POSTGRESQL_USER=DB_USER
38+
- POSTGRESQL_PASSWORD=DB_PASSWORD
39+
- POSTGRESQL_DATABASE=DB_USER
40+
- POSTGRESQL_ADMIN_PASSWORD=mysecretpassword
41+
ports:
42+
- 5433:5432
43+
volumes:
44+
- wallet-db-data:/var/lib/pgsql/data
45+
46+
volumes:
47+
wallet-db-data:

0 commit comments

Comments
 (0)