Skip to content

Commit

Permalink
Add presentation get (#65)
Browse files Browse the repository at this point in the history
* updated authorization status and message tracking

Signed-off-by: 2byrds <[email protected]>

* added auth cred filter test

Signed-off-by: 2byrds <[email protected]>

* integration tested setting authorization result

Signed-off-by: 2byrds <[email protected]>

* validating schema chains and updated additional schemas

Signed-off-by: 2byrds <[email protected]>

---------

Signed-off-by: 2byrds <[email protected]>
  • Loading branch information
2byrds authored Oct 22, 2024
1 parent e4fe809 commit e9cf7da
Show file tree
Hide file tree
Showing 9 changed files with 435 additions and 148 deletions.
162 changes: 123 additions & 39 deletions src/verifier/core/authorizing.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
EXN Message handling
"""
import datetime
from typing import List
from hio.base import doing

from keri import kering
Expand All @@ -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):
Expand Down Expand Up @@ -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}"
Expand All @@ -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
Expand All @@ -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.
Expand Down Expand Up @@ -236,7 +321,6 @@ def __init__(self, authn):
def recur(self, tyme):
"""Process all escrows once per recurrence."""
self.authn.processEscrows()

return False


Expand Down
18 changes: 18 additions & 0 deletions src/verifier/core/basing.py
Original file line number Diff line number Diff line change
Expand Up @@ -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] = []
Expand Down
1 change: 0 additions & 1 deletion src/verifier/core/utils.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from keri import kering
from keri.core import MtrDex, coring


class DigerBuilder:
@staticmethod
def sha256(dig):
Expand Down
Loading

0 comments on commit e9cf7da

Please sign in to comment.