From 67e5a3260d68dbbb40754c5ab59dd016fe06c2b2 Mon Sep 17 00:00:00 2001 From: Razco Date: Tue, 6 Aug 2024 15:55:39 +0300 Subject: [PATCH 1/3] add allowed ngnix support --- horizon/enforcer/api.py | 102 ++++++++++++++++++++++++ horizon/enforcer/utils/headers_utils.py | 6 ++ 2 files changed, 108 insertions(+) create mode 100644 horizon/enforcer/utils/headers_utils.py diff --git a/horizon/enforcer/api.py b/horizon/enforcer/api.py index 5d198c89..4d3e6485 100644 --- a/horizon/enforcer/api.py +++ b/horizon/enforcer/api.py @@ -35,6 +35,7 @@ UserTenantsResult, AuthorizedUsersResult, AuthorizedUsersAuthorizationQuery, + User, ) from horizon.enforcer.schemas_kong import ( KongAuthorizationInput, @@ -43,6 +44,7 @@ KongWrappedAuthorizationQuery, ) from horizon.enforcer.schemas_v1 import AuthorizationQueryV1 +from horizon.enforcer.utils.headers_utils import get_case_insensitive from horizon.enforcer.utils.mapping_rules_utils import MappingRulesUtils from horizon.enforcer.utils.statistics_utils import StatisticsManager from horizon.state import PersistentStateHandler @@ -553,6 +555,106 @@ async def is_allowed( ) return result + @router.post( + "/allowed", + response_model=AuthorizationResult, + status_code=status.HTTP_200_OK, + response_model_exclude_none=True, + dependencies=[Depends(enforce_pdp_token)], + ) + async def is_allowed( + request: Request, + query: Union[AuthorizationQuery, AuthorizationQueryV1], + x_permit_sdk_language: Optional[str] = Depends(notify_seen_sdk), + ): + if isinstance(query, AuthorizationQueryV1): + raise HTTPException( + status_code=status.HTTP_421_MISDIRECTED_REQUEST, + detail="Mismatch between client version and PDP version," + " required v2 request body, got v1. " + "hint: try to update your client version to v2", + ) + query = cast(AuthorizationQuery, query) + + response = await _is_allowed(query, request, MAIN_POLICY_PACKAGE) + log_query_result(query, response) + try: + raw_result = json.loads(response.body).get("result", {}) + processed_query = ( + get_v1_processed_query(raw_result) + or get_v2_processed_query(raw_result) + or {} + ) + result = { + "allow": raw_result.get("allow", False), + "result": raw_result.get( + "allow", False + ), # fallback for older sdks (TODO: remove) + "query": processed_query, + "debug": raw_result.get("debug", {}), + } + except: + result = dict(allow=False, result=False) + logger.warning( + "is allowed (fallback response)", reason="cannot decode opa response" + ) + return result + + @router.post( + "/nginx_allowed", + response_model=AuthorizationResult, + status_code=status.HTTP_200_OK, + response_model_exclude_none=True, + dependencies=[Depends(enforce_pdp_token)], + ) + async def is_allowed_nginx( + request: Request, + ): + user_key = get_case_insensitive(request.headers, "permit-user-key") + tenant_id = get_case_insensitive(request.headers, "permit-tenant-id") + action = get_case_insensitive(request.headers, "permit-action") + resource_type = get_case_insensitive(request.headers, "permit-resource-type") + + if ( + user_key is None + or tenant_id is None + or action is None + or resource_type is None + ): + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="Missing required headers: 'Permit-User-Key', 'Permit-Tenant-Id', 'Permit-Action', 'Permit-Resource-Type'", + ) + query = AuthorizationQuery( + user=User(key=user_key), + action=action, + resource=Resource(type=resource_type, tenant=tenant_id), + ) + + response = await _is_allowed(query, request, MAIN_POLICY_PACKAGE) + log_query_result(query, response) + try: + raw_result = json.loads(response.body).get("result", {}) + processed_query = ( + get_v1_processed_query(raw_result) + or get_v2_processed_query(raw_result) + or {} + ) + result = { + "allow": raw_result.get("allow", False), + "result": raw_result.get( + "allow", False + ), # fallback for older sdks (TODO: remove) + "query": processed_query, + "debug": raw_result.get("debug", {}), + } + except: + result = dict(allow=False, result=False) + logger.warning( + "is allowed (fallback response)", reason="cannot decode opa response" + ) + return result + @router.post( "/kong", response_model=KongAuthorizationResult, diff --git a/horizon/enforcer/utils/headers_utils.py b/horizon/enforcer/utils/headers_utils.py new file mode 100644 index 00000000..e304e648 --- /dev/null +++ b/horizon/enforcer/utils/headers_utils.py @@ -0,0 +1,6 @@ +def get_case_insensitive(dictionary, key) -> str | None: + if isinstance(key, str): + return next( + (dictionary[k] for k in dictionary if k.lower() == key.lower()), None + ) + return dictionary.get(key, None) From dfe780c40bda56cd9a61582083f87f37c21dafa8 Mon Sep 17 00:00:00 2001 From: Razco Date: Wed, 7 Aug 2024 11:12:05 +0300 Subject: [PATCH 2/3] remove redundant allowed --- horizon/enforcer/api.py | 45 ----------------------------------------- 1 file changed, 45 deletions(-) diff --git a/horizon/enforcer/api.py b/horizon/enforcer/api.py index 4d3e6485..cd455a81 100644 --- a/horizon/enforcer/api.py +++ b/horizon/enforcer/api.py @@ -555,51 +555,6 @@ async def is_allowed( ) return result - @router.post( - "/allowed", - response_model=AuthorizationResult, - status_code=status.HTTP_200_OK, - response_model_exclude_none=True, - dependencies=[Depends(enforce_pdp_token)], - ) - async def is_allowed( - request: Request, - query: Union[AuthorizationQuery, AuthorizationQueryV1], - x_permit_sdk_language: Optional[str] = Depends(notify_seen_sdk), - ): - if isinstance(query, AuthorizationQueryV1): - raise HTTPException( - status_code=status.HTTP_421_MISDIRECTED_REQUEST, - detail="Mismatch between client version and PDP version," - " required v2 request body, got v1. " - "hint: try to update your client version to v2", - ) - query = cast(AuthorizationQuery, query) - - response = await _is_allowed(query, request, MAIN_POLICY_PACKAGE) - log_query_result(query, response) - try: - raw_result = json.loads(response.body).get("result", {}) - processed_query = ( - get_v1_processed_query(raw_result) - or get_v2_processed_query(raw_result) - or {} - ) - result = { - "allow": raw_result.get("allow", False), - "result": raw_result.get( - "allow", False - ), # fallback for older sdks (TODO: remove) - "query": processed_query, - "debug": raw_result.get("debug", {}), - } - except: - result = dict(allow=False, result=False) - logger.warning( - "is allowed (fallback response)", reason="cannot decode opa response" - ) - return result - @router.post( "/nginx_allowed", response_model=AuthorizationResult, From 401128ecd6083d85a3d00907bc6e2dccd26f3fed Mon Sep 17 00:00:00 2001 From: Razco Date: Wed, 7 Aug 2024 11:18:29 +0300 Subject: [PATCH 3/3] move headers to fastapi reoute --- horizon/enforcer/api.py | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/horizon/enforcer/api.py b/horizon/enforcer/api.py index cd455a81..24b1dc8e 100644 --- a/horizon/enforcer/api.py +++ b/horizon/enforcer/api.py @@ -564,26 +564,16 @@ async def is_allowed( ) async def is_allowed_nginx( request: Request, + permit_user_key: str = Header(None), + permit_tenant_id: str = Header(None), + permit_action: str = Header(None), + permit_resource_type: str = Header(None), ): - user_key = get_case_insensitive(request.headers, "permit-user-key") - tenant_id = get_case_insensitive(request.headers, "permit-tenant-id") - action = get_case_insensitive(request.headers, "permit-action") - resource_type = get_case_insensitive(request.headers, "permit-resource-type") - - if ( - user_key is None - or tenant_id is None - or action is None - or resource_type is None - ): - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail="Missing required headers: 'Permit-User-Key', 'Permit-Tenant-Id', 'Permit-Action', 'Permit-Resource-Type'", - ) + query = AuthorizationQuery( - user=User(key=user_key), - action=action, - resource=Resource(type=resource_type, tenant=tenant_id), + user=User(key=permit_user_key), + action=permit_action, + resource=Resource(type=permit_resource_type, tenant=permit_tenant_id), ) response = await _is_allowed(query, request, MAIN_POLICY_PACKAGE)