Skip to content

Commit

Permalink
feat: storage fork-safe and dpop validation err handling
Browse files Browse the repository at this point in the history
  • Loading branch information
peppelinux committed Aug 22, 2023
1 parent 76f2603 commit 37ea605
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 7 deletions.
5 changes: 4 additions & 1 deletion pyeudiw/oauth2/dpop/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,10 @@ def __init__(
self.proof = http_header_dpop

@property
def is_valid(self):
def is_valid(self) -> bool:
return self.validate()

def validate(self) -> bool:
jws_verifier = JWSHelper(self.public_jwk)
try:
dpop_valid = jws_verifier.verify(self.proof)
Expand Down
36 changes: 31 additions & 5 deletions pyeudiw/satosa/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,11 @@ def __init__(self, auth_callback_func, internal_attributes, config, base_url, na

# HTML template loader
self.template = Jinja2TemplateHandler(self.config["ui"])

self.db_engine = DBEngine(self.config["storage"])

# we close the connection in this constructor to be sure to be fork safe
self._db_engine = self.db_engine
self.update_trust_anchors()
self._db_engine.close()

# it will be filled by .register_endpoints
self.absolute_redirect_url = None
Expand All @@ -110,6 +112,13 @@ def __init__(self, auth_callback_func, internal_attributes, config, base_url, na
)
)

@property
def db_engine(self):
if not getattr(self, '_db_engine', None) or not self._db_engine.is_connected:
self._db_engine = DBEngine(self.config["storage"])

return self._db_engine

def _render_metadata_conf_elements(self) -> None:
for k,v in self.config['metadata'].items():
if isinstance(v, (int, float, dict, list)): continue
Expand Down Expand Up @@ -145,7 +154,12 @@ def update_trust_anchors(self):
message=f"{ta} update failed: {e}"
)
)
# TODO: print all the updated TA taken from the storage
logger.info(
lu.LOG_FMT.format(
id="Trust Anchor update",
message=f"Trust Anchor updated: {ta}"
)
)

@property
def federation_jwk(self):
Expand Down Expand Up @@ -654,15 +668,27 @@ def _request_endpoint_dpop(self, context, *args) -> Union[JsonResponse, None]:
err_code="400"
)

if not dpop.is_valid:
_msg = "DPoP validation error"
dpop_valid = None
try:
dpop_valid = dpop.validate()
except Exception as e:
_msg = "DPoP validation exception"
return self.handle_error(
context = context,
# TODO: invalid param is not a OAuth2 standard error
message = "invalid_param",
troubleshoot = _msg,
err_code="400"
)

if not dpop_valid:
return self.handle_error(
context = context,
# TODO: invalid param is not a OAuth2 standard error
message = "invalid_param",
troubleshoot = "DPoP validation error",
err_code="400"
)

# TODO: assert and configure the wallet capabilities
# TODO: assert and configure the wallet Attested Security Context
Expand Down
25 changes: 25 additions & 0 deletions pyeudiw/storage/db_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,31 @@ def init_session(self, session_id: str, state: str) -> str:
raise e

return document_id

@property
def is_connected(self):
_connected = False
for db_name, storage in self.storages:
try:
_connected = storage.is_connected
except Exception as e:
logger.critical(
f"Error while checking db engine connection on {db_name}. "
f"{e.__class__.__name__}: {e}"
)
# raise e
return _connected

def close(self):
for db_name, storage in self.storages:
try:
storage.close()
except Exception as e:
logger.critical(
f"Error while closing db engine {db_name}. "
f"{e.__class__.__name__}: {e}"
)
raise e

def write(self, method: str, *args, **kwargs):
replica_count = 0
Expand Down
13 changes: 12 additions & 1 deletion pyeudiw/storage/mongo_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,16 @@ def __init__(self, conf: dict, url: str, connection_params: dict = {}) -> None:

self.client = None
self.db = None

@property
def is_connected(self):
if not self.client:
return False

return self.client and self.client.server_info()

def _connect(self):
if not self.client or not self.client.server_info():
if not self.is_connected:
self.client = pymongo.MongoClient(
self.url, **self.connection_params
)
Expand All @@ -37,6 +44,10 @@ def _connect(self):
self.db, self.storage_conf["db_trust_anchors_collection"]
)

def close(self):
self._connect()
self.client.close()

def get_by_id(self, document_id: str) -> dict:
self._connect()

Expand Down

0 comments on commit 37ea605

Please sign in to comment.