From f225c8e6cb1495fe7cc3955f14957e6870d70b59 Mon Sep 17 00:00:00 2001 From: Philip Feairheller Date: Wed, 14 Feb 2024 16:00:57 -0800 Subject: [PATCH 01/21] Upgrade KERIA to require latest python... 3.12.1 (#194) Signed-off-by: pfeairheller --- .github/workflows/python-app-ci.yml | 8 ++++---- README.md | 2 +- docs/README.rst | 2 +- setup.py | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/python-app-ci.yml b/.github/workflows/python-app-ci.yml index f82ba5fe..1cd3d834 100644 --- a/.github/workflows/python-app-ci.yml +++ b/.github/workflows/python-app-ci.yml @@ -21,10 +21,10 @@ jobs: steps: - uses: actions/checkout@v3 - - name: Set up Python 3.10.4 + - name: Set up Python 3.12.1 uses: actions/setup-python@v2 with: - python-version: 3.10.4 + python-version: 3.12.1 - name: Install dependencies run: | python -m pip install --upgrade pip @@ -44,10 +44,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Set up Python 3.10.4 + - name: Set up Python 3.12.1 uses: actions/setup-python@v2 with: - python-version: 3.10.4 + python-version: 3.12.1 - name: Install dependencies run: | python -m pip install --upgrade pip diff --git a/README.md b/README.md index f281ce28..540223ab 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ All Agent db access is through the associated Agent. ### Setup -* Ensure [Python](https://www.python.org/downloads/) `version 3.10.4+` is installed +* Ensure [Python](https://www.python.org/downloads/) `version 3.12.14+` is installed * Install [Keripy dependency](https://github.com/WebOfTrust/keripy#dependencies) (`libsodium 1.0.18+`) diff --git a/docs/README.rst b/docs/README.rst index b4d24334..daa0f280 100644 --- a/docs/README.rst +++ b/docs/README.rst @@ -14,7 +14,7 @@ Setup ~~~~~ - Ensure `Python `__ - ``version 3.10.4+`` is installed + ``version 3.12.1+`` is installed - Install `Keripy dependency `__ (``libsodium 1.0.18+``) diff --git a/setup.py b/setup.py index ca624705..836fb04b 100644 --- a/setup.py +++ b/setup.py @@ -73,7 +73,7 @@ "resolver", # eg: 'keyword1', 'keyword2', 'keyword3', ], - python_requires='>=3.10.4', + python_requires='>=3.12.1', install_requires=[ 'hio>=0.6.9', 'keri>=1.1.0', From eddf7bfb17e9c6aa20e731c5c437ffb47bbef4da Mon Sep 17 00:00:00 2001 From: Philip Feairheller Date: Thu, 15 Feb 2024 15:15:45 -0800 Subject: [PATCH 02/21] Prevent creating 2 registries per agent with the same name. (#195) Signed-off-by: pfeairheller --- src/keria/app/credentialing.py | 3 +++ tests/app/test_credentialing.py | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/keria/app/credentialing.py b/src/keria/app/credentialing.py index 3b655f27..8436061d 100644 --- a/src/keria/app/credentialing.py +++ b/src/keria/app/credentialing.py @@ -162,6 +162,9 @@ def on_post(self, req, rep, name): if hab is None: raise falcon.HTTPNotFound(description="alias is not a valid reference to an identifier") + if agent.rgy.registryByName(name=rname) is not None: + raise falcon.HTTPBadRequest(description=f"registry name {rname} already in use") + registry = agent.rgy.makeSignifyRegistry(name=rname, prefix=hab.pre, regser=vcp) if hab.kever.estOnly: diff --git a/tests/app/test_credentialing.py b/tests/app/test_credentialing.py index 3039856c..3f94e79d 100644 --- a/tests/app/test_credentialing.py +++ b/tests/app/test_credentialing.py @@ -143,11 +143,15 @@ def test_registry_end(helpers, seeder): assert regser.pre in agent.tvy.tevers + result = client.simulate_post(path="/identifiers/test/registries", body=json.dumps(body).encode("utf-8")) + assert result.status == falcon.HTTP_400 + assert result.json == {'description': 'registry name test already in use', 'title': '400 Bad Request'} body = dict(name="test", alias="test", vcp=regser.ked, ixn=serder.ked, sigs=sigers) result = client.simulate_post(path="/identifiers/bad_test/registries", body=json.dumps(body).encode("utf-8")) assert result.status == falcon.HTTP_404 - assert result.json == {'description': 'alias is not a valid reference to an identifier', 'title': '404 Not Found'} + assert result.json == {'description': 'alias is not a valid reference to an identifier', + 'title': '404 Not Found'} result = client.simulate_get(path="/identifiers/not_test/registries") From 79af2b8827a80ae24f5a7ee43643af4ca0c45229 Mon Sep 17 00:00:00 2001 From: Kevin Griffin Date: Fri, 23 Feb 2024 09:23:29 -0500 Subject: [PATCH 03/21] fixes for latest keripy development branch (#196) * fixes for latest keripy development branch Signed-off-by: Kevin Griffin * uses development KERI Signed-off-by: Kevin Griffin --------- Signed-off-by: Kevin Griffin --- setup.py | 2 +- src/keria/app/aiding.py | 4 ++-- src/keria/app/delegating.py | 4 ++-- src/keria/testing/testing_helper.py | 2 +- tests/app/test_credentialing.py | 2 +- tests/app/test_ipexing.py | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/setup.py b/setup.py index 836fb04b..c52dfc85 100644 --- a/setup.py +++ b/setup.py @@ -76,7 +76,7 @@ python_requires='>=3.12.1', install_requires=[ 'hio>=0.6.9', - 'keri>=1.1.0', + 'keri @ git+https://git@github.com/weboftrust/keripy.git@development', 'mnemonic>=0.20', 'multicommand>=1.0.0', 'falcon>=3.1.0', diff --git a/src/keria/app/aiding.py b/src/keria/app/aiding.py index b9968980..c2e8625b 100644 --- a/src/keria/app/aiding.py +++ b/src/keria/app/aiding.py @@ -405,7 +405,7 @@ def on_post(req, rep): # create Hab and incept the key store (if any) # Generate response, either the serder or a long running operaton indicator for the type rep.content_type = "application/json" - if hab.kever.delegator: + if hab.kever.delpre: agent.anchors.append(dict(pre=hab.pre, sn=0)) op = agent.monitor.submit(hab.kever.prefixer.qb64, longrunning.OpTypes.delegation, metadata=dict(pre=hab.pre, sn=0)) @@ -537,7 +537,7 @@ def rotate(agent, name, body): return op - if hab.kever.delegator: + if hab.kever.delpre: agent.anchors.append(dict(alias=name, pre=hab.pre, sn=serder.sn)) op = agent.monitor.submit(hab.kever.prefixer.qb64, longrunning.OpTypes.delegation, metadata=dict(pre=hab.pre, sn=serder.sn)) diff --git a/src/keria/app/delegating.py b/src/keria/app/delegating.py index b08dcd7b..cb68293b 100644 --- a/src/keria/app/delegating.py +++ b/src/keria/app/delegating.py @@ -41,7 +41,7 @@ def delegation(self, pre, sn=None, proxy=None): # load the hab of the delegated identifier to anchor hab = self.hby.habs[pre] - delpre = hab.kever.delegator # get the delegator identifier + delpre = hab.kever.delpre # get the delegator identifier if delpre not in hab.kevers: raise kering.ValidationError(f"delegator {delpre} not found, unable to process delegation") @@ -126,7 +126,7 @@ def processUnanchoredEscrow(self): """ for (pre, said), serder in self.hby.db.dune.getItemIter(): # group partial witness escrow kever = self.hby.kevers[pre] - dkever = self.hby.kevers[kever.delegator] + dkever = self.hby.kevers[kever.delpre] seal = dict(i=serder.pre, s=serder.snh, d=serder.said) if dserder := self.hby.db.findAnchoringSealEvent(dkever.prefixer.qb64, seal=seal): diff --git a/src/keria/testing/testing_helper.py b/src/keria/testing/testing_helper.py index e6cece6e..34287efa 100644 --- a/src/keria/testing/testing_helper.py +++ b/src/keria/testing/testing_helper.py @@ -473,7 +473,7 @@ def createRegistry(client, agent, salt, doist, deeds): baks=[], toad="0", nonce=nonce, - cnfg=[eventing.TraitCodex.NoBackers], + cnfg=[eventing.TraitCodex.NoRegistrarBackers], code=coring.MtrDex.Blake3_256) anchor = dict(i=regser.ked['i'], s=regser.ked["s"], d=regser.said) serder, sigers = Helpers.interact(pre=pre, bran=salt, pidx=0, ridx=0, dig=aid['d'], sn='1', data=[anchor]) diff --git a/tests/app/test_credentialing.py b/tests/app/test_credentialing.py index 3f94e79d..e8201305 100644 --- a/tests/app/test_credentialing.py +++ b/tests/app/test_credentialing.py @@ -118,7 +118,7 @@ def test_registry_end(helpers, seeder): baks=[], toad="0", nonce=nonce, - cnfg=[TraitCodex.NoBackers], + cnfg=[TraitCodex.NoRegistrarBackers], code=coring.MtrDex.Blake3_256) anchor = dict(i=regser.ked['i'], s=regser.ked["s"], d=regser.said) serder, sigers = helpers.interact(pre=pre, bran=salt, pidx=0, ridx=0, dig=aid['d'], sn='1', data=[anchor]) diff --git a/tests/app/test_ipexing.py b/tests/app/test_ipexing.py index 63dc8771..76ca941f 100644 --- a/tests/app/test_ipexing.py +++ b/tests/app/test_ipexing.py @@ -596,7 +596,7 @@ def test_multisig_grant_admit(seeder, helpers): baks=[], toad="0", nonce=nonce, - cnfg=[eventing.TraitCodex.NoBackers], + cnfg=[eventing.TraitCodex.NoRegistrarBackers], code=coring.MtrDex.Blake3_256) anchor = dict(i=regser.ked['i'], s=regser.ked["s"], d=regser.said) From 152d576f1ca92ba58b518f67ea5771e0b62b871e Mon Sep 17 00:00:00 2001 From: pfeairheller Date: Fri, 23 Feb 2024 06:56:31 -0800 Subject: [PATCH 04/21] Fix credential registry list endpoint to account for a not yet fully committed registry. Closes #147 Signed-off-by: pfeairheller --- src/keria/app/credentialing.py | 3 +++ tests/app/test_credentialing.py | 16 ++++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/keria/app/credentialing.py b/src/keria/app/credentialing.py index 3b655f27..69e13efa 100644 --- a/src/keria/app/credentialing.py +++ b/src/keria/app/credentialing.py @@ -84,6 +84,9 @@ def on_get(req, rep, name): res = [] for name, registry in agent.rgy.regs.items(): + if registry.regk not in registry.tevers: # defensive programming for a registry not being fully committed + continue + if registry.hab.pre == hab.pre: rd = dict( name=registry.name, diff --git a/tests/app/test_credentialing.py b/tests/app/test_credentialing.py index 3039856c..890ce292 100644 --- a/tests/app/test_credentialing.py +++ b/tests/app/test_credentialing.py @@ -118,7 +118,7 @@ def test_registry_end(helpers, seeder): baks=[], toad="0", nonce=nonce, - cnfg=[TraitCodex.NoBackers], + cnfg=[TraitCodex.NoRegistrarBackers], code=coring.MtrDex.Blake3_256) anchor = dict(i=regser.ked['i'], s=regser.ked["s"], d=regser.said) serder, sigers = helpers.interact(pre=pre, bran=salt, pidx=0, ridx=0, dig=aid['d'], sn='1', data=[anchor]) @@ -131,6 +131,10 @@ def test_registry_end(helpers, seeder): assert metadata["anchor"] == anchor assert result.status == falcon.HTTP_202 + result = client.simulate_get(path="/identifiers/test/registries") + assert result.status == falcon.HTTP_200 + assert result.json == [] + tock = 0.03125 limit = 1.0 doist = doing.Doist(limit=limit, tock=tock, real=True) @@ -143,11 +147,19 @@ def test_registry_end(helpers, seeder): assert regser.pre in agent.tvy.tevers + result = client.simulate_get(path="/identifiers/test/registries") + assert result.status == falcon.HTTP_200 + assert len(result.json) == 1 + + result = client.simulate_post(path="/identifiers/test/registries", body=json.dumps(body).encode("utf-8")) + assert result.status == falcon.HTTP_400 + assert result.json == {'description': 'registry name test already in use', 'title': '400 Bad Request'} body = dict(name="test", alias="test", vcp=regser.ked, ixn=serder.ked, sigs=sigers) result = client.simulate_post(path="/identifiers/bad_test/registries", body=json.dumps(body).encode("utf-8")) assert result.status == falcon.HTTP_404 - assert result.json == {'description': 'alias is not a valid reference to an identifier', 'title': '404 Not Found'} + assert result.json == {'description': 'alias is not a valid reference to an identifier', + 'title': '404 Not Found'} result = client.simulate_get(path="/identifiers/not_test/registries") From 37fc03ee3c1802c0e9db462ed526d5e571fee999 Mon Sep 17 00:00:00 2001 From: pfeairheller Date: Fri, 23 Feb 2024 16:47:45 -0800 Subject: [PATCH 05/21] Fix long running endpoint to allow for an operaiton for a KEL event not yet processed. Signed-off-by: pfeairheller --- src/keria/core/longrunning.py | 36 +++++++++++++++++++---------------- tests/app/test_aiding.py | 12 ++++++++++++ 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/src/keria/core/longrunning.py b/src/keria/core/longrunning.py index fa6aad5f..c0d41122 100644 --- a/src/keria/core/longrunning.py +++ b/src/keria/core/longrunning.py @@ -197,26 +197,30 @@ def status(self, op): sn = op.metadata["sn"] kever = self.hby.kevers[op.oid] sdig = self.hby.db.getKeLast(key=dbing.snKey(pre=kever.prefixer.qb64b, sn=sn)) + if sdig is not None: - dgkey = dbing.dgKey(kever.prefixer.qb64b, bytes(sdig)) - wigs = self.hby.db.getWigs(dgkey) + dgkey = dbing.dgKey(kever.prefixer.qb64b, bytes(sdig)) + wigs = self.hby.db.getWigs(dgkey) - if len(wigs) >= kever.toader.num: - evt = self.hby.db.getEvt(dbing.dgKey(pre=kever.prefixer.qb64, dig=bytes(sdig))) - serder = serdering.SerderKERI(raw=bytes(evt)) - operation.done = True - operation.response = serder.ked - - else: - start = helping.fromIso8601(op.start) - dtnow = helping.nowUTC() - if (dtnow - start) > datetime.timedelta(seconds=eventing.Kevery.TimeoutPWE): + if len(wigs) >= kever.toader.num: + evt = self.hby.db.getEvt(dbing.dgKey(pre=kever.prefixer.qb64, dig=bytes(sdig))) + serder = serdering.SerderKERI(raw=bytes(evt)) operation.done = True - operation.error = Status(code=408, # Using HTTP error codes here for lack of a better alternative - message=f"long running {op.type} for {op.oid} operation timed out before " - f"receiving sufficient witness receipts") + operation.response = serder.ked + else: - operation.done = False + start = helping.fromIso8601(op.start) + dtnow = helping.nowUTC() + if (dtnow - start) > datetime.timedelta(seconds=eventing.Kevery.TimeoutPWE): + operation.done = True + operation.error = Status(code=408, # Using HTTP error codes here for lack of a better alternative + message=f"long running {op.type} for {op.oid} operation timed out before " + f"receiving sufficient witness receipts") + else: + operation.done = False + + else: + operation.done = False elif op.type in (OpTypes.oobi,): if "oobi" not in op.metadata: diff --git a/tests/app/test_aiding.py b/tests/app/test_aiding.py index dc9caa17..23bc5d81 100644 --- a/tests/app/test_aiding.py +++ b/tests/app/test_aiding.py @@ -25,6 +25,7 @@ from keria.app import aiding, agenting from keria.app.aiding import IdentifierOOBICollectionEnd, RpyEscrowCollectionEnd +from keria.core import longrunning def test_load_ends(helpers): @@ -252,6 +253,11 @@ def test_identifier_collection_end(helpers): groupEnd = aiding.GroupMemberCollectionEnd() app.add_route("/identifiers/{name}/members", groupEnd) + opColEnd = longrunning.OperationCollectionEnd() + app.add_route("/operations", opColEnd) + opResEnd = longrunning.OperationResourceEnd() + app.add_route("/operations/{name}", opResEnd) + client = testing.TestClient(app) res = client.simulate_post(path="/identifiers", body=b'{}') @@ -393,6 +399,12 @@ def test_identifier_collection_end(helpers): res = client.simulate_post(path="/identifiers", body=json.dumps(body)) assert res.status_code == 202 + op = res.json + name = op['name'] + + res = client.simulate_get(path=f"/operations/{name}") + assert res.status_code == 200 + assert len(agent.witners) == 1 res = client.simulate_get(path="/identifiers") assert res.status_code == 200 From 87300ea2e0fa36ff90def1e6ae24ceb1b37bc3c7 Mon Sep 17 00:00:00 2001 From: pfeairheller Date: Sat, 24 Feb 2024 07:49:13 -0800 Subject: [PATCH 06/21] Remove extra test condition pulled in from development Signed-off-by: pfeairheller --- tests/app/test_credentialing.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/app/test_credentialing.py b/tests/app/test_credentialing.py index 890ce292..f650fec3 100644 --- a/tests/app/test_credentialing.py +++ b/tests/app/test_credentialing.py @@ -118,7 +118,7 @@ def test_registry_end(helpers, seeder): baks=[], toad="0", nonce=nonce, - cnfg=[TraitCodex.NoRegistrarBackers], + cnfg=[TraitCodex.NoBackers], code=coring.MtrDex.Blake3_256) anchor = dict(i=regser.ked['i'], s=regser.ked["s"], d=regser.said) serder, sigers = helpers.interact(pre=pre, bran=salt, pidx=0, ridx=0, dig=aid['d'], sn='1', data=[anchor]) @@ -151,10 +151,6 @@ def test_registry_end(helpers, seeder): assert result.status == falcon.HTTP_200 assert len(result.json) == 1 - result = client.simulate_post(path="/identifiers/test/registries", body=json.dumps(body).encode("utf-8")) - assert result.status == falcon.HTTP_400 - assert result.json == {'description': 'registry name test already in use', 'title': '400 Bad Request'} - body = dict(name="test", alias="test", vcp=regser.ked, ixn=serder.ked, sigs=sigers) result = client.simulate_post(path="/identifiers/bad_test/registries", body=json.dumps(body).encode("utf-8")) assert result.status == falcon.HTTP_404 From fa9eed35844c6d218b795ab50d6d844aeef40b59 Mon Sep 17 00:00:00 2001 From: pfeairheller Date: Sat, 24 Feb 2024 08:26:29 -0800 Subject: [PATCH 07/21] Add test for invalid sequence number in long running witness operation. Signed-off-by: pfeairheller --- tests/app/test_aiding.py | 11 +++++++++++ tests/core/test_longrunning.py | 2 ++ 2 files changed, 13 insertions(+) diff --git a/tests/app/test_aiding.py b/tests/app/test_aiding.py index 23bc5d81..ec34e63b 100644 --- a/tests/app/test_aiding.py +++ b/tests/app/test_aiding.py @@ -404,6 +404,17 @@ def test_identifier_collection_end(helpers): res = client.simulate_get(path=f"/operations/{name}") assert res.status_code == 200 + assert res.json['done'] is False + + # Modify sequence number to test invalid sn + op = agent.monitor.opr.ops.get(keys=(name,)) + op.metadata['sn'] = 4 + agent.monitor.opr.ops.pin(keys=(name,), val=op) + + res = client.simulate_get(path=f"/operations/{name}") + assert res.status_code == 200 + assert res.json['done'] is False + assert len(agent.witners) == 1 res = client.simulate_get(path="/identifiers") diff --git a/tests/core/test_longrunning.py b/tests/core/test_longrunning.py index 8cc26e9a..267a6ed5 100644 --- a/tests/core/test_longrunning.py +++ b/tests/core/test_longrunning.py @@ -185,6 +185,8 @@ def test_error(helpers): except ValidationError as e: err = e + assert err is not None + res = client.simulate_get(path="/operations") assert isinstance(res.json, list) assert len(res.json) == 1 From ad4a67c37117e9348442d03e432ba41e260fdafd Mon Sep 17 00:00:00 2001 From: pfeairheller Date: Sat, 24 Feb 2024 08:45:10 -0800 Subject: [PATCH 08/21] Add test for satisfied witness receipts for long running operation Signed-off-by: pfeairheller --- tests/app/test_aiding.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tests/app/test_aiding.py b/tests/app/test_aiding.py index ec34e63b..7b6efe29 100644 --- a/tests/app/test_aiding.py +++ b/tests/app/test_aiding.py @@ -18,7 +18,7 @@ from keri.core.coring import MtrDex from keri.db.basing import LocationRecord from keri.peer import exchanging -from keri.db import basing +from keri.db import basing, dbing from keri import kering from keri.vdr import credentialing from hio.base import doing @@ -426,6 +426,17 @@ def test_identifier_collection_end(helpers): ss = aid[Algos.salty] assert ss["pidx"] == 3 + # Reset sn + op.metadata['sn'] = 0 + agent.monitor.opr.ops.pin(keys=(name,), val=op) + + # Add fake witness receipts to test satified witnessing + dgkey = dbing.dgKey(serder.preb, serder.preb) + agent.hby.db.putWigs(dgkey, vals=[b'A', b'B', b'C']) + res = client.simulate_get(path=f"/operations/{name}") + assert res.status_code == 200 + assert res.json['done'] is True + res = client.simulate_get(path=f"/identifiers/aid1") mhab = res.json agent0 = mhab["state"] From 6d336fc1493f0c22bbba42af3eb58c43b6d5b5b0 Mon Sep 17 00:00:00 2001 From: pfeairheller Date: Sun, 25 Feb 2024 14:06:45 -0800 Subject: [PATCH 09/21] Fix reference to revocation anchor event. Signed-off-by: pfeairheller --- src/keria/app/credentialing.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/keria/app/credentialing.py b/src/keria/app/credentialing.py index 69e13efa..8e75f295 100644 --- a/src/keria/app/credentialing.py +++ b/src/keria/app/credentialing.py @@ -811,8 +811,9 @@ def revoke(self, regk, rserder, anc): self.rgy.reger.tpwe.add(keys=(vcid, rseq.qb64), val=(hab.kever.prefixer, seqner, saider)) return vcid, rseq.sn else: - sn = anc.sn - said = anc.said + serder = serdering.SerderKERI(sad=anc) + sn = serder.sn + said = serder.said prefixer = coring.Prefixer(qb64=hab.pre) seqner = coring.Seqner(sn=sn) From 5ab6768e916ea1addcb3ffcb35069b672b428b0c Mon Sep 17 00:00:00 2001 From: pfeairheller Date: Sun, 25 Feb 2024 15:07:32 -0800 Subject: [PATCH 10/21] Add test coverage for multisig revocation. Signed-off-by: pfeairheller --- tests/app/test_credentialing.py | 9 ++++--- tests/app/test_ipexing.py | 47 +++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/tests/app/test_credentialing.py b/tests/app/test_credentialing.py index f650fec3..69c91ee1 100644 --- a/tests/app/test_credentialing.py +++ b/tests/app/test_credentialing.py @@ -521,7 +521,8 @@ def test_revoke_credential(helpers, seeder): rev=sad, ixn=serder.ked, sigs=sigers) - res = client.simulate_delete(path=f"/identifiers/issuer/credentials/{creder.said}", body=json.dumps(badbody).encode("utf-8")) + res = client.simulate_delete(path=f"/identifiers/issuer/credentials/{creder.said}", + body=json.dumps(badbody).encode("utf-8")) assert res.status_code == 404 assert res.json == {'description': 'revocation against invalid registry SAID ' 'EIVtei3pGKGUw8H2Ri0h1uOevtSA6QGAq5wifbtHIaNI', @@ -535,12 +536,14 @@ def test_revoke_credential(helpers, seeder): rev=sad, ixn=serder.ked, sigs=sigers) - res = client.simulate_delete(path=f"/identifiers/issuer/credentials/{creder.said}", body=json.dumps(badbody).encode("utf-8")) + res = client.simulate_delete(path=f"/identifiers/issuer/credentials/{creder.said}", + body=json.dumps(badbody).encode("utf-8")) assert res.status_code == 400 assert res.json == {'description': "invalid revocation event.", 'title': '400 Bad Request'} - res = client.simulate_delete(path=f"/identifiers/issuer/credentials/{creder.said}", body=json.dumps(body).encode("utf-8")) + res = client.simulate_delete(path=f"/identifiers/issuer/credentials/{creder.said}", + body=json.dumps(body).encode("utf-8")) assert res.status_code == 200 while not agent.registrar.complete(creder.said, sn=1): diff --git a/tests/app/test_ipexing.py b/tests/app/test_ipexing.py index 63dc8771..2c363248 100644 --- a/tests/app/test_ipexing.py +++ b/tests/app/test_ipexing.py @@ -368,6 +368,9 @@ def test_multisig_grant_admit(seeder, helpers): endRolesEnd = aiding.EndRoleCollectionEnd() app.add_route("/identifiers/{name}/endroles", endRolesEnd) + credentialResourceDelEnd = credentialing.CredentialResourceDeleteEnd(aidEnd) + app.add_route("/identifiers/{name}/credentials/{said}", credentialResourceDelEnd) + # Create Issuer Participant 0 ipsalt0 = b'0123456789abcM00' op = helpers.createAid(client0, "issuerParticipant0", ipsalt0) @@ -852,6 +855,50 @@ def test_multisig_grant_admit(seeder, helpers): # Ensure that the credential has been persisted by both agents assert hagent1.rgy.reger.saved.get(keys=(creder.said,)) is not None + # Get latest state + ip0 = client0.simulate_get("/identifiers/issuerParticipant0").json + ip1 = client1.simulate_get("/identifiers/issuerParticipant1").json + + # Create the revocation event from the credential SAID and the registry SAID + issueSaid = regser.said + regser = veventing.revoke(vcdig=creder.said, regk=regk, dt=dt, dig=issueSaid) + + anchor = dict(i=regser.ked['i'], s=regser.ked["s"], d=regser.said) + interact = eventing.interact(pre=issuerPre, dig=interact.said, sn=3, data=[anchor]) + sigs = [issuerSigner0.sign(ser=interact.raw, index=0).qb64, issuerSigner1.sign(ser=interact.raw, index=1).qb64] + + # Submit the Revocation to Agent 0 + body = dict( + rev=regser.ked, + ixn=interact.ked, + sigs=sigs, + group={ + "mhab": ip0, + "keys": ikeys + } + ) + + result = client0.simulate_delete(path=f"/identifiers/issuer/credentials/{creder.said}", + body=json.dumps(body).encode("utf-8")) + + assert result.status == falcon.HTTP_200 + + # Submit the Revocation to Agent 1 + body = dict( + rev=regser.ked, + ixn=interact.ked, + sigs=sigs, + group={ + "mhab": ip1, + "keys": ikeys + } + ) + + result = client1.simulate_delete(path=f"/identifiers/issuer/credentials/{creder.said}", + body=json.dumps(body).encode("utf-8")) + + assert result.status == falcon.HTTP_200 + def test_granter(helpers): with helpers.openKeria() as (agency, agent, app, client): From 90f940d213d7b14e5dcd80a0cfec95fd2ab08cef Mon Sep 17 00:00:00 2001 From: pfeairheller Date: Sun, 25 Feb 2024 18:21:13 -0800 Subject: [PATCH 11/21] Rev version number Signed-off-by: pfeairheller --- Makefile | 2 +- setup.py | 2 +- src/keria/__init__.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index cfc239c9..19dfb9aa 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ .PHONY: build-keria build-keria: - @docker buildx build --platform=linux/amd64 --no-cache -f images/keria.dockerfile --tag weboftrust/keria:0.1.0 --tag weboftrust/keria:latest . + @docker buildx build --platform=linux/amd64 --no-cache -f images/keria.dockerfile --tag weboftrust/keria:0.1.1 --tag weboftrust/keria:latest . publish-keria: @docker push weboftrust/keria --all-tags \ No newline at end of file diff --git a/setup.py b/setup.py index ca624705..5539d42f 100644 --- a/setup.py +++ b/setup.py @@ -34,7 +34,7 @@ setup( name='keria', - version='0.1.0', # also change in src/keria/__init__.py + version='0.1.1', # also change in src/keria/__init__.py license='Apache Software License 2.0', description='KERIA: KERI Agent in the cloud', long_description="KERIA: KERI Agent in the cloud.", diff --git a/src/keria/__init__.py b/src/keria/__init__.py index e39210af..f609cc86 100644 --- a/src/keria/__init__.py +++ b/src/keria/__init__.py @@ -3,5 +3,5 @@ main package """ -__version__ = '0.1.0' # also change in setup.py +__version__ = '0.1.1' # also change in setup.py From da8e53fe92b9027ec5b547c00d4f54f278fbb1b2 Mon Sep 17 00:00:00 2001 From: Fergal Date: Sat, 9 Mar 2024 15:27:01 +0000 Subject: [PATCH 12/21] feat: IPEX apply, offer, agree endpoints (#198) --- src/keria/app/ipexing.py | 224 +++++++++++++++++++++++++-- tests/app/test_ipexing.py | 317 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 532 insertions(+), 9 deletions(-) diff --git a/src/keria/app/ipexing.py b/src/keria/app/ipexing.py index 39ec50bd..9c26222a 100644 --- a/src/keria/app/ipexing.py +++ b/src/keria/app/ipexing.py @@ -20,13 +20,19 @@ def loadEnds(app): app.add_route("/identifiers/{name}/ipex/admit", admitColEnd) grantColEnd = IpexGrantCollectionEnd() app.add_route("/identifiers/{name}/ipex/grant", grantColEnd) + applyColEnd = IpexApplyCollectionEnd() + app.add_route("/identifiers/{name}/ipex/apply", applyColEnd) + offerColEnd = IpexOfferCollectionEnd() + app.add_route("/identifiers/{name}/ipex/offer", offerColEnd) + agreeColEnd = IpexAgreeCollectionEnd() + app.add_route("/identifiers/{name}/ipex/agree", agreeColEnd) class IpexAdmitCollectionEnd: @staticmethod def on_post(req, rep, name): - """ Registries GET endpoint + """ IPEX Admit POST endpoint Parameters: req: falcon.Request HTTP request @@ -34,13 +40,13 @@ def on_post(req, rep, name): name (str): human readable name for AID --- - summary: List credential issuance and revocation registies - description: List credential issuance and revocation registies + summary: Accept a credential being issued or presented in response to an IPEX grant + description: Accept a credential being issued or presented in response to an IPEX grant tags: - Registries responses: 200: - description: array of current credential issuance and revocation registies + description: long running operation of IPEX admit """ agent = req.context.agent @@ -154,7 +160,7 @@ class IpexGrantCollectionEnd: @staticmethod def on_post(req, rep, name): - """ Registries GET endpoint + """ IPEX Grant POST endpoint Parameters: req: falcon.Request HTTP request @@ -162,13 +168,13 @@ def on_post(req, rep, name): name (str): human readable name for AID --- - summary: List credential issuance and revocation registies - description: List credential issuance and revocation registies + summary: Reply to IPEX agree message or initiate an IPEX exchange with a credential issuance or presentation + description: Reply to IPEX agree message or initiate an IPEX exchange with a credential issuance or presentation tags: - - Registries + - Credentials responses: 200: - description: array of current credential issuance and revocation registies + description: long running operation of IPEX grant """ agent = req.context.agent @@ -266,3 +272,203 @@ def sendMultisigExn(agent, hab, ked, sigs, atc, rec): agent.grants.append(dict(said=grant['d'], pre=hab.pre, rec=[holder])) return agent.monitor.submit(serder.pre, longrunning.OpTypes.exchange, metadata=dict(said=serder.said)) + + +class IpexApplyCollectionEnd: + + @staticmethod + def on_post(req, rep, name): + """ IPEX Apply POST endpoint + + Parameters: + req: falcon.Request HTTP request + rep: falcon.Response HTTP response + name (str): human readable name for AID + + --- + summary: Request a credential from another party by initiating an IPEX exchange + description: Request a credential from another party by initiating an IPEX exchange + tags: + - Credentials + responses: + 200: + description: long running operation of IPEX apply + + """ + agent = req.context.agent + # Get the hab + hab = agent.hby.habByName(name) + if hab is None: + raise falcon.HTTPNotFound(description=f"alias={name} is not a valid reference to an identifier") + + body = req.get_media() + + ked = httping.getRequiredParam(body, "exn") + sigs = httping.getRequiredParam(body, "sigs") + rec = httping.getRequiredParam(body, "rec") + + route = ked['r'] + + match route: + case "/ipex/apply": + op = IpexApplyCollectionEnd.sendApply(agent, hab, ked, sigs, rec) + case _: + raise falcon.HTTPBadRequest(description=f"invalid message route {route}") + + rep.status = falcon.HTTP_200 + rep.data = op.to_json().encode("utf-8") + + @staticmethod + def sendApply(agent, hab, ked, sigs, rec): + for recp in rec: # Have to verify we already know all the recipients. + if recp not in agent.hby.kevers: + raise falcon.HTTPBadRequest(description=f"attempt to send to unknown AID={recp}") + + # use that data to create th Serder and Sigers for the exn + serder = serdering.SerderKERI(sad=ked) + sigers = [coring.Siger(qb64=sig) for sig in sigs] + + # Now create the stream to send, need the signer seal + kever = hab.kever + seal = eventing.SealEvent(i=hab.pre, s="{:x}".format(kever.lastEst.s), d=kever.lastEst.d) + + # in this case, ims is a message is a sealed and signed message - signed by Signify (KERIA can't sign anything here...) + ims = eventing.messagize(serder=serder, sigers=sigers, seal=seal) + + # make a copy and parse + agent.hby.psr.parseOne(ims=bytearray(ims)) + + agent.exchanges.append(dict(said=serder.said, pre=hab.pre, rec=rec, topic='credential')) + return agent.monitor.submit(serder.pre, longrunning.OpTypes.exchange, metadata=dict(said=serder.said)) + +class IpexOfferCollectionEnd: + + @staticmethod + def on_post(req, rep, name): + """ IPEX Offer POST endpoint + + Parameters: + req: falcon.Request HTTP request + rep: falcon.Response HTTP response + name (str): human readable name for AID + + --- + summary: Reply to IPEX apply message or initiate an IPEX exchange with an offer for a credential with certain characteristics + description: Reply to IPEX apply message or initiate an IPEX exchange with an offer for a credential with certain characteristics + tags: + - Credentials + responses: + 200: + description: long running operation of IPEX offer + + """ + agent = req.context.agent + hab = agent.hby.habByName(name) + if hab is None: + raise falcon.HTTPNotFound(description=f"alias={name} is not a valid reference to an identifier") + + body = req.get_media() + + ked = httping.getRequiredParam(body, "exn") + sigs = httping.getRequiredParam(body, "sigs") + atc = httping.getRequiredParam(body, "atc") + rec = httping.getRequiredParam(body, "rec") + + route = ked['r'] + + match route: + case "/ipex/offer": + op = IpexOfferCollectionEnd.sendOffer(agent, hab, ked, sigs, atc, rec) + case _: + raise falcon.HTTPBadRequest(description=f"invalid route {route}") + + rep.status = falcon.HTTP_200 + rep.data = op.to_json().encode("utf-8") + + @staticmethod + def sendOffer(agent, hab, ked, sigs, atc, rec): + for recp in rec: # Have to verify we already know all the recipients. + if recp not in agent.hby.kevers: + raise falcon.HTTPBadRequest(description=f"attempt to send to unknown AID={recp}") + + # use that data to create th Serder and Sigers for the exn + serder = serdering.SerderKERI(sad=ked) + sigers = [coring.Siger(qb64=sig) for sig in sigs] + + # Now create the stream to send, need the signer seal + kever = hab.kever + seal = eventing.SealEvent(i=hab.pre, s="{:x}".format(kever.lastEst.s), d=kever.lastEst.d) + + ims = eventing.messagize(serder=serder, sigers=sigers, seal=seal) + ims = ims + atc.encode("utf-8") + + # make a copy and parse + agent.hby.psr.parseOne(ims=bytearray(ims)) + + agent.exchanges.append(dict(said=serder.said, pre=hab.pre, rec=rec, topic='credential')) + return agent.monitor.submit(serder.pre, longrunning.OpTypes.exchange, metadata=dict(said=serder.said)) + +class IpexAgreeCollectionEnd: + + @staticmethod + def on_post(req, rep, name): + """ IPEX Agree POST endpoint + + Parameters: + req: falcon.Request HTTP request + rep: falcon.Response HTTP response + name (str): human readable name for AID + + --- + summary: Reply to IPEX offer message acknowledged willingness to accept offered credential + description: Reply to IPEX offer message acknowledged willingness to accept offered credential + tags: + - Credentials + responses: + 200: + description: long running operation of IPEX agree + + """ + agent = req.context.agent + hab = agent.hby.habByName(name) + if hab is None: + raise falcon.HTTPNotFound(description=f"alias={name} is not a valid reference to an identifier") + + body = req.get_media() + + ked = httping.getRequiredParam(body, "exn") + sigs = httping.getRequiredParam(body, "sigs") + rec = httping.getRequiredParam(body, "rec") + + route = ked['r'] + + match route: + case "/ipex/agree": + op = IpexAgreeCollectionEnd.sendAgree(agent, hab, ked, sigs, rec) + case _: + raise falcon.HTTPBadRequest(description=f"invalid route {route}") + + rep.status = falcon.HTTP_200 + rep.data = op.to_json().encode("utf-8") + + @staticmethod + def sendAgree(agent, hab, ked, sigs, rec): + for recp in rec: # Have to verify we already know all the recipients. + if recp not in agent.hby.kevers: + raise falcon.HTTPBadRequest(description=f"attempt to send to unknown AID={recp}") + + # use that data to create th Serder and Sigers for the exn + serder = serdering.SerderKERI(sad=ked) + sigers = [coring.Siger(qb64=sig) for sig in sigs] + + # Now create the stream to send, need the signer seal + kever = hab.kever + seal = eventing.SealEvent(i=hab.pre, s="{:x}".format(kever.lastEst.s), d=kever.lastEst.d) + + ims = eventing.messagize(serder=serder, sigers=sigers, seal=seal) + + # make a copy and parse + agent.hby.psr.parseOne(ims=bytearray(ims)) + + agent.exchanges.append(dict(said=serder.said, pre=hab.pre, rec=rec, topic='credential')) + return agent.monitor.submit(serder.pre, longrunning.OpTypes.exchange, metadata=dict(said=serder.said)) diff --git a/tests/app/test_ipexing.py b/tests/app/test_ipexing.py index c2c42a65..06bfd9e4 100644 --- a/tests/app/test_ipexing.py +++ b/tests/app/test_ipexing.py @@ -36,6 +36,12 @@ def test_load_ends(helpers): assert isinstance(end, ipexing.IpexAdmitCollectionEnd) (end, *_) = app._router.find("/identifiers/NAME/ipex/grant") assert isinstance(end, ipexing.IpexGrantCollectionEnd) + (end, *_) = app._router.find("/identifiers/NAME/ipex/apply") + assert isinstance(end, ipexing.IpexApplyCollectionEnd) + (end, *_) = app._router.find("/identifiers/NAME/ipex/offer") + assert isinstance(end, ipexing.IpexOfferCollectionEnd) + (end, *_) = app._router.find("/identifiers/NAME/ipex/agree") + assert isinstance(end, ipexing.IpexAgreeCollectionEnd) def test_ipex_admit(helpers, mockHelpingNowIso8601): @@ -919,3 +925,314 @@ def test_granter(helpers): doist.recur(deeds=deeds) assert len(grants) == 1 + + +def test_ipex_apply(helpers, mockHelpingNowIso8601): + with helpers.openKeria() as (_, agent, app, client): + applyEnd = ipexing.IpexApplyCollectionEnd() + app.add_route("/identifiers/{name}/ipex/apply", applyEnd) + + end = aiding.IdentifierCollectionEnd() + app.add_route("/identifiers", end) + aidEnd = aiding.IdentifierResourceEnd() + app.add_route("/identifiers/{name}", aidEnd) + + salt = b'0123456789abcdef' + op = helpers.createAid(client, "test", salt) + aid = op["response"] + pre = aid['i'] + assert pre == "EHgwVwQT15OJvilVvW57HE4w0-GPs_Stj2OFoAHZSysY" + + salt2 = b'0123456789abcdeg' + op = helpers.createAid(client, "recp", salt2) + aid1 = op["response"] + pre1 = aid1['i'] + assert pre1 == "EFnYGvF_ENKJ_4PGsWsvfd_R6m5cN-3KYsz_0mAuNpCm" + + applySerder, end = exchanging.exchange(route="/ipex/apply", + payload={'m': 'Applying for a credential', 's': 'EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao', 'a': {'LEI': '78I9GKEFM361IFY3PIN0'}}, + sender=pre, + embeds=dict(), + recipient=pre1, + date=helping.nowIso8601()) + assert applySerder.ked == {'a': {'i': 'EFnYGvF_ENKJ_4PGsWsvfd_R6m5cN-3KYsz_0mAuNpCm', 'm': 'Applying for a credential', 's': 'EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao', 'a': {'LEI': '78I9GKEFM361IFY3PIN0'}}, + 'd': 'EJq6zSDUWw6iaBz8n1cY5cAW3Rrgp4E3sUsoz5JkoMZc', + 'dt': '2021-06-27T21:26:21.233257+00:00', + 'e': {}, + 'i': 'EHgwVwQT15OJvilVvW57HE4w0-GPs_Stj2OFoAHZSysY', + 'p': '', + 'q': {}, + 'r': '/ipex/apply', + 't': 'exn', + 'v': 'KERI10JSON000187_'} + assert end == b'' + sigs = ["AAAa70b4QnTOtGOsMqcezMtVzCFuRJHGeIMkWYHZ5ZxGIXM0XDVAzkYdCeadfPfzlKC6dkfiwuJ0IzLOElaanUgH"] + + body = dict( + exn=applySerder.ked, + sigs=sigs, + atc="", + rec=["EZ-i0d8JZAoTNZH3ULaU6JR2nmwyvYAfSVPzhzS6b5CM"] + ) + + data = json.dumps(body).encode("utf-8") + res = client.simulate_post(path="/identifiers/test/ipex/apply", body=data) + + assert res.status_code == 400 + assert res.json == {'description': 'attempt to send to unknown ' + 'AID=EZ-i0d8JZAoTNZH3ULaU6JR2nmwyvYAfSVPzhzS6b5CM', + 'title': '400 Bad Request'} + + body = dict( + exn=applySerder.ked, + sigs=sigs, + atc="", + rec=[pre1] + ) + + # Bad Sender + data = json.dumps(body).encode("utf-8") + res = client.simulate_post(path="/identifiers/BAD/ipex/apply", body=data) + assert res.status_code == 404 + assert res.json == {'description': 'alias=BAD is not a valid reference to an identifier', + 'title': '404 Not Found'} + + data = json.dumps(body).encode("utf-8") + res = client.simulate_post(path="/identifiers/test/ipex/apply", body=data) + assert res.json == {'done': False, + 'error': None, + 'metadata': {'said': 'EJq6zSDUWw6iaBz8n1cY5cAW3Rrgp4E3sUsoz5JkoMZc'}, + 'name': 'exchange.EHgwVwQT15OJvilVvW57HE4w0-GPs_Stj2OFoAHZSysY', + 'response': None} + + assert res.status_code == 200 + assert len(agent.exchanges) == 1 + + +def test_ipex_offer(helpers, mockHelpingNowIso8601): + with helpers.openKeria() as (_, agent, app, client): + offerEnd = ipexing.IpexOfferCollectionEnd() + app.add_route("/identifiers/{name}/ipex/offer", offerEnd) + + end0 = aiding.IdentifierCollectionEnd() + app.add_route("/identifiers", end0) + aidEnd = aiding.IdentifierResourceEnd() + app.add_route("/identifiers/{name}", aidEnd) + + salt = b'0123456789abcdef' + op = helpers.createAid(client, "test", salt) + aid = op["response"] + pre = aid['i'] + assert pre == "EHgwVwQT15OJvilVvW57HE4w0-GPs_Stj2OFoAHZSysY" + + salt2 = b'0123456789abcdeg' + op = helpers.createAid(client, "recp", salt2) + aid1 = op["response"] + pre1 = aid1['i'] + assert pre1 == "EFnYGvF_ENKJ_4PGsWsvfd_R6m5cN-3KYsz_0mAuNpCm" + + # This should be a metadata ACDC in reality + acdc = b'{"v":"ACDC10JSON000197_","d":"EBg1YzKmwZIDzZsMslTFwQARB6nUN85sRJF5oywlJr3N","i":"EIqTaQiZw73plMOq8pqHTi9BDgDrrE7iE9v2XfN2Izze","ri":"EACehJRd0wfteUAJgaTTJjMSaQqWvzeeHqAMMqxuqxU4","s":"EFgnk_c08WmZGgv9_mpldibRuqFMTQN-rAgtD-TCOwbs","a":{"d":"ELJ7Emhi0Bhxz3s7HyhZ45qcsgpvsT8p8pxwWkG362n3","dt":"2021-06-27T21:26:21.233257+00:00","i":"EFnYGvF_ENKJ_4PGsWsvfd_R6m5cN-3KYsz_0mAuNpCm","LEI":"78I9GKEFM361IFY3PIN0"}}' + embeds = dict(acdc = acdc) + + # First an offer initiated by discloser (no apply) + offer0Serder, end0 = exchanging.exchange(route="/ipex/offer", + payload={'m': 'Offering this'}, + sender=pre, + embeds=embeds, + recipient=pre1, + date=helping.nowIso8601()) + assert offer0Serder.ked == {'a': {'i': 'EFnYGvF_ENKJ_4PGsWsvfd_R6m5cN-3KYsz_0mAuNpCm', 'm': 'Offering this'}, + 'd': 'EDY-IFIMBR4umlYATxAqEAcT5jiHEMn5EyL6i1sUwxDO', + 'dt': '2021-06-27T21:26:21.233257+00:00', + 'e': {'acdc': {'a': {'d': 'ELJ7Emhi0Bhxz3s7HyhZ45qcsgpvsT8p8pxwWkG362n3', + 'dt': '2021-06-27T21:26:21.233257+00:00', + 'i': 'EFnYGvF_ENKJ_4PGsWsvfd_R6m5cN-3KYsz_0mAuNpCm', + 'LEI': '78I9GKEFM361IFY3PIN0'}, + 'd': 'EBg1YzKmwZIDzZsMslTFwQARB6nUN85sRJF5oywlJr3N', + 'i': 'EIqTaQiZw73plMOq8pqHTi9BDgDrrE7iE9v2XfN2Izze', + 'ri': 'EACehJRd0wfteUAJgaTTJjMSaQqWvzeeHqAMMqxuqxU4', + 's': 'EFgnk_c08WmZGgv9_mpldibRuqFMTQN-rAgtD-TCOwbs', + 'v': 'ACDC10JSON000197_'}, + 'd': 'EEcYZMP-zilz2w1w2hEFm6tF0eaX_1KaPEWhNfY3kf8i'}, + 'i': 'EHgwVwQT15OJvilVvW57HE4w0-GPs_Stj2OFoAHZSysY', + 'p': '', + 'q': {}, + 'r': '/ipex/offer', + 't': 'exn', + 'v': 'KERI10JSON0002f6_'} + assert end0 == b'' + sigs = ["AAAa70b4QnTOtGOsMqcezMtVzCFuRJHGeIMkWYHZ5ZxGIXM0XDVAzkYdCeadfPfzlKC6dkfiwuJ0IzLOElaanUgH"] + + body = dict( + exn=offer0Serder.ked, + sigs=sigs, + atc="", + rec=["EZ-i0d8JZAoTNZH3ULaU6JR2nmwyvYAfSVPzhzS6b5CM"] + ) + + data = json.dumps(body).encode("utf-8") + res = client.simulate_post(path="/identifiers/test/ipex/offer", body=data) + + assert res.status_code == 400 + assert res.json == {'description': 'attempt to send to unknown ' + 'AID=EZ-i0d8JZAoTNZH3ULaU6JR2nmwyvYAfSVPzhzS6b5CM', + 'title': '400 Bad Request'} + + body = dict( + exn=offer0Serder.ked, + sigs=sigs, + atc="", + rec=[pre1] + ) + + # Bad Sender + data = json.dumps(body).encode("utf-8") + res = client.simulate_post(path="/identifiers/BAD/ipex/offer", body=data) + assert res.status_code == 404 + assert res.json == {'description': 'alias=BAD is not a valid reference to an identifier', + 'title': '404 Not Found'} + + data = json.dumps(body).encode("utf-8") + res = client.simulate_post(path="/identifiers/test/ipex/offer", body=data) + assert res.json == {'done': False, + 'error': None, + 'metadata': {'said': 'EDY-IFIMBR4umlYATxAqEAcT5jiHEMn5EyL6i1sUwxDO'}, + 'name': 'exchange.EHgwVwQT15OJvilVvW57HE4w0-GPs_Stj2OFoAHZSysY', + 'response': None} + + assert res.status_code == 200 + assert len(agent.exchanges) == 1 + + # Now an offer in response to an agree + dig = "EB_Lr3fHezn1ygn-wbBT5JjzaCMxTmhUoegXeZzWC2eT" + offer1Serder, end1 = exchanging.exchange(route="/ipex/offer", + payload={'m': 'How about this'}, + sender=pre, + embeds=embeds, + dig=dig, + recipient=pre1, + date=helping.nowIso8601()) + assert offer1Serder.ked == {'a': {'i': 'EFnYGvF_ENKJ_4PGsWsvfd_R6m5cN-3KYsz_0mAuNpCm', 'm': 'How about this'}, + 'd': 'EDT7go7TfCTzeFnhNBl19JJqdabBfBx8tjBvi_asFCwT', + 'dt': '2021-06-27T21:26:21.233257+00:00', + 'e': {'acdc': {'a': {'d': 'ELJ7Emhi0Bhxz3s7HyhZ45qcsgpvsT8p8pxwWkG362n3', + 'dt': '2021-06-27T21:26:21.233257+00:00', + 'i': 'EFnYGvF_ENKJ_4PGsWsvfd_R6m5cN-3KYsz_0mAuNpCm', + 'LEI': '78I9GKEFM361IFY3PIN0'}, + 'd': 'EBg1YzKmwZIDzZsMslTFwQARB6nUN85sRJF5oywlJr3N', + 'i': 'EIqTaQiZw73plMOq8pqHTi9BDgDrrE7iE9v2XfN2Izze', + 'ri': 'EACehJRd0wfteUAJgaTTJjMSaQqWvzeeHqAMMqxuqxU4', + 's': 'EFgnk_c08WmZGgv9_mpldibRuqFMTQN-rAgtD-TCOwbs', + 'v': 'ACDC10JSON000197_'}, + 'd': 'EEcYZMP-zilz2w1w2hEFm6tF0eaX_1KaPEWhNfY3kf8i'}, + 'i': 'EHgwVwQT15OJvilVvW57HE4w0-GPs_Stj2OFoAHZSysY', + 'p': 'EB_Lr3fHezn1ygn-wbBT5JjzaCMxTmhUoegXeZzWC2eT', + 'q': {}, + 'r': '/ipex/offer', + 't': 'exn', + 'v': 'KERI10JSON000323_'} + assert end1 == b'' + sigs = ["AAAa70b4QnTOtGOsMqcezMtVzCFuRJHGeIMkWYHZ5ZxGIXM0XDVAzkYdCeadfPfzlKC6dkfiwuJ0IzLOElaanUgH"] + + body = dict( + exn=offer1Serder.ked, + sigs=sigs, + atc="", + rec=[pre1] + ) + + data = json.dumps(body).encode("utf-8") + res = client.simulate_post(path="/identifiers/test/ipex/offer", body=data) + assert res.json == {'done': False, + 'error': None, + 'metadata': {'said': 'EDT7go7TfCTzeFnhNBl19JJqdabBfBx8tjBvi_asFCwT'}, + 'name': 'exchange.EHgwVwQT15OJvilVvW57HE4w0-GPs_Stj2OFoAHZSysY', + 'response': None} + + assert res.status_code == 200 + assert len(agent.exchanges) == 2 + + +def test_ipex_agree(helpers, mockHelpingNowIso8601): + with helpers.openKeria() as (_, agent, app, client): + agreeEnd = ipexing.IpexAgreeCollectionEnd() + app.add_route("/identifiers/{name}/ipex/agree", agreeEnd) + + end = aiding.IdentifierCollectionEnd() + app.add_route("/identifiers", end) + aidEnd = aiding.IdentifierResourceEnd() + app.add_route("/identifiers/{name}", aidEnd) + + salt = b'0123456789abcdef' + op = helpers.createAid(client, "test", salt) + aid = op["response"] + pre = aid['i'] + assert pre == "EHgwVwQT15OJvilVvW57HE4w0-GPs_Stj2OFoAHZSysY" + + salt2 = b'0123456789abcdeg' + op = helpers.createAid(client, "recp", salt2) + aid1 = op["response"] + pre1 = aid1['i'] + assert pre1 == "EFnYGvF_ENKJ_4PGsWsvfd_R6m5cN-3KYsz_0mAuNpCm" + dig = "EB_Lr3fHezn1ygn-wbBT5JjzaCMxTmhUoegXeZzWC2eT" + + offerSerder, end = exchanging.exchange(route="/ipex/agree", + payload={'m': 'Agreed'}, + sender=pre, + embeds=dict(), + dig=dig, + recipient=pre1, + date=helping.nowIso8601()) + assert offerSerder.ked == {'a': {'i': 'EFnYGvF_ENKJ_4PGsWsvfd_R6m5cN-3KYsz_0mAuNpCm', 'm': 'Agreed'}, + 'd': 'ECxQe2TgUCRjbbxyCaXMEp6EtSMaqPmDstetoi4bEUrG', + 'dt': '2021-06-27T21:26:21.233257+00:00', + 'e': {}, + 'i': 'EHgwVwQT15OJvilVvW57HE4w0-GPs_Stj2OFoAHZSysY', + 'p': 'EB_Lr3fHezn1ygn-wbBT5JjzaCMxTmhUoegXeZzWC2eT', + 'q': {}, + 'r': '/ipex/agree', + 't': 'exn', + 'v': 'KERI10JSON00014a_'} + assert end == b'' + sigs = ["AAAa70b4QnTOtGOsMqcezMtVzCFuRJHGeIMkWYHZ5ZxGIXM0XDVAzkYdCeadfPfzlKC6dkfiwuJ0IzLOElaanUgH"] + + body = dict( + exn=offerSerder.ked, + sigs=sigs, + atc="", + rec=["EZ-i0d8JZAoTNZH3ULaU6JR2nmwyvYAfSVPzhzS6b5CM"] + ) + + data = json.dumps(body).encode("utf-8") + res = client.simulate_post(path="/identifiers/test/ipex/agree", body=data) + + assert res.status_code == 400 + assert res.json == {'description': 'attempt to send to unknown ' + 'AID=EZ-i0d8JZAoTNZH3ULaU6JR2nmwyvYAfSVPzhzS6b5CM', + 'title': '400 Bad Request'} + + body = dict( + exn=offerSerder.ked, + sigs=sigs, + atc="", + rec=[pre1] + ) + + # Bad Sender + data = json.dumps(body).encode("utf-8") + res = client.simulate_post(path="/identifiers/BAD/ipex/agree", body=data) + assert res.status_code == 404 + assert res.json == {'description': 'alias=BAD is not a valid reference to an identifier', + 'title': '404 Not Found'} + + data = json.dumps(body).encode("utf-8") + res = client.simulate_post(path="/identifiers/test/ipex/agree", body=data) + assert res.json == {'done': False, + 'error': None, + 'metadata': {'said': 'ECxQe2TgUCRjbbxyCaXMEp6EtSMaqPmDstetoi4bEUrG'}, + 'name': 'exchange.EHgwVwQT15OJvilVvW57HE4w0-GPs_Stj2OFoAHZSysY', + 'response': None} + + assert res.status_code == 200 + assert len(agent.exchanges) == 1 From 29e729eda9c416e14f2debf74b02bbfb2f350d47 Mon Sep 17 00:00:00 2001 From: Philip Feairheller Date: Sat, 9 Mar 2024 08:31:34 -0800 Subject: [PATCH 13/21] Update to match PR WebOfTrust/keripy#701 for defaulting to non-deterministic salts that broke a bunch of tests here. (#210) Signed-off-by: pfeairheller --- setup.py | 2 +- src/keria/app/agenting.py | 4 ++-- tests/app/test_agenting.py | 4 ++-- tests/app/test_aiding.py | 10 +++++----- tests/core/test_authing.py | 9 ++++++--- 5 files changed, 16 insertions(+), 13 deletions(-) diff --git a/setup.py b/setup.py index 5f5e2041..c30c5784 100644 --- a/setup.py +++ b/setup.py @@ -79,7 +79,7 @@ 'keri @ git+https://git@github.com/weboftrust/keripy.git@development', 'mnemonic>=0.20', 'multicommand>=1.0.0', - 'falcon>=3.1.0', + 'falcon>=3.1.3', 'http_sfv>=0.9.8', 'dataclasses_json>=0.5.7', 'apispec>=6.3.0', diff --git a/src/keria/app/agenting.py b/src/keria/app/agenting.py index 963fb716..78fbf345 100644 --- a/src/keria/app/agenting.py +++ b/src/keria/app/agenting.py @@ -173,7 +173,7 @@ def __init__(self, name, bran, base="", configFile=None, configDir=None, adb=Non self.adb = adb if adb is not None else basing.AgencyBaser(name="TheAgency", base=base, reopen=True, temp=temp) super(Agency, self).__init__(doers=[], always=True) - def create(self, caid): + def create(self, caid, salt=None): ks = keeping.Keeper(name=caid, base=self.base, temp=self.temp, @@ -196,7 +196,7 @@ def create(self, caid): cf.put(data) # Create the Hab for the Agent with only 2 AIDs - agentHby = habbing.Habery(name=caid, base=self.base, bran=self.bran, ks=ks, cf=cf, temp=self.temp) + agentHby = habbing.Habery(name=caid, base=self.base, bran=self.bran, ks=ks, cf=cf, temp=self.temp, salt=salt) agentHab = agentHby.makeHab(f"agent-{caid}", ns="agent", transferable=True, delpre=caid) agentRgy = Regery(hby=agentHby, name=agentHab.name, base=self.base, temp=self.temp) diff --git a/tests/app/test_agenting.py b/tests/app/test_agenting.py index d0d6d8df..42a05a56 100644 --- a/tests/app/test_agenting.py +++ b/tests/app/test_agenting.py @@ -79,7 +79,7 @@ def test_agency(): doist.enter(doers=[agency]) caid = "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose" - agent = agency.create(caid) + agent = agency.create(caid, salt=salter.qb64) assert agent.pre == "EIAEKYpTygdBtFHBrHKWeh0aYCdx0ZJqZtzQLFnaDB2b" badcaid = "E987eerAdhmvrjDeam2eAO2SR5niCgnjAJXJHtJoe" @@ -121,7 +121,7 @@ def test_agency(): doist.enter(doers=[agency]) caid = "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose" - agent = agency.create(caid) + agent = agency.create(caid, salt=salter.qb64) assert agent.pre == "EEXekkGu9IAzav6pZVJhkLnjtjM5v3AcyA-pdKUcaGei" # Rcreate the agency to see if agent is reloaded from disk diff --git a/tests/app/test_aiding.py b/tests/app/test_aiding.py index 7b6efe29..15f84477 100644 --- a/tests/app/test_aiding.py +++ b/tests/app/test_aiding.py @@ -242,9 +242,12 @@ def test_agent_resource(helpers, mockHelpingNowUTC): def test_identifier_collection_end(helpers): + salt = b'0123456789abcdef' + salter = coring.Salter(raw=salt) + with helpers.openKeria() as (agency, agent, app, client), \ - habbing.openHby(name="p1", temp=True) as p1hby, \ - habbing.openHby(name="p2", temp=True) as p2hby: + habbing.openHby(name="p1", temp=True, salt=salter.qb64) as p1hby, \ + habbing.openHby(name="p2", temp=True, salt=salter.qb64) as p2hby: end = aiding.IdentifierCollectionEnd() resend = aiding.IdentifierResourceEnd() app.add_route("/identifiers", end) @@ -1449,7 +1452,6 @@ def test_approve_delegation(helpers): assert res.status_code == 200 agt = res.json["agent"] ctrl = res.json["controller"] - assert agt["i"] == "EHyaw-1bCenigGQCZRs_hXNdndHw0fSf-Q5-LpUwOR8r" assert ctrl["state"]["i"] == controllerAID anchor = dict(i=agt["i"], s="0", d=agt["d"]) @@ -1533,9 +1535,7 @@ def test_rotation(helpers): res = client.simulate_get(path=f"/agent/{serder.pre}") assert res.status_code == 200 - agt = res.json["agent"] ctrl = res.json["controller"] - assert agt["i"] == "EHyaw-1bCenigGQCZRs_hXNdndHw0fSf-Q5-LpUwOR8r" assert ctrl["state"]["i"] == controllerAID op = helpers.createAid(client, name="salty_aid", salt=bran) diff --git a/tests/core/test_authing.py b/tests/core/test_authing.py index c425c104..5aa24692 100644 --- a/tests/core/test_authing.py +++ b/tests/core/test_authing.py @@ -12,7 +12,7 @@ from hio.help import Hict from keri import kering from keri.app import habbing -from keri.core import parsing, eventing +from keri.core import parsing, eventing, coring from keri.end import ending from keria.app import agenting @@ -21,6 +21,8 @@ def test_authenticater(mockHelpingNowUTC): salt = b'0123456789abcdef' + salter = coring.Salter(raw=salt) + with habbing.openHab(name="caid", salt=salt, temp=True) as (controllerHby, controller): agency = agenting.Agency(name="agency", base='', bran=None, temp=True) @@ -30,7 +32,7 @@ def test_authenticater(mockHelpingNowUTC): doist = doing.Doist(limit=1.0, tock=0.03125, real=True) doist.enter(doers=[agency]) - agent = agency.create(caid=controller.pre) + agent = agency.create(caid=controller.pre, salt=salter.qb64) # Create authenticater with Agent and controllers AID headers = Hict([ @@ -203,6 +205,7 @@ def test_signature_validation(mockHelpingNowUTC): assert rep.status == falcon.HTTP_401 salt = b'0123456789abcdef' + salter = coring.Salter(raw=salt) with habbing.openHab(name="caid", salt=salt, temp=True) as (controllerHby, controller): agency = agenting.Agency(name="agency", base='', bran=None, temp=True) @@ -212,7 +215,7 @@ def test_signature_validation(mockHelpingNowUTC): doist = doing.Doist(limit=1.0, tock=0.03125, real=True) doist.enter(doers=[agency]) - agent = agency.create(caid=controller.pre) + agent = agency.create(caid=controller.pre, salt=salter.qb64) req = testing.create_req(method="POST", path="/reward") req.context.agent = agent rep = falcon.Response() From 48e75c2703dafa1110e65a1357d6d3444af2499b Mon Sep 17 00:00:00 2001 From: Philip Feairheller Date: Sat, 9 Mar 2024 10:51:55 -0800 Subject: [PATCH 14/21] Test coverage for new registry rename endpoint (#211) Signed-off-by: pfeairheller --- src/keria/app/credentialing.py | 66 +++++++++++++++++++++++++++++++++ tests/app/test_credentialing.py | 34 +++++++++++++++++ 2 files changed, 100 insertions(+) diff --git a/src/keria/app/credentialing.py b/src/keria/app/credentialing.py index f70d0e0c..eb816226 100644 --- a/src/keria/app/credentialing.py +++ b/src/keria/app/credentialing.py @@ -14,6 +14,8 @@ from keri.app.habbing import SignifyGroupHab from keri.core import coring, scheming, serdering from keri.db import dbing +from keri.db.dbing import dgKey +from keri.vdr import viring from keria.core import httping, longrunning @@ -233,6 +235,70 @@ def on_get(req, rep, name, registryName): rep.content_type = "application/json" rep.data = json.dumps(rd).encode("utf-8") + @staticmethod + def on_put(req, rep, name, registryName): + """ Registry Resource PUT endpoint + + Parameters: + req: falcon.Request HTTP request + rep: falcon.Response HTTP response + name (str): human readable name for AID + registryName(str): human readable name for registry or its SAID + + --- + summary: Get a single credential issuance and revocation registy + description: Get a single credential issuance and revocation registy + tags: + - Registries + responses: + 200: + description: credential issuance and revocation registy + + """ + agent = req.context.agent + + hab = agent.hby.habByName(name) + if hab is None: + raise falcon.HTTPNotFound(description=f"{name} is not a valid reference to an identifier") + + body = req.get_media() + if 'name' not in body: + raise falcon.HTTPBadRequest(description="'name' is required in body") + + name = body['name'] + if agent.rgy.registryByName(name) is not None: + raise falcon.HTTPBadRequest(description=f"{name} is already in use for a registry") + + registry = agent.rgy.registryByName(registryName) + if registry is None: + if registryName in agent.rgy.regs: # Check to see if the registryName parameter is a SAID + registry = agent.rgy.regs[registryName] + else: + regk = registryName + key = dgKey(regk, regk) + raw = agent.rgy.reger.getTvt(key=key) + if raw is None: + raise falcon.HTTPNotFound( + description=f"{registryName} is not a valid reference to a credential registry") + + regser = serdering.SerderKERI(raw=bytes(raw)) + registry = agent.rgy.makeSignifyRegistry(name, hab.pre, regser) + + regord = viring.RegistryRecord(registryKey=registry.regk, prefix=hab.pre) + agent.rgy.reger.regs.pin(keys=(name,), val=regord) + agent.rgy.reger.regs.rem(keys=(registryName,)) + registry.name = name + + rd = dict( + name=registry.name, + regk=registry.regk, + pre=registry.hab.pre, + state=asdict(registry.tever.state()) + ) + rep.status = falcon.HTTP_200 + rep.content_type = "application/json" + rep.data = json.dumps(rd).encode("utf-8") + class SchemaResourceEnd: diff --git a/tests/app/test_credentialing.py b/tests/app/test_credentialing.py index b015f308..342542a8 100644 --- a/tests/app/test_credentialing.py +++ b/tests/app/test_credentialing.py @@ -94,6 +94,8 @@ def test_registry_end(helpers, seeder): idResEnd = aiding.IdentifierResourceEnd() registryEnd = credentialing.RegistryCollectionEnd(idResEnd) app.add_route("/identifiers/{name}/registries", registryEnd) + registryResEnd = credentialing.RegistryResourceEnd() + app.add_route("/identifiers/{name}/registries/{registryName}", registryResEnd) opEnd = longrunning.OperationResourceEnd() app.add_route("/operations/{name}", opEnd) @@ -160,6 +162,38 @@ def test_registry_end(helpers, seeder): assert result.json == {'description': 'alias is not a valid reference to an identifier', 'title': '404 Not Found'} + # Try with bad identifier name + body = b'{"name": "new-name"}' + result = client.simulate_put(path="/identifiers/test-bad/registries/test", body=body) + assert result.status == falcon.HTTP_404 + assert result.json == {'description': 'test-bad is not a valid reference to an identifier', + 'title': '404 Not Found'} + + result = client.simulate_put(path="/identifiers/test/registries/test", body=body) + assert result.status == falcon.HTTP_200 + regk = result.json['regk'] + + # Try to rename a the now used name + result = client.simulate_put(path="/identifiers/test/registries/new-name", body=b'{}') + assert result.status == falcon.HTTP_400 + assert result.json == {'description': "'name' is required in body", 'title': '400 Bad Request'} + + # Try to rename a the now used name + result = client.simulate_put(path="/identifiers/test/registries/test", body=body) + assert result.status == falcon.HTTP_400 + assert result.json == {'description': 'new-name is already in use for a registry', + 'title': '400 Bad Request'} + + # Try to rename a now non-existant registry + body = b'{"name": "newnew-name"}' + result = client.simulate_put(path="/identifiers/test/registries/test", body=body) + assert result.status == falcon.HTTP_404 + assert result.json == {'description': 'test is not a valid reference to a credential registry', + 'title': '404 Not Found'} + # Rename registry by SAID + body = b'{"name": "newnew-name"}' + result = client.simulate_put(path=f"/identifiers/test/registries/{regk}", body=body) + assert result.status == falcon.HTTP_200 result = client.simulate_get(path="/identifiers/not_test/registries") assert result.status == falcon.HTTP_404 From c38a54be390543cfb5e3dfc4afc59dbe87efd991 Mon Sep 17 00:00:00 2001 From: Shoeb Ahmed Tanjim <39959228+s-a-tanjim@users.noreply.github.com> Date: Mon, 11 Mar 2024 02:45:29 +0600 Subject: [PATCH 15/21] Make log level configurable from command line argument (#199) --- images/keria.dockerfile | 4 ++-- src/keria/app/cli/commands/start.py | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/images/keria.dockerfile b/images/keria.dockerfile index 34f9e3e4..5d2f0bb5 100644 --- a/images/keria.dockerfile +++ b/images/keria.dockerfile @@ -1,5 +1,5 @@ # Builder stage -FROM python:3.10.13-alpine3.18 as builder +FROM python:3.12-alpine3.19 as builder # Install compilation dependencies RUN apk --no-cache add \ @@ -29,7 +29,7 @@ RUN . "$HOME/.cargo/env" && \ pip install -r requirements.txt # Runtime stage -FROM python:3.10.13-alpine3.18 +FROM python:3.12-alpine3.19 # Install runtime dependencies RUN apk --no-cache add \ diff --git a/src/keria/app/cli/commands/start.py b/src/keria/app/cli/commands/start.py index 7381e024..7447f05f 100644 --- a/src/keria/app/cli/commands/start.py +++ b/src/keria/app/cli/commands/start.py @@ -60,10 +60,12 @@ help="TLS server signed certificate (public key) file") parser.add_argument("--cafilepath", action="store", required=False, default=None, help="TLS server CA certificate chain") +parser.add_argument("--loglevel", action="store", required=False, default="CRITICAL", + help="Set log level to DEBUG | INFO | WARNING | ERROR | CRITICAL. Default is CRITICAL") def launch(args): - help.ogler.level = logging.CRITICAL + help.ogler.level = logging.getLevelName(args.loglevel) help.ogler.reopen(name=args.name, temp=True, clear=True) logger = help.ogler.getLogger() From 2a0c19f792f4370ad895d35ac053d8596a1d1b6d Mon Sep 17 00:00:00 2001 From: Philip Feairheller Date: Fri, 29 Mar 2024 13:23:29 -0700 Subject: [PATCH 16/21] Fix access to renamed Protos (#219) Signed-off-by: pfeairheller --- src/keria/app/indirecting.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/keria/app/indirecting.py b/src/keria/app/indirecting.py index e249f875..7ff53c13 100644 --- a/src/keria/app/indirecting.py +++ b/src/keria/app/indirecting.py @@ -9,7 +9,7 @@ from keri.app import httping from keri.core import eventing from keri.core.coring import Ilks, Sadder -from keri.kering import Protos +from keri.kering import Protocols CESR_DESTINATION_HEADER = "CESR-DESTINATION" @@ -84,7 +84,7 @@ def on_post(self, req, rep): agent.parser.ims.extend(msg) - if serder.proto == Protos.acdc: + if serder.proto == Protocols.acdc: rep.status = falcon.HTTP_204 else: From 664cfee71cd3e5dff83a4e9a6800b0922221d3b2 Mon Sep 17 00:00:00 2001 From: Philip Feairheller Date: Thu, 4 Apr 2024 10:08:59 -0700 Subject: [PATCH 17/21] Updates to match the changes in KERIpy for tracking multisig participants. (#224) * Updated to match the new database for Habs and names from KERIpy as well as the new method for tracking signing and rotation members of a multisig AID. Signed-off-by: pfeairheller * Update event cloning. Signed-off-by: pfeairheller * Updates to match latest changes from KERIpy and fixing reference to NoBackers that should remain in this version Signed-off-by: pfeairheller * Upgrade python and hio versions Signed-off-by: pfeairheller --------- Signed-off-by: pfeairheller --- .github/workflows/python-app-ci.yml | 8 +++---- setup.py | 4 ++-- src/keria/app/agenting.py | 6 ++--- src/keria/app/aiding.py | 34 +++++++++++++++++++---------- src/keria/testing/testing_helper.py | 2 +- tests/app/test_aiding.py | 14 ++++++++---- tests/app/test_credentialing.py | 2 +- tests/app/test_ipexing.py | 4 ++-- 8 files changed, 46 insertions(+), 28 deletions(-) diff --git a/.github/workflows/python-app-ci.yml b/.github/workflows/python-app-ci.yml index 1cd3d834..b7b9e022 100644 --- a/.github/workflows/python-app-ci.yml +++ b/.github/workflows/python-app-ci.yml @@ -21,10 +21,10 @@ jobs: steps: - uses: actions/checkout@v3 - - name: Set up Python 3.12.1 + - name: Set up Python 3.12.2 uses: actions/setup-python@v2 with: - python-version: 3.12.1 + python-version: 3.12.2 - name: Install dependencies run: | python -m pip install --upgrade pip @@ -44,10 +44,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Set up Python 3.12.1 + - name: Set up Python 3.12.2 uses: actions/setup-python@v2 with: - python-version: 3.12.1 + python-version: 3.12.2 - name: Install dependencies run: | python -m pip install --upgrade pip diff --git a/setup.py b/setup.py index c30c5784..9f785855 100644 --- a/setup.py +++ b/setup.py @@ -73,9 +73,9 @@ "resolver", # eg: 'keyword1', 'keyword2', 'keyword3', ], - python_requires='>=3.12.1', + python_requires='>=3.12.2', install_requires=[ - 'hio>=0.6.9', + 'hio>=0.6.12', 'keri @ git+https://git@github.com/weboftrust/keripy.git@development', 'mnemonic>=0.20', 'multicommand>=1.0.0', diff --git a/src/keria/app/agenting.py b/src/keria/app/agenting.py index 78fbf345..1ce7444f 100644 --- a/src/keria/app/agenting.py +++ b/src/keria/app/agenting.py @@ -972,12 +972,12 @@ def on_get(req, rep): preb = pre.encode("utf-8") events = [] for fn, dig in agent.hby.db.getFelItemPreIter(preb, fn=0): - dgkey = dbing.dgKey(preb, dig) # get message - if not (raw := agent.hby.db.getEvt(key=dgkey)): + if not (raw := agent.hby.db.cloneEvtMsg(pre=preb, fn=fn, dig=dig)): raise falcon.HTTPInternalServerError(f"Missing event for dig={dig}.") serder = serdering.SerderKERI(raw=bytes(raw)) - events.append(serder.ked) + atc = raw[serder.size:] + events.append(dict(ked=serder.ked, atc=atc.decode("utf-8"))) rep.status = falcon.HTTP_200 rep.content_type = "application/json" diff --git a/src/keria/app/aiding.py b/src/keria/app/aiding.py index c2e8625b..0504406b 100644 --- a/src/keria/app/aiding.py +++ b/src/keria/app/aiding.py @@ -95,7 +95,12 @@ def on_get(self, _, rep, caid): if agent.caid not in agent.hby.kevers: raise falcon.HTTPBadRequest(description=f"invalid controller configuration, {agent.caid} not found") - pidx = agent.hby.db.habs.cntAll() + pidx = 0 + for name, _ in agent.hby.db.names.getItemIter(): + if name[0] != "agent": + pidx += 1 + + # pidx = agent.hby.db.habs.cntAll() state = asdict(agent.hby.kevers[agent.caid].state()) key = dbing.dgKey(state['i'], state['ee']['d']) # digest key @@ -269,17 +274,18 @@ def on_get(req, rep): start, end = httping.parseRangeHeader(rng, "aids") count = agent.hby.db.habs.cntAll() - it = agent.hby.db.habs.getItemIter() + it = agent.hby.db.names.getItemIter() for _ in range(start): try: next(it) except StopIteration: break - for name, habord in it: - name = ".".join(name) # detupleize the database key name + for (ns, name), pre in it: + if ns == "agent": + continue - hab = agent.hby.habByName(name) + hab = agent.hby.habs[pre] data = info(hab, agent.mgr) res.append(data) @@ -288,7 +294,7 @@ def on_get(req, rep): end = start + (len(res) - 1) if len(res) > 0 else 0 rep.set_header("Accept-Ranges", "aids") - rep.set_header("Content-Range", f"aids {start}-{end}/{count}") + rep.set_header("Content-Range", f"aids {start}-{end}/{count-1}") rep.content_type = "application/json" rep.data = json.dumps(res).encode("utf-8") @@ -350,10 +356,12 @@ def on_post(req, rep): ndigs = group["ndigs"] digers = [coring.Diger(qb64=ndig) for ndig in ndigs] - smids = httping.getRequiredParam(body, "smids") - rmids = httping.getRequiredParam(body, "rmids") - - hab = agent.hby.makeSignifyGroupHab(name, mhab=mhab, serder=serder, sigers=sigers) + states = httping.getRequiredParam(body, "smids") + rstates = httping.getRequiredParam(body, "rmids") + smids = [state['i'] for state in states] + rmids = [rstate['i'] for rstate in rstates] + hab = agent.hby.makeSignifyGroupHab(name, mhab=mhab, smids=smids, rmids=rmids, serder=serder, + sigers=sigers) try: agent.inceptGroup(pre=serder.pre, mpre=mhab.pre, verfers=verfers, digers=digers) except ValueError as e: @@ -361,7 +369,7 @@ def on_post(req, rep): raise falcon.HTTPInternalServerError(description=f"{e.args[0]}") # Generate response, a long running operaton indicator for the type - agent.groups.append(dict(pre=hab.pre, serder=serder, sigers=sigers, smids=smids, rmids=rmids)) + agent.groups.append(dict(pre=hab.pre, serder=serder, sigers=sigers, smids=states, rmids=rstates)) op = agent.monitor.submit(serder.pre, longrunning.OpTypes.group, metadata=dict(sn=0)) rep.content_type = "application/json" @@ -1145,6 +1153,10 @@ def on_get(self, req, rep): def authn(agent, contacts): for contact in contacts: aid = contact['id'] + + ends = agent.agentHab.endsFor(aid) + contact['ends'] = ends + accepted = [saider.qb64 for saider in agent.hby.db.chas.get(keys=(aid,))] received = [saider.qb64 for saider in agent.hby.db.reps.get(keys=(aid,))] diff --git a/src/keria/testing/testing_helper.py b/src/keria/testing/testing_helper.py index 34287efa..e6cece6e 100644 --- a/src/keria/testing/testing_helper.py +++ b/src/keria/testing/testing_helper.py @@ -473,7 +473,7 @@ def createRegistry(client, agent, salt, doist, deeds): baks=[], toad="0", nonce=nonce, - cnfg=[eventing.TraitCodex.NoRegistrarBackers], + cnfg=[eventing.TraitCodex.NoBackers], code=coring.MtrDex.Blake3_256) anchor = dict(i=regser.ked['i'], s=regser.ked["s"], d=regser.said) serder, sigers = Helpers.interact(pre=pre, bran=salt, pidx=0, ridx=0, dig=aid['d'], sn='1', data=[anchor]) diff --git a/tests/app/test_aiding.py b/tests/app/test_aiding.py index 15f84477..2ce49e80 100644 --- a/tests/app/test_aiding.py +++ b/tests/app/test_aiding.py @@ -626,10 +626,10 @@ def test_identifier_collection_end(helpers): assert res.status_code == 200 assert "signing" in res.json signing = res.json["signing"] - assert len(signing) == 5 # this number is a little janky because we reuse public keys above, leaving for now + assert len(signing) == 3 assert "rotation" in res.json rotation = res.json["rotation"] - assert len(rotation) == 5 # this number is a little janky because we reuse rotation keys above, leaving for now + assert len(rotation) == 3 # Try unknown witness serder, signers = helpers.incept(salt, "signify:aid", pidx=3, @@ -733,7 +733,7 @@ def test_identifier_collection_end(helpers): assert res.status_code == 200 events = res.json assert len(events) == 2 - assert events[1] == serder.ked + assert events[1]['ked'] == serder.ked serder = eventing.interact(pre=pre, dig=serder.said, sn=len(events), data=[pre]) sigers = [signer.sign(ser=serder.raw, index=0).qb64 for signer in signers] @@ -748,7 +748,7 @@ def test_identifier_collection_end(helpers): assert res.status_code == 200 events = res.json assert len(events) == 3 - assert events[2] == serder.ked + assert events[2]['ked'] == serder.ked # Bad interactions res = client.simulate_put(path="/identifiers/badrandy?type=ixn", body=json.dumps(body)) @@ -1135,6 +1135,7 @@ def test_contact_ends(helpers): assert response.status == falcon.HTTP_200 assert response.json == [{'challenges': [], 'company': 'GLEIF', + 'ends': {}, 'first': 'Ken3', 'id': 'EAjKmvW6flpWJfdYYZ2Lu4pllPWKFjCBz0dcX-S86Nvg', 'last': 'Burns3', @@ -1146,30 +1147,35 @@ def test_contact_ends(helpers): assert response.status == falcon.HTTP_200 assert response.json == [{'challenges': [], 'company': 'GLEIF', + 'ends': {}, 'first': 'Ken3', 'id': 'EAjKmvW6flpWJfdYYZ2Lu4pllPWKFjCBz0dcX-S86Nvg', 'last': 'Burns3', 'wellKnowns': []}, {'challenges': [], 'company': 'GLEIF', + 'ends': {}, 'first': 'Ken1', 'id': 'EER-n23rDM2RQB8Kw4KRrm8SFpoid4Jnelhauo6KxQpz', 'last': 'Burns1', 'wellKnowns': []}, {'challenges': [], 'company': 'ProSapien', + 'ends': {}, 'first': 'Ken4', 'id': 'EGwcSt3uvK5-oHI7hVU7dKMvWt0vRfMW2demzBBMDnBG', 'last': 'Burns4', 'wellKnowns': []}, {'challenges': [], 'company': 'ProSapien', + 'ends': {}, 'first': 'Ken2', 'id': 'ELTQ3tF3n7QS8LDpKMdJyCMhVyMdvNPTiisnqW5ZQP3C', 'last': 'Burns2', 'wellKnowns': []}, {'challenges': [], 'company': 'GLEIF', + 'ends': {}, 'first': 'Ken0', 'id': 'EPo8Wy1xpTa6ri25M4IlmWBBzs5y8v4Qn3Z8xP4kEjcK', 'last': 'Burns0', diff --git a/tests/app/test_credentialing.py b/tests/app/test_credentialing.py index 342542a8..229dfbfd 100644 --- a/tests/app/test_credentialing.py +++ b/tests/app/test_credentialing.py @@ -120,7 +120,7 @@ def test_registry_end(helpers, seeder): baks=[], toad="0", nonce=nonce, - cnfg=[TraitCodex.NoRegistrarBackers], + cnfg=[TraitCodex.NoBackers], code=coring.MtrDex.Blake3_256) anchor = dict(i=regser.ked['i'], s=regser.ked["s"], d=regser.said) serder, sigers = helpers.interact(pre=pre, bran=salt, pidx=0, ridx=0, dig=aid['d'], sn='1', data=[anchor]) diff --git a/tests/app/test_ipexing.py b/tests/app/test_ipexing.py index 06bfd9e4..2a1cede8 100644 --- a/tests/app/test_ipexing.py +++ b/tests/app/test_ipexing.py @@ -12,7 +12,7 @@ from hio.base import doing from hio.help import decking from keri.app import habbing, signing -from keri.core import eventing, coring, parsing, serdering +from keri.core import eventing, coring, serdering from keri.help import helping from keri.kering import Roles from keri.peer import exchanging @@ -605,7 +605,7 @@ def test_multisig_grant_admit(seeder, helpers): baks=[], toad="0", nonce=nonce, - cnfg=[eventing.TraitCodex.NoRegistrarBackers], + cnfg=[eventing.TraitCodex.NoBackers], code=coring.MtrDex.Blake3_256) anchor = dict(i=regser.ked['i'], s=regser.ked["s"], d=regser.said) From 0d4f8a73714a2fcbe93584733006e2f909441db1 Mon Sep 17 00:00:00 2001 From: Shoeb Ahmed Tanjim <39959228+s-a-tanjim@users.noreply.github.com> Date: Fri, 5 Apr 2024 03:39:16 +0600 Subject: [PATCH 18/21] Writing logs into file is configurable from cli argument (#223) --- src/keria/app/cli/commands/start.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/keria/app/cli/commands/start.py b/src/keria/app/cli/commands/start.py index 7447f05f..9a9dc2dd 100644 --- a/src/keria/app/cli/commands/start.py +++ b/src/keria/app/cli/commands/start.py @@ -62,11 +62,15 @@ help="TLS server CA certificate chain") parser.add_argument("--loglevel", action="store", required=False, default="CRITICAL", help="Set log level to DEBUG | INFO | WARNING | ERROR | CRITICAL. Default is CRITICAL") +parser.add_argument("--logfile", action="store", required=False, default=None, + help="path of the log file. If not defined, logs will not be written to the file.") def launch(args): help.ogler.level = logging.getLevelName(args.loglevel) - help.ogler.reopen(name=args.name, temp=True, clear=True) + if(args.logfile != None): + help.ogler.headDirPath = args.logfile + help.ogler.reopen(name=args.name, temp=False, clear=True) logger = help.ogler.getLogger() From 5fdfebb8d4cd0831e1bc11506d4250695022cace Mon Sep 17 00:00:00 2001 From: Rodolfo Date: Fri, 5 Apr 2024 21:07:02 -0300 Subject: [PATCH 19/21] IdentifierResourceEnd: rename alias, events, and delete aid (#218) * IdentifierResourceEnd changes * fix tests * add rename and delete tests * rebase --- src/keria/app/aiding.py | 61 +++++++++++++++++++++++++++++++++++---- tests/app/test_aiding.py | 44 +++++++++++++++++++--------- tests/app/test_specing.py | 2 +- 3 files changed, 87 insertions(+), 20 deletions(-) diff --git a/src/keria/app/aiding.py b/src/keria/app/aiding.py index 0504406b..2b63061d 100644 --- a/src/keria/app/aiding.py +++ b/src/keria/app/aiding.py @@ -31,6 +31,7 @@ def loadEnds(app, agency, authn): app.add_route("/identifiers", aidsEnd) aidEnd = IdentifierResourceEnd() app.add_route("/identifiers/{name}", aidEnd) + app.add_route("/identifiers/{name}/events", aidEnd) aidOOBIsEnd = IdentifierOOBICollectionEnd() app.add_route("/identifiers/{name}/oobis", aidOOBIsEnd) @@ -464,7 +465,54 @@ def on_get(req, rep, name): rep.data = json.dumps(data).encode("utf-8") def on_put(self, req, rep, name): - """ Identifier UPDATE endpoint + """ Identifier rename endpoint + + Parameters: + req (Request): falcon.Request HTTP request object + rep (Response): falcon.Response HTTP response object + name (str): human readable name for Hab to rename + + """ + if not name: + raise falcon.HTTPBadRequest(description="name is required") + agent = req.context.agent + hab = agent.hby.habByName(name) + + if hab is None: + raise falcon.HTTPNotFound(title=f"No AID with name {name} found") + body = req.get_media() + newName = body.get("name") + habord = hab.db.habs.get(keys=name) + hab.db.habs.put(keys=newName, + val=habord) + hab.db.habs.rem(keys=name) + hab.name = newName + hab = agent.hby.habByName(newName) + data = info(hab, agent.mgr, full=True) + rep.status = falcon.HTTP_200 + rep.content_type = "application/json" + rep.data = json.dumps(data).encode("utf-8") + + def on_delete(self, req, rep, name): + """ Identifier delete endpoint + + Parameters: + req (Request): falcon.Request HTTP request object + rep (Response): falcon.Response HTTP response object + name (str): human readable name for Hab to delete + + """ + if not name: + raise falcon.HTTPBadRequest(description="name is required") + agent = req.context.agent + hab = agent.hby.habByName(name) + if hab is None: + raise falcon.HTTPNotFound(title=f"No AID with name {name} found") + hab.db.habs.rem(keys=name) + rep.status = falcon.HTTP_200 + + def on_post(self, req, rep, name): + """ Identifier events endpoint Parameters: req (Request): falcon.Request HTTP request object @@ -472,15 +520,18 @@ def on_put(self, req, rep, name): name (str): human readable name for Hab to rotate or interact """ + if not name: + raise falcon.HTTPBadRequest(description="name is required") agent = req.context.agent try: body = req.get_media() - typ = Ilks.ixn if req.params.get("type") == "ixn" else Ilks.rot - - if typ in (Ilks.rot,): + if body.get("rot") is not None: op = self.rotate(agent, name, body) - else: + elif body.get("ixn") is not None: op = self.interact(agent, name, body) + else: + raise falcon.HTTPBadRequest(title="invalid request", + description=f"required field 'rot' or 'ixn' missing from request") rep.status = falcon.HTTP_200 rep.content_type = "application/json" diff --git a/tests/app/test_aiding.py b/tests/app/test_aiding.py index 2ce49e80..a8f5717f 100644 --- a/tests/app/test_aiding.py +++ b/tests/app/test_aiding.py @@ -252,6 +252,7 @@ def test_identifier_collection_end(helpers): resend = aiding.IdentifierResourceEnd() app.add_route("/identifiers", end) app.add_route("/identifiers/{name}", resend) + app.add_route("/identifiers/{name}/events", resend) groupEnd = aiding.GroupMemberCollectionEnd() app.add_route("/identifiers/{name}/members", groupEnd) @@ -365,7 +366,7 @@ def test_identifier_collection_end(helpers): 'salty': {'stem': 'signify:aid', 'pidx': 0, 'tier': 'low', 'sxlt': sxlt, 'transferable': True, 'kidx': 1, 'icodes': [MtrDex.Ed25519_Seed], 'ncodes': [MtrDex.Ed25519_Seed]} } - res = client.simulate_put(path="/identifiers/aid1", body=json.dumps(body)) + res = client.simulate_post(path="/identifiers/aid1/events", body=json.dumps(body)) assert res.status_code == 200 # Try with missing arguments @@ -470,8 +471,21 @@ def test_identifier_collection_end(helpers): 'salty': {'stem': 'signify:aid', 'pidx': 0, 'tier': 'low', 'sxlt': sxlt, 'transferable': True, 'kidx': 3, 'icodes': [MtrDex.Ed25519_Seed], 'ncodes': [MtrDex.Ed25519_Seed]} } - res = client.simulate_put(path="/identifiers/aid3", body=json.dumps(body)) + res = client.simulate_post(path="/identifiers/aid3/events", body=json.dumps(body)) + assert res.status_code == 200 + + # rename aid3 + res = client.simulate_put(path="/identifiers/aid3", body=json.dumps({"name": "aid3Renamed"})) + assert res.status_code == 200 + aid = res.json + assert aid["name"] == "aid3Renamed" + + # delete aid3renamed + res = client.simulate_delete(path="/identifiers/aid3Renamed") assert res.status_code == 200 + res = client.simulate_get(path="/identifiers") + assert res.status_code == 200 + assert len(res.json) == 2 # create member habs for group AID p1 = p1hby.makeHab(name="p1") @@ -599,8 +613,8 @@ def test_identifier_collection_end(helpers): res = client.simulate_get(path="/identifiers") assert res.status_code == 200 - assert len(res.json) == 4 - aid = res.json[3] + assert len(res.json) == 3 + aid = res.json[2] assert aid["name"] == "multisig" assert aid["prefix"] == serder.pre group = aid["group"] @@ -654,6 +668,7 @@ def test_identifier_collection_end(helpers): resend = aiding.IdentifierResourceEnd() app.add_route("/identifiers", end) app.add_route("/identifiers/{name}", resend) + app.add_route("/identifiers/{name}/events", resend) eventsEnd = agenting.KeyEventCollectionEnd() app.add_route("/events", eventsEnd) @@ -722,7 +737,7 @@ def test_identifier_collection_end(helpers): "transferable": True, } } - res = client.simulate_put(path="/identifiers/randy1", body=json.dumps(body)) + res = client.simulate_post(path="/identifiers/randy1/events", body=json.dumps(body)) assert res.status_code == 200 assert res.json["response"] == serder.ked res = client.simulate_get(path="/identifiers") @@ -740,7 +755,7 @@ def test_identifier_collection_end(helpers): body = {'ixn': serder.ked, 'sigs': sigers } - res = client.simulate_put(path="/identifiers/randy1?type=ixn", body=json.dumps(body)) + res = client.simulate_post(path="/identifiers/randy1/events", body=json.dumps(body)) assert res.status_code == 200 assert res.json["response"] == serder.ked @@ -751,17 +766,17 @@ def test_identifier_collection_end(helpers): assert events[2]['ked'] == serder.ked # Bad interactions - res = client.simulate_put(path="/identifiers/badrandy?type=ixn", body=json.dumps(body)) + res = client.simulate_post(path="/identifiers/badrandy/events", body=json.dumps(body)) assert res.status_code == 404 assert res.json == {'title': 'No AID badrandy found'} body = {'sigs': sigers} - res = client.simulate_put(path="/identifiers/randy1?type=ixn", body=json.dumps(body)) + res = client.simulate_post(path="/identifiers/randy1/events", body=json.dumps(body)) assert res.status_code == 400 - assert res.json == {'description': "required field 'ixn' missing from request", 'title': 'invalid interaction'} + assert res.json == {'description': "required field 'rot' or 'ixn' missing from request", 'title': 'invalid request'} body = {'ixn': serder.ked} - res = client.simulate_put(path="/identifiers/randy1?type=ixn", body=json.dumps(body)) + res = client.simulate_post(path="/identifiers/randy1/events", body=json.dumps(body)) assert res.status_code == 400 assert res.json == {'description': "required field 'sigs' missing from request", 'title': 'invalid interaction'} @@ -774,7 +789,7 @@ def test_identifier_collection_end(helpers): "transferable": True, } } - res = client.simulate_put(path="/identifiers/randybad?type=rot", body=json.dumps(body)) + res = client.simulate_post(path="/identifiers/randybad/events", body=json.dumps(body)) assert res.status_code == 404 assert res.json == {'title': 'No AID with name randybad found'} @@ -786,9 +801,9 @@ def test_identifier_collection_end(helpers): "transferable": True, } } - res = client.simulate_put(path="/identifiers/randy1", body=json.dumps(body)) + res = client.simulate_post(path="/identifiers/randy1/events", body=json.dumps(body)) assert res.status_code == 400 - assert res.json == {'description': "required field 'rot' missing from request", 'title': 'invalid rotation'} + assert res.json == {'description': "required field 'rot' or 'ixn' missing from request", 'title': 'invalid request'} # rotate to unknown witness serder = eventing.rotate(keys=keys, @@ -812,7 +827,7 @@ def test_identifier_collection_end(helpers): "transferable": True, } } - res = client.simulate_put(path="/identifiers/randy1", body=json.dumps(body)) + res = client.simulate_post(path="/identifiers/randy1/events", body=json.dumps(body)) assert res.status_code == 400 assert res.json == {'description': "unknown witness EJJR2nmwyYAZAoTNZH3ULvaU6Z-i0d8fSVPzhzS6b5CM", 'title': '400 Bad Request'} @@ -823,6 +838,7 @@ def test_identifier_collection_end(helpers): resend = aiding.IdentifierResourceEnd() app.add_route("/identifiers", end) app.add_route("/identifiers/{name}", resend) + app.add_route("/identifiers/{name}/events", resend) eventsEnd = agenting.KeyEventCollectionEnd() app.add_route("/events", eventsEnd) diff --git a/tests/app/test_specing.py b/tests/app/test_specing.py index 134cb337..77115bd1 100644 --- a/tests/app/test_specing.py +++ b/tests/app/test_specing.py @@ -46,7 +46,7 @@ def test_spec_resource(helpers): js = json.dumps(sd) print(js) # Assert on the entire JSON to ensure we are getting all the docs - assert js == """{"paths": {"/operations": {"get": {"summary": "Get list of long running operations", "parameters": [{"in": "query", "name": "type", "schema": {"type": "string"}, "required": false, "description": "filter list of long running operations by type"}], "responses": {"200": {"content": {"application/json": {"schema": {"type": "array"}}}}}}}, "/oobis": {"post": {"summary": "Resolve OOBI and assign an alias for the remote identifier", "description": "Resolve OOBI URL or `rpy` message by process results of request and assign 'alias' in contact data for resolved identifier", "tags": ["OOBIs"], "requestBody": {"required": true, "content": {"application/json": {"schema": {"description": "OOBI", "properties": {"oobialias": {"type": "string", "description": "alias to assign to the identifier resolved from this OOBI", "required": false}, "url": {"type": "string", "description": "URL OOBI"}, "rpy": {"type": "object", "description": "unsigned KERI `rpy` event message with endpoints"}}}}}}, "responses": {"202": {"description": "OOBI resolution to key state successful"}}}}, "/states": {"get": {"summary": "Display key event log (KEL) for given identifier prefix", "description": "If provided qb64 identifier prefix is in Kevers, return the current state of the identifier along with the KEL and all associated signatures and receipts", "tags": ["Key Event Log"], "parameters": [{"in": "path", "name": "prefix", "schema": {"type": "string"}, "required": true, "description": "qb64 identifier prefix of KEL to load"}], "responses": {"200": {"description": "Key event log and key state of identifier"}, "404": {"description": "Identifier not found in Key event database"}}}}, "/events": {"get": {"summary": "Display key event log (KEL) for given identifier prefix", "description": "If provided qb64 identifier prefix is in Kevers, return the current state of the identifier along with the KEL and all associated signatures and receipts", "tags": ["Key Event Log"], "parameters": [{"in": "path", "name": "prefix", "schema": {"type": "string"}, "required": true, "description": "qb64 identifier prefix of KEL to load"}], "responses": {"200": {"description": "Key event log and key state of identifier"}, "404": {"description": "Identifier not found in Key event database"}}}}, "/queries": {"post": {"summary": "Display key event log (KEL) for given identifier prefix", "description": "If provided qb64 identifier prefix is in Kevers, return the current state of the identifier along with the KEL and all associated signatures and receipts", "tags": ["Query"], "parameters": [{"in": "body", "name": "pre", "schema": {"type": "string"}, "required": true, "description": "qb64 identifier prefix of KEL to load"}], "responses": {"200": {"description": "Key event log and key state of identifier"}, "404": {"description": "Identifier not found in Key event database"}}}}, "/identifiers": {"get": {}, "options": {}, "post": {}}, "/challenges": {"get": {"summary": "Get random list of words for a 2 factor auth challenge", "description": "Get the list of identifiers associated with this agent", "tags": ["Challenge/Response"], "parameters": [{"in": "query", "name": "strength", "schema": {"type": "int"}, "description": "cryptographic strength of word list", "required": false}], "responses": {"200": {"description": "An array of random words", "content": {"application/json": {"schema": {"description": "Random word list", "type": "object", "properties": {"words": {"type": "array", "description": "random challenge word list", "items": {"type": "string"}}}}}}}}}}, "/contacts": {"get": {"summary": "Get list of contact information associated with remote identifiers", "description": "Get list of contact information associated with remote identifiers. All information is metadata and kept in local storage only", "tags": ["Contacts"], "parameters": [{"in": "query", "name": "group", "schema": {"type": "string"}, "required": false, "description": "field name to group results by"}, {"in": "query", "name": "filter_field", "schema": {"type": "string"}, "description": "field name to search", "required": false}, {"in": "query", "name": "filter_value", "schema": {"type": "string"}, "description": "value to search for", "required": false}], "responses": {"200": {"description": "List of contact information for remote identifiers"}}}}, "/notifications": {"get": {"summary": "Get list of notifications for the controller of the agent", "description": "Get list of notifications for the controller of the agent. Notifications will be sorted by creation date/time", "parameters": [{"in": "header", "name": "Range", "schema": {"type": "string"}, "required": false, "description": "size of the result list. Defaults to 25"}], "tags": ["Notifications"], "responses": {"200": {"description": "List of contact information for remote identifiers"}}}}, "/oobi": {"get": {}}, "/": {"post": {"summary": "Accept KERI events with attachment headers and parse", "description": "Accept KERI events with attachment headers and parse.", "tags": ["Events"], "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "description": "KERI event message"}}}}, "responses": {"204": {"description": "KEL EXN, QRY, RPY event accepted."}}}, "put": {"summary": "Accept KERI events with attachment headers and parse", "description": "Accept KERI events with attachment headers and parse.", "tags": ["Events"], "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "description": "KERI event message"}}}}, "responses": {"200": {"description": "Mailbox query response for server sent events"}, "204": {"description": "KEL or EXN event accepted."}}}}, "/operations/{name}": {"delete": {}, "get": {}}, "/oobis/{alias}": {"get": {"summary": "Get OOBI for specific identifier", "description": "Generate OOBI for the identifier of the specified alias and role", "tags": ["OOBIs"], "parameters": [{"in": "path", "name": "alias", "schema": {"type": "string"}, "required": true, "description": "human readable alias for the identifier generate OOBI for"}, {"in": "query", "name": "role", "schema": {"type": "string"}, "required": true, "description": "role for which to generate OOBI"}], "responses": {"200": {"description": "An array of Identifier key state information", "content": {"application/json": {"schema": {"description": "Key state information for current identifiers", "type": "object"}}}}}}}, "/agent/{caid}": {"get": {}, "put": {}}, "/identifiers/{name}": {"get": {}, "put": {}}, "/endroles/{aid}": {"get": {}, "post": {}}, "/escrows/rpy": {"get": {}}, "/challenges/{name}": {"post": {"summary": "Sign challenge message and forward to peer identifier", "description": "Sign a challenge word list received out of bands and send `exn` peer to peer message to recipient", "tags": ["Challenge/Response"], "parameters": [{"in": "path", "name": "name", "schema": {"type": "string"}, "required": true, "description": "Human readable alias for the identifier to create"}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"description": "Challenge response", "properties": {"recipient": {"type": "string", "description": "human readable alias recipient identifier to send signed challenge to"}, "words": {"type": "array", "description": "challenge in form of word list", "items": {"type": "string"}}}}}}}, "responses": {"202": {"description": "Success submission of signed challenge/response"}}}}, "/challenges_verify/{source}": {"post": {"summary": "Sign challenge message and forward to peer identifier", "description": "Sign a challenge word list received out of bands and send `exn` peer to peer message to recipient", "tags": ["Challenge/Response"], "parameters": [{"in": "path", "name": "name", "schema": {"type": "string"}, "required": true, "description": "Human readable alias for the identifier to create"}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"description": "Challenge response", "properties": {"recipient": {"type": "string", "description": "human readable alias recipient identifier to send signed challenge to"}, "words": {"type": "array", "description": "challenge in form of word list", "items": {"type": "string"}}}}}}}, "responses": {"202": {"description": "Success submission of signed challenge/response"}}}, "put": {"summary": "Mark challenge response exn message as signed", "description": "Mark challenge response exn message as signed", "tags": ["Challenge/Response"], "parameters": [{"in": "path", "name": "name", "schema": {"type": "string"}, "required": true, "description": "Human readable alias for the identifier to create"}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"description": "Challenge response", "properties": {"aid": {"type": "string", "description": "aid of signer of accepted challenge response"}, "said": {"type": "array", "description": "SAID of challenge message signed", "items": {"type": "string"}}}}}}}, "responses": {"202": {"description": "Success submission of signed challenge/response"}}}}, "/contacts/{prefix}": {"delete": {"summary": "Delete contact information associated with remote identifier", "description": "Delete contact information associated with remote identifier", "tags": ["Contacts"], "parameters": [{"in": "path", "name": "prefix", "schema": {"type": "string"}, "required": true, "description": "qb64 identifier prefix of contact to delete"}], "responses": {"202": {"description": "Contact information successfully deleted for prefix"}, "404": {"description": "No contact information found for prefix"}}}, "get": {"summary": "Get contact information associated with single remote identifier", "description": "Get contact information associated with single remote identifier. All information is meta-data and kept in local storage only", "tags": ["Contacts"], "parameters": [{"in": "path", "name": "prefix", "schema": {"type": "string"}, "required": true, "description": "qb64 identifier prefix of contact to get"}], "responses": {"200": {"description": "Contact information successfully retrieved for prefix"}, "404": {"description": "No contact information found for prefix"}}}, "post": {"summary": "Create new contact information for an identifier", "description": "Creates new information for an identifier, overwriting all existing information for that identifier", "tags": ["Contacts"], "parameters": [{"in": "path", "name": "prefix", "schema": {"type": "string"}, "required": true, "description": "qb64 identifier prefix to add contact metadata to"}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"description": "Contact information", "type": "object"}}}}, "responses": {"200": {"description": "Updated contact information for remote identifier"}, "400": {"description": "Invalid identifier used to update contact information"}, "404": {"description": "Prefix not found in identifier contact information"}}}, "put": {"summary": "Update provided fields in contact information associated with remote identifier prefix", "description": "Update provided fields in contact information associated with remote identifier prefix. All information is metadata and kept in local storage only", "tags": ["Contacts"], "parameters": [{"in": "path", "name": "prefix", "schema": {"type": "string"}, "required": true, "description": "qb64 identifier prefix to add contact metadata to"}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"description": "Contact information", "type": "object"}}}}, "responses": {"200": {"description": "Updated contact information for remote identifier"}, "400": {"description": "Invalid identifier used to update contact information"}, "404": {"description": "Prefix not found in identifier contact information"}}}}, "/notifications/{said}": {"delete": {"summary": "Delete notification", "description": "Delete notification", "tags": ["Notifications"], "parameters": [{"in": "path", "name": "said", "schema": {"type": "string"}, "required": true, "description": "qb64 said of note to delete"}], "responses": {"202": {"description": "Notification successfully deleted for prefix"}, "404": {"description": "No notification information found for prefix"}}}, "put": {"summary": "Mark notification as read", "description": "Mark notification as read", "tags": ["Notifications"], "parameters": [{"in": "path", "name": "said", "schema": {"type": "string"}, "required": true, "description": "qb64 said of note to mark as read"}], "responses": {"202": {"description": "Notification successfully marked as read for prefix"}, "404": {"description": "No notification information found for SAID"}}}}, "/oobi/{aid}": {"get": {}}, "/identifiers/{name}/oobis": {"get": {}}, "/identifiers/{name}/endroles": {"get": {}, "post": {}}, "/identifiers/{name}/members": {"get": {}}, "/endroles/{aid}/{role}": {"get": {}, "post": {}}, "/contacts/{prefix}/img": {"get": {"summary": "Get contact image for identifer prefix", "description": "Get contact image for identifer prefix", "tags": ["Contacts"], "parameters": [{"in": "path", "name": "prefix", "schema": {"type": "string"}, "required": true, "description": "qb64 identifier prefix of contact image to get"}], "responses": {"200": {"description": "Contact information successfully retrieved for prefix", "content": {"image/jpg": {"schema": {"description": "Image", "type": "binary"}}}}, "404": {"description": "No contact information found for prefix"}}}, "post": {"summary": "Uploads an image to associate with identifier.", "description": "Uploads an image to associate with identifier.", "tags": ["Contacts"], "parameters": [{"in": "path", "name": "prefix", "schema": {"type": "string"}, "description": "identifier prefix to associate image to", "required": true}], "requestBody": {"required": true, "content": {"image/jpg": {"schema": {"type": "string", "format": "binary"}}, "image/png": {"schema": {"type": "string", "format": "binary"}}}}, "responses": {"200": {"description": "Image successfully uploaded"}}}}, "/oobi/{aid}/{role}": {"get": {}}, "/identifiers/{name}/endroles/{role}": {"get": {}, "post": {}}, "/oobi/{aid}/{role}/{eid}": {"get": {}}, "/identifiers/{name}/endroles/{role}/{eid}": {"delete": {}}}, "info": {"title": "KERIA Interactive Web Interface API", "version": "1.0.1"}, "openapi": "3.1.0"}""" + assert js == """{"paths": {"/operations": {"get": {"summary": "Get list of long running operations", "parameters": [{"in": "query", "name": "type", "schema": {"type": "string"}, "required": false, "description": "filter list of long running operations by type"}], "responses": {"200": {"content": {"application/json": {"schema": {"type": "array"}}}}}}}, "/oobis": {"post": {"summary": "Resolve OOBI and assign an alias for the remote identifier", "description": "Resolve OOBI URL or `rpy` message by process results of request and assign 'alias' in contact data for resolved identifier", "tags": ["OOBIs"], "requestBody": {"required": true, "content": {"application/json": {"schema": {"description": "OOBI", "properties": {"oobialias": {"type": "string", "description": "alias to assign to the identifier resolved from this OOBI", "required": false}, "url": {"type": "string", "description": "URL OOBI"}, "rpy": {"type": "object", "description": "unsigned KERI `rpy` event message with endpoints"}}}}}}, "responses": {"202": {"description": "OOBI resolution to key state successful"}}}}, "/states": {"get": {"summary": "Display key event log (KEL) for given identifier prefix", "description": "If provided qb64 identifier prefix is in Kevers, return the current state of the identifier along with the KEL and all associated signatures and receipts", "tags": ["Key Event Log"], "parameters": [{"in": "path", "name": "prefix", "schema": {"type": "string"}, "required": true, "description": "qb64 identifier prefix of KEL to load"}], "responses": {"200": {"description": "Key event log and key state of identifier"}, "404": {"description": "Identifier not found in Key event database"}}}}, "/events": {"get": {"summary": "Display key event log (KEL) for given identifier prefix", "description": "If provided qb64 identifier prefix is in Kevers, return the current state of the identifier along with the KEL and all associated signatures and receipts", "tags": ["Key Event Log"], "parameters": [{"in": "path", "name": "prefix", "schema": {"type": "string"}, "required": true, "description": "qb64 identifier prefix of KEL to load"}], "responses": {"200": {"description": "Key event log and key state of identifier"}, "404": {"description": "Identifier not found in Key event database"}}}}, "/queries": {"post": {"summary": "Display key event log (KEL) for given identifier prefix", "description": "If provided qb64 identifier prefix is in Kevers, return the current state of the identifier along with the KEL and all associated signatures and receipts", "tags": ["Query"], "parameters": [{"in": "body", "name": "pre", "schema": {"type": "string"}, "required": true, "description": "qb64 identifier prefix of KEL to load"}], "responses": {"200": {"description": "Key event log and key state of identifier"}, "404": {"description": "Identifier not found in Key event database"}}}}, "/identifiers": {"get": {}, "options": {}, "post": {}}, "/challenges": {"get": {"summary": "Get random list of words for a 2 factor auth challenge", "description": "Get the list of identifiers associated with this agent", "tags": ["Challenge/Response"], "parameters": [{"in": "query", "name": "strength", "schema": {"type": "int"}, "description": "cryptographic strength of word list", "required": false}], "responses": {"200": {"description": "An array of random words", "content": {"application/json": {"schema": {"description": "Random word list", "type": "object", "properties": {"words": {"type": "array", "description": "random challenge word list", "items": {"type": "string"}}}}}}}}}}, "/contacts": {"get": {"summary": "Get list of contact information associated with remote identifiers", "description": "Get list of contact information associated with remote identifiers. All information is metadata and kept in local storage only", "tags": ["Contacts"], "parameters": [{"in": "query", "name": "group", "schema": {"type": "string"}, "required": false, "description": "field name to group results by"}, {"in": "query", "name": "filter_field", "schema": {"type": "string"}, "description": "field name to search", "required": false}, {"in": "query", "name": "filter_value", "schema": {"type": "string"}, "description": "value to search for", "required": false}], "responses": {"200": {"description": "List of contact information for remote identifiers"}}}}, "/notifications": {"get": {"summary": "Get list of notifications for the controller of the agent", "description": "Get list of notifications for the controller of the agent. Notifications will be sorted by creation date/time", "parameters": [{"in": "header", "name": "Range", "schema": {"type": "string"}, "required": false, "description": "size of the result list. Defaults to 25"}], "tags": ["Notifications"], "responses": {"200": {"description": "List of contact information for remote identifiers"}}}}, "/oobi": {"get": {}}, "/": {"post": {"summary": "Accept KERI events with attachment headers and parse", "description": "Accept KERI events with attachment headers and parse.", "tags": ["Events"], "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "description": "KERI event message"}}}}, "responses": {"204": {"description": "KEL EXN, QRY, RPY event accepted."}}}, "put": {"summary": "Accept KERI events with attachment headers and parse", "description": "Accept KERI events with attachment headers and parse.", "tags": ["Events"], "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "description": "KERI event message"}}}}, "responses": {"200": {"description": "Mailbox query response for server sent events"}, "204": {"description": "KEL or EXN event accepted."}}}}, "/operations/{name}": {"delete": {}, "get": {}}, "/oobis/{alias}": {"get": {"summary": "Get OOBI for specific identifier", "description": "Generate OOBI for the identifier of the specified alias and role", "tags": ["OOBIs"], "parameters": [{"in": "path", "name": "alias", "schema": {"type": "string"}, "required": true, "description": "human readable alias for the identifier generate OOBI for"}, {"in": "query", "name": "role", "schema": {"type": "string"}, "required": true, "description": "role for which to generate OOBI"}], "responses": {"200": {"description": "An array of Identifier key state information", "content": {"application/json": {"schema": {"description": "Key state information for current identifiers", "type": "object"}}}}}}}, "/agent/{caid}": {"get": {}, "put": {}}, "/identifiers/{name}": {"delete": {}, "get": {}, "post": {}, "put": {}}, "/endroles/{aid}": {"get": {}, "post": {}}, "/escrows/rpy": {"get": {}}, "/challenges/{name}": {"post": {"summary": "Sign challenge message and forward to peer identifier", "description": "Sign a challenge word list received out of bands and send `exn` peer to peer message to recipient", "tags": ["Challenge/Response"], "parameters": [{"in": "path", "name": "name", "schema": {"type": "string"}, "required": true, "description": "Human readable alias for the identifier to create"}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"description": "Challenge response", "properties": {"recipient": {"type": "string", "description": "human readable alias recipient identifier to send signed challenge to"}, "words": {"type": "array", "description": "challenge in form of word list", "items": {"type": "string"}}}}}}}, "responses": {"202": {"description": "Success submission of signed challenge/response"}}}}, "/challenges_verify/{source}": {"post": {"summary": "Sign challenge message and forward to peer identifier", "description": "Sign a challenge word list received out of bands and send `exn` peer to peer message to recipient", "tags": ["Challenge/Response"], "parameters": [{"in": "path", "name": "name", "schema": {"type": "string"}, "required": true, "description": "Human readable alias for the identifier to create"}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"description": "Challenge response", "properties": {"recipient": {"type": "string", "description": "human readable alias recipient identifier to send signed challenge to"}, "words": {"type": "array", "description": "challenge in form of word list", "items": {"type": "string"}}}}}}}, "responses": {"202": {"description": "Success submission of signed challenge/response"}}}, "put": {"summary": "Mark challenge response exn message as signed", "description": "Mark challenge response exn message as signed", "tags": ["Challenge/Response"], "parameters": [{"in": "path", "name": "name", "schema": {"type": "string"}, "required": true, "description": "Human readable alias for the identifier to create"}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"description": "Challenge response", "properties": {"aid": {"type": "string", "description": "aid of signer of accepted challenge response"}, "said": {"type": "array", "description": "SAID of challenge message signed", "items": {"type": "string"}}}}}}}, "responses": {"202": {"description": "Success submission of signed challenge/response"}}}}, "/contacts/{prefix}": {"delete": {"summary": "Delete contact information associated with remote identifier", "description": "Delete contact information associated with remote identifier", "tags": ["Contacts"], "parameters": [{"in": "path", "name": "prefix", "schema": {"type": "string"}, "required": true, "description": "qb64 identifier prefix of contact to delete"}], "responses": {"202": {"description": "Contact information successfully deleted for prefix"}, "404": {"description": "No contact information found for prefix"}}}, "get": {"summary": "Get contact information associated with single remote identifier", "description": "Get contact information associated with single remote identifier. All information is meta-data and kept in local storage only", "tags": ["Contacts"], "parameters": [{"in": "path", "name": "prefix", "schema": {"type": "string"}, "required": true, "description": "qb64 identifier prefix of contact to get"}], "responses": {"200": {"description": "Contact information successfully retrieved for prefix"}, "404": {"description": "No contact information found for prefix"}}}, "post": {"summary": "Create new contact information for an identifier", "description": "Creates new information for an identifier, overwriting all existing information for that identifier", "tags": ["Contacts"], "parameters": [{"in": "path", "name": "prefix", "schema": {"type": "string"}, "required": true, "description": "qb64 identifier prefix to add contact metadata to"}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"description": "Contact information", "type": "object"}}}}, "responses": {"200": {"description": "Updated contact information for remote identifier"}, "400": {"description": "Invalid identifier used to update contact information"}, "404": {"description": "Prefix not found in identifier contact information"}}}, "put": {"summary": "Update provided fields in contact information associated with remote identifier prefix", "description": "Update provided fields in contact information associated with remote identifier prefix. All information is metadata and kept in local storage only", "tags": ["Contacts"], "parameters": [{"in": "path", "name": "prefix", "schema": {"type": "string"}, "required": true, "description": "qb64 identifier prefix to add contact metadata to"}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"description": "Contact information", "type": "object"}}}}, "responses": {"200": {"description": "Updated contact information for remote identifier"}, "400": {"description": "Invalid identifier used to update contact information"}, "404": {"description": "Prefix not found in identifier contact information"}}}}, "/notifications/{said}": {"delete": {"summary": "Delete notification", "description": "Delete notification", "tags": ["Notifications"], "parameters": [{"in": "path", "name": "said", "schema": {"type": "string"}, "required": true, "description": "qb64 said of note to delete"}], "responses": {"202": {"description": "Notification successfully deleted for prefix"}, "404": {"description": "No notification information found for prefix"}}}, "put": {"summary": "Mark notification as read", "description": "Mark notification as read", "tags": ["Notifications"], "parameters": [{"in": "path", "name": "said", "schema": {"type": "string"}, "required": true, "description": "qb64 said of note to mark as read"}], "responses": {"202": {"description": "Notification successfully marked as read for prefix"}, "404": {"description": "No notification information found for SAID"}}}}, "/oobi/{aid}": {"get": {}}, "/identifiers/{name}/events": {"delete": {}, "get": {}, "post": {}, "put": {}}, "/identifiers/{name}/oobis": {"get": {}}, "/identifiers/{name}/endroles": {"get": {}, "post": {}}, "/identifiers/{name}/members": {"get": {}}, "/endroles/{aid}/{role}": {"get": {}, "post": {}}, "/contacts/{prefix}/img": {"get": {"summary": "Get contact image for identifer prefix", "description": "Get contact image for identifer prefix", "tags": ["Contacts"], "parameters": [{"in": "path", "name": "prefix", "schema": {"type": "string"}, "required": true, "description": "qb64 identifier prefix of contact image to get"}], "responses": {"200": {"description": "Contact information successfully retrieved for prefix", "content": {"image/jpg": {"schema": {"description": "Image", "type": "binary"}}}}, "404": {"description": "No contact information found for prefix"}}}, "post": {"summary": "Uploads an image to associate with identifier.", "description": "Uploads an image to associate with identifier.", "tags": ["Contacts"], "parameters": [{"in": "path", "name": "prefix", "schema": {"type": "string"}, "description": "identifier prefix to associate image to", "required": true}], "requestBody": {"required": true, "content": {"image/jpg": {"schema": {"type": "string", "format": "binary"}}, "image/png": {"schema": {"type": "string", "format": "binary"}}}}, "responses": {"200": {"description": "Image successfully uploaded"}}}}, "/oobi/{aid}/{role}": {"get": {}}, "/identifiers/{name}/endroles/{role}": {"get": {}, "post": {}}, "/oobi/{aid}/{role}/{eid}": {"get": {}}, "/identifiers/{name}/endroles/{role}/{eid}": {"delete": {}}}, "info": {"title": "KERIA Interactive Web Interface API", "version": "1.0.1"}, "openapi": "3.1.0"}""" "" \ No newline at end of file From eeafb716f426edf3213dc5de672bad33c3547af4 Mon Sep 17 00:00:00 2001 From: Samuel Smith Date: Fri, 5 Apr 2024 18:07:23 -0600 Subject: [PATCH 20/21] Change Sealer to Anchorer. Change import of Siger from keri (#225) * changed interface import to keri for Siger to keri.core.Siger from keri.core.coring.Siger * Change Sealer to Anchorer --- src/keria/app/agenting.py | 16 ++++++++++------ src/keria/app/aiding.py | 19 ++++++++++--------- src/keria/app/delegating.py | 4 ++-- src/keria/app/grouping.py | 5 +++-- src/keria/app/ipexing.py | 29 +++++++++++++++-------------- src/keria/core/longrunning.py | 8 ++++---- src/keria/peer/exchanging.py | 3 ++- tests/app/test_delegating.py | 22 +++++++++++----------- tests/app/test_ipexing.py | 13 +++++++------ 9 files changed, 64 insertions(+), 55 deletions(-) diff --git a/src/keria/app/agenting.py b/src/keria/app/agenting.py index 1ce7444f..a548aae7 100644 --- a/src/keria/app/agenting.py +++ b/src/keria/app/agenting.py @@ -9,15 +9,19 @@ from dataclasses import asdict from urllib.parse import urlparse, urljoin -from keri import kering -from keri.app.notifying import Notifier -from keri.app.storing import Mailboxer import falcon from falcon import media from hio.base import doing from hio.core import http, tcp from hio.help import decking + +from keri import kering +from keri import core +from keri.app.notifying import Notifier +from keri.app.storing import Mailboxer + + from keri.app import configing, keeping, habbing, storing, signaling, oobiing, agenting, \ forwarding, querying, connecting, grouping from keri.app.grouping import Counselor @@ -149,7 +153,7 @@ def createHttpServer(port, app, keypath=None, certpath=None, cafilepath=None): class Agency(doing.DoDoer): """ Agency - + """ def __init__(self, name, bran, base="", configFile=None, configDir=None, adb=None, temp=False): @@ -287,7 +291,7 @@ def __init__(self, hby, rgy, agentHab, agency, caid, **opts): self.agency = agency self.caid = caid - self.swain = delegating.Sealer(hby=hby, proxy=agentHab) + self.swain = delegating.Anchorer(hby=hby, proxy=agentHab) self.counselor = Counselor(hby=hby, swain=self.swain, proxy=agentHab) self.org = connecting.Organizer(hby=hby) @@ -814,7 +818,7 @@ def on_post(self, req, rep): if "sig" not in body: raise falcon.HTTPBadRequest(title="invalid inception", description=f'required field "sig" missing from body') - siger = coring.Siger(qb64=body["sig"]) + siger = core.Siger(qb64=body["sig"]) caid = icp.pre diff --git a/src/keria/app/aiding.py b/src/keria/app/aiding.py index 2b63061d..83605f1a 100644 --- a/src/keria/app/aiding.py +++ b/src/keria/app/aiding.py @@ -10,6 +10,7 @@ import falcon from keri import kering +from keri import core from keri.app import habbing from keri.app.keeping import Algos from keri.core import coring, serdering @@ -161,11 +162,11 @@ def on_put(self, req, rep, caid): sigs = body["sigs"] ctrlHab = agent.hby.habByName(caid, ns="agent") - ctrlHab.rotate(serder=rot, sigers=[coring.Siger(qb64=sig) for sig in sigs]) + ctrlHab.rotate(serder=rot, sigers=[core.Siger(qb64=sig) for sig in sigs]) if not self.authn.verify(req): raise falcon.HTTPForbidden(description="invalid signature on request") - + sxlt = body["sxlt"] agent.mgr.sxlt = sxlt @@ -216,7 +217,7 @@ def interact(req, rep, agent, caid): ked = body['ixn'] sigs = body['sigs'] ixn = serdering.SerderKERI(sad=ked) - sigers = [coring.Siger(qb64=sig) for sig in sigs] + sigers = [core.Siger(qb64=sig) for sig in sigs] ctrlHab = agent.hby.habByName(caid, ns="agent") @@ -317,7 +318,7 @@ def on_post(req, rep): serder = serdering.SerderKERI(sad=icp) - sigers = [coring.Siger(qb64=sig) for sig in sigs] + sigers = [core.Siger(qb64=sig) for sig in sigs] if agent.hby.habByName(name) is not None: raise falcon.HTTPBadRequest(title=f"AID with name {name} already incepted") @@ -545,7 +546,7 @@ def rotate(agent, name, body): hab = agent.hby.habByName(name) if hab is None: raise falcon.HTTPNotFound(title=f"No AID with name {name} found") - + rot = body.get("rot") if rot is None: raise falcon.HTTPBadRequest(title="invalid rotation", @@ -563,7 +564,7 @@ def rotate(agent, name, body): description=f"required field 'sigs' missing from request") serder = serdering.SerderKERI(sad=rot) - sigers = [coring.Siger(qb64=sig) for sig in sigs] + sigers = [core.Siger(qb64=sig) for sig in sigs] hab.rotate(serder=serder, sigers=sigers) @@ -629,7 +630,7 @@ def interact(agent, name, body): description=f"required field 'sigs' missing from request") serder = serdering.SerderKERI(sad=ixn) - sigers = [coring.Siger(qb64=sig) for sig in sigs] + sigers = [core.Siger(qb64=sig) for sig in sigs] hab.interact(serder=serder, sigers=sigers) @@ -670,7 +671,7 @@ def info(hab, rm, full=False): data["state"] = asdict(kever.state()) dgkey = dbing.dgKey(kever.prefixer.qb64b, kever.serder.saidb) wigs = hab.db.getWigs(dgkey) - data["windexes"] = [coring.Siger(qb64b=bytes(wig)).index for wig in wigs] + data["windexes"] = [core.Siger(qb64b=bytes(wig)).index for wig in wigs] return data @@ -851,7 +852,7 @@ def on_post(req, rep, name, aid=None, role=None): raise falcon.errors.HTTPBadRequest( description=f"error trying to create end role for unknown local AID {pre}") - rsigers = [coring.Siger(qb64=rsig) for rsig in rsigs] + rsigers = [core.Siger(qb64=rsig) for rsig in rsigs] tsg = (hab.kever.prefixer, coring.Seqner(sn=hab.kever.sn), coring.Saider(qb64=hab.kever.serder.said), rsigers) try: agent.hby.rvy.processReply(rserder, tsgs=[tsg]) diff --git a/src/keria/app/delegating.py b/src/keria/app/delegating.py index cb68293b..45f080ed 100644 --- a/src/keria/app/delegating.py +++ b/src/keria/app/delegating.py @@ -5,7 +5,7 @@ from keri.db import dbing -class Sealer(doing.DoDoer): +class Anchorer(doing.DoDoer): """ Sends messages to Delegator of an identifier and wait for the anchoring event to be processed to ensure the inception or rotation event has been approved by the delegator. @@ -32,7 +32,7 @@ def __init__(self, hby, proxy=None, **kwa): self.witDoer = agenting.Receiptor(hby=self.hby) self.proxy = proxy - super(Sealer, self).__init__(doers=[self.witq, self.witDoer, self.postman, doing.doify(self.escrowDo)], + super(Anchorer, self).__init__(doers=[self.witq, self.witDoer, self.postman, doing.doify(self.escrowDo)], **kwa) def delegation(self, pre, sn=None, proxy=None): diff --git a/src/keria/app/grouping.py b/src/keria/app/grouping.py index 90ebd548..0d61df22 100644 --- a/src/keria/app/grouping.py +++ b/src/keria/app/grouping.py @@ -7,6 +7,7 @@ import json import falcon +from keri import core from keri.app import habbing from keri.core import coring, eventing, serdering from keri.kering import SerializeError @@ -56,7 +57,7 @@ def on_post(req, rep, name): atc = httping.getRequiredParam(body, "atc") # create sigers from the edge signatures so we can messagize the whole thing - sigers = [coring.Siger(qb64=sig) for sig in sigs] + sigers = [core.Siger(qb64=sig) for sig in sigs] # create seal for the proper location to find the signatures kever = hab.mhab.kever @@ -116,7 +117,7 @@ def on_post(req, rep, name): agent.hby.deleteHab(name=name) raise falcon.HTTPBadRequest(description=f"attempt to merge with unknown AID={recp}") - sigers = [coring.Siger(qb64=sig) for sig in sigs] + sigers = [core.Siger(qb64=sig) for sig in sigs] verfers = [coring.Verfer(qb64=k) for k in rot['k']] digers = [coring.Diger(qb64=n) for n in rot['n']] diff --git a/src/keria/app/ipexing.py b/src/keria/app/ipexing.py index 9c26222a..37289c2f 100644 --- a/src/keria/app/ipexing.py +++ b/src/keria/app/ipexing.py @@ -8,6 +8,7 @@ import json import falcon +from keri import core from keri.app import habbing from keri.core import coring, eventing, serdering from keri.peer import exchanging @@ -46,7 +47,7 @@ def on_post(req, rep, name): - Registries responses: 200: - description: long running operation of IPEX admit + description: long running operation of IPEX admit """ agent = req.context.agent @@ -83,7 +84,7 @@ def sendAdmit(agent, hab, ked, sigs, rec): # use that data to create th Serder and Sigers for the exn serder = serdering.SerderKERI(sad=ked) - sigers = [coring.Siger(qb64=sig) for sig in sigs] + sigers = [core.Siger(qb64=sig) for sig in sigs] # Now create the stream to send, need the signer seal kever = hab.kever @@ -122,7 +123,7 @@ def sendMultisigExn(agent, hab, ked, sigs, atc, rec): # use that data to create th Serder and Sigers for the exn serder = serdering.SerderKERI(sad=ked) - sigers = [coring.Siger(qb64=sig) for sig in sigs] + sigers = [core.Siger(qb64=sig) for sig in sigs] # Now create the stream to send, need the signer seal kever = hab.mhab.kever @@ -210,7 +211,7 @@ def sendGrant(agent, hab, ked, sigs, atc, rec): # use that data to create th Serder and Sigers for the exn serder = serdering.SerderKERI(sad=ked) - sigers = [coring.Siger(qb64=sig) for sig in sigs] + sigers = [core.Siger(qb64=sig) for sig in sigs] # Now create the stream to send, need the signer seal kever = hab.kever @@ -246,7 +247,7 @@ def sendMultisigExn(agent, hab, ked, sigs, atc, rec): # use that data to create th Serder and Sigers for the exn serder = serdering.SerderKERI(sad=ked) - sigers = [coring.Siger(qb64=sig) for sig in sigs] + sigers = [core.Siger(qb64=sig) for sig in sigs] # Now create the stream to send, need the signer seal kever = hab.mhab.kever @@ -293,7 +294,7 @@ def on_post(req, rep, name): responses: 200: description: long running operation of IPEX apply - + """ agent = req.context.agent # Get the hab @@ -326,7 +327,7 @@ def sendApply(agent, hab, ked, sigs, rec): # use that data to create th Serder and Sigers for the exn serder = serdering.SerderKERI(sad=ked) - sigers = [coring.Siger(qb64=sig) for sig in sigs] + sigers = [core.Siger(qb64=sig) for sig in sigs] # Now create the stream to send, need the signer seal kever = hab.kever @@ -342,7 +343,7 @@ def sendApply(agent, hab, ked, sigs, rec): return agent.monitor.submit(serder.pre, longrunning.OpTypes.exchange, metadata=dict(said=serder.said)) class IpexOfferCollectionEnd: - + @staticmethod def on_post(req, rep, name): """ IPEX Offer POST endpoint @@ -360,7 +361,7 @@ def on_post(req, rep, name): responses: 200: description: long running operation of IPEX offer - + """ agent = req.context.agent hab = agent.hby.habByName(name) @@ -393,7 +394,7 @@ def sendOffer(agent, hab, ked, sigs, atc, rec): # use that data to create th Serder and Sigers for the exn serder = serdering.SerderKERI(sad=ked) - sigers = [coring.Siger(qb64=sig) for sig in sigs] + sigers = [core.Siger(qb64=sig) for sig in sigs] # Now create the stream to send, need the signer seal kever = hab.kever @@ -407,9 +408,9 @@ def sendOffer(agent, hab, ked, sigs, atc, rec): agent.exchanges.append(dict(said=serder.said, pre=hab.pre, rec=rec, topic='credential')) return agent.monitor.submit(serder.pre, longrunning.OpTypes.exchange, metadata=dict(said=serder.said)) - + class IpexAgreeCollectionEnd: - + @staticmethod def on_post(req, rep, name): """ IPEX Agree POST endpoint @@ -427,7 +428,7 @@ def on_post(req, rep, name): responses: 200: description: long running operation of IPEX agree - + """ agent = req.context.agent hab = agent.hby.habByName(name) @@ -459,7 +460,7 @@ def sendAgree(agent, hab, ked, sigs, rec): # use that data to create th Serder and Sigers for the exn serder = serdering.SerderKERI(sad=ked) - sigers = [coring.Siger(qb64=sig) for sig in sigs] + sigers = [core.Siger(qb64=sig) for sig in sigs] # Now create the stream to send, need the signer seal kever = hab.kever diff --git a/src/keria/core/longrunning.py b/src/keria/core/longrunning.py index c0d41122..06016184 100644 --- a/src/keria/core/longrunning.py +++ b/src/keria/core/longrunning.py @@ -88,7 +88,7 @@ class Monitor: Attributes: hby (Habery): identifier database environment opr(Operator): long running operations database - swain(Sealer): Delegation processes tracker + swain(Anchorer): Delegation processes tracker """ @@ -98,7 +98,7 @@ def __init__(self, hby, swain, counselor=None, registrar=None, exchanger=None, c Parameters: hby (Habery): identifier database environment - swain(Sealer): Delegation processes tracker + swain(Anchorer): Delegation processes tracker opr (Operator): long running operations database """ @@ -410,8 +410,8 @@ def status(self, op): class OperationCollectionEnd: @staticmethod def on_get(req, rep): - """ Get list of long running operations - + """ Get list of long running operations + Parameters: req (Request): Falcon HTTP Request object rep (Response): Falcon HTTP Response object diff --git a/src/keria/peer/exchanging.py b/src/keria/peer/exchanging.py index fb27ee3c..3a98cf7e 100644 --- a/src/keria/peer/exchanging.py +++ b/src/keria/peer/exchanging.py @@ -7,6 +7,7 @@ import json import falcon +from keri import core from keri.core import coring, eventing, serdering from keri.peer import exchanging @@ -58,7 +59,7 @@ def on_post(req, rep, name): # use that data to create th Serder and Sigers for the exn serder = serdering.SerderKERI(sad=ked) - sigers = [coring.Siger(qb64=sig) for sig in sigs] + sigers = [core.Siger(qb64=sig) for sig in sigs] # Now create the stream to send, need the signer seal kever = hab.kever diff --git a/tests/app/test_delegating.py b/tests/app/test_delegating.py index 2c569357..01d4fb54 100644 --- a/tests/app/test_delegating.py +++ b/tests/app/test_delegating.py @@ -3,7 +3,7 @@ KERIA keria.app.delegating module -Testing the Mark II Agent Sealer +Testing the Mark II Agent Anchorer """ import pytest from hio.base import doing @@ -16,12 +16,12 @@ def test_sealer(): with habbing.openHby(name="p1", temp=True) as hby: - # Create Sealer to test - sealer = delegating.Sealer(hby=hby) + # Create Anchorer to test + anchorer = delegating.Anchorer(hby=hby) # Doer hierarchy doist = doing.Doist(tock=0.03125, real=True) - deeds = doist.enter(doers=[sealer]) + deeds = doist.enter(doers=[anchorer]) # Create delegator and delegate Habs delegator = hby.makeHab("delegator") @@ -30,34 +30,34 @@ def test_sealer(): # Try with a bad AID with pytest.raises(kering.ValidationError): - sealer.delegation(pre="EHgwVwQT15OJvilVvW57HE4w0-GPs_Stj2OFoAHZSysY") + anchorer.delegation(pre="EHgwVwQT15OJvilVvW57HE4w0-GPs_Stj2OFoAHZSysY") # Needs a proxy with pytest.raises(kering.ValidationError): - sealer.delegation(pre=delegate.pre) + anchorer.delegation(pre=delegate.pre) # Run delegation to escrow inception event - sealer.delegation(pre=delegate.pre, proxy=proxy) + anchorer.delegation(pre=delegate.pre, proxy=proxy) doist.recur(deeds=deeds) prefixer = coring.Prefixer(qb64=delegate.pre) seqner = coring.Seqner(sn=0) - assert sealer.complete(prefixer=prefixer, seqner=seqner) is False + assert anchorer.complete(prefixer=prefixer, seqner=seqner) is False # Anchor the seal in delegator's KEL, approving the delegation seal = eventing.SealEvent(prefixer.qb64, "0", prefixer.qb64) delegator.interact(data=[seal._asdict()]) - while sealer.complete(prefixer=prefixer, seqner=seqner) is False: + while anchorer.complete(prefixer=prefixer, seqner=seqner) is False: doist.recur(deeds=deeds) # Will raise with a bad digest with pytest.raises(kering.ValidationError): # Create saider for the wrong event saider = coring.Saider(qb64=delegator.kever.serder.said) - sealer.complete(prefixer=prefixer, seqner=seqner, saider=saider) + anchorer.complete(prefixer=prefixer, seqner=seqner, saider=saider) - assert sealer.complete(prefixer=prefixer, seqner=seqner) is True + assert anchorer.complete(prefixer=prefixer, seqner=seqner) is True diff --git a/tests/app/test_ipexing.py b/tests/app/test_ipexing.py index 2a1cede8..4182b491 100644 --- a/tests/app/test_ipexing.py +++ b/tests/app/test_ipexing.py @@ -11,6 +11,7 @@ from falcon import testing from hio.base import doing from hio.help import decking +from keri import core from keri.app import habbing, signing from keri.core import eventing, coring, serdering from keri.help import helping @@ -312,7 +313,7 @@ def test_ipex_grant(helpers, mockHelpingNowIso8601, seeder): assert len(agent.exchanges) == 1 assert len(agent.grants) == 1 - ims = eventing.messagize(serder=exn, sigers=[coring.Siger(qb64=sigs[0])]) + ims = eventing.messagize(serder=exn, sigers=[core.Siger(qb64=sigs[0])]) # Test sending embedded admit in multisig/exn message exn, end = exchanging.exchange(route="/multisig/exn", payload=dict(), @@ -977,7 +978,7 @@ def test_ipex_apply(helpers, mockHelpingNowIso8601): data = json.dumps(body).encode("utf-8") res = client.simulate_post(path="/identifiers/test/ipex/apply", body=data) - + assert res.status_code == 400 assert res.json == {'description': 'attempt to send to unknown ' 'AID=EZ-i0d8JZAoTNZH3ULaU6JR2nmwyvYAfSVPzhzS6b5CM', @@ -1073,7 +1074,7 @@ def test_ipex_offer(helpers, mockHelpingNowIso8601): data = json.dumps(body).encode("utf-8") res = client.simulate_post(path="/identifiers/test/ipex/offer", body=data) - + assert res.status_code == 400 assert res.json == {'description': 'attempt to send to unknown ' 'AID=EZ-i0d8JZAoTNZH3ULaU6JR2nmwyvYAfSVPzhzS6b5CM', @@ -1104,7 +1105,7 @@ def test_ipex_offer(helpers, mockHelpingNowIso8601): assert res.status_code == 200 assert len(agent.exchanges) == 1 - # Now an offer in response to an agree + # Now an offer in response to an agree dig = "EB_Lr3fHezn1ygn-wbBT5JjzaCMxTmhUoegXeZzWC2eT" offer1Serder, end1 = exchanging.exchange(route="/ipex/offer", payload={'m': 'How about this'}, @@ -1134,7 +1135,7 @@ def test_ipex_offer(helpers, mockHelpingNowIso8601): 'v': 'KERI10JSON000323_'} assert end1 == b'' sigs = ["AAAa70b4QnTOtGOsMqcezMtVzCFuRJHGeIMkWYHZ5ZxGIXM0XDVAzkYdCeadfPfzlKC6dkfiwuJ0IzLOElaanUgH"] - + body = dict( exn=offer1Serder.ked, sigs=sigs, @@ -1206,7 +1207,7 @@ def test_ipex_agree(helpers, mockHelpingNowIso8601): data = json.dumps(body).encode("utf-8") res = client.simulate_post(path="/identifiers/test/ipex/agree", body=data) - + assert res.status_code == 400 assert res.json == {'description': 'attempt to send to unknown ' 'AID=EZ-i0d8JZAoTNZH3ULaU6JR2nmwyvYAfSVPzhzS6b5CM', From 5f06007de208aeacebd7da75c04233d39b24656c Mon Sep 17 00:00:00 2001 From: Philip Feairheller Date: Sat, 6 Apr 2024 12:46:56 -0700 Subject: [PATCH 21/21] Update reference to KERI in development branches to point to the now published development releases of KERI on PyPi. (#226) Fixed broken tests from recent PRs and KERI branch confusion. Signed-off-by: pfeairheller --- setup.py | 2 +- src/keria/app/aiding.py | 29 ++++++++++++++++++----------- tests/app/test_aiding.py | 3 +-- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/setup.py b/setup.py index 9f785855..12fff845 100644 --- a/setup.py +++ b/setup.py @@ -76,7 +76,7 @@ python_requires='>=3.12.2', install_requires=[ 'hio>=0.6.12', - 'keri @ git+https://git@github.com/weboftrust/keripy.git@development', + 'keri>=1.2.0-dev0', 'mnemonic>=0.20', 'multicommand>=1.0.0', 'falcon>=3.1.3', diff --git a/src/keria/app/aiding.py b/src/keria/app/aiding.py index 83605f1a..581cee8b 100644 --- a/src/keria/app/aiding.py +++ b/src/keria/app/aiding.py @@ -296,7 +296,7 @@ def on_get(req, rep): end = start + (len(res) - 1) if len(res) > 0 else 0 rep.set_header("Accept-Ranges", "aids") - rep.set_header("Content-Range", f"aids {start}-{end}/{count-1}") + rep.set_header("Content-Range", f"aids {start}-{end}/{count - 1}") rep.content_type = "application/json" rep.data = json.dumps(res).encode("utf-8") @@ -483,17 +483,19 @@ def on_put(self, req, rep, name): raise falcon.HTTPNotFound(title=f"No AID with name {name} found") body = req.get_media() newName = body.get("name") - habord = hab.db.habs.get(keys=name) - hab.db.habs.put(keys=newName, - val=habord) - hab.db.habs.rem(keys=name) + habord = hab.db.habs.get(keys=(hab.pre,)) + habord.name = newName + hab.db.habs.pin(keys=(hab.pre,), + val=habord) + hab.db.names.pin(keys=("", newName), val=hab.pre) + hab.db.names.rem(keys=("", name)) hab.name = newName hab = agent.hby.habByName(newName) data = info(hab, agent.mgr, full=True) rep.status = falcon.HTTP_200 rep.content_type = "application/json" rep.data = json.dumps(data).encode("utf-8") - + def on_delete(self, req, rep, name): """ Identifier delete endpoint @@ -509,7 +511,7 @@ def on_delete(self, req, rep, name): hab = agent.hby.habByName(name) if hab is None: raise falcon.HTTPNotFound(title=f"No AID with name {name} found") - hab.db.habs.rem(keys=name) + agent.hby.deleteHab(name) rep.status = falcon.HTTP_200 def on_post(self, req, rep, name): @@ -532,7 +534,7 @@ def on_post(self, req, rep, name): op = self.interact(agent, name, body) else: raise falcon.HTTPBadRequest(title="invalid request", - description=f"required field 'rot' or 'ixn' missing from request") + description=f"required field 'rot' or 'ixn' missing from request") rep.status = falcon.HTTP_200 rep.content_type = "application/json" @@ -709,7 +711,8 @@ def on_get(req, rep, name): if role in (kering.Roles.witness,): # Fetch URL OOBIs for all witnesses oobis = [] for wit in hab.kever.wits: - urls = hab.fetchUrls(eid=wit, scheme=kering.Schemes.http) or hab.fetchUrls(eid=wit, scheme=kering.Schemes.https) + urls = hab.fetchUrls(eid=wit, scheme=kering.Schemes.http) or hab.fetchUrls(eid=wit, + scheme=kering.Schemes.https) if not urls: raise falcon.HTTPNotFound(description=f"unable to query witness {wit}, no http endpoint") @@ -719,7 +722,8 @@ def on_get(req, rep, name): res["oobis"] = oobis elif role in (kering.Roles.controller,): # Fetch any controller URL OOBIs oobis = [] - urls = hab.fetchUrls(eid=hab.pre, scheme=kering.Schemes.http) or hab.fetchUrls(eid=hab.pre, scheme=kering.Schemes.https) + urls = hab.fetchUrls(eid=hab.pre, scheme=kering.Schemes.http) or hab.fetchUrls(eid=hab.pre, + scheme=kering.Schemes.https) if not urls: raise falcon.HTTPNotFound(description=f"unable to query controller {hab.pre}, no http endpoint") @@ -728,7 +732,10 @@ def on_get(req, rep, name): oobis.append(urljoin(up.geturl(), f"/oobi/{hab.pre}/controller")) res["oobis"] = oobis elif role in (kering.Roles.agent,): # Fetch URL OOBIs for all witnesses - roleUrls = hab.fetchRoleUrls(cid=hab.pre, role=kering.Roles.agent, scheme=kering.Schemes.http) or hab.fetchRoleUrls(cid=hab.pre, role=kering.Roles.agent, scheme=kering.Schemes.https) + roleUrls = hab.fetchRoleUrls(cid=hab.pre, role=kering.Roles.agent, + scheme=kering.Schemes.http) or hab.fetchRoleUrls(cid=hab.pre, + role=kering.Roles.agent, + scheme=kering.Schemes.https) if kering.Roles.agent not in roleUrls: res['oobis'] = [] else: diff --git a/tests/app/test_aiding.py b/tests/app/test_aiding.py index a8f5717f..0c5bde34 100644 --- a/tests/app/test_aiding.py +++ b/tests/app/test_aiding.py @@ -376,7 +376,7 @@ def test_identifier_collection_end(helpers): 'salty': {'stem': 'signify:aid', 'pidx': 0, 'tier': 'low', 'sxlt': sxlt, 'icodes': [MtrDex.Ed25519_Seed], 'ncodes': [MtrDex.Ed25519_Seed]} } - res = client.simulate_put(path="/identifiers/aid1", body=json.dumps(body)) + res = client.simulate_post(path="/identifiers/aid1/events", body=json.dumps(body)) assert res.status_code == 500 # Test with witnesses @@ -419,7 +419,6 @@ def test_identifier_collection_end(helpers): assert res.status_code == 200 assert res.json['done'] is False - assert len(agent.witners) == 1 res = client.simulate_get(path="/identifiers") assert res.status_code == 200