Skip to content

Commit

Permalink
Many additions and fixes to kli commands, new ESSR payload attachment…
Browse files Browse the repository at this point in the history
… support (#803)

* New mailbox commands for using bespoke mailbox services.

Signed-off-by: pfeairheller <[email protected]>

* Resolving merge conflicts

Signed-off-by: pfeairheller <[email protected]>

* Resolving merge conflicts

Signed-off-by: pfeairheller <[email protected]>

* Improvements to kli commands `mailbox add` and `witness authenticate` to work in all cases (like delegated AIDs).

New `kli aid` command for printing the AID of an alias (useful in scripts).

Added `--delpre` as a command line argument to `kli icncept` (useful in scripts).

Fix for witness publisher to account for multiple events in one cue'ed message.

Added support for witness auth codes for delegated AIDs.

Removed logging messages in the INFO case for escrow reprocessing.

Signed-off-by: pfeairheller <[email protected]>

* Updated test

Signed-off-by: pfeairheller <[email protected]>

---------

Signed-off-by: pfeairheller <[email protected]>
  • Loading branch information
pfeairheller authored Jun 13, 2024
1 parent b6e4df9 commit c1163dd
Show file tree
Hide file tree
Showing 29 changed files with 736 additions and 260 deletions.
15 changes: 15 additions & 0 deletions images/keripy.base.dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Builder layer
FROM python:3.12-alpine as builder

# Install compilation dependencies
RUN apk --no-cache add \
bash \
alpine-sdk \
libffi-dev \
libsodium \
libsodium-dev

SHELL ["/bin/bash", "-c"]

# Setup Rust for blake3 dependency build
RUN curl https://sh.rustup.rs -sSf | bash -s -- -y
13 changes: 6 additions & 7 deletions src/keri/app/agenting.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,8 +227,9 @@ def witDo(self, tymth=None, tock=0.0):
msg = self.msgs.popleft()
pre = msg["pre"]
sn = msg["sn"] if "sn" in msg else None
auths = msg["auths"] if "auths" in msg else None

yield from self.receipt(pre, sn)
yield from self.receipt(pre, sn, auths)
self.cues.push(msg)

yield self.tock
Expand Down Expand Up @@ -616,12 +617,10 @@ def sendDo(self, tymth=None, tock=0.0, **opts):

_ = (yield self.tock)

total = len(witers)
count = 0
while count < total:
for witer in witers:
count += len(witer.sent)
_ = (yield self.tock)
while witers:
witer = witers.pop()
while not witer.idle:
_ = (yield self.tock)

self.remove(witers)
self.cues.push(evt)
Expand Down
58 changes: 58 additions & 0 deletions src/keri/app/cli/commands/aid.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# -*- encoding: utf-8 -*-
"""
KERI
keri.kli.commands module
"""
import argparse

from hio import help
from hio.base import doing

from keri.app.cli.common import existing
from keri.kering import ConfigurationError

logger = help.ogler.getLogger()

parser = argparse.ArgumentParser(description='Print the AID for a given alias')
parser.set_defaults(handler=lambda args: handler(args),
transferable=True)
parser.add_argument('--name', '-n', help='keystore name and file location of KERI keystore', required=True)
parser.add_argument('--base', '-b', help='additional optional prefix to file location of KERI keystore',
required=False, default="")
parser.add_argument('--alias', '-a', help='human readable alias for the new identifier prefix', default=None,
required=True)
parser.add_argument('--passcode', '-p', help='21 character encryption passcode for keystore (is not saved)',
dest="bran", default=None) # passcode => bran


def handler(args):
kwa = dict(args=args)
return [doing.doify(status, **kwa)]


def status(tymth, tock=0.0, **opts):
""" Command line status handler
"""
_ = (yield tock)
args = opts["args"]
name = args.name
alias = args.alias
base = args.base
bran = args.bran

try:
with existing.existingHby(name=name, base=base, bran=bran) as hby:
if alias is None:
alias = existing.aliasInput(hby)

hab = hby.habByName(alias)
if hab is None:
print(f"{alias} is not a valid alias for an identifier")

print(hab.pre)

except ConfigurationError as e:
print(f"identifier prefix for {name} does not exist, incept must be run first", )
return -1
28 changes: 25 additions & 3 deletions src/keri/app/cli/commands/delegate/confirm.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from keri import core
from keri.core import coring, serdering
from keri.db import dbing
from keri.help import helping
from keri.peer import exchanging

logger = help.ogler.getLogger()
Expand All @@ -32,6 +33,10 @@
parser.add_argument("--interact", "-i", help="anchor the delegation approval in an interaction event. "
"Default is to use a rotation event.", action="store_true")
parser.add_argument("--auto", "-Y", help="auto approve any delegation request non-interactively", action="store_true")
parser.add_argument("--authenticate", '-z', help="Prompt the controller for authentication codes for each witness",
action='store_true')
parser.add_argument('--code', help='<Witness AID>:<code> formatted witness auth codes. Can appear multiple times',
default=[], action="append", required=False)


def confirm(args):
Expand All @@ -47,22 +52,27 @@ def confirm(args):
alias = args.alias
interact = args.interact
auto = args.auto
authenticate = args.authenticate
codes = args.code

confirmDoer = ConfirmDoer(name=name, base=base, alias=alias, bran=bran, interact=interact, auto=auto)
confirmDoer = ConfirmDoer(name=name, base=base, alias=alias, bran=bran, interact=interact, auto=auto,
authenticate=authenticate, codes=codes)

doers = [confirmDoer]
return doers


class ConfirmDoer(doing.DoDoer):
def __init__(self, name, base, alias, bran, interact=False, auto=False):
def __init__(self, name, base, alias, bran, interact=False, auto=False, authenticate=False, codes=None):
hby = existing.setupHby(name=name, base=base, bran=bran)
self.hbyDoer = habbing.HaberyDoer(habery=hby) # setup doer
self.witq = agenting.WitnessInquisitor(hby=hby)
self.postman = forwarding.Poster(hby=hby)
self.counselor = grouping.Counselor(hby=hby)
self.notifier = notifying.Notifier(hby=hby)
self.mux = grouping.Multiplexor(hby=hby, notifier=self.notifier)
self.authenticate = authenticate
self.codes = codes if codes is not None else []

exc = exchanging.Exchanger(hby=hby, handlers=[])
delegating.loadHandlers(hby=hby, exc=exc, notifier=self.notifier)
Expand Down Expand Up @@ -173,7 +183,19 @@ def confirmDo(self, tymth, tock=0.0):
else:
hab.rotate(data=[anchor])

witDoer = agenting.WitnessReceiptor(hby=self.hby)
auths = {}
if self.authenticate:
for arg in self.codes:
(wit, code) = arg.split(":")
auths[wit] = f"{code}#{helping.nowIso8601()}"

for wit in hab.kever.wits:
if wit in auths:
continue
code = input(f"Entire code for {wit}: ")
auths[wit] = f"{code}#{helping.nowIso8601()}"

witDoer = agenting.WitnessReceiptor(hby=self.hby, auths=auths)
self.extend(doers=[witDoer])
self.toRemove.append(witDoer)
yield self.tock
Expand Down
2 changes: 2 additions & 0 deletions src/keri/app/cli/commands/incept.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ def mergeArgsWithFile(args):
incept_opts.estOnly = args.est_only
if args.data is not None:
incept_opts.data = config.parseData(args.data)
if args.delpre is not None:
incept_opts.delpre = args.delpre

return incept_opts

Expand Down
135 changes: 135 additions & 0 deletions src/keri/app/cli/commands/mailbox/add.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# -*- encoding: utf-8 -*-
"""
KERI
keri.kli.commands module
"""
import argparse

from hio import help
from hio.base import doing
from hio.help import Hict

from keri import kering
from keri.app import connecting, habbing, forwarding
from keri.app.agenting import httpClient, WitnessPublisher
from keri.app.cli.common import existing
from keri.core import serdering

logger = help.ogler.getLogger()

parser = argparse.ArgumentParser(description='Add mailbox role')
parser.set_defaults(handler=lambda args: add(args),
transferable=True)
parser.add_argument('--name', '-n', help='keystore name and file location of KERI keystore', required=True)
parser.add_argument('--alias', '-a', help='human readable alias for the identifier to whom the credential was issued',
required=True)
parser.add_argument('--base', '-b', help='additional optional prefix to file location of KERI keystore',
required=False, default="")
parser.add_argument('--passcode', '-p', help='22 character encryption passcode for keystore (is not saved)',
dest="bran", default=None) # passcode => bran
parser.add_argument("--mailbox", '-w', help="the mailbox AID or alias to add", required=True)


def add(args):
""" Command line handler for adding an aid to a watcher's list of AIds to watch
Parameters:
args(Namespace): parsed command line arguments
"""

ed = AddDoer(name=args.name,
alias=args.alias,
base=args.base,
bran=args.bran,
mailbox=args.mailbox)
return [ed]


class AddDoer(doing.DoDoer):

def __init__(self, name, alias, base, bran, mailbox):
self.hby = existing.setupHby(name=name, base=base, bran=bran)
self.hab = self.hby.habByName(alias)
self.org = connecting.Organizer(hby=self.hby)
self.witpub = WitnessPublisher(hby=self.hby)

if mailbox in self.hby.kevers:
mbx = mailbox
else:
mbx = self.org.find("alias", mailbox)
if len(mbx) != 1:
raise ValueError(f"invalid mailbox {mailbox}")
mbx = mbx[0]['id']

if not mbx:
raise ValueError(f"unknown mailbox {mailbox}")

self.mailbox = mbx

doers = [doing.doify(self.addDo), self.witpub]

super(AddDoer, self).__init__(doers=doers)

def addDo(self, tymth, tock=0.0):
""" Grant credential by creating /ipex/grant exn message
Parameters:
tymth (function): injected function wrapper closure returned by .tymen() of
Tymist instance. Calling tymth() returns associated Tymist .tyme.
tock (float): injected initial tock value
Returns: doifiable Doist compatible generator method
"""
# enter context
self.wind(tymth)
self.tock = tock
_ = (yield self.tock)

if isinstance(self.hab, habbing.GroupHab):
raise ValueError("watchers for multisig AIDs not currently supported")

kel = self.hab.replay()
data = dict(cid=self.hab.pre,
role=kering.Roles.mailbox,
eid=self.mailbox)

route = "/end/role/add"
msg = self.hab.reply(route=route, data=data)
self.hab.psr.parseOne(ims=(bytes(msg))) # make copy to preserve

fargs = dict([("kel", kel.decode("utf-8")),
("rpy", msg.decode("utf-8"))])

headers = (Hict([
("Content-Type", "multipart/form-data"),
]))

client, clientDoer = httpClient(self.hab, self.mailbox)
self.extend([clientDoer])

client.request(
method="POST",
path=f"{client.requester.path}/mailboxes",
headers=headers,
fargs=fargs
)
while not client.responses:
yield self.tock

rep = client.respond()
if rep.status == 200:
msg = self.hab.replyEndRole(cid=self.hab.pre, role=kering.Roles.mailbox)
self.witpub.msgs.append(dict(pre=self.hab.pre, msg=bytes(msg)))

while not self.witpub.cues:
yield self.tock

print(f"Mailbox {self.mailbox} added for {self.hab.name}")

else:
print(rep.status, rep.data)

self.remove([clientDoer, self.witpub])
13 changes: 13 additions & 0 deletions src/keri/app/cli/commands/oobi/generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,16 @@ def generate(tymth, tock=0.0, **opts):
url = urls[kering.Schemes.http] if kering.Schemes.http in urls else urls[kering.Schemes.https]
up = urlparse(url)
print(f"{up.scheme}://{up.hostname}:{up.port}/oobi/{hab.pre}/controller")
elif role in (kering.Roles.mailbox,):
for (_, _, eid), end in hab.db.ends.getItemIter(keys=(hab.pre, kering.Roles.mailbox, )):
if not (end.allowed and end.enabled is not False):
continue

urls = hab.fetchUrls(eid=eid, scheme=kering.Schemes.http) or hab.fetchUrls(eid=hab.pre,
scheme=kering.Schemes.https)
if not urls:
print(f"{alias} identifier {hab.pre} does not have any mailbox endpoints")
return
url = urls[kering.Schemes.http] if kering.Schemes.http in urls else urls[kering.Schemes.https]
up = urlparse(url)
print(f"{up.scheme}://{up.hostname}:{up.port}/oobi/{hab.pre}/mailbox/{eid}")
17 changes: 14 additions & 3 deletions src/keri/app/cli/commands/rotate.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@
dest="endpoint", action='store_true')
parser.add_argument("--authenticate", '-z', help="Prompt the controller for authentication codes for each witness",
action='store_true')
parser.add_argument('--code', help='<Witness AID>:<code> formatted witness auth codes. Can appear multiple times',
default=[], action="append", required=False)

parser.add_argument("--proxy", help="alias for delegation communication proxy", default="")

rotating.addRotationArgs(parser)
Expand Down Expand Up @@ -63,7 +66,7 @@ def rotate(args):
cuts=opts.witsCut, adds=opts.witsAdd,
isith=opts.isith, nsith=opts.nsith,
count=opts.ncount, toad=opts.toad,
data=opts.data, proxy=args.proxy, authenticate=args.authenticate)
data=opts.data, proxy=args.proxy, authenticate=args.authenticate, codes=args.code)

doers = [rotDoer]

Expand Down Expand Up @@ -118,7 +121,8 @@ class RotateDoer(doing.DoDoer):
"""

def __init__(self, name, base, bran, alias, endpoint=False, isith=None, nsith=None, count=None,
toad=None, wits=None, cuts=None, adds=None, data: list = None, proxy=None, authenticate=False):
toad=None, wits=None, cuts=None, adds=None, data: list = None, proxy=None, authenticate=False,
codes=None):
"""
Returns DoDoer with all registered Doers needed to perform rotation.
Expand All @@ -144,6 +148,7 @@ def __init__(self, name, base, bran, alias, endpoint=False, isith=None, nsith=No
self.endpoint = endpoint
self.proxy = proxy
self.authenticate = authenticate
self.codes = codes if codes is not None else []

self.wits = wits if wits is not None else []
self.cuts = cuts if cuts is not None else []
Expand Down Expand Up @@ -193,12 +198,18 @@ def rotateDo(self, tymth, tock=0.0):

auths = {}
if self.authenticate:
for arg in self.codes:
(wit, code) = arg.split(":")
auths[wit] = f"{code}#{helping.nowIso8601()}"

for wit in hab.kever.wits:
if wit in auths:
continue
code = input(f"Entire code for {wit}: ")
auths[wit] = f"{code}#{helping.nowIso8601()}"

if hab.kever.delpre:
self.swain.delegation(pre=hab.pre, sn=hab.kever.sn)
self.swain.delegation(pre=hab.pre, sn=hab.kever.sn, auths=auths)
print("Waiting for delegation approval...")
while not self.swain.complete(hab.kever.prefixer, coring.Seqner(sn=hab.kever.sn)):
yield self.tock
Expand Down
Loading

0 comments on commit c1163dd

Please sign in to comment.