"""Utility Functions for Handlers.""" from functools import wraps from flask import request from owsresponse import response from owsresponse.adaptors.flask import flaskify from account.constants import error, header, tenants from account.logic.resource_getters.attributes import Tenant from account.models import ows_permissions, subaccount from account.utils.exception import ExceptionDictOwsResponse def check_subaccount_ownership(subaccount_id, account_type, account_id): """Check whether given subccount_id is owned by given account_id. Args: subaccount_id (int): the id of the subaccount to check account_type (str): type of account (vendor or subaccount) account_id (int): unique identifier of the vendor. Returns: response.Response: status code 200 if owner, 403 if not owner """ if account_type == header.GRASS_ACCOUNT_TYPE_VENDOR: result = subaccount.get_subaccount_by_vendor_id(subaccount_id, account_id) if result: return response.Response(True) return response.create_error_response( code=error.ERROR_CODE_AUTHORIZATION, message='No authorization', status=403 ) def check_admin_access(tenant_type: tenants.TenantType, tenant_uuid_key: str): def decorator(func): @wraps(func) def wrapper(*args, **kwargs): tenant_uuid = kwargs.get(tenant_uuid_key) if not tenant_uuid: return response.create_error_response( code=error.ERROR_CODE_INVALID_INPUT, message=error.ERROR_MESSAGE_MISSING_VENDOR_ID, ) try: result = ows_permissions.check_tenant_admin_access( [Tenant(tenant_uuid=tenant_uuid, tenant_type=tenant_type, tenant_hierarchy=[])] ) # If the result is a list, check if the tenant is accessible. accessible_tenant = next( filter( lambda tenant: tenant.get('tenant_uuid') == tenant_uuid and tenant.get('access') is True, result, ), None, ) if accessible_tenant: return func(*args, **kwargs) # if not accessible, return a 403 return flaskify( response.create_error_response( code=error.ERROR_CODE_AUTHORIZATION, message=error.ERROR_MESSAGE_UNAUTHORIZED_TENANT_ACCESS, status=403, ) ) # catch exceptions from ows_permissions.check_tenant_admin_access except ExceptionDictOwsResponse as err: return flaskify( response.create_error_response( code=err.code, message=err.message, status=err.status, ) ) return wrapper return decorator TENANT_TYPE_TO_PAYLOAD_KEY = { tenants.TenantType.ACCOUNT: 'vendor_uuids', tenants.TenantType.SUBACCOUNT: 'subaccount_uuids', } def check_bulk_admin_access(tenant_type: tenants.TenantType): def decorator(func): @wraps(func) def wrapper(*args, **kwargs): # Get the JSON payload (assumes it's already validated by marshmallow schema) json_data = request.get_json() tenant_uuids = json_data.get(TENANT_TYPE_TO_PAYLOAD_KEY[tenant_type], []) # Create tenant objects for bulk permission check tenants_to_check = [ Tenant(tenant_uuid=tenant_uuid, tenant_type=tenant_type, tenant_hierarchy=[]) for tenant_uuid in tenant_uuids ] try: result = ows_permissions.check_tenant_admin_access(tenants_to_check) # Check if ALL tenants have access=True if not all(tenant.get('access') is True for tenant in result): return flaskify( response.create_error_response( code=error.ERROR_CODE_AUTHORIZATION, message=error.ERROR_MESSAGE_UNAUTHORIZED_BULK_TENANT_ACCESS, status=403, ) ) # All tenants are accessible, proceed with the function return func(*args, **kwargs) except ExceptionDictOwsResponse as err: return flaskify( response.create_error_response( code=err.code, message=err.message, status=err.status, ) ) return wrapper return decorator