diff --git a/scripts/keri/cf/scripts.json b/scripts/keri/cf/scripts.json new file mode 100755 index 00000000..e69de29b diff --git a/scripts/keri/cf/scripts/keri/cf/demo-witness-oobis.json b/scripts/keri/cf/scripts/keri/cf/demo-witness-oobis.json new file mode 100755 index 00000000..e69de29b diff --git a/src/keria/app/agenting.py b/src/keria/app/agenting.py index 14874089..7f0f9fdc 100644 --- a/src/keria/app/agenting.py +++ b/src/keria/app/agenting.py @@ -7,6 +7,7 @@ import json import os from dataclasses import asdict +from urllib import parse from urllib.parse import urlparse from keri import kering @@ -46,10 +47,12 @@ logger = ogler.getLogger() -def setup(name, bran, adminPort, bootPort, base='', httpPort=None, configFile=None, configDir=None): +def setup(name, bran, adminPort, bootPort, base='', httpPort=None, configFile=None, configDir=None, + interceptor_webhook=None, interceptor_headers=None): """ Set up an ahab in Signify mode """ - agency = Agency(name=name, base=base, bran=bran, configFile=configFile, configDir=configDir) + agency = Agency(name=name, base=base, bran=bran, configFile=configFile, configDir=configDir, + interceptor_webhook=interceptor_webhook, interceptor_headers=interceptor_headers) bootApp = falcon.App(middleware=falcon.CORSMiddleware( allow_origins='*', allow_credentials='*', expose_headers=['cesr-attachment', 'cesr-date', 'content-type', 'signature', 'signature-input', @@ -109,7 +112,8 @@ def setup(name, bran, adminPort, bootPort, base='', httpPort=None, configFile=No class Agency(doing.DoDoer): - def __init__(self, name, bran, base="", configFile=None, configDir=None, adb=None, temp=False): + def __init__(self, name, bran, base="", configFile=None, configDir=None, adb=None, temp=False, + interceptor_webhook=None, interceptor_headers=None): self.name = name self.base = base self.bran = bran @@ -117,6 +121,13 @@ def __init__(self, name, bran, base="", configFile=None, configDir=None, adb=Non self.configFile = configFile self.configDir = configDir self.cf = None + self.intercepts = decking.Deck() + doers = [] + self.interceptor = None + if interceptor_webhook is not None: + self.interceptor = InterceptorDoer(interceptor_webhook, interceptor_headers, cues=self.intercepts) + doers.append(self.interceptor) + if self.configFile is not None: # Load config file if creating database self.cf = configing.Configer(name=self.configFile, base="", @@ -128,7 +139,7 @@ def __init__(self, name, bran, base="", configFile=None, configDir=None, adb=Non self.agents = dict() 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) + super(Agency, self).__init__(doers=doers, always=True) def create(self, caid): ks = keeping.Keeper(name=caid, @@ -254,6 +265,7 @@ def __init__(self, hby, rgy, agentHab, agency, caid, **opts): self.anchors = decking.Deck() self.witners = decking.Deck() self.queries = decking.Deck() + self.agency.intercepts receiptor = agenting.Receiptor(hby=hby) self.postman = forwarding.Poster(hby=hby) @@ -313,19 +325,19 @@ def __init__(self, hby, rgy, agentHab, agency, caid, **opts): vry=self.verifier) doers.extend([ - self.exc, - Initer(agentHab=agentHab, caid=caid), + Initer(agentHab=agentHab, caid=caid, intercepts=self.agency.intercepts), Querier(hby=hby, agentHab=agentHab, kvy=self.kvy, queries=self.queries), Escrower(kvy=self.kvy, rgy=self.rgy, rvy=self.rvy, tvy=self.tvy, exc=self.exc, vry=self.verifier, registrar=self.registrar, credentialer=self.credentialer), Messager(kvy=self.kvy, parser=self.parser), - Witnesser(receiptor=receiptor, witners=self.witners), - Delegator(agentHab=agentHab, swain=self.swain, anchors=self.anchors), + Witnesser(receiptor=receiptor, witners=self.witners, intercepts=self.agency.intercepts), + Delegator(agentHab=agentHab, swain=self.swain, anchors=self.anchors, intercepts=self.agency.intercepts), GroupRequester(hby=hby, agentHab=agentHab, postman=self.postman, counselor=self.counselor, - groups=self.groups), + groups=self.groups, intercepts=self.agency.intercepts), SeekerDoer(seeker=self.seeker, cues=self.verifier.cues) ]) - + if self.agency.interceptor is not None: + doers.append(self.agency.interceptor) super(Agent, self).__init__(doers=doers, always=True, **opts) @property @@ -357,6 +369,37 @@ def inceptExtern(self, pre, verfers, digers, **kwargs): self.agency.incept(self.caid, pre) +class InterceptorDoer(doing.DoDoer): + + def __init__(self, webhook, headers, cues=None): + self.webhook = webhook + self.headers = headers + self.cues = cues if cues is not None else decking.Deck() + self.purl = parse.urlparse(webhook) + self.client = http.clienting.Client(scheme=self.purl.scheme, + hostname=self.purl.hostname, + port=self.purl.port, + portOptional=True) + clientDoer = http.clienting.ClientDoer(client=self.client) + + super(InterceptorDoer, self).__init__(doers=[clientDoer], always=True) + + def recur(self, tyme, deeds=None): + if self.cues: + msg = self.cues.popleft() + body = json.dumps(msg).encode("utf-8") + # TODO: Sent the message somewhere + self.client.request( + method="POST", + path=f"{self.purl.path}?{self.purl.query}", + qargs=None, + headers=self.headers, + body=body + ) + + return super(InterceptorDoer, self).recur(tyme, deeds) + + class Messager(doing.Doer): def __init__(self, kvy, parser): @@ -373,9 +416,10 @@ def recur(self, tyme=None): class Witnesser(doing.Doer): - def __init__(self, receiptor, witners): + def __init__(self, receiptor, witners, intercepts=None): self.receiptor = receiptor self.witners = witners + self.intercepts = intercepts super(Witnesser, self).__init__() def recur(self, tyme=None): @@ -383,7 +427,8 @@ def recur(self, tyme=None): if self.witners: msg = self.witners.popleft() serder = msg["serder"] - + if self.intercepts is not None: + self.intercepts.append(dict(evt="witnessed", ked=serder.ked)) # If we are a rotation event, may need to catch new witnesses up to current key state if serder.ked['t'] in (Ilks.rot, Ilks.drt): adds = serder.ked["ba"] @@ -391,16 +436,19 @@ def recur(self, tyme=None): yield from self.receiptor.catchup(serder.pre, wit) yield from self.receiptor.receipt(serder.pre, serder.sn) + if self.intercepts is not None: + self.intercepts.append(dict(evt="witnessing", data=dict(aid=serder.pre))) yield self.tock class Delegator(doing.Doer): - def __init__(self, agentHab, swain, anchors): + def __init__(self, agentHab, swain, anchors, intercepts=None): self.agentHab = agentHab self.swain = swain self.anchors = anchors + self.intercepts = intercepts super(Delegator, self).__init__() def recur(self, tyme=None): @@ -408,6 +456,8 @@ def recur(self, tyme=None): msg = self.anchors.popleft() sn = msg["sn"] if "sn" in msg else None self.swain.delegation(pre=msg["pre"], sn=sn, proxy=self.agentHab) + if self.intercepts is not None: + self.intercepts.append(dict(msg)) return False @@ -430,28 +480,31 @@ def recur(self, tyme=None): class Initer(doing.Doer): - def __init__(self, agentHab, caid): + def __init__(self, agentHab, caid, intercepts): self.agentHab = agentHab self.caid = caid + self.intercepts = intercepts super(Initer, self).__init__() def recur(self, tyme): """ Prints Agent name and prefix """ if not self.agentHab.inited: return False - + self.intercepts.append(dict(evt="init", data=dict(aid=self.agentHab.pre))) print(" Agent:", self.agentHab.pre, " Controller:", self.caid) + return True class GroupRequester(doing.Doer): - def __init__(self, hby, agentHab, postman, counselor, groups): + def __init__(self, hby, agentHab, postman, counselor, groups, intercepts): self.hby = hby self.agentHab = agentHab self.postman = postman self.counselor = counselor self.groups = groups + self.intercepts = intercepts super(GroupRequester, self).__init__() @@ -461,7 +514,7 @@ def recur(self, tyme): msg = self.groups.popleft() serder = msg["serder"] sigers = msg["sigers"] - + self.intercepts.append(dict(evt="group", data=dict(msg))) ghab = self.hby.habs[serder.pre] if "smids" in msg: smids = msg['smids'] @@ -917,7 +970,6 @@ def on_get(req, rep, alias): rep.status = falcon.HTTP_404 return - rep.status = falcon.HTTP_200 rep.content_type = "application/json" rep.data = json.dumps(res).encode("utf-8") diff --git a/src/keria/app/aiding.py b/src/keria/app/aiding.py index 5bb90dda..da438ad5 100644 --- a/src/keria/app/aiding.py +++ b/src/keria/app/aiding.py @@ -272,6 +272,7 @@ def on_post(req, rep): """ agent = req.context.agent + deserialized_media = req.get_media() try: body = req.get_media() icp = httping.getRequiredParam(body, "icp") @@ -284,6 +285,7 @@ def on_post(req, rep): # client is requesting agent to join multisig group if "group" in body: + agent.agency.intercepts.append(dict(event="creat multisig aid", data=deserialized_media)) group = body["group"] if "mhab" not in group: @@ -317,7 +319,7 @@ def on_post(req, rep): except ValueError as e: agent.hby.deleteHab(name=name) raise falcon.HTTPInternalServerError(description=f"{e.args[0]}") - + agent.agency.intercepts.append(dict(event="multisig created", data=serder.pretty())) # 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)) op = agent.monitor.submit(serder.pre, longrunning.OpTypes.group, metadata=dict(sn=0)) @@ -329,6 +331,7 @@ def on_post(req, rep): else: # client is requesting that the Agent track the Salty parameters if Algos.salty in body: + agent.agency.intercepts.append(dict(event="create salty aid", data=serder.pretty())) salt = body[Algos.salty] hab = agent.hby.makeSignifyHab(name, serder=serder, sigers=sigers) try: @@ -339,6 +342,7 @@ def on_post(req, rep): # client is storing encrypted randomly generated key material on agent elif Algos.randy in body: + agent.agency.intercepts.append(dict(event="create randy aid", data=serder.pretty())) rand = body[Algos.randy] hab = agent.hby.makeSignifyHab(name, serder=serder, sigers=sigers) try: @@ -348,6 +352,7 @@ def on_post(req, rep): raise falcon.HTTPInternalServerError(description=f"{e.args[0]}") elif Algos.extern in body: + agent.agency.intercepts.append(dict(event="create extern aid", data=serder.pretty())) extern = body[Algos.extern] hab = agent.hby.makeSignifyHab(name, serder=serder, sigers=sigers) try: diff --git a/src/keria/app/cli/commands/start.py b/src/keria/app/cli/commands/start.py index 92fd3e30..a436b47c 100644 --- a/src/keria/app/cli/commands/start.py +++ b/src/keria/app/cli/commands/start.py @@ -7,6 +7,7 @@ """ import argparse import logging +import json from keri import __version__ from keri import help @@ -54,6 +55,16 @@ action="store", default=None, help="directory override for configuration data") +parser.add_argument("--interceptor-webhook", + dest="interceptor_webhook", + action="store", + default=None, + help="webhook to send intercepted messages") +parser.add_argument("--interceptor-headers", + dest="interceptor_headers", + action="store", + default=None, + help="headers to send with intercepted messages") def launch(args): @@ -72,14 +83,16 @@ def launch(args): http=int(args.http), boot=int(args.boot), configFile=args.configFile, - configDir=args.configDir) + configDir=args.configDir, + interceptor_webhook=args.interceptor_webhook, + interceptor_headers=json.loads(args.interceptor_headers) if args.interceptor_headers is not None else {'Content-Type': 'application/json'}) logger.info("******* Ended Agent for %s listening: admin/%s, http/%s" ".******", args.name, args.admin, args.http) def runAgent(name="ahab", base="", bran="", admin=3901, http=3902, boot=3903, configFile=None, - configDir=None, expire=0.0): + configDir=None, expire=0.0, interceptor_webhook = None, interceptor_headers = None): """ Setup and run one witness """ @@ -90,6 +103,8 @@ def runAgent(name="ahab", base="", bran="", admin=3901, http=3902, boot=3903, co httpPort=http, bootPort=boot, configFile=configFile, - configDir=configDir)) + configDir=configDir, + interceptor_webhook=interceptor_webhook, + interceptor_headers=interceptor_headers)) directing.runController(doers=doers, expire=expire) diff --git a/src/keria/app/cli/keria.py b/src/keria/app/cli/keria.py index 0789d073..3836435e 100644 --- a/src/keria/app/cli/keria.py +++ b/src/keria/app/cli/keria.py @@ -26,8 +26,9 @@ def main(): directing.runController(doers=doers, expire=0.0) except Exception as ex: - print(f"ERR: {ex}") - return -1 + raise ex + # print(f"ERR: {ex}") + # return -1 if __name__ == "__main__": diff --git a/tests/app/test_agenting.py b/tests/app/test_agenting.py index c51e7cde..797f72ac 100644 --- a/tests/app/test_agenting.py +++ b/tests/app/test_agenting.py @@ -188,7 +188,6 @@ def test_witnesser(helpers): deeds = doist.enter(doers=[wr]) doist.recur(deeds) - def test_keystate_ends(helpers): caid = "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose" salt = b'0123456789abcdef'