From b7284cba5232a69f5ffc8e32fbafcc12aa89d680 Mon Sep 17 00:00:00 2001 From: iFergal Date: Wed, 25 Sep 2024 18:13:46 +0100 Subject: [PATCH 1/2] feat: endpoint to process credential from (acdc, iss) without IPEX --- 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..104fce4 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) + credentialProcessEnd = CredentialProcessCollectionEnd() + app.add_route("/credentials/process", credentialProcessEnd) + class RegistryCollectionEnd: """ @@ -407,6 +410,69 @@ def on_get(req, rep): rep.data = json.dumps(data).encode("utf-8") +class CredentialProcessCollectionEnd: + @staticmethod + def on_post(req, rep): + """ Process credential endpoint + + Parameters: + req: falcon.Request HTTP request + rep: falcon.Response HTTP response + + --- + summary: Process or parse a credential + description: Process or parse a credential without using IPEX + 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..15a71b5 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 + + credProcessEnd = credentialing.CredentialProcessCollectionEnd() + app1.add_route("/credentials/process", credProcessEnd) + + body = dict(acdc=creder.sad, iss=regser.ked) # still has changed LEI + result = client1.simulate_post(path="/credentials/process", 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/process", 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' From e839110326f67c7ba27de0b881d9ffe3a492189b Mon Sep 17 00:00:00 2001 From: iFergal Date: Wed, 25 Sep 2024 20:41:57 +0100 Subject: [PATCH 2/2] refactor: rename process to verify for credential verification endpoint --- src/keria/app/credentialing.py | 12 ++++++------ tests/app/test_credentialing.py | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/keria/app/credentialing.py b/src/keria/app/credentialing.py index 104fce4..08641d3 100644 --- a/src/keria/app/credentialing.py +++ b/src/keria/app/credentialing.py @@ -43,8 +43,8 @@ def loadEnds(app, identifierResource): queryCollectionEnd = CredentialQueryCollectionEnd() app.add_route("/credentials/query", queryCollectionEnd) - credentialProcessEnd = CredentialProcessCollectionEnd() - app.add_route("/credentials/process", credentialProcessEnd) + credentialVerificationEnd = CredentialVerificationCollectionEnd() + app.add_route("/credentials/verify", credentialVerificationEnd) class RegistryCollectionEnd: @@ -410,18 +410,18 @@ def on_get(req, rep): rep.data = json.dumps(data).encode("utf-8") -class CredentialProcessCollectionEnd: +class CredentialVerificationCollectionEnd: @staticmethod def on_post(req, rep): - """ Process credential endpoint + """ Verify credential endpoint (no IPEX) Parameters: req: falcon.Request HTTP request rep: falcon.Response HTTP response --- - summary: Process or parse a credential - description: Process or parse a credential without using IPEX + summary: Verify a credential without IPEX + description: Verify a credential without using IPEX (TEL should be updated separately) tags: - Credentials requestBody: diff --git a/tests/app/test_credentialing.py b/tests/app/test_credentialing.py index 15a71b5..0ef649e 100644 --- a/tests/app/test_credentialing.py +++ b/tests/app/test_credentialing.py @@ -321,15 +321,15 @@ def test_issue_credential(helpers, seeder): agent1.parser.parse(ims=agent.rgy.reger.cloneTvtAt(creder.said)) assert agent1.rgy.tevers[registry["regk"]].vcSn(creder.said) is not None - credProcessEnd = credentialing.CredentialProcessCollectionEnd() - app1.add_route("/credentials/process", credProcessEnd) + 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/process", body=json.dumps(body).encode("utf-8")) + 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/process", body=json.dumps(body).encode("utf-8")) + result = client1.simulate_post(path="/credentials/verify", body=json.dumps(body).encode("utf-8")) assert result.status_code == 202 deeds = doist.enter(doers=[agent1])