-
Notifications
You must be signed in to change notification settings - Fork 33
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New IPEX admit endpoint and support for parsing credentials on admit …
…message submission. (#119) * New IPEX admit endpoint and support for parsing credentials on admit message submission. Signed-off-by: pfeairheller <[email protected]> * Fix reference to topic Signed-off-by: pfeairheller <[email protected]> * Ipexing test coverage Signed-off-by: pfeairheller <[email protected]> --------- Signed-off-by: pfeairheller <[email protected]>
- Loading branch information
1 parent
d81d444
commit f80d976
Showing
4 changed files
with
300 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
# -*- encoding: utf-8 -*- | ||
""" | ||
KERIA | ||
keria.app.ipexing module | ||
services and endpoint for IPEX message managements | ||
""" | ||
import json | ||
|
||
import falcon | ||
from keri.core import coring, eventing | ||
|
||
from keria.core import httping | ||
|
||
|
||
def loadEnds(app): | ||
admitColEnd = IpexAdmitCollectonEnd() | ||
app.add_route("/identifiers/{name}/ipex/admit", admitColEnd) | ||
|
||
|
||
class IpexAdmitCollectonEnd: | ||
|
||
@staticmethod | ||
def on_post(req, rep, name): | ||
""" Registries GET endpoint | ||
Parameters: | ||
req: falcon.Request HTTP request | ||
rep: falcon.Response HTTP response | ||
name (str): human readable name for AID | ||
--- | ||
summary: List credential issuance and revocation registies | ||
description: List credential issuance and revocation registies | ||
tags: | ||
- Registries | ||
responses: | ||
200: | ||
description: array of current credential issuance and revocation registies | ||
""" | ||
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") | ||
atc = httping.getRequiredParam(body, "atc") | ||
rec = httping.getRequiredParam(body, "rec") | ||
|
||
route = ked['r'] | ||
|
||
match route: | ||
case "/ipex/admit": | ||
IpexAdmitCollectonEnd.sendAdmit(agent, hab, ked, sigs, atc, rec) | ||
case "/multisig/exn": | ||
IpexAdmitCollectonEnd.sendMultisigExn(agent, hab, ked, sigs, atc, rec) | ||
|
||
rep.status = falcon.HTTP_202 | ||
rep.data = json.dumps(ked).encode("utf-8") | ||
|
||
@staticmethod | ||
def sendAdmit(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(f"attempt to send to unknown AID={recp}") | ||
|
||
# use that data to create th Serder and Sigers for the exn | ||
serder = coring.Serder(ked=ked) | ||
sigers = [coring.Siger(qb64=sig) for sig in sigs] | ||
|
||
# Now create the stream to send, need the signer seal | ||
kever = hab.kever | ||
seal = eventing.SealEvent(i=hab.pre, s=hex(kever.lastEst.s), d=kever.lastEst.d) | ||
|
||
ims = eventing.messagize(serder=serder, sigers=sigers, seal=seal) | ||
|
||
# Have to add the atc to the end... this will be Pathed signatures for embeds | ||
ims.extend(atc.encode("utf-8")) # add the pathed attachments | ||
|
||
# make a copy and parse | ||
agent.hby.psr.parseOne(ims=bytearray(ims)) | ||
|
||
# now get rid of the event so we can pass it as atc to send | ||
del ims[:serder.size] | ||
|
||
agent.exchanges.append(dict(said=serder.said, pre=hab.pre, rec=rec, topic='credential')) | ||
agent.admits.append(dict(said=ked['p'], pre=hab.pre)) | ||
|
||
@staticmethod | ||
def sendMultisigExn(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(f"attempt to send to unknown AID={recp}") | ||
|
||
embeds = ked['e'] | ||
admit = embeds['exn'] | ||
if admit['r'] != "/ipex/admit": | ||
raise falcon.HTTPBadRequest(f"invalid route for embedded ipex admit {ked['r']}") | ||
|
||
holder = admit['a']['i'] | ||
serder = coring.Serder(ked=admit) | ||
ims = bytearray(serder.raw) + atc['exn'].encode("utf-8") | ||
agent.hby.psr.parseOne(ims=ims) | ||
agent.exchanges.append(dict(said=serder.said, pre=hab.pre, rec=holder, topic="credential")) | ||
agent.admits.append(dict(said=admit['p'], pre=hab.pre)) | ||
|
||
# use that data to create th Serder and Sigers for the exn | ||
serder = coring.Serder(ked=ked) | ||
sigers = [coring.Siger(qb64=sig) for sig in sigs] | ||
|
||
# Now create the stream to send, need the signer seal | ||
kever = hab.kever | ||
seal = eventing.SealEvent(i=hab.pre, s=hex(kever.lastEst.s), d=kever.lastEst.d) | ||
|
||
ims = eventing.messagize(serder=serder, sigers=sigers, seal=seal) | ||
|
||
# Have to add the atc to the end... this will be Pathed signatures for embeds | ||
ims.extend(atc['exn'].encode("utf-8")) # add the pathed attachments | ||
|
||
# make a copy and parse | ||
agent.hby.psr.parseOne(ims=bytearray(ims)) | ||
|
||
# now get rid of the event so we can pass it as atc to send | ||
del ims[:serder.size] | ||
|
||
agent.exchanges.append(dict(said=serder.said, pre=hab.pre, rec=rec, topic='credential')) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
# -*- encoding: utf-8 -*- | ||
""" | ||
KERIA | ||
keria.app.ipe module | ||
Testing credentialing endpoint in the Mark II Agent | ||
""" | ||
import json | ||
|
||
from falcon import testing | ||
from keri.core import eventing, coring | ||
from keri.help import helping | ||
from keri.peer import exchanging | ||
|
||
from keria.app import ipexing, aiding | ||
|
||
|
||
def test_load_ends(helpers): | ||
with helpers.openKeria() as (agency, agent, app, client): | ||
ipexing.loadEnds(app=app) | ||
assert app._router is not None | ||
|
||
res = app._router.find("/test") | ||
assert res is None | ||
|
||
(end, *_) = app._router.find("/identifiers/NAME/ipex/admit") | ||
assert isinstance(end, ipexing.IpexAdmitCollectonEnd) | ||
|
||
|
||
def test_ipex_admit(helpers, mockHelpingNowIso8601): | ||
with helpers.openKeria() as (agency, agent, app, client): | ||
client = testing.TestClient(app) | ||
|
||
admitEnd = ipexing.IpexAdmitCollectonEnd() | ||
app.add_route("/identifiers/{name}/ipex/admit", admitEnd) | ||
|
||
end = aiding.IdentifierCollectionEnd() | ||
app.add_route("/identifiers", end) | ||
salt = b'0123456789abcdef' | ||
op = helpers.createAid(client, "test", salt) | ||
aid = op["response"] | ||
pre = aid['i'] | ||
assert pre == "EHgwVwQT15OJvilVvW57HE4w0-GPs_Stj2OFoAHZSysY" | ||
dig = "EB_Lr3fHezn1ygn-wbBT5JjzaCMxTmhUoegXeZzWC2eT" | ||
|
||
salt2 = b'0123456789abcdeg' | ||
op = helpers.createAid(client, "recp", salt2) | ||
aid1 = op["response"] | ||
pre1 = aid1['i'] | ||
assert pre1 == "EFnYGvF_ENKJ_4PGsWsvfd_R6m5cN-3KYsz_0mAuNpCm" | ||
|
||
exn, end = exchanging.exchange(route="/ipex/admit", | ||
payload=dict(), | ||
sender=pre, | ||
embeds=dict(), | ||
dig=dig, | ||
recipient=pre1, | ||
date=helping.nowIso8601()) | ||
assert exn.ked == {'a': {'i': 'EFnYGvF_ENKJ_4PGsWsvfd_R6m5cN-3KYsz_0mAuNpCm'}, | ||
'd': 'EBrMlfQbJRS9RYuP90t2PPPV24Qynmtu7BefWAqWzb0Q', | ||
'dt': '2021-06-27T21:26:21.233257+00:00', | ||
'e': {}, | ||
'i': 'EHgwVwQT15OJvilVvW57HE4w0-GPs_Stj2OFoAHZSysY', | ||
'p': 'EB_Lr3fHezn1ygn-wbBT5JjzaCMxTmhUoegXeZzWC2eT', | ||
'q': {}, | ||
'r': '/ipex/admit', | ||
't': 'exn', | ||
'v': 'KERI10JSON00013d_'} | ||
assert end == b'' | ||
sigs = ["AAAa70b4QnTOtGOsMqcezMtVzCFuRJHGeIMkWYHZ5ZxGIXM0XDVAzkYdCeadfPfzlKC6dkfiwuJ0IzLOElaanUgH"] | ||
|
||
body = dict( | ||
exn=exn.ked, | ||
sigs=sigs, | ||
atc="", | ||
rec=["EZ-i0d8JZAoTNZH3ULaU6JR2nmwyvYAfSVPzhzS6b5CM"] | ||
) | ||
|
||
data = json.dumps(body).encode("utf-8") | ||
res = client.simulate_post(path="/identifiers/test/ipex/admit", body=data) | ||
|
||
assert res.status_code == 400 | ||
assert res.json == {'title': 'attempt to send to unknown ' | ||
'AID=EZ-i0d8JZAoTNZH3ULaU6JR2nmwyvYAfSVPzhzS6b5CM'} | ||
|
||
body = dict( | ||
exn=exn.ked, | ||
sigs=sigs, | ||
atc="", | ||
rec=[pre1] | ||
) | ||
|
||
data = json.dumps(body).encode("utf-8") | ||
res = client.simulate_post(path="/identifiers/test/ipex/admit", body=data) | ||
|
||
assert res.status_code == 202 | ||
assert len(agent.exchanges) == 1 | ||
assert len(agent.admits) == 1 | ||
|
||
agent.exchanges.clear() | ||
agent.admits.clear() | ||
|
||
ims = eventing.messagize(serder=exn, sigers=[coring.Siger(qb64=sigs[0])]) | ||
# Test sending embedded admit in multisig/exn message | ||
exn, end = exchanging.exchange(route="/multisig/exn", | ||
payload=dict(), | ||
sender=pre, | ||
embeds=dict(exn=ims), | ||
dig=dig, | ||
date=helping.nowIso8601()) | ||
|
||
body = dict( | ||
exn=exn.ked, | ||
sigs=sigs, | ||
atc=dict(exn=end.decode("utf-8")), | ||
rec=[pre1] | ||
) | ||
|
||
data = json.dumps(body).encode("utf-8") | ||
res = client.simulate_post(path="/identifiers/test/ipex/admit", body=data) | ||
|
||
print(res.json) | ||
assert res.status_code == 202 | ||
assert len(agent.exchanges) == 2 | ||
assert len(agent.admits) == 1 |