From e9cf7da9b4c3b36611415df86eb6996c2a711dea Mon Sep 17 00:00:00 2001 From: Lance Date: Tue, 22 Oct 2024 02:16:20 -0400 Subject: [PATCH] Add presentation get (#65) * updated authorization status and message tracking Signed-off-by: 2byrds <2byrds@gmail.com> * added auth cred filter test Signed-off-by: 2byrds <2byrds@gmail.com> * integration tested setting authorization result Signed-off-by: 2byrds <2byrds@gmail.com> * validating schema chains and updated additional schemas Signed-off-by: 2byrds <2byrds@gmail.com> --------- Signed-off-by: 2byrds <2byrds@gmail.com> --- src/verifier/core/authorizing.py | 162 +++++++++++++++++------ src/verifier/core/basing.py | 18 +++ src/verifier/core/utils.py | 1 - src/verifier/core/verifying.py | 206 ++++++++++++++++++++---------- tests/common.py | 6 +- tests/conftest.py | 8 +- tests/core/test_authorizing.py | 106 +++++++++++++++ tests/core/test_verifying.py | 40 +++--- tests/integration/test_service.py | 36 +++--- 9 files changed, 435 insertions(+), 148 deletions(-) create mode 100644 tests/core/test_authorizing.py diff --git a/src/verifier/core/authorizing.py b/src/verifier/core/authorizing.py index ce087b0..96839ba 100644 --- a/src/verifier/core/authorizing.py +++ b/src/verifier/core/authorizing.py @@ -6,6 +6,7 @@ EXN Message handling """ import datetime +from typing import List from hio.base import doing from keri import kering @@ -28,11 +29,28 @@ # Hard coded credential JSON Schema SAID for the vLEI Engagement Context Role Credential class Schema: DES_ALIASES_SCHEMA = "EN6Oh5XSD5_q2Hgu-aqpdfbVepdpYpFlgz6zvJL5b_r5" - ECR_AUTH_SCHEMA = "EJOkgTilEMjPgrEr0yZDS_MScnI0pBb75tO54lvXugOy" + ECR_AUTH_SCHEMA1 = "EH6ekLjSr8V32WyFbGe1zXjTzFs9PkTYmupJ9H65O14g" + ECR_AUTH_SCHEMA2 = "EJOkgTilEMjPgrEr0yZDS_MScnI0pBb75tO54lvXugOy" ECR_SCHEMA = "EHAuBf02w-FIH8yEVrD_qIkgr0uI_rDzZ-kTABmdmUFP" ECR_SCHEMA_PROD = "EEy9PkikFcANV1l7EHukCeXqrzT1hNZjGlUk7wuMO5jw" - LEI_SCHEMA = "EHyKQS68x_oWy8_vNmYubA5Y0Tse4XMPFggMfoPoERaM" - QVI_SCHEMA = "EFgnk_c08WmZGgv9_mpldibRuqFMTQN-rAgtD-TCOwbs" + LE_SCHEMA1 = "EHyKQS68x_oWy8_vNmYubA5Y0Tse4XMPFggMfoPoERaM" + LE_SCHEMA2 = "ENPXp1vQzRF6JwIuS-mp2U8Uf1MoADoP_GqQ62VsDZWY" + OOR_AUTH_SCHEMA = "EKA57bKBKxr_kN7iN5i7lMUxpMG-s19dRcmov1iDxz-E" + OOR_SCHEMA = "EBNaNu-M9P5cgrnfl2Fvymy4E_jvxxyjb70PRtiANlJy" + QVI_SCHEMA1 = "EFgnk_c08WmZGgv9_mpldibRuqFMTQN-rAgtD-TCOwbs" + QVI_SCHEMA2 = "EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao" + + schema_names = {} + schema_names[ECR_AUTH_SCHEMA1] = "ECR_AUTH" + schema_names[ECR_AUTH_SCHEMA2] = "ECR_AUTH" + schema_names[ECR_SCHEMA] = "ECR" + schema_names[ECR_SCHEMA_PROD] = "ECR" + schema_names[LE_SCHEMA1] = "LE" + schema_names[LE_SCHEMA2] = "LE" + schema_names[OOR_AUTH_SCHEMA] = "OOR_AUTH" + schema_names[OOR_SCHEMA] = "OOR" + schema_names[QVI_SCHEMA1] = "QVI" + schema_names[QVI_SCHEMA2] = "QVI" def setup(hby, vdb, reger, cf): @@ -111,8 +129,10 @@ def processPresentations(self): now = helping.nowUTC() age = now - datetime.datetime.fromisoformat(state.date) cred_state = None - if state.state != AUTH_EXPIRE and age > datetime.timedelta(seconds=self.TimeoutAuth): - cred_state = CredProcessState(said=state.said, state=AUTH_EXPIRE) + if state.state == AUTH_EXPIRE and age > datetime.timedelta(seconds=self.TimeoutAuth*2): + self.vdb.iss.rem(keys=(aid,)) + elif state.state != AUTH_EXPIRE and age > datetime.timedelta(seconds=self.TimeoutAuth): + cred_state = CredProcessState(said=state.said, state=AUTH_EXPIRE, msg=f"Cred state exceeded {self.TimeoutAuth}") self.vdb.iss.pin(keys=(aid,), val=cred_state) print( f"{state.said} for {aid}, has expired: {age} greater than {self.TimeoutAuth}" @@ -126,19 +146,17 @@ def processPresentations(self): cred_state = CredProcessState(said=state.said, state=AUTH_PENDING) creder = self.reger.creds.get(keys=(state.said,)) # are there multiple creds for the same said? - match creder.schema: - case Schema.ECR_SCHEMA | Schema.ECR_SCHEMA_PROD: - self.processEcr(creder) - break - case _: - cred_state = CredProcessState(said=state.said, state=AUTH_FAIL) + res = self.cred_filters(creder) + if(res[0]): + cred_state = CredProcessState(said=state.said, state=AUTH_SUCCESS) + else: + cred_state = CredProcessState(said=state.said, state=AUTH_FAIL, msg=f"{res[1]}") else: - # No need to process state.state == AUTH_EXPIRE or state.state == AUTH_FAIL or state.state == AUTH_REVOKED or state.state == AUTH_SUCCESS: - + # No need to process state.state == CRYPT_INVALID or state.state == AUTH_EXPIRE or state.state == AUTH_FAIL or state.state == AUTH_REVOKED or state.state == AUTH_SUCCESS: continue self.vdb.iss.pin(keys=(aid,), val=cred_state) - def processEcr(self, creder): + def cred_filters(self, creder) -> tuple[bool,str]: """Process a fully verified engagement context role vLEI credential presentation 1. If the LEI filter is configured, ensure the LEI is in the list of acceptable LEIs @@ -149,30 +167,97 @@ def processEcr(self, creder): creder (Creder): Serializable credential object """ - if creder.issuer not in self.hby.kevers: - print(f"unknown issuer {creder.issuer}") - return - - issuee = creder.attrib["i"] - if issuee not in self.hby.kevers: - print(f"unknown issuee {issuee}") - return - - LEI = creder.attrib["LEI"] - # only process LEI filter if LEI list has been configured - if len(self.leis) > 0 and LEI not in self.leis: - print(f"LEI: {LEI} not allowed") - return - - role = creder.attrib["engagementContextRole"] - - if role not in (EBA_DOCUMENT_SUBMITTER_ROLE,): - print(f"{role} in not a valid submitter role") - return - - acct = Account(issuee, creder.said, LEI) - print("Successful authentication, storing user.") - self.vdb.accts.pin(keys=(issuee,), val=acct) + res = False, f"Cred filters not processed" + match creder.schema: + case Schema.ECR_SCHEMA | Schema.ECR_SCHEMA_PROD: + # passed schema check + res = True, f"passed schema check" + case _: + if Schema.schema_names.get(creder.schema): + res = False, f"Can't authorize cred with {Schema.schema_names[creder.schema]} schema" + else: + res = False, f"Can't authorize cred with unknown schema {creder.schema}" + + if res[0]: + if creder.issuer not in self.hby.kevers: + res = False, f"unknown issuer {creder.issuer}" + elif creder.attrib["i"] == None or creder.attrib["i"] not in self.hby.kevers: + print(f"unknown issuee {creder.attrib["i"]}") + elif len(self.leis) > 0 and creder.attrib["LEI"] not in self.leis: + # only process LEI filter if LEI list has been configured + res = False, f"LEI: {creder.attrib["LEI"]} not allowed" + elif creder.attrib["engagementContextRole"] not in (EBA_DOCUMENT_SUBMITTER_ROLE,): + res = False, f"{creder.attrib["engagementContextRole"]} in not a valid submitter role" + elif not (chain := self.chain_complete(creder))[0]: + res = chain + else: + acct = Account(creder.attrib["i"], creder.said, creder.attrib["LEI"]) + res = True, f"Successful authentication, storing user {creder.attrib["i"]} with LEI {creder.attrib["LEI"]}" + self.vdb.accts.pin(keys=(creder.attrib["i"],), val=acct) + print(f"Cred filter status {res[0]}, {res[1]}") + return res + + def chain_complete(self, creder) -> tuple[bool,str]: + cred_type = "NON_VLEI" + chain_success = False + chain_msg = f"Unknown credential schema type {creder.schema} not supported" + + cred_type = Schema.schema_names.get(creder.schema) + match creder.schema: + case Schema.ECR_SCHEMA | Schema.ECR_SCHEMA_PROD: + if creder.edge.get("auth"): + chain_success, chain_msg = self.expect_edge(cred_type, creder.edge["auth"], [Schema.ECR_AUTH_SCHEMA1,Schema.ECR_AUTH_SCHEMA2]) + elif creder.edge.get("le"): + chain_success, chain_msg = self.expect_edge(cred_type, creder.edge["le"], [Schema.LE_SCHEMA1, Schema.LE_SCHEMA2]) + else: + chain_success, chain_msg = (False,f"Unexpected {cred_type} cred edge {creder.edge}") + case Schema.ECR_AUTH_SCHEMA1 | Schema.ECR_AUTH_SCHEMA2: + if creder.edge.get("le"): + chain_success, chain_msg = self.expect_edge(cred_type, creder.edge["le"], [Schema.LE_SCHEMA1, Schema.LE_SCHEMA2]) + else: + chain_success, chain_msg = (False,f"Unexpected {cred_type} cred edge {creder.edge}") + case Schema.LE_SCHEMA1 | Schema.LE_SCHEMA2: + if creder.edge.get("qvi"): + chain_success, chain_msg = self.expect_edge(cred_type, creder.edge["qvi"], [Schema.QVI_SCHEMA1, Schema.QVI_SCHEMA2]) + else: + chain_success, chain_msg = (False,f"Unexpected {cred_type} cred edge {creder.edge}") + case Schema.QVI_SCHEMA1 | Schema.QVI_SCHEMA2: + if creder.edge: + chain_success, chain_msg = (False,f"Unexpected {cred_type} cred edge {creder.edge}") + else: + chain_success, chain_msg = (True, "QVI") + # case Schema.GLEIF_EXTERNAL_SCHEMA: + # cred_type = "GLEIF_EXTERNAL" + # chain_success, chain_msg = self.expect_edge(cred_type, creder.edge, [Schema.GLEIF_INTERNAL_SCHEMA]) + # case Schema.GLEIF_INTERNAL_SCHEMA: + # cred_type = "GLEIF_INTERNAL" + case _: + print(f"{chain_msg}") + + return chain_success, chain_msg + + def expect_edge(self, cred_type: str, edge, chained_schemas: List[str]): + chain_msg = "Expected edge " + chain_success = False + chain = None + if not edge: + chain_msg = f"{cred_type} cred should have an edge" + chain_success = False + elif edge["s"] in chained_schemas: + chain = self.chain_complete(self.reger.creds.get(keys=(edge["n"],))) + chain_success = chain[0] + if not chain_success: + chain_msg = chain[1] + else: + chain_msg = chain[1] + f"->{cred_type}" + else: + chain_success = False + chain_msg = f"{cred_type} should chain to schema {chained_schemas}, not {edge["s"]}" + + if not chain_success: + chain_msg = f"{cred_type} chain validation failed, " + chain_msg + + return chain_success, chain_msg def processRevocations(self): """Loop over database of credential revocations. @@ -236,7 +321,6 @@ def __init__(self, authn): def recur(self, tyme): """Process all escrows once per recurrence.""" self.authn.processEscrows() - return False diff --git a/src/verifier/core/basing.py b/src/verifier/core/basing.py index 51bd713..5839f15 100644 --- a/src/verifier/core/basing.py +++ b/src/verifier/core/basing.py @@ -14,15 +14,33 @@ from keri.db.subing import CesrIoSetSuber from keri.help.helping import nowUTC +import datetime + @dataclass class CredProcessState: said: str = None state: str = None + msg: str = None date: str = nowUTC().isoformat() def __iter__(self): return iter(asdict(self)) +CRYPT_INVALID = "Credential cryptographically invalid" +CRYPT_VALID = "Credential cryptographically valid" +CRED_AGE_OFF = "Credential presentation has aged off" + + +def cred_age_off(state: CredProcessState, timeout: float): + # cancel presentations that have been around longer than timeout + now = nowUTC() + age = now - datetime.datetime.fromisoformat(state.date) + state = None + if state.state != CRED_AGE_OFF and age > datetime.timedelta(seconds=timeout): + state = CredProcessState(said=state.said, state=CRED_AGE_OFF) + return True, state + return False, state + # @dataclass # class CredProcessStates: # states: List[CredProcessState] = [] diff --git a/src/verifier/core/utils.py b/src/verifier/core/utils.py index 1922b53..c8bc530 100644 --- a/src/verifier/core/utils.py +++ b/src/verifier/core/utils.py @@ -1,7 +1,6 @@ from keri import kering from keri.core import MtrDex, coring - class DigerBuilder: @staticmethod def sha256(dig): diff --git a/src/verifier/core/verifying.py b/src/verifier/core/verifying.py index 774fc17..d4cd26e 100644 --- a/src/verifier/core/verifying.py +++ b/src/verifier/core/verifying.py @@ -4,13 +4,10 @@ from keri.core import coring, parsing from keri.vdr import verifying, eventing -from verifier.core.basing import CredProcessState - -CRYPT_INVALID = "Credential cryptographically invalid" -CRYPT_VALID = "Credential cryptographically valid" +from verifier.core.basing import CRYPT_INVALID, CRYPT_VALID, CredProcessState, cred_age_off def setup(app, hby, vdb, reger, local=False): - """ Set up verifying endpoints to process vLEI credential verifications + """Set up verifying endpoints to process vLEI credential verifications Parameters: app (App): Falcon app to register endpoints against @@ -27,7 +24,7 @@ def setup(app, hby, vdb, reger, local=False): def loadEnds(app, hby, vdb, tvy, vry): - """ Load and map endpoints to process vLEI credential verifications + """Load and map endpoints to process vLEI credential verifications Parameters: app (App): Falcon app to register endpoints against @@ -51,7 +48,7 @@ def loadEnds(app, hby, vdb, tvy, vry): class PresentationResourceEndpoint: - """ Credential presentation resource endpoint class + """Credential presentation resource endpoint class This class allows for a PUT to a credential SAID specific endpoint to trigger credential presentation verification. @@ -59,7 +56,7 @@ class PresentationResourceEndpoint: """ def __init__(self, hby, vdb, tvy, vry): - """ Create credential presentation resource endpoint instance + """Create credential presentation resource endpoint instance Parameters: hby (Habery): Database environment for exposed KERI AIDs @@ -74,7 +71,7 @@ def __init__(self, hby, vdb, tvy, vry): self.vry = vry def on_put(self, req, rep, said): - """ Credential Presentation Resource PUT Method + """Credential Presentation Resource PUT Method Parameters: req: falcon.Request HTTP request @@ -108,18 +105,20 @@ def on_put(self, req, rep, said): if req.content_type not in ("application/json+cesr",): rep.status = falcon.HTTP_BAD_REQUEST - rep.data = json.dumps(dict(msg=f"invalid content type={req.content_type} for VC presentation")).encode( - "utf-8") + rep.data = json.dumps( + dict(msg=f"invalid content type={req.content_type} for VC presentation") + ).encode("utf-8") return ims = req.bounded_stream.read() - self.vry.cues.clear() + if len(self.vry.cues) > 0: + rep.status = falcon.HTTP_SERVICE_UNAVAILABLE + rep.data = json.dumps( + dict(msg=f"Verifier is busy processing another VC presentation") + ).encode("utf-8") - parsing.Parser().parse(ims=ims, - kvy=self.hby.kvy, - tvy=self.tvy, - vry=self.vry) + parsing.Parser().parse(ims=ims, kvy=self.hby.kvy, tvy=self.tvy, vry=self.vry) found = False saids = [] @@ -130,40 +129,97 @@ def on_put(self, req, rep, said): if creder.said == said: found = True - if not found: + self.vry.cues.clear() + + if not found: + # TODO provide endpoint to check status of credential by said, to see crypt_invalid? + cred_state = CredProcessState(said=said, state=CRYPT_INVALID) + self.vdb.iss.pin(keys=(said,), val=cred_state) rep.status = falcon.HTTP_BAD_REQUEST - rep.data = json.dumps(dict(msg=f"credential {said} from body of request did not verify")).encode("utf-8") + rep.data = json.dumps( + dict( + msg=f"credential {said} from body of request did not cryptographically verify" + ) + ).encode("utf-8") return - + saider = coring.Saider(qb64=said) - cred_attrs = creder.sad['a'] + cred_attrs = creder.sad["a"] creds = None - aid = "" - if 'i' in cred_attrs: + aid = None + saids = None + type = None + if "i" in cred_attrs: # use issuee AID - aid = cred_attrs['i'] - # clear any previous login, now that a valid credential has been presented - self.vdb.accts.rem(keys=(aid,)) - print(f"{aid} account cleared after successful presentation, validation of new account will begin soon.") - - saids = self.vry.reger.subjs.get(keys=aid,) + aid = cred_attrs["i"] + saids = self.vry.reger.subjs.get( + keys=aid, + ) creds = self.vry.reger.cloneCreds(saids, self.hby.db) + type = "issuee" else: # no issuee AID, use issuer - aid = creder.sad['i'] + aid = creder.sad["i"] + saids = self.vry.reger.issus.get( + keys=aid, + ) creds = self.vry.reger.cloneCreds((saider,), self.hby.db) - - print(f"Credential {said} presented is cryptographically valid.") + type = "issuer" + print(f"{aid} account cleared after successful presentation") + # clear any previous login, now that a valid credential has been presented + self.vdb.accts.rem(keys=(aid,)) + + print(f"Credential {said} presented for {aid} is cryptographically valid.") cred_state = CredProcessState(said=said, state=CRYPT_VALID) self.vdb.iss.pin(keys=(aid,), val=cred_state) + self.vdb.iss.pin(keys=(said,), val=cred_state) rep.status = falcon.HTTP_ACCEPTED - rep.data = json.dumps(dict(creds=json.dumps(creds), msg=f"{said} from {aid} is {cred_state.state} ", - lei=creder.sad['a'].get('LEI'), aid=creder.sad['a'].get('i'))).encode("utf-8") + rep.data = json.dumps( + dict( + creds=json.dumps(creds), + msg=f"{said} for {aid} as {type} is {cred_state.state}", + ) + ).encode("utf-8") return + def on_get(self, req, rep, said): + """Loop over any credential presentations in the iss database. + + Credential presentations are placed in the iss database and this loop processes them, first checking to see + if the credential has been cryptographically verified then applies the EBA specific business logic. + + """ + + state: CredProcessState = self.vdb.iss.get(keys=(said,)) + aged_off, state = cred_age_off(state, 600.0) + if state is None: + rep.status = falcon.HTTP_NO_CONTENT + rep.data = json.dumps( + dict( + msg=f"Cred {said} is not found: {state.state}, msg: {state.msg}", + ) + ).encode("utf-8") + return + elif aged_off: + rep.status = falcon.HTTP_RESET_CONTENT + rep.data = json.dumps( + dict( + msg=f"Cred {said} has aged_off: {state.state}, msg: {state.msg}", + ) + ).encode("utf-8") + return + else: + rep.status = falcon.HTTP_ACCEPTED + rep.data = json.dumps( + dict( + msg=f"Cred {said} state is: {state.state}", + ) + ).encode("utf-8") + return + class AuthorizationResourceEnd: - """ Authroization resource endpoint + """Authroization resource endpoint This resource endpoint class provides a GET method for verifying if an AID has previously presented a valid vLEI ECR credential. @@ -171,7 +227,7 @@ class AuthorizationResourceEnd: """ def __init__(self, hby, vdb): - """ Create authorization resource endpoint + """Create authorization resource endpoint Parameters: hby (Habery): Database environment for exposed KERI AIDs @@ -181,7 +237,7 @@ def __init__(self, hby, vdb): self.vdb = vdb def on_get(self, req, rep, aid): - """ Authorization Resource GET Method + """Authorization Resource GET Method Parameters: req: falcon.Request HTTP request @@ -209,36 +265,40 @@ def on_get(self, req, rep, aid): """ rep.content_type = "application/json" - + acct = self.vdb.accts.get(keys=(aid,)) if aid not in self.hby.kevers: rep.status = falcon.HTTP_UNAUTHORIZED rep.data = json.dumps(dict(msg=f"unknown AID: {aid}")).encode("utf-8") - return - - acct = self.vdb.accts.get(keys=(aid,)) - if (acct) is None: + elif acct is None: rep.status = falcon.HTTP_UNAUTHORIZED state: CredProcessState = self.vdb.iss.get(keys=(aid,)) if state is None: - rep.data = json.dumps(dict(msg=f"identifier {aid} has no access and no authorization being processed")).encode("utf-8") - return + rep.data = json.dumps( + dict( + msg=f"identifier {aid} has no access and no authorization being processed" + ) + ).encode("utf-8") else: - rep.data = json.dumps(dict(msg=f"identifier {aid} presented credentials {state.said}, w/ status {state.state}")).encode("utf-8") - return - - body = dict( - aid=aid, - said=acct.said, - msg=f"AID w/ lei {acct.lei} presented valid credential" - ) - - rep.status = falcon.HTTP_OK - rep.data = json.dumps(body).encode("utf-8") + rep.data = json.dumps( + dict( + msg=f"identifier {aid} presented credentials {state.said}, w/ status {state.state}, msg: {state.msg}" + ) + ).encode("utf-8") + else: + body = dict( + aid=aid, + said=acct.said, + lei=acct.lei, + msg=f"AID {aid} w/ lei {acct.lei} presented valid credential", + ) + + rep.status = falcon.HTTP_OK + rep.data = json.dumps(body).encode("utf-8") return class RequestVerifierResourceEnd: - """ Request Verifier Resource endpoint class + """Request Verifier Resource endpoint class This class provides a POST method endpoint that validating HTTP request signatures for AIDs that have previously presented a valid vLEI credential. @@ -246,7 +306,7 @@ class RequestVerifierResourceEnd: """ def __init__(self, hby, vdb): - """ Create a request verifier resource endpoint class + """Create a request verifier resource endpoint class Parameters: hby (Habery): Database environment for exposed KERI AIDs @@ -257,7 +317,7 @@ def __init__(self, hby, vdb): self.vdb = vdb def on_post(self, req, rep, aid): - """ Request verifier resource POST method + """Request verifier resource POST method Parameters: req: falcon.Request HTTP request @@ -291,7 +351,9 @@ def on_post(self, req, rep, aid): data = req.params.get("data") if data is None: rep.status = falcon.HTTP_BAD_REQUEST - rep.data = json.dumps(dict(msg="request missing data parameter")).encode("utf-8") + rep.data = json.dumps(dict(msg="request missing data parameter")).encode( + "utf-8" + ) return encoded_data = data.encode("utf-8") # signature is based on encoded data @@ -299,18 +361,24 @@ def on_post(self, req, rep, aid): sign = req.params.get("sig") if sign is None: rep.status = falcon.HTTP_BAD_REQUEST - rep.data = json.dumps(dict(msg="request missing sig parameter")).encode("utf-8") + rep.data = json.dumps(dict(msg="request missing sig parameter")).encode( + "utf-8" + ) return if aid not in self.hby.kevers: rep.status = falcon.HTTP_NOT_FOUND - rep.data = json.dumps(dict(msg=f"unknown {aid} used to sign header")).encode("utf-8") + rep.data = json.dumps( + dict(msg=f"unknown {aid} used to sign header") + ).encode("utf-8") return acct = self.vdb.accts.get(keys=(aid,)) if acct is None: rep.status = falcon.HTTP_FORBIDDEN - rep.data = json.dumps(dict(msg=f"identifier {aid} has no valid credential for access")).encode("utf-8") + rep.data = json.dumps( + dict(msg=f"identifier {aid} has no valid credential for access") + ).encode("utf-8") return kever = self.hby.kevers[aid] @@ -319,14 +387,20 @@ def on_post(self, req, rep, aid): cigar = coring.Cigar(qb64=sign) except Exception as ex: rep.status = falcon.HTTP_BAD_REQUEST - rep.data = json.dumps(dict(msg=f"{aid} provided invalid Cigar signature on encoded request data")).encode( - "utf-8") + rep.data = json.dumps( + dict( + msg=f"{aid} provided invalid Cigar signature on encoded request data" + ) + ).encode("utf-8") return if not verfers[0].verify(sig=cigar.raw, ser=encoded_data): rep.status = falcon.HTTP_UNAUTHORIZED rep.data = json.dumps( - dict(msg=f"{aid} signature (Cigar) verification failed on encoding of request data")).encode("utf-8") + dict( + msg=f"{aid} signature (Cigar) verification failed on encoding of request data" + ) + ).encode("utf-8") return rep.status = falcon.HTTP_ACCEPTED @@ -341,5 +415,7 @@ def __init__(self): def on_get(self, req, rep): rep.content_type = "application/json" rep.status = falcon.HTTP_OK - rep.data = json.dumps(dict(msg="vLEI verification service is healthy")).encode("utf-8") + rep.data = json.dumps(dict(msg="vLEI verification service is healthy")).encode( + "utf-8" + ) return diff --git a/tests/common.py b/tests/common.py index 0d1e254..64db15d 100644 --- a/tests/common.py +++ b/tests/common.py @@ -290,12 +290,12 @@ def get_ecr_auth_cred(aid, issuer, recipient, schema, registry, sedge, lei: str) return cred -def get_ecr_auth_edge(lei_dig, lei_schema): +def get_ecr_auth_edge(le_dig, le_schema): sad = dict( d="", le = dict( - n=f"{lei_dig}", - s=f"{lei_schema}", + n=f"{le_dig}", + s=f"{le_schema}", ) ) _, edge = coring.Saider.saidify(sad=sad, label=coring.Saids.d) diff --git a/tests/conftest.py b/tests/conftest.py index e07daf0..5de3887 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -456,7 +456,7 @@ def seedSchema(db): _, sad = coring.Saider.saidify(sad, label=coring.Saids.dollar) schemer = scheming.Schemer(sed=sad) # NEW: EHyKQS68x_oWy8_vNmYubA5Y0Tse4XMPFggMfoPoERaM - assert schemer.said == Schema.LEI_SCHEMA + assert schemer.said == Schema.LE_SCHEMA1 db.schema.pin(schemer.said, schemer) # OLD: EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao @@ -501,7 +501,7 @@ def seedSchema(db): _, sad = coring.Saider.saidify(sad, label=coring.Saids.dollar) schemer = scheming.Schemer(sed=sad) # NEW: EFgnk_c08WmZGgv9_mpldibRuqFMTQN-rAgtD-TCOwbs - assert schemer.said == Schema.QVI_SCHEMA + assert schemer.said == Schema.QVI_SCHEMA1 db.schema.pin(schemer.said, schemer) sad = { @@ -829,7 +829,7 @@ def seedSchema(db): "s": { "description": "SAID of required schema of the credential pointed to by this node", "type": "string", - "const": f"{Schema.ECR_AUTH_SCHEMA}" + "const": f"{Schema.ECR_AUTH_SCHEMA2}" }, "o": { "description": "Operator indicating this node is the issuer", @@ -1174,7 +1174,7 @@ def seedSchema(db): _, sad = coring.Saider.saidify(sad, label=coring.Saids.dollar) schemer = scheming.Schemer(sed=sad) # NEW: EJOkgTilEMjPgrEr0yZDS_MScnI0pBb75tO54lvXugOy - assert schemer.said == Schema.ECR_AUTH_SCHEMA + assert schemer.said == Schema.ECR_AUTH_SCHEMA2 db.schema.pin(schemer.said, schemer) sad = { diff --git a/tests/core/test_authorizing.py b/tests/core/test_authorizing.py new file mode 100644 index 0000000..db82a70 --- /dev/null +++ b/tests/core/test_authorizing.py @@ -0,0 +1,106 @@ +from ..common import * + +import falcon +import falcon.testing + +from keri.app import habbing +from keri.core import coring +from keri.vdr import viring + +import pytest + +from verifier.core import basing, verifying +from verifier.core.authorizing import Authorizer, Schema + +def test_ecr(seeder): + + with habbing.openHab(name="sid", temp=True, salt=b"0123456789abcdef") as (hby, hab): + vdb = basing.VerifierBaser(name=hby.name, temp=True) + + # habbing.openHab(name="wan", temp=True, salt=b'0123456789abcdef', transferable=False) as (wanHby, wanHab)): + seeder.seedSchema(db=hby.db) + regery, registry, verifier, seqner = reg_and_verf( + hby, hab, registryName="qvireg" + ) + qvicred = get_qvi_cred( + issuer=hab.pre, + recipient=hab.pre, + schema=Schema.QVI_SCHEMA1, + registry=registry, + lei=LEI1, + ) + hab, qcrdntler, qsaid, qkmsgs, qtmsgs, qimsgs, qvimsgs = get_cred( + hby, hab, regery, registry, verifier, Schema.QVI_SCHEMA1, qvicred, seqner + ) + + qviedge = get_qvi_edge(qvicred.sad["d"], Schema.QVI_SCHEMA1) + + leicred = get_lei_cred( + issuer=hab.pre, + recipient=hab.pre, + schema=Schema.LE_SCHEMA1, + registry=registry, + sedge=qviedge, + lei=LEI1, + ) + hab, lcrdntler, lsaid, lkmsgs, ltmsgs, limsgs, leimsgs = get_cred( + hby, hab, regery, registry, verifier, Schema.LE_SCHEMA1, leicred, seqner + ) + + # chained ecr auth cred + eaedge = get_ecr_auth_edge(lsaid, Schema.LE_SCHEMA1) + + eacred = get_ecr_auth_cred( + aid=hab.pre, + issuer=hab.pre, + recipient=hab.pre, + schema=Schema.ECR_AUTH_SCHEMA2, + registry=registry, + sedge=eaedge, + lei=LEI1, + ) + hab, eacrdntler, easaid, eakmsgs, eatmsgs, eaimsgs, eamsgs = get_cred( + hby, hab, regery, registry, verifier, Schema.ECR_AUTH_SCHEMA2, eacred, seqner + ) + + # try submitting the ECR auth cred + issAndCred = bytearray() + issAndCred.extend(eamsgs) + acdc = issAndCred.decode("utf-8") + hby.kevers[hab.pre] = hab.kever + auth = Authorizer(hby, vdb, eacrdntler.rgy.reger, [LEI1]) + success, msg = auth.cred_filters(eacred) + assert not success + assert msg == f"Can't authorize cred with ECR_AUTH schema" + + # chained ecr auth cred + ecredge = get_ecr_edge(easaid, Schema.ECR_AUTH_SCHEMA2) + + ecr = get_ecr_cred( + issuer=hab.pre, + recipient=hab.pre, + schema=Schema.ECR_SCHEMA, + registry=registry, + sedge=ecredge, + lei=LEI1, + ) + hab, eccrdntler, ecsaid, eckmsgs, ectmsgs, ecimsgs, ecmsgs = get_cred( + hby, hab, regery, registry, verifier, Schema.ECR_SCHEMA, ecr, seqner + ) + + issAndCred = bytearray() + issAndCred.extend(ecmsgs) + hby.kevers[hab.pre] = hab.kever + auth = Authorizer(hby, vdb, eccrdntler.rgy.reger, [LEI1]) + success, msg = auth.cred_filters(ecr) + assert success + assert msg == 'Successful authentication, storing user EKC8085pwSwzLwUGzh-HrEoFDwZnCJq27bVp5atdMT9o with LEI 254900OPPU84GM83MG36' + + data = '"@method": GET\n"@path": /verify/header\n"signify-resource": EHYfRWfM6RxYbzyodJ6SwYytlmCCW2gw5V-FsoX5BgGx\n"signify-timestamp": 2024-05-01T19:54:53.571000+00:00\n"@signature-params: (@method @path signify-resource signify-timestamp);created=1714593293;keyid=BOieebDzg4uaqZ2zuRAX1sTiCrD3pgGT3HtxqSEAo05b;alg=ed25519"' + raw = data.encode("utf-8") + cig = hab.sign(ser=raw, indexed=False)[0] + assert ( + cig.qb64 + == "0BB1Z2DS3QvIBdZJ1Q7yuZCUG-6YkVXDm7dcGbIFEIsLYEBfFXk8P_Y9FUACTlv5vCHeCet70QzVdR8fu5tLBKkP" + ) + assert hby.kevers[hab.pre].verfers[0].verify(sig=cig.raw, ser=raw) \ No newline at end of file diff --git a/tests/core/test_verifying.py b/tests/core/test_verifying.py index c934813..8b88d30 100644 --- a/tests/core/test_verifying.py +++ b/tests/core/test_verifying.py @@ -104,42 +104,42 @@ def test_ecr(seeder): qvicred = get_qvi_cred( issuer=hab.pre, recipient=hab.pre, - schema=Schema.QVI_SCHEMA, + schema=Schema.QVI_SCHEMA1, registry=registry, lei=LEI1, ) hab, qcrdntler, qsaid, qkmsgs, qtmsgs, qimsgs, qvimsgs = get_cred( - hby, hab, regery, registry, verifier, Schema.QVI_SCHEMA, qvicred, seqner + hby, hab, regery, registry, verifier, Schema.QVI_SCHEMA1, qvicred, seqner ) - qviedge = get_qvi_edge(qvicred.sad["d"], Schema.QVI_SCHEMA) + qviedge = get_qvi_edge(qvicred.sad["d"], Schema.QVI_SCHEMA1) leicred = get_lei_cred( issuer=hab.pre, recipient=hab.pre, - schema=Schema.LEI_SCHEMA, + schema=Schema.LE_SCHEMA1, registry=registry, sedge=qviedge, lei=LEI1, ) hab, lcrdntler, lsaid, lkmsgs, ltmsgs, limsgs, leimsgs = get_cred( - hby, hab, regery, registry, verifier, Schema.LEI_SCHEMA, leicred, seqner + hby, hab, regery, registry, verifier, Schema.LE_SCHEMA1, leicred, seqner ) # chained ecr auth cred - eaedge = get_ecr_auth_edge(lsaid, Schema.LEI_SCHEMA) + eaedge = get_ecr_auth_edge(lsaid, Schema.LE_SCHEMA1) eacred = get_ecr_auth_cred( aid=hab.pre, issuer=hab.pre, recipient=hab.pre, - schema=Schema.ECR_AUTH_SCHEMA, + schema=Schema.ECR_AUTH_SCHEMA2, registry=registry, sedge=eaedge, lei=LEI1, ) hab, eacrdntler, easaid, eakmsgs, eatmsgs, eaimsgs, eamsgs = get_cred( - hby, hab, regery, registry, verifier, Schema.ECR_AUTH_SCHEMA, eacred, seqner + hby, hab, regery, registry, verifier, Schema.ECR_AUTH_SCHEMA2, eacred, seqner ) # try submitting the ECR auth cred @@ -163,7 +163,7 @@ def test_ecr(seeder): assert result.status == falcon.HTTP_401 # chained ecr auth cred - ecredge = get_ecr_edge(easaid, Schema.ECR_AUTH_SCHEMA) + ecredge = get_ecr_edge(easaid, Schema.ECR_AUTH_SCHEMA2) ecr = get_ecr_cred( issuer=hab.pre, @@ -194,6 +194,10 @@ def test_ecr(seeder): result = client.simulate_get(f"/authorizations/{hab.pre}") assert result.status == falcon.HTTP_OK + assert result.json['aid'] == hab.pre + assert result.json['said'] == ecsaid + assert result.json['lei'] == LEI1 + assert result.json['msg'] == f"AID {hab.pre} w/ lei {LEI1} presented valid credential" data = "this is the raw data" raw = data.encode("utf-8") @@ -249,46 +253,46 @@ def test_ecr_missing(seeder): qvicred = get_qvi_cred( issuer=hab.pre, recipient=hab.pre, - schema=Schema.QVI_SCHEMA, + schema=Schema.QVI_SCHEMA1, registry=registry, lei=LEI1, ) # created verifiable credential. hab, qcrdntler, qsaid, qkmsgs, qtmsgs, qimsgs, qvimsgs = get_cred( - hby, hab, regery, registry, verifier, Schema.QVI_SCHEMA, qvicred, seqner + hby, hab, regery, registry, verifier, Schema.QVI_SCHEMA1, qvicred, seqner ) - qviedge = get_qvi_edge(qvicred.sad["d"], Schema.QVI_SCHEMA) + qviedge = get_qvi_edge(qvicred.sad["d"], Schema.QVI_SCHEMA1) leicred = get_lei_cred( issuer=hab.pre, recipient=hab.pre, - schema=Schema.LEI_SCHEMA, + schema=Schema.LE_SCHEMA1, registry=registry, sedge=qviedge, lei=LEI1, ) hab, lcrdntler, lsaid, lkmsgs, ltmsgs, limsgs, leimsgs = get_cred( - hby, hab, regery, registry, verifier, Schema.LEI_SCHEMA, leicred, seqner + hby, hab, regery, registry, verifier, Schema.LE_SCHEMA1, leicred, seqner ) # chained ecr auth cred - eaedge = get_ecr_auth_edge(lsaid, Schema.LEI_SCHEMA) + eaedge = get_ecr_auth_edge(lsaid, Schema.LE_SCHEMA1) eacred = get_ecr_auth_cred( aid=hab.pre, issuer=hab.pre, recipient=hab.pre, - schema=Schema.ECR_AUTH_SCHEMA, + schema=Schema.ECR_AUTH_SCHEMA2, registry=registry, sedge=eaedge, lei=LEI1, ) hab, eacrdntler, easaid, eakmsgs, eatmsgs, eaimsgs, eamsgs = get_cred( - hby, hab, regery, registry, verifier, Schema.ECR_AUTH_SCHEMA, eacred, seqner + hby, hab, regery, registry, verifier, Schema.ECR_AUTH_SCHEMA2, eacred, seqner ) # chained ecr auth cred - # ecredge = get_ecr_edge(easaid,Schema.ECR_AUTH_SCHEMA) + # ecredge = get_ecr_edge(easaid,Schema.ECR_AUTH_SCHEMA2) # ecr = get_ecr_cred(issuer=hab.pre, recipient=hab.pre, schema=Schema.ECR_SCHEMA, registry=registry, sedge=ecredge) # hab, eccrdntler, ecsaid, eckmsgs, ectmsgs, ecimsgs, ecmsgs = get_cred(hby, hab, regery, registry, verifier, Schema.ECR_SCHEMA, ecr, seqner) diff --git a/tests/integration/test_service.py b/tests/integration/test_service.py index 762b37e..0fe0b88 100644 --- a/tests/integration/test_service.py +++ b/tests/integration/test_service.py @@ -22,22 +22,22 @@ def test_service_ecr(seeder): seeder.seedSchema(db=hby.db) regery, registry, verifier, seqner = reg_and_verf(hby, hab, registryName="qvireg") - qvicred = get_qvi_cred(issuer=hab.pre, recipient=hab.pre, schema=Schema.QVI_SCHEMA, registry=registry, lei=LEI1) - hab, qcrdntler, qsaid, qkmsgs, qtmsgs, qimsgs, qvimsgs = get_cred(hby, hab, regery, registry, verifier, Schema.QVI_SCHEMA, qvicred, seqner) + qvicred = get_qvi_cred(issuer=hab.pre, recipient=hab.pre, schema=Schema.QVI_SCHEMA1, registry=registry, lei=LEI1) + hab, qcrdntler, qsaid, qkmsgs, qtmsgs, qimsgs, qvimsgs = get_cred(hby, hab, regery, registry, verifier, Schema.QVI_SCHEMA1, qvicred, seqner) - qviedge = get_qvi_edge(qvicred.sad["d"], Schema.QVI_SCHEMA) + qviedge = get_qvi_edge(qvicred.sad["d"], Schema.QVI_SCHEMA1) - leicred = get_lei_cred(issuer=hab.pre, recipient=hab.pre, schema=Schema.LEI_SCHEMA, registry=registry, sedge=qviedge, lei=LEI1) - hab, lcrdntler, lsaid, lkmsgs, ltmsgs, limsgs, leimsgs = get_cred(hby, hab, regery, registry, verifier, Schema.LEI_SCHEMA, leicred, seqner) + leicred = get_lei_cred(issuer=hab.pre, recipient=hab.pre, schema=Schema.LE_SCHEMA1, registry=registry, sedge=qviedge, lei=LEI1) + hab, lcrdntler, lsaid, lkmsgs, ltmsgs, limsgs, leimsgs = get_cred(hby, hab, regery, registry, verifier, Schema.LE_SCHEMA1, leicred, seqner) #chained ecr auth cred - eaedge = get_ecr_auth_edge(lsaid,Schema.LEI_SCHEMA) + eaedge = get_ecr_auth_edge(lsaid,Schema.LE_SCHEMA1) - eacred = get_ecr_auth_cred(aid=hab.pre, issuer=hab.pre, recipient=hab.pre, schema=Schema.ECR_AUTH_SCHEMA, registry=registry, sedge=eaedge, lei=LEI1) - hab, eacrdntler, easaid, eakmsgs, eatmsgs, eaimsgs, eamsgs = get_cred(hby, hab, regery, registry, verifier, Schema.ECR_AUTH_SCHEMA, eacred, seqner) + eacred = get_ecr_auth_cred(aid=hab.pre, issuer=hab.pre, recipient=hab.pre, schema=Schema.ECR_AUTH_SCHEMA2, registry=registry, sedge=eaedge, lei=LEI1) + hab, eacrdntler, easaid, eakmsgs, eatmsgs, eaimsgs, eamsgs = get_cred(hby, hab, regery, registry, verifier, Schema.ECR_AUTH_SCHEMA2, eacred, seqner) #chained ecr auth cred - ecredge = get_ecr_edge(easaid,Schema.ECR_AUTH_SCHEMA) + ecredge = get_ecr_edge(easaid,Schema.ECR_AUTH_SCHEMA2) ecr = get_ecr_cred(issuer=hab.pre, recipient=hab.pre, schema=Schema.ECR_SCHEMA, registry=registry, sedge=ecredge, lei=LEI1) hab, eccrdntler, ecsaid, eckmsgs, ectmsgs, ecimsgs, ecmsgs = get_cred(hby, hab, regery, registry, verifier, Schema.ECR_SCHEMA, ecr, seqner) @@ -118,22 +118,22 @@ def get(): # # seeder.seedSchema(db=hby.db) # regery, registry, verifier, seqner = reg_and_verf(hby, hab, registryName="qvireg") -# qvicred = get_qvi_cred(issuer=hab.pre, recipient=hab.pre, schema=Schema.QVI_SCHEMA, registry=registry) -# hab, qcrdntler, qsaid, qkmsgs, qtmsgs, qimsgs, qvimsgs = get_cred(hby, hab, regery, registry, verifier, Schema.QVI_SCHEMA, qvicred, seqner) +# qvicred = get_qvi_cred(issuer=hab.pre, recipient=hab.pre, schema=Schema.QVI_SCHEMA1, registry=registry) +# hab, qcrdntler, qsaid, qkmsgs, qtmsgs, qimsgs, qvimsgs = get_cred(hby, hab, regery, registry, verifier, Schema.QVI_SCHEMA1, qvicred, seqner) # -# qviedge = get_qvi_edge(qvicred.sad["d"], Schema.QVI_SCHEMA) +# qviedge = get_qvi_edge(qvicred.sad["d"], Schema.QVI_SCHEMA1) # -# leicred = get_lei_cred(issuer=hab.pre, recipient=hab.pre, schema=Schema.LEI_SCHEMA, registry=registry, sedge=qviedge) -# hab, lcrdntler, lsaid, lkmsgs, ltmsgs, limsgs, leimsgs = get_cred(hby, hab, regery, registry, verifier, Schema.LEI_SCHEMA, leicred, seqner) +# leicred = get_lei_cred(issuer=hab.pre, recipient=hab.pre, schema=Schema.LE_SCHEMA1, registry=registry, sedge=qviedge) +# hab, lcrdntler, lsaid, lkmsgs, ltmsgs, limsgs, leimsgs = get_cred(hby, hab, regery, registry, verifier, Schema.LE_SCHEMA1, leicred, seqner) # # #chained ecr auth cred -# eaedge = get_ecr_auth_edge(lsaid,Schema.LEI_SCHEMA) +# eaedge = get_ecr_auth_edge(lsaid,Schema.LE_SCHEMA1) # -# eacred = get_ecr_auth_cred(aid=hab.pre, issuer=hab.pre, recipient=hab.pre, schema=Schema.ECR_AUTH_SCHEMA, registry=registry, sedge=eaedge) -# hab, eacrdntler, easaid, eakmsgs, eatmsgs, eaimsgs, eamsgs = get_cred(hby, hab, regery, registry, verifier, Schema.ECR_AUTH_SCHEMA, eacred, seqner) +# eacred = get_ecr_auth_cred(aid=hab.pre, issuer=hab.pre, recipient=hab.pre, schema=Schema.ECR_AUTH_SCHEMA2, registry=registry, sedge=eaedge) +# hab, eacrdntler, easaid, eakmsgs, eatmsgs, eaimsgs, eamsgs = get_cred(hby, hab, regery, registry, verifier, Schema.ECR_AUTH_SCHEMA2, eacred, seqner) # # #chained ecr auth cred -# ecredge = get_ecr_edge(easaid,Schema.ECR_AUTH_SCHEMA) +# ecredge = get_ecr_edge(easaid,Schema.ECR_AUTH_SCHEMA2) # # ecr = get_ecr_cred(issuer=hab.pre, recipient=hab.pre, schema=Schema.ECR_SCHEMA, registry=registry, sedge=ecredge) # hab, eccrdntler, ecsaid, eckmsgs, ectmsgs, ecimsgs, ecmsgs = get_cred(hby, hab, regery, registry, verifier, Schema.ECR_SCHEMA, ecr, seqner)