From 6e6f1f8e61fd2fdda25bc7ebfc350b2c4d088ec3 Mon Sep 17 00:00:00 2001 From: Fergal Date: Wed, 25 Sep 2024 20:57:32 +0100 Subject: [PATCH] feat: endpoint to process credential from (acdc, iss) without IPEX (#297) * feat: endpoint to process credential from (acdc, iss) without IPEX * refactor: rename process to verify for credential verification endpoint --- src/keria/app/credentialing.py | 66 +++++++++++++++++++++++++++++++++ tests/app/test_credentialing.py | 32 +++++++++++++++- 2 files changed, 96 insertions(+), 2 deletions(-) diff --git a/src/keria/app/credentialing.py b/src/keria/app/credentialing.py index b92fed2..08641d3 100644 --- a/src/keria/app/credentialing.py +++ b/src/keria/app/credentialing.py @@ -43,6 +43,9 @@ def loadEnds(app, identifierResource): queryCollectionEnd = CredentialQueryCollectionEnd() app.add_route("/credentials/query", queryCollectionEnd) + credentialVerificationEnd = CredentialVerificationCollectionEnd() + app.add_route("/credentials/verify", credentialVerificationEnd) + class RegistryCollectionEnd: """ @@ -407,6 +410,69 @@ def on_get(req, rep): rep.data = json.dumps(data).encode("utf-8") +class CredentialVerificationCollectionEnd: + @staticmethod + def on_post(req, rep): + """ Verify credential endpoint (no IPEX) + + Parameters: + req: falcon.Request HTTP request + rep: falcon.Response HTTP response + + --- + summary: Verify a credential without IPEX + description: Verify a credential without using IPEX (TEL should be updated separately) + tags: + - Credentials + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - acdc + - iss + properties: + acdc: + type: object + description: KED of ACDC + iss: + type: object + description: KED of issuing event in VC TEL + responses: + 202: + description: Credential accepted for parsing + content: + application/json: + schema: + description: long running operation of credential processing + type: object + 404: + description: Malformed ACDC or iss event + """ + agent = req.context.agent + body = req.get_media() + + try: + creder = serdering.SerderACDC(sad=httping.getRequiredParam(body, "acdc")) + iserder = serdering.SerderKERI(sad=httping.getRequiredParam(body, "iss")) + except (kering.ValidationError, json.decoder.JSONDecodeError) as e: + rep.status = falcon.HTTP_400 + rep.text = e.args[0] + return + + prefixer = coring.Prefixer(qb64=iserder.pre) + seqner = coring.Seqner(sn=iserder.sn) + saider = coring.Saider(qb64=iserder.said) + + agent.parser.ims.extend(signing.serialize(creder, prefixer, seqner, saider)) + op = agent.monitor.submit(creder.said, longrunning.OpTypes.credential, + metadata=dict(ced=creder.sad)) + rep.status = falcon.HTTP_202 + rep.data = op.to_json().encode("utf-8") + + class CredentialQueryCollectionEnd: """ This class provides a collection endpoint for creating credential queries. diff --git a/tests/app/test_credentialing.py b/tests/app/test_credentialing.py index 861a3c0..0ef649e 100644 --- a/tests/app/test_credentialing.py +++ b/tests/app/test_credentialing.py @@ -223,7 +223,8 @@ def test_registry_end(helpers, seeder): def test_issue_credential(helpers, seeder): - with helpers.openKeria() as (agency, agent, app, client): + with (helpers.openKeria() as (agency, agent, app, client), + helpers.openKeria() as (agency1, agent1, app1, client1)): idResEnd = aiding.IdentifierResourceEnd() app.add_route("/identifiers/{name}", idResEnd) registryEnd = credentialing.RegistryCollectionEnd(idResEnd) @@ -238,6 +239,7 @@ def test_issue_credential(helpers, seeder): app.add_route("/identifiers/{name}/endroles", endRolesEnd) seeder.seedSchema(agent.hby.db) + seeder.seedSchema(agent1.hby.db) # create the server that will receive the credential issuance messages serverDoer = helpers.server(agency) @@ -293,7 +295,7 @@ def test_issue_credential(helpers, seeder): assert result.status_code == 404 assert result.json == {'description': "badname is not a valid reference to an identifier", 'title': '404 Not Found'} - + result = client.simulate_post(path="/identifiers/issuer/credentials", body=json.dumps(body).encode("utf-8")) op = result.json @@ -309,6 +311,32 @@ def test_issue_credential(helpers, seeder): result = client.simulate_post(path="/identifiers/issuer/credentials", body=json.dumps(body).encode("utf-8")) assert result.status_code == 400 + # Try to load into another agent after TEL query without IPEX + agent1.parser.parse(ims=agent.hby.habByName("issuer").replay()) + assert iaid in agent1.hby.kevers + + agent1.parser.parse(ims=agent.rgy.reger.cloneTvtAt(registry["regk"])) + assert registry["regk"] in agent1.rgy.tevers + + agent1.parser.parse(ims=agent.rgy.reger.cloneTvtAt(creder.said)) + assert agent1.rgy.tevers[registry["regk"]].vcSn(creder.said) is not None + + credVerifyEnd = credentialing.CredentialVerificationCollectionEnd() + app1.add_route("/credentials/verify", credVerifyEnd) + + body = dict(acdc=creder.sad, iss=regser.ked) # still has changed LEI + result = client1.simulate_post(path="/credentials/verify", body=json.dumps(body).encode("utf-8")) + assert result.status_code == 400 + + body["acdc"]["a"]["LEI"] = "254900DA0GOGCFVWB618" # change back + result = client1.simulate_post(path="/credentials/verify", body=json.dumps(body).encode("utf-8")) + assert result.status_code == 202 + + deeds = doist.enter(doers=[agent1]) + while not agent1.rgy.reger.creds.get(keys=(creder.said,)): + doist.recur(deeds=deeds) + + def test_credentialing_ends(helpers, seeder): salt = b'0123456789abcdef'