Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Granting chained credentials #137

Merged
merged 4 commits into from
Nov 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.PHONY: build-keria
build-keria:
@docker build --no-cache -f images/keria.dockerfile --tag weboftrust/keria:0.0.1 .
@docker buildx build --platform=linux/amd64 --no-cache -f images/keria.dockerfile --tag weboftrust/keria:0.1.0 --tag weboftrust/keria:latest .

publish-keria:
@docker push weboftrust/keria:0.0.1
@docker push weboftrust/keria --all-tags
51 changes: 50 additions & 1 deletion src/keria/app/agenting.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
from keri.help import helping, ogler
from keri.peer import exchanging
from keri.vdr import verifying
from keri.vdr.credentialing import Regery
from keri.vdr.credentialing import Regery, sendArtifacts
from keri.vdr.eventing import Tevery
from keri.app import challenging

Expand Down Expand Up @@ -294,6 +294,7 @@ def __init__(self, hby, rgy, agentHab, agency, caid, **opts):
self.witners = decking.Deck()
self.queries = decking.Deck()
self.exchanges = decking.Deck()
self.grants = decking.Deck()
self.admits = decking.Deck()

receiptor = agenting.Receiptor(hby=hby)
Expand Down Expand Up @@ -360,6 +361,7 @@ def __init__(self, hby, rgy, agentHab, agency, caid, **opts):
Witnesser(receiptor=receiptor, witners=self.witners),
Delegator(agentHab=agentHab, swain=self.swain, anchors=self.anchors),
ExchangeSender(hby=hby, agentHab=agentHab, exc=self.exc, exchanges=self.exchanges),
Granter(hby=hby, rgy=rgy, agentHab=agentHab, exc=self.exc, grants=self.grants),
Admitter(hby=hby, witq=self.witq, psr=self.parser, agentHab=agentHab, exc=self.exc, admits=self.admits),
GroupRequester(hby=hby, agentHab=agentHab, counselor=self.counselor, groups=self.groups),
SeekerDoer(seeker=self.seeker, cues=self.verifier.cues),
Expand Down Expand Up @@ -492,6 +494,52 @@ def recur(self, tyme, deeds=None):
return super(ExchangeSender, self).recur(tyme, deeds)


class Granter(doing.DoDoer):

def __init__(self, hby, rgy, agentHab, exc, grants):
self.hby = hby
self.rgy = rgy
self.agentHab = agentHab
self.exc = exc
self.grants = grants
super(Granter, self).__init__(always=True)

def recur(self, tyme, deeds=None):
if self.grants:
msg = self.grants.popleft()
said = msg['said']
if not self.exc.complete(said=said):
self.grants.append(msg)
return super(Granter, self).recur(tyme, deeds)

serder, pathed = exchanging.cloneMessage(self.hby, said)

pre = msg["pre"]
rec = msg["rec"]
hab = self.hby.habs[pre]
if self.exc.lead(hab, said=said):
for recp in rec:
postman = forwarding.StreamPoster(hby=self.hby, hab=self.agentHab, recp=recp, topic="credential")
try:
credSaid = serder.ked['e']['acdc']['d']
creder = self.rgy.creds.get(keys=(credSaid,))
sendArtifacts(self.hby, self.rgy.reger, postman, creder, recp)
sources = self.rgy.reger.sources(self.hby.db, creder)
for source, atc in sources:
sendArtifacts(self.hby, self.rgy.reger, postman, source, recp)
postman.send(serder=source, attachment=atc)

except kering.ValidationError:
logger.info(f"unable to send to recipient={recp}")
except KeyError:
logger.info(f"invalid grant message={serder.ked}")
else:
doer = doing.DoDoer(doers=postman.deliver())
self.extend([doer])

return super(Granter, self).recur(tyme, deeds)


class Admitter(doing.Doer):

def __init__(self, hby, witq, psr, agentHab, exc, admits):
Expand Down Expand Up @@ -811,6 +859,7 @@ def on_post(self, req, rep):
raise falcon.HTTPBadRequest(description="multisig groups not supported as agent controller")

rep.status = falcon.HTTP_202
rep.data = json.dumps(asdict(ctrlHab.kever.state())).encode("utf-8")


class KeyStateCollectionEnd:
Expand Down
116 changes: 111 additions & 5 deletions src/keria/app/ipexing.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@


def loadEnds(app):
admitColEnd = IpexAdmitCollectonEnd()
admitColEnd = IpexAdmitCollectionEnd()
app.add_route("/identifiers/{name}/ipex/admit", admitColEnd)
grantColEnd = IpexGrantCollectionEnd()
app.add_route("/identifiers/{name}/ipex/grant", grantColEnd)


class IpexAdmitCollectonEnd:
class IpexAdmitCollectionEnd:

@staticmethod
def on_post(req, rep, name):
Expand Down Expand Up @@ -56,9 +58,9 @@ def on_post(req, rep, name):

match route:
case "/ipex/admit":
IpexAdmitCollectonEnd.sendAdmit(agent, hab, ked, sigs, rec)
IpexAdmitCollectionEnd.sendAdmit(agent, hab, ked, sigs, rec)
case "/multisig/exn":
IpexAdmitCollectonEnd.sendMultisigExn(agent, hab, ked, sigs, atc, rec)
IpexAdmitCollectionEnd.sendMultisigExn(agent, hab, ked, sigs, atc, rec)

rep.status = falcon.HTTP_202
rep.data = json.dumps(ked).encode("utf-8")
Expand Down Expand Up @@ -120,13 +122,117 @@ def sendMultisigExn(agent, hab, ked, sigs, atc, rec):

ims = eventing.messagize(serder=serder, sigers=sigers, seal=seal)


ims.extend(atc['exn'].encode("utf-8")) # add the pathed attachments

# 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'))


class IpexGrantCollectionEnd:

@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/grant":
IpexGrantCollectionEnd.sendGrant(agent, hab, ked, sigs, rec)
case "/multisig/exn":
IpexGrantCollectionEnd.sendMultisigExn(agent, hab, ked, sigs, atc, rec)

rep.status = falcon.HTTP_202
rep.data = json.dumps(ked).encode("utf-8")

@staticmethod
def sendGrant(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 = 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)

# 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.grants.append(dict(said=ked['d'], 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(description=f"attempt to send to unknown AID={recp}")

embeds = ked['e']
grant = embeds['exn']
if grant['r'] != "/ipex/grant":
raise falcon.HTTPBadRequest(description=f"invalid route for embedded ipex grant {ked['r']}")

# Have to add the atc to the end... this will be Pathed signatures for embeds
if 'exn' not in atc or not atc['exn']:
raise falcon.HTTPBadRequest(description=f"attachment missing for ACDC, unable to process request.")

holder = grant['a']['i']
serder = coring.Serder(ked=grant)
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.grants.append(dict(said=grant['d'], 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)

ims.extend(atc['exn'].encode("utf-8")) # add the pathed attachments

# 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'))
1 change: 1 addition & 0 deletions tests/app/test_agenting.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ def test_keystate_ends(helpers):
'd': 'EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3',
's': '0'}


def test_oobi_ends(seeder, helpers):
with helpers.openKeria() as (agency, agent, app, client), \
habbing.openHby(name="wes", salt=coring.Salter(raw=b'wess-the-witness').qb64) as wesHby:
Expand Down
Loading
Loading