Skip to content

Commit

Permalink
Merge pull request #23 from peacekeeper/resolver-services
Browse files Browse the repository at this point in the history
Updates for "resolve" and "resolver-service" components
  • Loading branch information
2byrds authored Oct 14, 2023
2 parents 86c63c0 + f96b0a2 commit c7145dc
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 86 deletions.
26 changes: 15 additions & 11 deletions src/dkr/app/cli/commands/did/keri/resolve.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"""
import argparse
import json
import sys

from hio.base import doing
from keri.app import habbing, oobiing
Expand All @@ -31,33 +32,34 @@


def handler(args):
res = Resolver(name=args.name, base=args.base, bran=args.bran, did=args.did, oobi=args.oobi, metadata=args.metadata)
hby = existing.setupHby(name=args.name, base=args.base, bran=args.bran)
hbyDoer = habbing.HaberyDoer(habery=hby) # setup doer
obl = oobiing.Oobiery(hby=hby)
res = KeriResolver(hby=hby, hbyDoer=hbyDoer, obl=obl, did=args.did, oobi=args.oobi, metadata=args.metadata)
return [res]


class Resolver(doing.DoDoer):
class KeriResolver(doing.DoDoer):

def __init__(self, name, base, bran, did, oobi, metadata):
def __init__(self, hby, hbyDoer, obl, did, oobi, metadata):

self.hby = existing.setupHby(name=name, base=base, bran=bran)
hbyDoer = habbing.HaberyDoer(habery=self.hby) # setup doer
obl = oobiing.Oobiery(hby=self.hby)
self.hby = hby
self.did = did
self.oobi = oobi
self.metadata = metadata

self.toRemove = [hbyDoer] + obl.doers
doers = list(self.toRemove) + [doing.doify(self.resolve)]
super(Resolver, self).__init__(doers=doers)
super(KeriResolver, self).__init__(doers=doers)

def resolve(self, tymth, tock=0.0, **opts):
self.wind(tymth)
self.tock = tock
_ = (yield self.tock)

aid = didding.parseDIDKeri(self.did)
print(f"From arguments got aid: {aid}")
print(f"From arguments got oobi: {self.oobi}")
print(f"From arguments got aid: {aid}", file=sys.stderr)
print(f"From arguments got oobi: {self.oobi}", file=sys.stderr)

obr = basing.OobiRecord(date=helping.nowIso8601())
obr.cid = aid
Expand All @@ -66,11 +68,13 @@ def resolve(self, tymth, tock=0.0, **opts):
while self.hby.db.roobi.get(keys=(self.oobi,)) is None:
_ = yield tock

result = didding.generateDIDDoc(self.hby, did=self.did, aid=aid, oobi=self.oobi, metadata=self.metadata)
didresult = didding.generateDIDDoc(self.hby, did=self.did, aid=aid, oobi=self.oobi, metadata=True)
dd = didresult['didDocument']
result = didresult if self.metadata else dd
data = json.dumps(result, indent=2)

print(data)
self.remove(self.toRemove)
return True
return result


2 changes: 1 addition & 1 deletion src/dkr/app/cli/commands/did/keri/resolver-service.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ def launch(args, expire=0.0):
obl = oobiing.Oobiery(hby=hby)

doers = obl.doers + [hbyDoer]
doers += resolving.setup(hby, httpPort=httpPort)
doers += resolving.setup(hby, hbyDoer, obl, httpPort=httpPort)

print(f"Launched did:keri resolver as an HTTP web service on {httpPort}")
return doers
61 changes: 37 additions & 24 deletions src/dkr/app/cli/commands/did/webs/resolve.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import argparse
import json
import requests
import sys

from hio.base import doing
from keri.app import habbing, oobiing
Expand All @@ -32,23 +33,24 @@


def handler(args):
res = Resolver(name=args.name, base=args.base, bran=args.bran, did=args.did)
hby = existing.setupHby(name=args.name, base=args.base, bran=args.bran)
hbyDoer = habbing.HaberyDoer(habery=hby) # setup doer
obl = oobiing.Oobiery(hby=hby)
res = WebsResolver(hby=hby, hbyDoer=hbyDoer, obl=obl, did=args.did, metadata=args.metadata)
return [res]


class Resolver(doing.DoDoer):
class WebsResolver(doing.DoDoer):

def __init__(self, name, base, bran, did, metadata):
def __init__(self, hby, hbyDoer, obl, did, metadata):

self.hby = existing.setupHby(name=name, base=base, bran=bran)
hbyDoer = habbing.HaberyDoer(habery=self.hby) # setup doer
obl = oobiing.Oobiery(hby=self.hby)
self.hby = hby
self.did = did
self.metadata = metadata

self.toRemove = [hbyDoer] + obl.doers
doers = list(self.toRemove) + [doing.doify(self.resolve)]
super(Resolver, self).__init__(doers=doers)
super(WebsResolver, self).__init__(doers=doers)

def resolve(self, tymth, tock=0.0, **opts):
self.wind(tymth)
Expand All @@ -61,25 +63,36 @@ def resolve(self, tymth, tock=0.0, **opts):

# Load the did doc
dd_url = f"{base_url}/{webbing.DID_JSON}"
print(f"Loading DID Doc from {dd_url}")
print(f"Loading DID Doc from {dd_url}", file=sys.stderr)
dd_actual = didding.fromDidWeb(json.loads(self.loadUrl(dd_url).decode("utf-8")))

# Load the KERI CESR
kc_url = f"{base_url}/{webbing.KERI_CESR}"
print(f"Loading KERI CESR from {kc_url}")
print(f"Loading KERI CESR from {kc_url}", file=sys.stderr)
self.hby.psr.parse(ims=bytearray(self.loadUrl(kc_url)))

dd_expected = didding.generateDIDDoc(self.hby, did=self.did, aid=aid, oobi=None, metadata=self.metadata)

didresult = didding.generateDIDDoc(self.hby, did=self.did, aid=aid, oobi=None, metadata=True)
didresult['didDocumentMetadata']['didDocUrl'] = dd_url
didresult['didDocumentMetadata']['keriCesrUrl'] = kc_url

dd_expected = didresult['didDocument']

verified = self.verifyDidDocs(dd_expected, dd_actual)

self.remove(self.toRemove)

if verified:
return dd_actual
result = didresult if self.metadata else dd_expected
else:
return None

didresult['didDocument'] = None
didresult['didResolutionMetadata']['error'] = 'notVerified'
didresult['didResolutionMetadata']['errorMessage'] = 'The DID document could not be verified against the KERI event stream'
result = didresult

data = json.dumps(result, indent=2)
print(data)
return result

def loadUrl(self, url):
response = requests.get(f"{url}")
# Ensure the request was successful
Expand All @@ -97,49 +110,49 @@ def loadFile(self, aid):

def verifyDidDocs(self, expected, actual):
if expected != actual:
print("DID Doc does not verify")
print("DID Doc does not verify", file=sys.stderr)
compare_dicts(expected, actual)
return False
else:
print("DID Doc verified")
print("DID Doc verified", file=sys.stderr)
return True

def compare_dicts(expected, actual, path=""):
print("Comparing dictionaries:\nexpected:\n{expected} \nand\n \nactual:\n{actual}")
print("Comparing dictionaries:\nexpected:\n{expected} \nand\n \nactual:\n{actual}", file=sys.stderr)

"""Recursively compare two dictionaries and print differences."""
for k in expected.keys():
# Construct current path
current_path = f"{path}.{k}" if path else k
print(f"Comparing key {current_path}")
print(f"Comparing key {current_path}", file=sys.stderr)

# Key not present in the actual dictionary
if k not in actual:
print(f"Key {current_path} not found in the actual dictionary")
print(f"Key {current_path} not found in the actual dictionary", file=sys.stderr)
continue

# If value in expected is a dictionary but not in actual
if isinstance(expected[k], dict) and not isinstance(actual[k], dict):
print(f"{current_path} is a dictionary in expected, but not in actual")
print(f"{current_path} is a dictionary in expected, but not in actual", file=sys.stderr)
continue

# If value in actual is a dictionary but not in expected
if isinstance(actual[k], dict) and not isinstance(expected[k], dict):
print(f"{current_path} is a dictionary in actual, but not in expected")
print(f"{current_path} is a dictionary in actual, but not in expected", file=sys.stderr)
continue

# If value is another dictionary, recurse
if isinstance(expected[k], dict) and isinstance(actual[k], dict):
compare_dicts(expected[k], actual[k], current_path)
# Compare non-dict values
elif expected[k] != actual[k]:
print(f"Different values for key {current_path}: {expected[k]} (expected) vs. {actual[k]} (actual)")
print(f"Different values for key {current_path}: {expected[k]} (expected) vs. {actual[k]} (actual)", file=sys.stderr)

# Check for keys in actual that are not present in expected
for k in actual.keys():
current_path = f"{path}.{k}" if path else k
if k not in expected:
print(f"Key {current_path} not found in the expected dictionary")
print(f"Key {current_path} not found in the expected dictionary", file=sys.stderr)

# # Test with the provided dictionaries
# expected_dict = {
Expand Down
4 changes: 2 additions & 2 deletions src/dkr/app/cli/commands/did/webs/resolver-service.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
parser.add_argument('-p', '--http',
action='store',
default=7677,
help="Port on which to listen for did:webs resolution requests. Defaults to 967")
help="Port on which to listen for did:webs resolution requests. Defaults to 7677")
parser.add_argument('-n', '--name',
action='store',
default="dkr",
Expand Down Expand Up @@ -71,7 +71,7 @@ def launch(args, expire=0.0):
obl = oobiing.Oobiery(hby=hby)

doers = obl.doers + [hbyDoer]
doers += resolving.setup(hby, httpPort=httpPort)
doers += resolving.setup(hby, hbyDoer, obl, httpPort=httpPort)

print(f"Launched did:webs resolver as an HTTP web service on {httpPort}")
return doers
95 changes: 47 additions & 48 deletions src/dkr/core/resolving.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,28 @@
"""
import json
import os

import falcon
from hio.base import doing
from hio.core import http
from keri.db import basing
from keri.help import helping
from dkr.app.cli.commands.did.keri.resolve import KeriResolver
from dkr.app.cli.commands.did.webs.resolve import WebsResolver

from dkr.core import didding


def setup(hby, *, httpPort):
def setup(hby, hbyDoer, obl, *, httpPort):
""" Setup serving package and endpoints
Parameters:
hby (Habery): identifier database environment
httpPort (int): external port to listen on for HTTP messages
"""
print(f"Setup resolving")
app = falcon.App(
middleware=falcon.CORSMiddleware(
allow_origins='*',
Expand All @@ -31,18 +35,20 @@ def setup(hby, *, httpPort):
server = http.Server(port=httpPort, app=app)
httpServerDoer = http.ServerDoer(server=server)

loadEnds(app, hby=hby)
loadEnds(app, hby=hby, hbyDoer=hbyDoer, obl=obl)

doers = [httpServerDoer]

return doers


def loadEnds(app, *, hby, prefix=""):
oobiEnd = ResolveResource(hby=hby)
app.add_route(prefix + "/resolve", oobiEnd)
def loadEnds(app, *, hby, hbyDoer, obl, prefix=""):
print(f"Loading resolving endpoints")
resolveEnd = ResolveResource(hby=hby, hbyDoer=hbyDoer, obl=obl)
result = app.add_route('/1.0/identifiers/{did}', resolveEnd)
print(f"Loaded resolving endpoints: {app}")

return [oobiEnd]
return [resolveEnd]


class ResolveResource(doing.DoDoer):
Expand All @@ -51,68 +57,61 @@ class ResolveResource(doing.DoDoer):
"""

def __init__(self, hby):
def __init__(self, hby, hbyDoer, obl):
""" Create Endpoints for discovery and resolution of OOBIs
Parameters:
hby (Habery): identifier database environment
"""
self.hby = hby
self.hbyDoer = hbyDoer
self.obl = obl

super(ResolveResource, self).__init__(doers=[])
print(f"Init resolver endpoint")

def on_post(self, req, rep):
""" Resolve did:keri DID endpoint.
def on_get(self, req, rep, did):
print(f"Request to resolve did: {did}")

Parameters:
req: falcon.Request HTTP request
rep: falcon.Response HTTP response
---
summary: Resolve did:keri DID using OOBI resolution and return DIDDoc
description: Resolve OOBI URL or `rpy` message by process results of request and return DIDDoc
tags:
- Resolution
requestBody:
required: true
content:
application/json:
schema:
description: DID
properties:
did:
type: string
description: alias to assign to the identifier resolved from this OOBI
required: false
responses:
200:
description: Valid DIDDoc for resolved did:keri DID
404:
description: DID not found
"""
body = req.get_media()

if "did" not in body:
if did is None:
rep.status = falcon.HTTP_400
rep.text = "invalid resolution request body, 'did' is required"
return

did = body["did"]
aid, oobi = didding.parseDID(did)

obr = basing.OobiRecord(date=helping.nowIso8601())
obr.cid = aid
self.hby.db.oobis.pin(keys=(oobi,), val=obr)
if 'oobi' in req.params:
oobi = req.params['oobi']
print(f"From parameters {req.params} got oobi: {oobi}")
else:
oobi = None

metadata = False

if did.startswith('did:webs:'):
#res = WebsResolver(hby=self.hby, hbyDoer=self.hbyDoer, obl=self.obl, did=did)
#tymth = None # ???
#data = res.resolve(tymth)
cmd = f"dkr did webs resolve --name dkr --did {did} --metadata {metadata}"
stream = os.popen(cmd)
data = stream.read()
elif did.startswith('did:keri'):
#res = KeriResolver(hby=self.hby, hbyDoer=self.hbyDoer, obl=self.obl, did=did, oobi=oobi, metadata=False)
#tymth = None # ???
#data = res.resolve(tymth)
cmd = f"dkr did keri resolve --name dkr --did {did} --oobi {oobi} --metadata {metadata}"
stream = os.popen(cmd)
data = stream.read()
else:
rep.status = falcon.HTTP_400
rep.text = "invalid 'did'"
return

rep.status = falcon.HTTP_200
rep.set_header('Content-Type', "application/json")
rep.stream = OobiIterable(hby=self.hby, aid=aid, did=did, oobi=oobi)
rep.set_header('Content-Type', "application/did+ld+json")
rep.body = data

return


class OobiIterable:
def __init__(self, hby, aid, did, oobi):
self.hby = hby
Expand Down

0 comments on commit c7145dc

Please sign in to comment.