diff --git a/src/dkr/app/cli/commands/did/keri/resolve.py b/src/dkr/app/cli/commands/did/keri/resolve.py index a0ebb63..2bec84f 100644 --- a/src/dkr/app/cli/commands/did/keri/resolve.py +++ b/src/dkr/app/cli/commands/did/keri/resolve.py @@ -5,6 +5,7 @@ """ import argparse import json +import sys from hio.base import doing from keri.app import habbing, oobiing @@ -31,24 +32,25 @@ 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) @@ -56,8 +58,8 @@ def resolve(self, tymth, tock=0.0, **opts): _ = (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 @@ -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 diff --git a/src/dkr/app/cli/commands/did/keri/resolver-service.py b/src/dkr/app/cli/commands/did/keri/resolver-service.py index 7cb4bc5..96dd9f1 100644 --- a/src/dkr/app/cli/commands/did/keri/resolver-service.py +++ b/src/dkr/app/cli/commands/did/keri/resolver-service.py @@ -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 diff --git a/src/dkr/app/cli/commands/did/webs/resolve.py b/src/dkr/app/cli/commands/did/webs/resolve.py index ca1b3bf..d863591 100644 --- a/src/dkr/app/cli/commands/did/webs/resolve.py +++ b/src/dkr/app/cli/commands/did/webs/resolve.py @@ -6,6 +6,7 @@ import argparse import json import requests +import sys from hio.base import doing from keri.app import habbing, oobiing @@ -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) @@ -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 @@ -97,35 +110,35 @@ 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 @@ -133,13 +146,13 @@ def compare_dicts(expected, actual, path=""): 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 = { diff --git a/src/dkr/app/cli/commands/did/webs/resolver-service.py b/src/dkr/app/cli/commands/did/webs/resolver-service.py index 45957ab..9da3c03 100644 --- a/src/dkr/app/cli/commands/did/webs/resolver-service.py +++ b/src/dkr/app/cli/commands/did/webs/resolver-service.py @@ -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", @@ -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 diff --git a/src/dkr/core/resolving.py b/src/dkr/core/resolving.py index 1c17fde..6287c37 100644 --- a/src/dkr/core/resolving.py +++ b/src/dkr/core/resolving.py @@ -4,17 +4,20 @@ """ 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: @@ -22,6 +25,7 @@ def setup(hby, *, httpPort): httpPort (int): external port to listen on for HTTP messages """ + print(f"Setup resolving") app = falcon.App( middleware=falcon.CORSMiddleware( allow_origins='*', @@ -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): @@ -51,7 +57,7 @@ class ResolveResource(doing.DoDoer): """ - def __init__(self, hby): + def __init__(self, hby, hbyDoer, obl): """ Create Endpoints for discovery and resolution of OOBIs Parameters: @@ -59,60 +65,53 @@ def __init__(self, hby): """ 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