Skip to content

Commit

Permalink
Enhanced interact and rotate kli commands to allow for --authenticate…
Browse files Browse the repository at this point in the history
… parameter to ask user for a 2-factor auth code to send to witnesses for secure witness interactions.

Signed-off-by: pfeairheller <[email protected]>
  • Loading branch information
pfeairheller committed Apr 11, 2024
1 parent 3518a5d commit ba5655e
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 21 deletions.
7 changes: 4 additions & 3 deletions src/keri/app/agenting.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ def receipt(self, pre, sn=None, auths=None):
headers = dict()
if wit in auths:
headers["Authorization"] = auths[wit]
httping.streamCESRRequests(client=client, dest=wit, ims=bytearray(msg), path="/receipts", headers=headers)

httping.streamCESRRequests(client=client, dest=wit, ims=bytearray(msg), path="receipts", headers=headers)
while not client.responses:
yield self.tock

Expand All @@ -106,7 +107,7 @@ def receipt(self, pre, sn=None, auths=None):
coring.Counter(qb64b=rct, strip=True)
rcts[wit] = rct
else:
logger.error(f"invalid response {rep.status} from witnesses {wit}")
print(f"invalid response {rep.status} from witnesses {wit}")

for wit in rcts:
ewits = [w for w in rcts if w != wit]
Expand Down Expand Up @@ -1062,7 +1063,7 @@ def httpClient(hab, wit):

url = urls[kering.Schemes.http] if kering.Schemes.http in urls else urls[kering.Schemes.https]
up = urlparse(url)
client = http.clienting.Client(scheme=up.scheme, hostname=up.hostname, port=up.port)
client = http.clienting.Client(scheme=up.scheme, hostname=up.hostname, port=up.port, path=up.path)
clientDoer = http.clienting.ClientDoer(client=client)

return client, clientDoer
Expand Down
45 changes: 34 additions & 11 deletions src/keri/app/cli/commands/interact.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
"""
import argparse
import json
from ordered_set import OrderedSet as oset

from hio.base import doing

from keri import kering
from keri.help import helping
from ..common import existing
from ... import habbing, agenting, indirecting

Expand All @@ -22,6 +22,10 @@
parser.add_argument('--passcode', '-p', help='22 character encryption passcode for keystore (is not saved)',
dest="bran", default=None) # passcode => bran
parser.add_argument('--data', '-d', help='Anchor data, \'@\' allowed', default=None, action="store", required=False)
parser.add_argument("--receipt-endpoint", help="Attempt to connect to witness receipt endpoint for witness receipts.",
dest="endpoint", action='store_true')
parser.add_argument("--authenticate", '-z', help="Prompt the controller for authentication codes for each witness",
action='store_true')


def interact(args):
Expand Down Expand Up @@ -52,7 +56,8 @@ def interact(args):
else:
data = None

ixnDoer = InteractDoer(name=name, base=base, alias=alias, bran=bran, data=data)
ixnDoer = InteractDoer(name=name, base=base, alias=alias, bran=bran, data=data, authenticate=args.authenticate,
endpoint=args.endpoint)

return [ixnDoer]

Expand All @@ -63,7 +68,7 @@ class InteractDoer(doing.DoDoer):
to all appropriate witnesses
"""

def __init__(self, name, base, bran, alias, data: list = None):
def __init__(self, name, base, bran, alias, data: list = None, endpoint=False, authenticate=False):
"""
Returns DoDoer with all registered Doers needed to perform interaction event.
Expand All @@ -75,6 +80,8 @@ def __init__(self, name, base, bran, alias, data: list = None):

self.alias = alias
self.data = data
self.endpoint = endpoint
self.authenticate = authenticate

self.hby = existing.setupHby(name=name, base=base, bran=bran)
self.hbyDoer = habbing.HaberyDoer(habery=self.hby) # setup doer
Expand All @@ -96,20 +103,36 @@ def interactDo(self, tymth, tock=0.0, **opts):
hab = self.hby.habByName(name=self.alias)
hab.interact(data=self.data)

witDoer = agenting.WitnessReceiptor(hby=self.hby)
self.extend(doers=[witDoer])
if self.endpoint or self.authenticate:
receiptor = agenting.Receiptor(hby=self.hby)
self.extend([receiptor])

if hab.kever.wits:
witDoer.msgs.append(dict(pre=hab.pre))
while not witDoer.cues:
_ = yield self.tock
auths = {}
if self.authenticate:
for wit in hab.kever.wits:
code = input(f"Entire code for {wit}: ")
auths[wit] = f"{code}#{helping.nowIso8601()}"
yield from receiptor.receipt(hab.pre, sn=hab.kever.sn, auths=auths)
self.remove([receiptor])

else:

witDoer = agenting.WitnessReceiptor(hby=self.hby)
self.extend(doers=[witDoer])

if hab.kever.wits:
witDoer.msgs.append(dict(pre=hab.pre))
while not witDoer.cues:
_ = yield self.tock

self.remove([witDoer])

print(f'Prefix {hab.pre}')
print(f'New Sequence No. {hab.kever.sn}')
for idx, verfer in enumerate(hab.kever.verfers):
print(f'\tPublic key {idx+1}: {verfer.qb64}')
print(f'\tPublic key {idx + 1}: {verfer.qb64}')

toRemove = [self.hbyDoer, witDoer, self.mbx]
toRemove = [self.hbyDoer, self.mbx]
self.remove(toRemove)

return
2 changes: 1 addition & 1 deletion src/keri/app/cli/commands/rotate.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ def rotateDo(self, tymth, tock=0.0):
yield self.tock

elif hab.kever.wits:
if self.endpoint:
if self.endpoint or self.authenticate:
auths = {}
if self.authenticate:
for wit in hab.kever.wits:
Expand Down
12 changes: 7 additions & 5 deletions src/keri/app/cli/commands/witness/authenticate.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,9 @@ def authDo(self, tymth, tock=0.0):
self.tock = tock
_ = (yield self.tock)

body = self.hab.makeOwnEvent(sn=self.hab.kever.sn)
body = bytearray()
for msg in self.hab.db.clonePreIter(pre=self.hab.pre):
body.extend(msg)

headers = (Hict([
("Content-Type", "application/cesr"),
Expand All @@ -99,9 +101,9 @@ def authDo(self, tymth, tock=0.0):

client.request(
method="POST",
path="/aids",
path=f"{client.requester.path}/aids",
headers=headers,
body=body
body=bytes(body)
)
while not client.responses:
yield self.tock
Expand All @@ -122,6 +124,6 @@ def authDo(self, tymth, tock=0.0):
print(otpurl)

else:
raise ValueError(rep.status)
raise ValueError(rep.body)

self.remove([clientDoer, self.clienter])
self.remove([clientDoer, self.clienter])
3 changes: 2 additions & 1 deletion src/keri/app/httping.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import json
from dataclasses import dataclass
from urllib import parse
from urllib.parse import urlparse
from pathlib import Path

import falcon
from hio.base import doing
Expand Down Expand Up @@ -167,6 +167,7 @@ def streamCESRRequests(client, ims, dest, path=None, headers=None):
"""
path = path if path is not None else "/"
path = str(Path(client.requester.path) / path)

cold = kering.sniff(ims) # check for spurious counters at front of stream
if cold in (parsing.Colds.txt, parsing.Colds.bny): # not message error out to flush stream
Expand Down
5 changes: 5 additions & 0 deletions tests/app/test_httping.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,15 @@ def test_parse_cesr_request():
assert cr.attachments == "-H000000000"


class MockRequester:
path = '/'


class MockClient:

def __init__(self):
self.args = []
self.requester = MockRequester()

def request(self, **kwargs):
self.args.append(kwargs)
Expand Down

0 comments on commit ba5655e

Please sign in to comment.