async def alice_bob_connect_multi( bob_member_client: AsyncClient, alice_member_client: AsyncClient, bob_multi_use_invitation: MultiInvite, ) -> BobAliceConnect: invitation = bob_multi_use_invitation["multi_use_invitation"]["invitation"] # accept invitation on alice side invitation_response = (await alice_member_client.post( "/generic/connections/accept-invitation", json={"invitation": invitation}, )).json() assert check_webhook_state( client=alice_member_client, filter_map={"state": "request-sent"}, topic="connections", max_duration=30, ) bob_connection_id = bob_multi_use_invitation["multi_use_invitation"][ "connection_id"] alice_connection_id = invitation_response["connection_id"] # fetch and validate # both connections should be active - we have waited long enough for events to be exchanged bob_connection_records = ( await bob_member_client.get("/generic/connections")).json() print(json.dumps(bob_connection_records, indent=2)) bob_connection_id = bob_connection_records[0]["connection_id"] assert check_webhook_state( client=alice_member_client, filter_map={"state": "completed"}, topic="connections", max_duration=30, ) assert check_webhook_state( client=bob_member_client, filter_map={"state": "completed"}, topic="connections", max_duration=30, ) return { "alice_connection_id": alice_connection_id, "bob_connection_id": bob_connection_id, }
async def test_oob_connect_via_public_did( bob_member_client: AsyncClient, alice_member_client: AsyncClient, bob_and_alice_public_did: BobAlicePublicDid, ): time.sleep(5) connect_response = await bob_member_client.post( "/generic/connections/oob/connect-public-did", json={"public_did": bob_and_alice_public_did["alice_public_did"]}, ) bob_connection_record = connect_response.json() assert check_webhook_state( client=bob_member_client, topic="connections", filter_map={ "state": "request-sent", "connection_id": bob_connection_record["connection_id"], }, ) public_did_response = await alice_member_client.get("/wallet/dids/public") alice_public_did = public_did_response.json() assert_that(bob_connection_record).has_their_public_did( alice_public_did["did"])
async def test_accept_invitation( bob_member_client: AsyncClient, alice_member_client: AsyncClient, ): invitation_response = await bob_member_client.post( "/generic/connections/create-invitation") invitation = invitation_response.json() accept_response = await alice_member_client.post( "/generic/connections/accept-invitation", json={"invitation": invitation["invitation"]}, ) connection_record = accept_response.json() assert check_webhook_state( client=alice_member_client, topic="connections", filter_map={ "state": "completed", "connection_id": connection_record["connection_id"], }, ) assert_that(connection_record).contains("connection_id", "state", "created_at", "updated_at", "invitation_key") assert_that(connection_record).has_state("request-sent")
async def test_bob_and_alice_connect( bob_member_client: AsyncClient, alice_member_client: AsyncClient, ): time.sleep(1) invitation_response = await bob_member_client.post( "/generic/connections/create-invitation", ) invitation = invitation_response.json() accept_response = await alice_member_client.post( "/generic/connections/accept-invitation", json={"invitation": invitation["invitation"]}, ) connection_record = accept_response.json() assert check_webhook_state( client=alice_member_client, topic="connections", filter_map={ "state": "completed", "connection_id": connection_record["connection_id"], }, ) alice_connection_id = connection_record["connection_id"] bob_connection_id = invitation["connection_id"] bob_connection = (await bob_member_client.get( f"/generic/connections/{bob_connection_id}")).json() alice_connection = (await alice_member_client.get( f"/generic/connections/{alice_connection_id}")).json() assert "completed" in alice_connection["state"] assert "completed" in bob_connection["state"]
async def bob_and_alice_connection( bob_member_client: AsyncClient, alice_member_client: AsyncClient, ) -> BobAliceConnect: # create invitation on bob side invitation = (await bob_member_client.post( "/generic/connections/create-invitation")).json() # accept invitation on alice side invitation_response = (await alice_member_client.post( "/generic/connections/accept-invitation", json={"invitation": invitation["invitation"]}, )).json() assert check_webhook_state(alice_member_client, topic="connections", filter_map={"state": "completed"}) bob_connection_id = invitation["connection_id"] alice_connection_id = invitation_response["connection_id"] # fetch and validate # both connections should be active - we have waited long enough for events to be exchanged assert check_webhook_state( alice_member_client, topic="connections", filter_map={"state": "completed"}, ) assert check_webhook_state( bob_member_client, topic="connections", filter_map={"state": "completed"}, ) return { "alice_connection_id": alice_connection_id, "bob_connection_id": bob_connection_id, }
async def test_send_credential_request( alice_member_client: AsyncClient, faber_client: AsyncClient, faber_and_alice_connection: FaberAliceConnect, credential_definition_id: str, ): credential = { "protocol_version": "v1", "credential_definition_id": credential_definition_id, "connection_id": faber_and_alice_connection["faber_connection_id"], "attributes": {"speed": "10"}, } response = await faber_client.post( BASE_PATH, json=credential, ) credential_exchange = response.json() assert credential_exchange["protocol_version"] == "v1" assert check_webhook_state( client=faber_client, filter_map={ "state": "offer-sent", "credential_id": credential_exchange["credential_id"], }, topic="credentials", ) response = await alice_member_client.get( BASE_PATH, params={"connection_id": faber_and_alice_connection["alice_connection_id"]}, ) assert check_webhook_state( client=alice_member_client, filter_map={"state": "offer-received"}, topic="credentials", )
async def credential_exchange_id( faber_client: AsyncClient, credential_definition_id: str, faber_and_alice_connection: FaberAliceConnect, alice_member_client: AsyncClient, ): """this fixture produces the CRED_X_ID but if the test that produces the CRED_X_ID has already run then this fixture just returns it...""" credential = { "protocol_version": "v1", "connection_id": faber_and_alice_connection["faber_connection_id"], "credential_definition_id": credential_definition_id, "attributes": {"speed": "average"}, } response = await faber_client.post( BASE_PATH, json=credential, ) response.raise_for_status() credential_exchange = response.json() credential_exchange_id = credential_exchange["credential_id"] assert credential_exchange["protocol_version"] == "v1" assert check_webhook_state( client=faber_client, filter_map={ "state": "offer-sent", "credential_id": credential_exchange["credential_id"], }, topic="credentials", ) response = await alice_member_client.get( BASE_PATH, params={"connection_id": faber_and_alice_connection["alice_connection_id"]}, ) response.raise_for_status() records = response.json() assert len(records) > 0 return credential_exchange_id
async def test_create_tenant_ecosystem_issuer( ecosystem_admin_client: AsyncClient, ecosystem_admin_acapy_client: AcaPyClient, governance_acapy_client: AcaPyClient, ): name = uuid4().hex response = await ecosystem_admin_client.post( BASE_PATH, json={ "image_url": "https://image.ca", "name": name, "roles": ["issuer"], }, ) assert response.status_code == 200 tenant = response.json() tenant_id = tenant["tenant_id"] wallet = await ecosystem_admin_acapy_client.multitenancy.get_wallet( wallet_id=tenant_id) acapy_token: str = tenant["access_token"].split(".", 1)[1] actor = await trust_registry.actor_by_id(tenant_id) endorser_did = await acapy_wallet.get_public_did(governance_acapy_client) async with get_tenant_controller(Role.ECOSYSTEM, acapy_token) as tenant_controller: public_did = await acapy_wallet.get_public_did(tenant_controller) connections = await tenant_controller.connection.get_connections() connections = [ connection for connection in connections.results if connection.their_public_did == endorser_did.did ] if not actor: raise Exception("Missing actor") connection = connections[0] async with ecosystem_client(token=tenant["access_token"]) as client: # Wait for connection to be completed assert check_webhook_state( client, "connections", { "state": "completed", "connection_id": connection.connection_id, }, ) # Actor assert_that(actor).has_name(tenant["tenant_name"]) assert_that(actor).has_did(f"did:sov:{public_did.did}") assert_that(actor).has_roles(["issuer"]) # Connection with endorser assert_that(connection).has_their_public_did(endorser_did.did) # Tenant assert_that(tenant).has_tenant_id(wallet.wallet_id) assert_that(tenant).has_tenant_name(name) assert_that(tenant).has_created_at(wallet.created_at) assert_that(tenant).has_updated_at(wallet.updated_at) assert wallet.settings["wallet.name"].startswith("ecosystem.")
async def test_update_tenant_ecosystem_verifier_to_issuer( ecosystem_admin_client: AsyncClient, ecosystem_admin_acapy_client: AcaPyClient, governance_acapy_client: AcaPyClient, ): name = uuid4().hex response = await ecosystem_admin_client.post( BASE_PATH, json={ "image_url": "https://image.ca", "name": name, "roles": ["verifier"], }, ) assert response.status_code == 200 tenant = response.json() tenant_id = tenant["tenant_id"] actor = await trust_registry.actor_by_id(tenant_id) wallet = await ecosystem_admin_acapy_client.multitenancy.get_wallet( wallet_id=tenant_id) acapy_token: str = tenant["access_token"].split(".", 1)[1] async with get_tenant_controller(Role.ECOSYSTEM, acapy_token) as tenant_controller: connections = await tenant_controller.connection.get_connections( alias=f"Trust Registry {name}") connection = connections.results[0] # Connection invitation assert_that(connection).has_state("invitation") assert actor assert_that(actor).has_name(name) assert_that(actor).has_did( ed25519_verkey_to_did_key(connection.invitation_key)) assert_that(actor).has_roles(["verifier"]) # Tenant assert_that(tenant).has_tenant_id(wallet.wallet_id) assert_that(tenant).has_image_url("https://image.ca") assert_that(tenant).has_tenant_name(name) assert_that(tenant).has_created_at(wallet.created_at) assert_that(tenant).has_updated_at(wallet.updated_at) assert wallet.settings["wallet.name"].startswith("ecosystem.") new_name = uuid4().hex new_image_url = "https://some-ssi-site.org/image.png" new_roles = ["issuer", "verifier"] response = await ecosystem_admin_client.put( f"{BASE_PATH}/{tenant_id}", json={ "image_url": new_image_url, "name": new_name, "roles": new_roles, }, ) assert response.status_code == 200 new_tenant = response.json() new_actor = await trust_registry.actor_by_id(tenant_id) endorser_did = await acapy_wallet.get_public_did(governance_acapy_client) async with get_tenant_controller(Role.ECOSYSTEM, acapy_token) as tenant_controller: public_did = await acapy_wallet.get_public_did(tenant_controller) _connections = (await tenant_controller.connection.get_connections()).results connections = [ connection for connection in _connections if connection.their_public_did == endorser_did.did ] endorser_connection = connections[0] async with ecosystem_client(token=tenant["access_token"]) as client: # Wait for connection to be completed assert check_webhook_state( client, "connections", { "state": "completed", "connection_id": endorser_connection.connection_id, }, ) # Connection invitation assert_that(endorser_connection).has_their_public_did(endorser_did.did) assert new_actor assert_that(new_actor).has_name(new_name) assert_that(new_actor).has_did(f"did:sov:{public_did.did}") assert_that(new_actor["roles"]).contains_only("issuer", "verifier") assert new_actor["didcomm_invitation"] is None # Tenant assert_that(new_tenant).has_tenant_id(wallet.wallet_id) assert_that(new_tenant).has_image_url(new_image_url) assert_that(new_tenant).has_tenant_name(new_name) assert_that(new_tenant).has_created_at(wallet.created_at) assert wallet.settings["wallet.name"].startswith("ecosystem.")
async def test_accept_proof_request_v1( issue_credential_to_alice: CredentialExchange, alice_member_client: AsyncClient, acme_client: AsyncClient, acme_and_alice_connection: AcmeAliceConnect, ): response = await acme_client.post( BASE_PATH + "/send-request", json={ "connection_id": acme_and_alice_connection["acme_connection_id"], "protocol_version": "v1", "proof_request": indy_proof_request.dict(), }, ) response.raise_for_status() acme_exchange = response.json() acme_proof_id = acme_exchange["proof_id"] assert check_webhook_state( client=alice_member_client, filter_map={"state": "request-received"}, topic="proofs", max_duration=120, ) proof_records_alice = await alice_member_client.get(BASE_PATH + "/proofs") alice_proof_id = proof_records_alice.json()[0]["proof_id"] requested_credentials = await alice_member_client.get( f"/generic/verifier/proofs/{alice_proof_id}/credentials") referent = requested_credentials.json()[0]["cred_info"]["referent"] indy_request_attrs = IndyRequestedCredsRequestedAttr(cred_id=referent, revealed=True) proof_accept = AcceptProofRequest( proof_id=alice_proof_id, presentation_spec=IndyPresSpec( requested_attributes={"0_speed_uuid": indy_request_attrs}, requested_predicates={}, self_attested_attributes={}, ), ) response = await alice_member_client.post( BASE_PATH + "/accept-request", json=proof_accept.dict(), ) assert check_webhook_state( client=alice_member_client, filter_map={ "state": "done", "proof_id": alice_proof_id }, topic="proofs", max_duration=120, ) assert check_webhook_state( client=acme_client, filter_map={ "state": "done", "proof_id": acme_proof_id }, topic="proofs", max_duration=120, ) result = response.json() pres_exchange_result = PresentationExchange(**result) assert isinstance(pres_exchange_result, PresentationExchange) assert response.status_code == 200
async def test_store_credential( alice_member_client: AsyncClient, faber_client: AsyncClient, credential_definition_id: str, faber_and_alice_connection: FaberAliceConnect, ): credential = { "protocol_version": "v1", "credential_definition_id": credential_definition_id, "connection_id": faber_and_alice_connection["faber_connection_id"], "attributes": {"speed": "10"}, } response = await faber_client.post( BASE_PATH, json=credential, ) credential_exchange = response.json() assert credential_exchange["protocol_version"] == "v1" assert check_webhook_state( client=faber_client, filter_map={ "state": "offer-sent", "credential_id": credential_exchange["credential_id"], }, topic="credentials", ) response = await alice_member_client.get( BASE_PATH, params={"connection_id": faber_and_alice_connection["alice_connection_id"]}, ) assert check_webhook_state( client=alice_member_client, filter_map={"state": "offer-received"}, topic="credentials", ) cred_hooks = get_hooks_per_topic_per_wallet( client=alice_member_client, topic="credentials" ) cred_hook = [h for h in cred_hooks if h["payload"]["state"] == "offer-received"][0] credential_id = cred_hook["payload"]["credential_id"] # alice send request for that credential response = await alice_member_client.post(f"{BASE_PATH}/{credential_id}/request") response.raise_for_status() # Bob check he received the request; Credential is send because of using # 'automating the entire flow' send credential earlier. # See also: app/generic/issuer/issuer.py::send_credential assert check_webhook_state( client=faber_client, filter_map={"state": "request-received"}, topic="credentials", ) # Check alice has received the credential assert check_webhook_state( client=alice_member_client, filter_map={"state": "credential-received"}, topic="credentials", ) # Alice stores credential response = await alice_member_client.post(f"{BASE_PATH}/{credential_id}/store") # Check alice has received the credential assert check_webhook_state( client=alice_member_client, filter_map={"state": "done"}, topic="credentials" )
async def test_send_credential( faber_client: AsyncClient, schema_definition: CredentialSchema, credential_definition_id: str, faber_and_alice_connection: FaberAliceConnect, alice_member_client: AsyncClient, ): credential = { "protocol_version": "v1", "connection_id": faber_and_alice_connection["faber_connection_id"], "credential_definition_id": credential_definition_id, "attributes": {"speed": "10"}, } response = await alice_member_client.get( BASE_PATH, params={"connection_id": faber_and_alice_connection["alice_connection_id"]}, ) records = response.json() # nothing currently in alice's records assert len(records) == 0 response = await faber_client.post( BASE_PATH, json=credential, ) response.raise_for_status() data = response.json() assert_that(data).contains("credential_id") assert_that(data).has_state("offer-sent") assert_that(data).has_protocol_version("v1") assert_that(data).has_attributes({"speed": "10"}) assert_that(data).has_schema_id(schema_definition.id) credential["protocol_version"] = "v2" response = await faber_client.post( BASE_PATH, json=credential, ) response.raise_for_status() data = response.json() assert_that(data).has_state("offer-sent") assert_that(data).has_protocol_version("v2") assert_that(data).has_attributes({"speed": "10"}) assert_that(data).has_schema_id(schema_definition.id) assert check_webhook_state( client=faber_client, filter_map={ "state": "offer-sent", "credential_id": data["credential_id"], }, topic="credentials", ) response = await alice_member_client.get( BASE_PATH, params={"connection_id": faber_and_alice_connection["alice_connection_id"]}, ) records = response.json() assert check_webhook_state( client=alice_member_client, filter_map={ "state": "offer-received", "credential_id": records[-1]["credential_id"], }, topic="credentials", ) assert len(records) == 2 # Expect one v1 record, one v2 record assert_that(records).extracting("protocol_version").contains("v1", "v2")