"""Logic for checking auth0 application access for users.""" from typing import Callable from users import constants from users.models import ows_pdp class AppAccessCheckError(Exception): """Custom exception for application access check errors.""" def __init__(self, status_code: int, message: str): super().__init__(message) self.status_code = status_code def app_access_functions() -> dict[str, Callable]: """Return a mapping of application names to their access check functions.""" return { 'seat': check_seat_application_access, } def check_application_access(identity_id: str, app_name: str) -> bool: """Check if a user has access to a specific application.""" functions = app_access_functions() if app_name not in functions: raise AppAccessCheckError(status_code=422, message='Invalid application name') # If caller isn't authorized to get tenant roles, this will raise an error caught in handler tenant_roles = ows_pdp.get_tenant_roles_by_identity(identity_id) return functions[app_name](tenant_roles) def check_seat_application_access(tenant_roles: dict[str, dict[str, list[dict]]]) -> bool: """Check if the user has a seat role for both parent companies. Tenant roles are expected to be in the format: { "": { "tenant_type": "parent_company", "tenant_uuid": "", "roles": [ {"role": "seat_can_administer_users"}, ... ] }, ... } """ sme_roles = tenant_roles.get(constants.SME_PARENT_COMPANY_UUID, {}) if constants.SEAT_ROLE not in [roles_obj['role'] for roles_obj in sme_roles.get('roles', [])]: return False orchard_roles = tenant_roles.get(constants.ORCHARD_PARENT_COMPANY_UUID, {}) if constants.SEAT_ROLE not in [ roles_obj['role'] for roles_obj in orchard_roles.get('roles', []) ]: return False return True