diff --git a/.github/workflows/python-app-ci.yml b/.github/workflows/python-app-ci.yml
index 0a00cfc0..c4097aeb 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.2
uses: actions/setup-python@v2
with:
- python-version: 3.10.4
+ 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.10.4
+ - name: Set up Python 3.12.2
uses: actions/setup-python@v2
with:
- python-version: 3.10.4
+ python-version: 3.12.2
- 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/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/setup.py b/setup.py
index 62b62a11..91683141 100644
--- a/setup.py
+++ b/setup.py
@@ -73,13 +73,13 @@
"resolver",
# eg: 'keyword1', 'keyword2', 'keyword3',
],
- python_requires='>=3.10.4',
+ python_requires='>=3.12.2',
install_requires=[
- 'hio>=0.6.9',
- 'keri>=1.1.6',
+ 'hio>=0.6.12',
+ 'keri>=1.2.0-dev0',
'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..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):
@@ -173,7 +177,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 +200,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)
@@ -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
@@ -972,12 +976,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 b9968980..581cee8b 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
@@ -31,6 +32,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)
@@ -95,7 +97,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
@@ -155,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
@@ -210,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")
@@ -269,17 +276,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 +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}")
+ rep.set_header("Content-Range", f"aids {start}-{end}/{count - 1}")
rep.content_type = "application/json"
rep.data = json.dumps(res).encode("utf-8")
@@ -310,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")
@@ -350,10 +358,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 +371,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"
@@ -405,7 +415,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))
@@ -456,7 +466,56 @@ 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=(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
+
+ 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")
+ agent.hby.deleteHab(name)
+ rep.status = falcon.HTTP_200
+
+ def on_post(self, req, rep, name):
+ """ Identifier events endpoint
Parameters:
req (Request): falcon.Request HTTP request object
@@ -464,15 +523,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"
@@ -486,7 +548,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",
@@ -504,7 +566,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)
@@ -537,7 +599,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))
@@ -570,7 +632,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)
@@ -611,7 +673,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
@@ -649,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")
@@ -659,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")
@@ -668,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:
@@ -792,7 +859,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])
@@ -1145,6 +1212,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/app/cli/commands/start.py b/src/keria/app/cli/commands/start.py
index 7381e024..9a9dc2dd 100644
--- a/src/keria/app/cli/commands/start.py
+++ b/src/keria/app/cli/commands/start.py
@@ -60,11 +60,17 @@
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")
+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.CRITICAL
- help.ogler.reopen(name=args.name, temp=True, clear=True)
+ help.ogler.level = logging.getLevelName(args.loglevel)
+ if(args.logfile != None):
+ help.ogler.headDirPath = args.logfile
+ help.ogler.reopen(name=args.name, temp=False, clear=True)
logger = help.ogler.getLogger()
diff --git a/src/keria/app/credentialing.py b/src/keria/app/credentialing.py
index 70185c9a..eb816226 100644
--- a/src/keria/app/credentialing.py
+++ b/src/keria/app/credentialing.py
@@ -167,6 +167,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:
@@ -283,6 +286,7 @@ def on_put(req, rep, name, registryName):
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(
diff --git a/src/keria/app/delegating.py b/src/keria/app/delegating.py
index b08dcd7b..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):
@@ -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/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/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:
diff --git a/src/keria/app/ipexing.py b/src/keria/app/ipexing.py
index 39ec50bd..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
@@ -20,13 +21,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 +41,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
@@ -77,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
@@ -116,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
@@ -154,7 +161,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 +169,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
@@ -204,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
@@ -240,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
@@ -266,3 +273,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 = [core.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 = [core.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 = [core.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/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_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..0c5bde34 100644
--- a/tests/app/test_aiding.py
+++ b/tests/app/test_aiding.py
@@ -242,13 +242,17 @@ 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)
app.add_route("/identifiers/{name}", resend)
+ app.add_route("/identifiers/{name}/events", resend)
groupEnd = aiding.GroupMemberCollectionEnd()
app.add_route("/identifiers/{name}/members", groupEnd)
@@ -362,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
@@ -372,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
@@ -415,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
@@ -467,8 +470,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")
@@ -596,8 +612,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"]
@@ -623,10 +639,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,
@@ -651,6 +667,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)
@@ -719,7 +736,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")
@@ -730,14 +747,14 @@ 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]
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
@@ -745,20 +762,20 @@ 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))
+ 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'}
@@ -771,7 +788,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'}
@@ -783,9 +800,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,
@@ -809,7 +826,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'}
@@ -820,6 +837,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)
@@ -1132,6 +1150,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',
@@ -1143,30 +1162,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',
@@ -1449,7 +1473,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 +1556,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/app/test_credentialing.py b/tests/app/test_credentialing.py
index 69c91ee1..e40ee7e7 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)
@@ -150,13 +152,48 @@ def test_registry_end(helpers, seeder):
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'}
+
+ # 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
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 2c363248..4182b491 100644
--- a/tests/app/test_ipexing.py
+++ b/tests/app/test_ipexing.py
@@ -11,8 +11,9 @@
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, parsing, serdering
+from keri.core import eventing, coring, serdering
from keri.help import helping
from keri.kering import Roles
from keri.peer import exchanging
@@ -36,6 +37,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):
@@ -306,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(),
@@ -919,3 +926,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
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
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()